Repository: jgm/pandoc Branch: main Commit: f1e061478dbe Files: 2729 Total size: 12.6 MB Directory structure: gitextract_gv6b7vjt/ ├── .cirrus.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .hlint.yaml ├── .mailmap ├── .stylish-haskell.yaml ├── AUTHORS.md ├── BUGS ├── CITATION.cff ├── CONTRIBUTING.md ├── COPYING.md ├── COPYRIGHT ├── INSTALL.md ├── MANUAL.txt ├── Makefile ├── README.md ├── README.template ├── RELEASE-CHECKLIST-TEMPLATE.org ├── SECURITY.md ├── benchmark/ │ └── benchmark-pandoc.hs ├── cabal.project ├── changelog.md ├── citeproc/ │ └── biblatex-localization/ │ ├── .strings │ ├── UKenglish.lbx.strings │ ├── USenglish.lbx.strings │ ├── american.lbx.strings │ ├── australian.lbx.strings │ ├── austrian.lbx.strings │ ├── brazil.lbx.strings │ ├── brazilian.lbx.strings │ ├── british.lbx.strings │ ├── bulgarian.lbx.strings │ ├── canadian.lbx.strings │ ├── catalan.lbx.strings │ ├── croatian.lbx.strings │ ├── czech.lbx.strings │ ├── danish.lbx.strings │ ├── dutch.lbx.strings │ ├── english.lbx.strings │ ├── estonian.lbx.strings │ ├── finnish.lbx.strings │ ├── french.lbx.strings │ ├── galician.lbx.strings │ ├── german.lbx.strings │ ├── greek.lbx.strings │ ├── hungarian.lbx.strings │ ├── icelandic.lbx.strings │ ├── italian.lbx.strings │ ├── latvian.lbx.strings │ ├── lithuanian.lbx.strings │ ├── magyar.lbx.strings │ ├── naustrian.lbx.strings │ ├── newzealand.lbx.strings │ ├── ngerman.lbx.strings │ ├── norsk.lbx.strings │ ├── nswissgerman.lbx.strings │ ├── nynorsk.lbx.strings │ ├── polish.lbx.strings │ ├── portuges.lbx.strings │ ├── portuguese.lbx.strings │ ├── russian.lbx.strings │ ├── serbian.lbx.strings │ ├── serbianc.lbx.strings │ ├── slovak.lbx.strings │ ├── slovene.lbx.strings │ ├── slovenian.lbx.strings │ ├── spanish.lbx.strings │ ├── swedish.lbx.strings │ ├── swissgerman.lbx.strings │ ├── turkish.lbx.strings │ └── ukrainian.lbx.strings ├── data/ │ ├── abbreviations │ ├── bash_completion.tpl │ ├── creole.lua │ ├── default.csl │ ├── docbook-entities.txt │ ├── docx/ │ │ ├── [Content_Types].xml │ │ ├── _rels/ │ │ │ └── .rels │ │ ├── docProps/ │ │ │ ├── app.xml │ │ │ ├── core.xml │ │ │ └── custom.xml │ │ └── word/ │ │ ├── _rels/ │ │ │ ├── document.xml.rels │ │ │ └── footnotes.xml.rels │ │ ├── comments.xml │ │ ├── document.xml │ │ ├── fontTable.xml │ │ ├── footnotes.xml │ │ ├── numbering.xml │ │ ├── settings.xml │ │ ├── styles.xml │ │ ├── theme/ │ │ │ └── theme1.xml │ │ └── webSettings.xml │ ├── dzslides/ │ │ └── template.html │ ├── epub.css │ ├── init.lua │ ├── odt/ │ │ ├── META-INF/ │ │ │ └── manifest.xml │ │ ├── content.xml │ │ ├── manifest.rdf │ │ ├── meta.xml │ │ ├── mimetype │ │ └── styles.xml │ ├── pptx/ │ │ ├── [Content_Types].xml │ │ ├── _rels/ │ │ │ └── .rels │ │ ├── docProps/ │ │ │ ├── app.xml │ │ │ └── core.xml │ │ └── ppt/ │ │ ├── _rels/ │ │ │ └── presentation.xml.rels │ │ ├── notesMasters/ │ │ │ ├── _rels/ │ │ │ │ └── notesMaster1.xml.rels │ │ │ └── notesMaster1.xml │ │ ├── notesSlides/ │ │ │ ├── _rels/ │ │ │ │ ├── notesSlide1.xml.rels │ │ │ │ └── notesSlide2.xml.rels │ │ │ ├── notesSlide1.xml │ │ │ └── notesSlide2.xml │ │ ├── presProps.xml │ │ ├── presentation.xml │ │ ├── slideLayouts/ │ │ │ ├── _rels/ │ │ │ │ ├── slideLayout1.xml.rels │ │ │ │ ├── slideLayout10.xml.rels │ │ │ │ ├── slideLayout11.xml.rels │ │ │ │ ├── slideLayout2.xml.rels │ │ │ │ ├── slideLayout3.xml.rels │ │ │ │ ├── slideLayout4.xml.rels │ │ │ │ ├── slideLayout5.xml.rels │ │ │ │ ├── slideLayout6.xml.rels │ │ │ │ ├── slideLayout7.xml.rels │ │ │ │ ├── slideLayout8.xml.rels │ │ │ │ └── slideLayout9.xml.rels │ │ │ ├── slideLayout1.xml │ │ │ ├── slideLayout10.xml │ │ │ ├── slideLayout11.xml │ │ │ ├── slideLayout2.xml │ │ │ ├── slideLayout3.xml │ │ │ ├── slideLayout4.xml │ │ │ ├── slideLayout5.xml │ │ │ ├── slideLayout6.xml │ │ │ ├── slideLayout7.xml │ │ │ ├── slideLayout8.xml │ │ │ └── slideLayout9.xml │ │ ├── slideMasters/ │ │ │ ├── _rels/ │ │ │ │ └── slideMaster1.xml.rels │ │ │ └── slideMaster1.xml │ │ ├── slides/ │ │ │ ├── _rels/ │ │ │ │ ├── slide1.xml.rels │ │ │ │ ├── slide2.xml.rels │ │ │ │ ├── slide3.xml.rels │ │ │ │ └── slide4.xml.rels │ │ │ ├── slide1.xml │ │ │ ├── slide2.xml │ │ │ ├── slide3.xml │ │ │ └── slide4.xml │ │ ├── tableStyles.xml │ │ ├── theme/ │ │ │ ├── theme1.xml │ │ │ └── theme2.xml │ │ └── viewProps.xml │ ├── templates/ │ │ ├── affiliations.jats │ │ ├── after-header-includes.latex │ │ ├── article.jats_publishing │ │ ├── common.latex │ │ ├── default.ansi │ │ ├── default.asciidoc │ │ ├── default.bbcode │ │ ├── default.beamer │ │ ├── default.biblatex │ │ ├── default.bibtex │ │ ├── default.chunkedhtml │ │ ├── default.commonmark │ │ ├── default.context │ │ ├── default.djot │ │ ├── default.docbook4 │ │ ├── default.docbook5 │ │ ├── default.dokuwiki │ │ ├── default.dzslides │ │ ├── default.epub2 │ │ ├── default.epub3 │ │ ├── default.haddock │ │ ├── default.html4 │ │ ├── default.html5 │ │ ├── default.icml │ │ ├── default.jats_archiving │ │ ├── default.jats_articleauthoring │ │ ├── default.jats_publishing │ │ ├── default.jira │ │ ├── default.latex │ │ ├── default.man │ │ ├── default.markdown │ │ ├── default.markua │ │ ├── default.mediawiki │ │ ├── default.ms │ │ ├── default.muse │ │ ├── default.opendocument │ │ ├── default.openxml │ │ ├── default.opml │ │ ├── default.org │ │ ├── default.plain │ │ ├── default.revealjs │ │ ├── default.rst │ │ ├── default.rtf │ │ ├── default.s5 │ │ ├── default.slideous │ │ ├── default.slidy │ │ ├── default.tei │ │ ├── default.texinfo │ │ ├── default.textile │ │ ├── default.typst │ │ ├── default.vimdoc │ │ ├── default.xwiki │ │ ├── default.zimwiki │ │ ├── document-metadata.latex │ │ ├── font-settings.latex │ │ ├── fonts.latex │ │ ├── hypersetup.latex │ │ ├── passoptions.latex │ │ ├── styles.citations.html │ │ ├── styles.html │ │ └── template.typst │ └── translations/ │ ├── af.yaml │ ├── alt.yaml │ ├── am.yaml │ ├── ar.yaml │ ├── as.yaml │ ├── ast.yaml │ ├── az.yaml │ ├── be.yaml │ ├── bg.yaml │ ├── bn.yaml │ ├── bo.yaml │ ├── br.yaml │ ├── bs.yaml │ ├── bua.yaml │ ├── ca.yaml │ ├── ckb-Arab.yaml │ ├── ckb-Latn.yaml │ ├── cs.yaml │ ├── cu.yaml │ ├── cy.yaml │ ├── cz.yaml │ ├── da.yaml │ ├── de.yaml │ ├── dsb.yaml │ ├── el.yaml │ ├── en.yaml │ ├── eo.yaml │ ├── es-ES.yaml │ ├── es-MX.yaml │ ├── es.yaml │ ├── et.yaml │ ├── eu.yaml │ ├── fa.yaml │ ├── fi.yaml │ ├── fil.yaml │ ├── fr.yaml │ ├── fur.yaml │ ├── ga.yaml │ ├── gd.yaml │ ├── gl.yaml │ ├── grc.yaml │ ├── gu.yaml │ ├── ha.yaml │ ├── he.yaml │ ├── hi.yaml │ ├── hr.yaml │ ├── hsb.yaml │ ├── hu.yaml │ ├── hy.yaml │ ├── ia.yaml │ ├── id.yaml │ ├── is.yaml │ ├── it.yaml │ ├── ja.yaml │ ├── ka.yaml │ ├── km.yaml │ ├── kmr-Arab.yaml │ ├── kmr-Latn.yaml │ ├── kn.yaml │ ├── ko.yaml │ ├── la.yaml │ ├── lb.yaml │ ├── lo.yaml │ ├── lt.yaml │ ├── lv.yaml │ ├── mk.yaml │ ├── ml.yaml │ ├── mn.yaml │ ├── mr.yaml │ ├── ms.yaml │ ├── nb.yaml │ ├── nko.yaml │ ├── nl.yaml │ ├── nn.yaml │ ├── oc.yaml │ ├── or.yaml │ ├── pa.yaml │ ├── pl.yaml │ ├── pms.yaml │ ├── pt-BR.yaml │ ├── pt-PT.yaml │ ├── pt.yaml │ ├── rm.yaml │ ├── ro.yaml │ ├── ru.yaml │ ├── se.yaml │ ├── si.yaml │ ├── sk.yaml │ ├── sl.yaml │ ├── sq.yaml │ ├── sr-Cyrl.yaml │ ├── sr-Latn.yaml │ ├── sr.yaml │ ├── sv.yaml │ ├── ta.yaml │ ├── te.yaml │ ├── th.yaml │ ├── tk.yaml │ ├── tr.yaml │ ├── ua.yaml │ ├── ug.yaml │ ├── uk.yaml │ ├── ur.yaml │ ├── vi.yaml │ ├── zh-Hans.yaml │ └── zh-Hant.yaml ├── doc/ │ ├── custom-readers.md │ ├── custom-writers.md │ ├── customizing-pandoc.md │ ├── epub.md │ ├── extras.md │ ├── faqs.md │ ├── filters.md │ ├── getting-started.md │ ├── jats.md │ ├── libraries.md │ ├── lua-filters.md │ ├── nix.md │ ├── org.md │ ├── pandoc-lua.md │ ├── pandoc-server.md │ ├── press.md │ ├── short-guide-to-pandocs-sources.md │ ├── typst-property-output.md │ ├── using-the-pandoc-api.md │ └── xml.md ├── flake.lock ├── flake.nix ├── hie.yaml ├── linux/ │ ├── control.in │ └── make_artifacts.sh ├── macos/ │ ├── Makefile │ ├── distribution.xml.in │ ├── make_macos_release.sh │ └── uninstall-pandoc.pl ├── man/ │ ├── manfilter.lua │ ├── pandoc.1.after │ └── pandoc.1.before ├── pandoc-cli/ │ ├── README.md │ ├── lua/ │ │ └── PandocCLI/ │ │ └── Lua.hs │ ├── man/ │ │ ├── pandoc-lua.1 │ │ ├── pandoc-server.1 │ │ └── pandoc.1 │ ├── no-lua/ │ │ └── PandocCLI/ │ │ └── Lua.hs │ ├── no-server/ │ │ └── PandocCLI/ │ │ └── Server.hs │ ├── pandoc-cli.cabal │ ├── server/ │ │ └── PandocCLI/ │ │ └── Server.hs │ ├── src/ │ │ └── pandoc.hs │ └── wasm/ │ └── PandocWasm.hs ├── pandoc-lua-engine/ │ ├── README.md │ ├── pandoc-lua-engine.cabal │ ├── src/ │ │ └── Text/ │ │ └── Pandoc/ │ │ ├── Lua/ │ │ │ ├── Custom.hs │ │ │ ├── Documentation.hs │ │ │ ├── Engine.hs │ │ │ ├── Filter.hs │ │ │ ├── Global.hs │ │ │ ├── Init.hs │ │ │ ├── Marshal/ │ │ │ │ ├── Chunks.hs │ │ │ │ ├── CommonState.hs │ │ │ │ ├── Context.hs │ │ │ │ ├── Format.hs │ │ │ │ ├── ImageSize.hs │ │ │ │ ├── LogMessage.hs │ │ │ │ ├── PandocError.hs │ │ │ │ ├── ReaderOptions.hs │ │ │ │ ├── Reference.hs │ │ │ │ ├── Sources.hs │ │ │ │ ├── Template.hs │ │ │ │ └── WriterOptions.hs │ │ │ ├── Module/ │ │ │ │ ├── CLI.hs │ │ │ │ ├── Format.hs │ │ │ │ ├── Image.hs │ │ │ │ ├── JSON.hs │ │ │ │ ├── Log.hs │ │ │ │ ├── MediaBag.hs │ │ │ │ ├── Pandoc.hs │ │ │ │ ├── Path.hs │ │ │ │ ├── Scaffolding.hs │ │ │ │ ├── Structure.hs │ │ │ │ ├── System.hs │ │ │ │ ├── Template.hs │ │ │ │ ├── Text.hs │ │ │ │ ├── Types.hs │ │ │ │ └── Utils.hs │ │ │ ├── Module.hs │ │ │ ├── Orphans.hs │ │ │ ├── PandocLua.hs │ │ │ ├── Run.hs │ │ │ ├── SourcePos.hs │ │ │ └── Writer/ │ │ │ ├── Classic.hs │ │ │ └── Scaffolding.hs │ │ └── Lua.hs │ └── test/ │ ├── Tests/ │ │ ├── Lua/ │ │ │ ├── Module.hs │ │ │ ├── Reader.hs │ │ │ └── Writer.hs │ │ └── Lua.hs │ ├── bytestring-reader.lua │ ├── bytestring.lua │ ├── extensions.lua │ ├── lua/ │ │ ├── attr-test.lua │ │ ├── block-count.lua │ │ ├── blocks-filter.lua │ │ ├── hello-world-doc.lua │ │ ├── implicit-doc-filter.lua │ │ ├── inlines-filter.lua │ │ ├── markdown-reader.lua │ │ ├── math.lua │ │ ├── meta.lua │ │ ├── metatable-catch-all.lua │ │ ├── module/ │ │ │ ├── globals.lua │ │ │ ├── include.tex │ │ │ ├── pandoc-format.lua │ │ │ ├── pandoc-image.lua │ │ │ ├── pandoc-json.lua │ │ │ ├── pandoc-list.lua │ │ │ ├── pandoc-log.lua │ │ │ ├── pandoc-mediabag.lua │ │ │ ├── pandoc-path.lua │ │ │ ├── pandoc-structure.lua │ │ │ ├── pandoc-template.lua │ │ │ ├── pandoc-text.lua │ │ │ ├── pandoc-types.lua │ │ │ ├── pandoc-utils.lua │ │ │ ├── pandoc.lua │ │ │ ├── partial.test │ │ │ ├── sample.epub │ │ │ └── tiny.epub │ │ ├── plain-to-para.lua │ │ ├── require-file.lua │ │ ├── script-name.lua │ │ ├── single-to-double-quoted.lua │ │ ├── smallcaps-title.lua │ │ ├── smart-constructors.lua │ │ ├── strmacro.lua │ │ ├── undiv.lua │ │ └── uppercase-header.lua │ ├── sample.lua │ ├── tables.custom │ ├── test-pandoc-lua-engine.hs │ ├── writer-template.lua │ ├── writer-template.out.txt │ └── writer.custom ├── pandoc-server/ │ ├── README.md │ ├── pandoc-server.cabal │ └── src/ │ └── Text/ │ └── Pandoc/ │ └── Server.hs ├── pandoc.cabal ├── release.nix ├── shell.nix ├── src/ │ └── Text/ │ ├── Pandoc/ │ │ ├── App/ │ │ │ ├── CommandLineOptions.hs │ │ │ ├── Input.hs │ │ │ ├── Opt.hs │ │ │ └── OutputSettings.hs │ │ ├── App.hs │ │ ├── Asciify.hs │ │ ├── CSS.hs │ │ ├── CSV.hs │ │ ├── Char.hs │ │ ├── Chunks.hs │ │ ├── Citeproc/ │ │ │ ├── BibTeX.hs │ │ │ ├── CslJson.hs │ │ │ ├── Data.hs │ │ │ ├── Locator.hs │ │ │ ├── MetaValue.hs │ │ │ ├── Name.hs │ │ │ └── Util.hs │ │ ├── Citeproc.hs │ │ ├── Class/ │ │ │ ├── CommonState.hs │ │ │ ├── IO/ │ │ │ │ └── HTTP.hs │ │ │ ├── IO.hs │ │ │ ├── PandocIO.hs │ │ │ ├── PandocMonad.hs │ │ │ ├── PandocPure.hs │ │ │ └── Sandbox.hs │ │ ├── Class.hs │ │ ├── Data/ │ │ │ └── BakedIn.hs │ │ ├── Data.hs │ │ ├── Emoji.hs │ │ ├── Error.hs │ │ ├── Extensions.hs │ │ ├── Filter/ │ │ │ ├── Environment.hs │ │ │ └── JSON.hs │ │ ├── Filter.hs │ │ ├── Format.hs │ │ ├── Highlighting.hs │ │ ├── Image.hs │ │ ├── ImageSize.hs │ │ ├── Logging.hs │ │ ├── MIME.hs │ │ ├── MediaBag.hs │ │ ├── Options.hs │ │ ├── PDF.hs │ │ ├── Parsing/ │ │ │ ├── Capabilities.hs │ │ │ ├── Citations.hs │ │ │ ├── Future.hs │ │ │ ├── General.hs │ │ │ ├── GridTable.hs │ │ │ ├── Lists.hs │ │ │ ├── Math.hs │ │ │ ├── Smart.hs │ │ │ └── State.hs │ │ ├── Parsing.hs │ │ ├── Process.hs │ │ ├── Readers/ │ │ │ ├── AsciiDoc.hs │ │ │ ├── BibTeX.hs │ │ │ ├── CSV.hs │ │ │ ├── CommonMark.hs │ │ │ ├── Creole.hs │ │ │ ├── CslJson.hs │ │ │ ├── Djot.hs │ │ │ ├── DocBook.hs │ │ │ ├── Docx/ │ │ │ │ ├── Combine.hs │ │ │ │ ├── Fields.hs │ │ │ │ ├── Lists.hs │ │ │ │ ├── Parse/ │ │ │ │ │ └── Styles.hs │ │ │ │ ├── Parse.hs │ │ │ │ ├── Symbols.hs │ │ │ │ └── Util.hs │ │ │ ├── Docx.hs │ │ │ ├── DokuWiki.hs │ │ │ ├── EPUB.hs │ │ │ ├── EndNote.hs │ │ │ ├── FB2.hs │ │ │ ├── HTML/ │ │ │ │ ├── Parsing.hs │ │ │ │ ├── Table.hs │ │ │ │ ├── TagCategories.hs │ │ │ │ └── Types.hs │ │ │ ├── HTML.hs │ │ │ ├── Haddock.hs │ │ │ ├── Ipynb.hs │ │ │ ├── JATS.hs │ │ │ ├── Jira.hs │ │ │ ├── LaTeX/ │ │ │ │ ├── Citation.hs │ │ │ │ ├── Inline.hs │ │ │ │ ├── Lang.hs │ │ │ │ ├── Macro.hs │ │ │ │ ├── Math.hs │ │ │ │ ├── Parsing.hs │ │ │ │ ├── SIunitx.hs │ │ │ │ └── Table.hs │ │ │ ├── LaTeX.hs │ │ │ ├── Man.hs │ │ │ ├── Markdown.hs │ │ │ ├── Mdoc/ │ │ │ │ ├── Lex.hs │ │ │ │ ├── Macros.hs │ │ │ │ └── Standards.hs │ │ │ ├── Mdoc.hs │ │ │ ├── MediaWiki.hs │ │ │ ├── Metadata.hs │ │ │ ├── Muse.hs │ │ │ ├── Native.hs │ │ │ ├── ODT/ │ │ │ │ ├── Arrows/ │ │ │ │ │ ├── State.hs │ │ │ │ │ └── Utils.hs │ │ │ │ ├── Base.hs │ │ │ │ ├── ContentReader.hs │ │ │ │ ├── Generic/ │ │ │ │ │ ├── Fallible.hs │ │ │ │ │ ├── Namespaces.hs │ │ │ │ │ ├── SetMap.hs │ │ │ │ │ ├── Utils.hs │ │ │ │ │ └── XMLConverter.hs │ │ │ │ ├── Namespaces.hs │ │ │ │ └── StyleReader.hs │ │ │ ├── ODT.hs │ │ │ ├── OOXML/ │ │ │ │ └── Shared.hs │ │ │ ├── OPML.hs │ │ │ ├── Org/ │ │ │ │ ├── BlockStarts.hs │ │ │ │ ├── Blocks.hs │ │ │ │ ├── DocumentTree.hs │ │ │ │ ├── ExportSettings.hs │ │ │ │ ├── Inlines.hs │ │ │ │ ├── Meta.hs │ │ │ │ ├── ParserState.hs │ │ │ │ ├── Parsing.hs │ │ │ │ └── Shared.hs │ │ │ ├── Org.hs │ │ │ ├── Pod.hs │ │ │ ├── Pptx/ │ │ │ │ ├── Parse.hs │ │ │ │ ├── Shapes.hs │ │ │ │ ├── Slides.hs │ │ │ │ └── SmartArt.hs │ │ │ ├── Pptx.hs │ │ │ ├── RIS.hs │ │ │ ├── RST.hs │ │ │ ├── RTF.hs │ │ │ ├── Roff/ │ │ │ │ └── Escape.hs │ │ │ ├── Roff.hs │ │ │ ├── TWiki.hs │ │ │ ├── Textile.hs │ │ │ ├── TikiWiki.hs │ │ │ ├── Txt2Tags.hs │ │ │ ├── Typst/ │ │ │ │ ├── Math.hs │ │ │ │ └── Parsing.hs │ │ │ ├── Typst.hs │ │ │ ├── Vimwiki.hs │ │ │ ├── XML.hs │ │ │ ├── Xlsx/ │ │ │ │ ├── Cells.hs │ │ │ │ ├── Parse.hs │ │ │ │ └── Sheets.hs │ │ │ └── Xlsx.hs │ │ ├── Readers.hs │ │ ├── RoffChar.hs │ │ ├── Scripting.hs │ │ ├── SelfContained.hs │ │ ├── Shared.hs │ │ ├── Slides.hs │ │ ├── Sources.hs │ │ ├── TeX.hs │ │ ├── Templates.hs │ │ ├── Transforms.hs │ │ ├── Translations/ │ │ │ └── Types.hs │ │ ├── Translations.hs │ │ ├── URI.hs │ │ ├── UTF8.hs │ │ ├── UUID.hs │ │ ├── Version.hs │ │ ├── Writers/ │ │ │ ├── ANSI.hs │ │ │ ├── AnnotatedTable.hs │ │ │ ├── AsciiDoc.hs │ │ │ ├── BBCode.hs │ │ │ ├── BibTeX.hs │ │ │ ├── Blaze.hs │ │ │ ├── ChunkedHTML.hs │ │ │ ├── CommonMark.hs │ │ │ ├── ConTeXt.hs │ │ │ ├── CslJson.hs │ │ │ ├── Djot.hs │ │ │ ├── DocBook.hs │ │ │ ├── Docx/ │ │ │ │ ├── OpenXML.hs │ │ │ │ ├── StyleMap.hs │ │ │ │ ├── Table.hs │ │ │ │ └── Types.hs │ │ │ ├── Docx.hs │ │ │ ├── DokuWiki.hs │ │ │ ├── EPUB.hs │ │ │ ├── FB2.hs │ │ │ ├── GridTable.hs │ │ │ ├── HTML.hs │ │ │ ├── Haddock.hs │ │ │ ├── ICML.hs │ │ │ ├── Ipynb.hs │ │ │ ├── JATS/ │ │ │ │ ├── References.hs │ │ │ │ ├── Table.hs │ │ │ │ └── Types.hs │ │ │ ├── JATS.hs │ │ │ ├── Jira.hs │ │ │ ├── LaTeX/ │ │ │ │ ├── Caption.hs │ │ │ │ ├── Citation.hs │ │ │ │ ├── Lang.hs │ │ │ │ ├── Notes.hs │ │ │ │ ├── Table.hs │ │ │ │ ├── Types.hs │ │ │ │ └── Util.hs │ │ │ ├── LaTeX.hs │ │ │ ├── Man.hs │ │ │ ├── Markdown/ │ │ │ │ ├── Inline.hs │ │ │ │ ├── Table.hs │ │ │ │ └── Types.hs │ │ │ ├── Markdown.hs │ │ │ ├── Math.hs │ │ │ ├── MediaWiki.hs │ │ │ ├── Ms.hs │ │ │ ├── Muse.hs │ │ │ ├── Native.hs │ │ │ ├── ODT.hs │ │ │ ├── OOXML.hs │ │ │ ├── OPML.hs │ │ │ ├── OpenDocument.hs │ │ │ ├── Org.hs │ │ │ ├── Powerpoint/ │ │ │ │ ├── Output.hs │ │ │ │ └── Presentation.hs │ │ │ ├── Powerpoint.hs │ │ │ ├── RST.hs │ │ │ ├── RTF.hs │ │ │ ├── Roff.hs │ │ │ ├── Shared.hs │ │ │ ├── TEI.hs │ │ │ ├── Texinfo.hs │ │ │ ├── Textile.hs │ │ │ ├── Typst.hs │ │ │ ├── Vimdoc.hs │ │ │ ├── XML.hs │ │ │ ├── XWiki.hs │ │ │ └── ZimWiki.hs │ │ ├── Writers.hs │ │ ├── XML.hs │ │ └── XMLFormat.hs │ └── Pandoc.hs ├── stack.yaml ├── test/ │ ├── Tests/ │ │ ├── Command.hs │ │ ├── Helpers.hs │ │ ├── MediaBag.hs │ │ ├── Old.hs │ │ ├── Readers/ │ │ │ ├── Creole.hs │ │ │ ├── Docx.hs │ │ │ ├── DokuWiki.hs │ │ │ ├── EPUB.hs │ │ │ ├── FB2.hs │ │ │ ├── HTML.hs │ │ │ ├── JATS.hs │ │ │ ├── Jira.hs │ │ │ ├── LaTeX.hs │ │ │ ├── Man.hs │ │ │ ├── Markdown.hs │ │ │ ├── Mdoc.hs │ │ │ ├── Muse.hs │ │ │ ├── ODT.hs │ │ │ ├── Org/ │ │ │ │ ├── Block/ │ │ │ │ │ ├── CodeBlock.hs │ │ │ │ │ ├── Figure.hs │ │ │ │ │ ├── Header.hs │ │ │ │ │ ├── List.hs │ │ │ │ │ └── Table.hs │ │ │ │ ├── Block.hs │ │ │ │ ├── Directive.hs │ │ │ │ ├── Inline/ │ │ │ │ │ ├── Citation.hs │ │ │ │ │ ├── Note.hs │ │ │ │ │ └── Smart.hs │ │ │ │ ├── Inline.hs │ │ │ │ ├── Meta.hs │ │ │ │ └── Shared.hs │ │ │ ├── Org.hs │ │ │ ├── Pod.hs │ │ │ ├── Pptx.hs │ │ │ ├── RST.hs │ │ │ ├── RTF.hs │ │ │ ├── Txt2Tags.hs │ │ │ └── Xlsx.hs │ │ ├── Shared.hs │ │ ├── Writers/ │ │ │ ├── AnnotatedTable.hs │ │ │ ├── AsciiDoc.hs │ │ │ ├── BBCode.hs │ │ │ ├── ConTeXt.hs │ │ │ ├── DocBook.hs │ │ │ ├── Docx.hs │ │ │ ├── FB2.hs │ │ │ ├── HTML.hs │ │ │ ├── JATS.hs │ │ │ ├── Jira.hs │ │ │ ├── LaTeX.hs │ │ │ ├── Markdown.hs │ │ │ ├── Markua.hs │ │ │ ├── Ms.hs │ │ │ ├── Muse.hs │ │ │ ├── Native.hs │ │ │ ├── OOXML.hs │ │ │ ├── Org.hs │ │ │ ├── Plain.hs │ │ │ ├── Powerpoint.hs │ │ │ ├── RST.hs │ │ │ └── TEI.hs │ │ └── XML.hs │ ├── ansi-test.ansi │ ├── ansi-test.txt │ ├── asciidoc-reader-include.adoc │ ├── asciidoc-reader-include.rb │ ├── asciidoc-reader.adoc │ ├── asciidoc-reader.native │ ├── command/ │ │ ├── 01.csv │ │ ├── 10002.md │ │ ├── 10057.md │ │ ├── 10062.md │ │ ├── 10071.md │ │ ├── 10093.md │ │ ├── 10094.md │ │ ├── 10105.md │ │ ├── 10127.md │ │ ├── 10145.md │ │ ├── 10148.md │ │ ├── 10149.md │ │ ├── 10152.md │ │ ├── 10160.md │ │ ├── 10185.md │ │ ├── 10236.md │ │ ├── 10271.md │ │ ├── 10279.md │ │ ├── 10281.md │ │ ├── 10318.md │ │ ├── 10328.md │ │ ├── 10338-rst-multiple-header-rows.md │ │ ├── 10385.md │ │ ├── 10390.md │ │ ├── 10414.md │ │ ├── 10459.md │ │ ├── 10484.md │ │ ├── 10490.md │ │ ├── 10491.md │ │ ├── 10497.md │ │ ├── 10537.md │ │ ├── 10594.md │ │ ├── 10621.md │ │ ├── 10631.md │ │ ├── 10635.md │ │ ├── 10643.md │ │ ├── 10650.md │ │ ├── 10659.md │ │ ├── 10672.md │ │ ├── 10708.md │ │ ├── 10730.md │ │ ├── 10747.md │ │ ├── 10755.md │ │ ├── 10758.md │ │ ├── 10781.md │ │ ├── 10791.md │ │ ├── 10805.md │ │ ├── 10812.md │ │ ├── 10816.md │ │ ├── 10825.md │ │ ├── 10836.md │ │ ├── 10848.md │ │ ├── 10855.md │ │ ├── 10862.md │ │ ├── 10867.md │ │ ├── 10884.md │ │ ├── 10889.md │ │ ├── 10890.md │ │ ├── 10912.md │ │ ├── 10915.md │ │ ├── 10919.md │ │ ├── 10926.md │ │ ├── 10942.md │ │ ├── 10965.md │ │ ├── 10983.md │ │ ├── 10984.md │ │ ├── 11006.md │ │ ├── 11013.md │ │ ├── 11014.md │ │ ├── 11017.md │ │ ├── 11046.md │ │ ├── 11047.md │ │ ├── 11048.md │ │ ├── 11090/ │ │ │ └── ch1.typ │ │ ├── 11090.md │ │ ├── 11101.md │ │ ├── 11113.docx │ │ ├── 11113.md │ │ ├── 11124.md │ │ ├── 11128.md │ │ ├── 11140.md │ │ ├── 11150.md │ │ ├── 11162.md │ │ ├── 11188.md │ │ ├── 11210.md │ │ ├── 11211.md │ │ ├── 11253.md │ │ ├── 1126.md │ │ ├── 11266.md │ │ ├── 11270.md │ │ ├── 11299.md │ │ ├── 11300.md │ │ ├── 11309.md │ │ ├── 11312.md │ │ ├── 11323.md │ │ ├── 11341.md │ │ ├── 11342.md │ │ ├── 11348.md │ │ ├── 11362.md │ │ ├── 11364.md │ │ ├── 11374.md │ │ ├── 11384.md │ │ ├── 11409.md │ │ ├── 11420.md │ │ ├── 11422.md │ │ ├── 11450.md │ │ ├── 11455.md │ │ ├── 11463.md │ │ ├── 11479.md │ │ ├── 11486/ │ │ │ └── scroll.revealjs │ │ ├── 11486.md │ │ ├── 11490.md │ │ ├── 11494.md │ │ ├── 11498.md │ │ ├── 11511.md │ │ ├── 11534.md │ │ ├── 1166.md │ │ ├── 1279.md │ │ ├── 1390.md │ │ ├── 1592.md │ │ ├── 1608.md │ │ ├── 1629.md │ │ ├── 168.md │ │ ├── 1710.md │ │ ├── 1718.md │ │ ├── 1745.md │ │ ├── 1762.md │ │ ├── 1773.md │ │ ├── 1841.md │ │ ├── 1881.md │ │ ├── 1905.md │ │ ├── 2103.md │ │ ├── 2118.md │ │ ├── 2228.md │ │ ├── 2337.md │ │ ├── 2378.md │ │ ├── 2397.md │ │ ├── 2434.md │ │ ├── 2465.md │ │ ├── 2549.md │ │ ├── 2552.md │ │ ├── 256.md │ │ ├── 2602.md │ │ ├── 2606.md │ │ ├── 262.md │ │ ├── 2649.md │ │ ├── 2662.md │ │ ├── 2834.md │ │ ├── 2874.md │ │ ├── 2994.md │ │ ├── 3113.md │ │ ├── 3123.md │ │ ├── 3236.md │ │ ├── 3257.md │ │ ├── 3309.md │ │ ├── 3314.md │ │ ├── 3324.md │ │ ├── 3337.md │ │ ├── 3348.md │ │ ├── 3401.md │ │ ├── 3407.md │ │ ├── 3422.md │ │ ├── 3432.md │ │ ├── 3432a.md │ │ ├── 3450.md │ │ ├── 3475.md │ │ ├── 3487.md │ │ ├── 3494.md │ │ ├── 3497.md │ │ ├── 3499.md │ │ ├── 3510-export.latex │ │ ├── 3510-src.hs │ │ ├── 3510-subdoc.org │ │ ├── 3510.md │ │ ├── 3511.md │ │ ├── 3512.md │ │ ├── 3516.md │ │ ├── 3518.md │ │ ├── 3523.md │ │ ├── 3526.md │ │ ├── 3529.md │ │ ├── 3530.md │ │ ├── 3531.md │ │ ├── 3533-rst-csv-tables.csv │ │ ├── 3533-rst-csv-tables.md │ │ ├── 3534.md │ │ ├── 3537.md │ │ ├── 3539.md │ │ ├── 3558.md │ │ ├── 3568.md │ │ ├── 3570.md │ │ ├── 3577.md │ │ ├── 3585.md │ │ ├── 3587.md │ │ ├── 3596.md │ │ ├── 3615.md │ │ ├── 3619.md │ │ ├── 3630.md │ │ ├── 3667.md │ │ ├── 3674.md │ │ ├── 3675.md │ │ ├── 3681.md │ │ ├── 3690.md │ │ ├── 3701.md │ │ ├── 3706.md │ │ ├── 3708.md │ │ ├── 3715.md │ │ ├── 3716.md │ │ ├── 3730.md │ │ ├── 3733.md │ │ ├── 3734.md │ │ ├── 3736.md │ │ ├── 3752.md │ │ ├── 3755.md │ │ ├── 3771.md │ │ ├── 3773.md │ │ ├── 3779.md │ │ ├── 3792.md │ │ ├── 3794.md │ │ ├── 3803.md │ │ ├── 3804.md │ │ ├── 3816.md │ │ ├── 3824.md │ │ ├── 3840.md │ │ ├── 3853.md │ │ ├── 3880.md │ │ ├── 3880.txt │ │ ├── 3916.md │ │ ├── 3937.md │ │ ├── 3947.md │ │ ├── 3958.md │ │ ├── 3968.md │ │ ├── 3971.md │ │ ├── 3971b.tex │ │ ├── 3974.md │ │ ├── 3978.md │ │ ├── 3983.md │ │ ├── 3989.md │ │ ├── 4007.md │ │ ├── 4012.md │ │ ├── 4016.md │ │ ├── 4019.md │ │ ├── 4038.md │ │ ├── 4054.md │ │ ├── 4056.md │ │ ├── 4061.md │ │ ├── 4062.md │ │ ├── 4063.md │ │ ├── 4068.md │ │ ├── 4091.md │ │ ├── 4102.md │ │ ├── 4113.md │ │ ├── 4119.md │ │ ├── 4125.md │ │ ├── 4134.md │ │ ├── 4156.md │ │ ├── 4159.md │ │ ├── 4162.md │ │ ├── 4164.md │ │ ├── 4171.md │ │ ├── 4172.md │ │ ├── 4183.md │ │ ├── 4186.md │ │ ├── 4193.md │ │ ├── 4199.md │ │ ├── 4208.md │ │ ├── 4235.md │ │ ├── 4240.md │ │ ├── 4253.md │ │ ├── 4254.md │ │ ├── 4280.md │ │ ├── 4281.md │ │ ├── 4284.md │ │ ├── 4306.md │ │ ├── 4320.md │ │ ├── 4374.md │ │ ├── 4382.md │ │ ├── 4420.md │ │ ├── 4424.md │ │ ├── 4442.md │ │ ├── 4454.md │ │ ├── 4465.md │ │ ├── 4470.md │ │ ├── 4499.md │ │ ├── 4513.md │ │ ├── 4527.md │ │ ├── 4528.md │ │ ├── 4529.md │ │ ├── 4545.md │ │ ├── 4550.md │ │ ├── 4553.md │ │ ├── 4564.md │ │ ├── 4576.md │ │ ├── 4578.md │ │ ├── 4579.md │ │ ├── 4589.md │ │ ├── 4594.md │ │ ├── 4598.md │ │ ├── 4624.md │ │ ├── 4635.md │ │ ├── 4637.md │ │ ├── 4639.md │ │ ├── 4653.md │ │ ├── 4667.md │ │ ├── 4669.md │ │ ├── 4677.md │ │ ├── 4690.md │ │ ├── 4715.md │ │ ├── 4722.md │ │ ├── 4742.md │ │ ├── 4743.md │ │ ├── 4746.md │ │ ├── 4748.md │ │ ├── 4768.md │ │ ├── 4781.md │ │ ├── 4794.md │ │ ├── 4805-beamer-columns-alignment.md │ │ ├── 4811.md │ │ ├── 4817.md │ │ ├── 4819.md │ │ ├── 4832.md │ │ ├── 4833.md │ │ ├── 4842.md │ │ ├── 4845.md │ │ ├── 4848.md │ │ ├── 4860.md │ │ ├── 4877.md │ │ ├── 4880.md │ │ ├── 4885.md │ │ ├── 4908.md │ │ ├── 4913.md │ │ ├── 4919.md │ │ ├── 4928.md │ │ ├── 4933.md │ │ ├── 4960.md │ │ ├── 5010.md │ │ ├── 5014.md │ │ ├── 5039.md │ │ ├── 5050.md │ │ ├── 5053.md │ │ ├── 5071.md │ │ ├── 5072.md │ │ ├── 5079.md │ │ ├── 5080.md │ │ ├── 5081.md │ │ ├── 5099.md │ │ ├── 5107.md │ │ ├── 5116.md │ │ ├── 5119.md │ │ ├── 512.md │ │ ├── 5121.md │ │ ├── 5128.md │ │ ├── 5177.md │ │ ├── 5178.md │ │ ├── 5182.md │ │ ├── 5182.txt │ │ ├── 5195.md │ │ ├── 5233.md │ │ ├── 5241.md │ │ ├── 5271.md │ │ ├── 5285.md │ │ ├── 5304.md │ │ ├── 5321.md │ │ ├── 5340.md │ │ ├── 5360.md │ │ ├── 5367.md │ │ ├── 5368.md │ │ ├── 5369.md │ │ ├── 5407.md │ │ ├── 5410.md │ │ ├── 5416.md │ │ ├── 5420.md │ │ ├── 5439.md │ │ ├── 5446.md │ │ ├── 5474-figures.md │ │ ├── 5474-tables.md │ │ ├── 5476.md │ │ ├── 5495.md │ │ ├── 5519.md │ │ ├── 5529.md │ │ ├── 5540.md │ │ ├── 5541-localLink.md │ │ ├── 5541-nesting.md │ │ ├── 5541-urlLink.md │ │ ├── 5543.md │ │ ├── 5549.md │ │ ├── 5565.md │ │ ├── 5566.md │ │ ├── 5574.md │ │ ├── 5619.md │ │ ├── 5620.md │ │ ├── 5627.md │ │ ├── 5635.md │ │ ├── 5642.md │ │ ├── 5650.md │ │ ├── 5654.md │ │ ├── 5655.md │ │ ├── 5682.md │ │ ├── 5684.md │ │ ├── 5686.md │ │ ├── 5690.md │ │ ├── 5700-metadata-file-1.yml │ │ ├── 5700-metadata-file-2.yml │ │ ├── 5700.md │ │ ├── 5705.md │ │ ├── 5708.md │ │ ├── 5711.md │ │ ├── 5714.md │ │ ├── 5740.md │ │ ├── 5753.md │ │ ├── 5793.md │ │ ├── 5795.md │ │ ├── 5797.md │ │ ├── 5805.md │ │ ├── 5813.md │ │ ├── 5819.md │ │ ├── 5836.md │ │ ├── 5845.md │ │ ├── 5846.md │ │ ├── 5849-prefix.md │ │ ├── 5857.md │ │ ├── 5876/ │ │ │ └── metadata/ │ │ │ ├── 5876.yaml │ │ │ └── command/ │ │ │ └── 5876.yaml │ │ ├── 5876.md │ │ ├── 5876.yaml │ │ ├── 5878.md │ │ ├── 5881.md │ │ ├── 5885.md │ │ ├── 5898.md │ │ ├── 5899.md │ │ ├── 5904.md │ │ ├── 5918.md │ │ ├── 5936.md │ │ ├── 5967.md │ │ ├── 5986.md │ │ ├── 6009.md │ │ ├── 6021.md │ │ ├── 6026.md │ │ ├── 6030.md │ │ ├── 6033.md │ │ ├── 6034.md │ │ ├── 6043.md │ │ ├── 6062.md │ │ ├── 6107.md │ │ ├── 6114.md │ │ ├── 6119.md │ │ ├── 6133.md │ │ ├── 6137.md │ │ ├── 6194.md │ │ ├── 6265.md │ │ ├── 6285.md │ │ ├── 6288.md │ │ ├── 6296.md │ │ ├── 6308.md │ │ ├── 6324.md │ │ ├── 6348.md │ │ ├── 6350.md │ │ ├── 6360.md │ │ ├── 6367.md │ │ ├── 6384.md │ │ ├── 6385.md │ │ ├── 6388.md │ │ ├── 6424.md │ │ ├── 6441.md │ │ ├── 645.md │ │ ├── 6466-beg.hs │ │ ├── 6466-end.hs │ │ ├── 6466-mid.hs │ │ ├── 6466-whole.hs │ │ ├── 6466.md │ │ ├── 6481.md │ │ ├── 6541.md │ │ ├── 6549.md │ │ ├── 6588.md │ │ ├── 6620.md │ │ ├── 6658.md │ │ ├── 6675.md │ │ ├── 6699.md │ │ ├── 6709.md │ │ ├── 6719.md │ │ ├── 6723.md │ │ ├── 6739.md │ │ ├── 6740.md │ │ ├── 6741.md │ │ ├── 6752.md │ │ ├── 6755.md │ │ ├── 6765.md │ │ ├── 6768.md │ │ ├── 6774.md │ │ ├── 6783.md │ │ ├── 6791.md │ │ ├── 6792.md │ │ ├── 6796.md │ │ ├── 6802.md │ │ ├── 6821.md │ │ ├── 6836.md │ │ ├── 6837.md │ │ ├── 6844.md │ │ ├── 6855.md │ │ ├── 6858.md │ │ ├── 6869.md │ │ ├── 6873.md │ │ ├── 6890.md │ │ ├── 6925.md │ │ ├── 6948.md │ │ ├── 6951.md │ │ ├── 6958.md │ │ ├── 6959.md │ │ ├── 6970.md │ │ ├── 6992.md │ │ ├── 6993.md │ │ ├── 7003.md │ │ ├── 7006.md │ │ ├── 7009.md │ │ ├── 7016.md │ │ ├── 7041.md │ │ ├── 7042.md │ │ ├── 7058.md │ │ ├── 7064.md │ │ ├── 7067.md │ │ ├── 7080.md │ │ ├── 7092.md │ │ ├── 7099.md │ │ ├── 7112.md │ │ ├── 7129.md │ │ ├── 7132.md │ │ ├── 7134.md │ │ ├── 7145.md │ │ ├── 7155.md │ │ ├── 7172.md │ │ ├── 7173.md │ │ ├── 7181.md │ │ ├── 7201.md │ │ ├── 7208.md │ │ ├── 7214.md │ │ ├── 7216.md │ │ ├── 7219.md │ │ ├── 7266.md │ │ ├── 7272.md │ │ ├── 7278.md │ │ ├── 7282.md │ │ ├── 7288.md │ │ ├── 7299.md │ │ ├── 7321.md │ │ ├── 7323.md │ │ ├── 7324.md │ │ ├── 7326.md │ │ ├── 7329.md │ │ ├── 7339.md │ │ ├── 7340.md │ │ ├── 7376.md │ │ ├── 7394.md │ │ ├── 7397.md │ │ ├── 7400.md │ │ ├── 7416.md │ │ ├── 7434.md │ │ ├── 7436.md │ │ ├── 7473.md │ │ ├── 7482.md │ │ ├── 7494.md │ │ ├── 7497.md │ │ ├── 7512.md │ │ ├── 7520.md │ │ ├── 7521.md │ │ ├── 7525.md │ │ ├── 7529.md │ │ ├── 7546.md │ │ ├── 7557.md │ │ ├── 7568.md │ │ ├── 7573.md │ │ ├── 7582.md │ │ ├── 7589.md │ │ ├── 7615.md │ │ ├── 7623.md │ │ ├── 7632.md │ │ ├── 7668.md │ │ ├── 7678.md │ │ ├── 7691.docx │ │ ├── 7691.md │ │ ├── 7692.md │ │ ├── 7697.md │ │ ├── 7713.md │ │ ├── 7723.md │ │ ├── 7726.md │ │ ├── 7738.md │ │ ├── 7743.md │ │ ├── 7761.md │ │ ├── 7778.md │ │ ├── 7803.md │ │ ├── 7808.md │ │ ├── 7810.md │ │ ├── 7813-meta.yaml │ │ ├── 7813.md │ │ ├── 7826.md │ │ ├── 7847.md │ │ ├── 7857.md │ │ ├── 7858.md │ │ ├── 7861/ │ │ │ └── metadata/ │ │ │ └── placeholder │ │ ├── 7861.md │ │ ├── 7861.yaml │ │ ├── 7863.md │ │ ├── 7871.md │ │ ├── 7884.md │ │ ├── 7894.md │ │ ├── 7919.md │ │ ├── 7920.md │ │ ├── 7930.md │ │ ├── 7939.md │ │ ├── 7941.md │ │ ├── 7953.md │ │ ├── 7965.md │ │ ├── 8003.md │ │ ├── 8011.md │ │ ├── 8024.md │ │ ├── 8024a.yaml │ │ ├── 8024b.yaml │ │ ├── 8028.md │ │ ├── 8047.md │ │ ├── 8070.md │ │ ├── 8079.md │ │ ├── 8088.md │ │ ├── 8097.md │ │ ├── 8098.md │ │ ├── 8110.md │ │ ├── 8131.md │ │ ├── 8150.md │ │ ├── 8170.md │ │ ├── 8174.md │ │ ├── 8178.md │ │ ├── 8179.md │ │ ├── 8182.md │ │ ├── 8201.md │ │ ├── 8204.md │ │ ├── 8216.md │ │ ├── 8219.md │ │ ├── 8236.md │ │ ├── 8243.md │ │ ├── 8251.md │ │ ├── 8254.md │ │ ├── 8256.md │ │ ├── 8257.md │ │ ├── 8281.md │ │ ├── 8302.md │ │ ├── 8307.md │ │ ├── 8344.md │ │ ├── 8354.md │ │ ├── 8364.md │ │ ├── 8365.md │ │ ├── 8380.md │ │ ├── 8402.md │ │ ├── 8437.md │ │ ├── 8486.md │ │ ├── 8487.md │ │ ├── 8504.md │ │ ├── 8508.md │ │ ├── 8511.md │ │ ├── 8513.md │ │ ├── 853.md │ │ ├── 8534.md │ │ ├── 8573.md │ │ ├── 8611.md │ │ ├── 8638.md │ │ ├── 8652.md │ │ ├── 8653.md │ │ ├── 8659.md │ │ ├── 8661.md │ │ ├── 8665.md │ │ ├── 8666.md │ │ ├── 8681.md │ │ ├── 8689.md │ │ ├── 8711.md │ │ ├── 8738.md │ │ ├── 8745.md │ │ ├── 8764.md │ │ ├── 8770-block.md │ │ ├── 8770-document.md │ │ ├── 8770-section.md │ │ ├── 8777.md │ │ ├── 8783.md │ │ ├── 8789.md │ │ ├── 8853.md │ │ ├── 8863.md │ │ ├── 8867.md │ │ ├── 8869.md │ │ ├── 8872.md │ │ ├── 8948.md │ │ ├── 8956.md │ │ ├── 8957.md │ │ ├── 8966.md │ │ ├── 8981.md │ │ ├── 8984.md │ │ ├── 8992.md │ │ ├── 8997.md │ │ ├── 9000.md │ │ ├── 9002.docx │ │ ├── 9002.md │ │ ├── 9017.md │ │ ├── 9021.md │ │ ├── 9038.md │ │ ├── 9042.md │ │ ├── 9043.md │ │ ├── 9045.md │ │ ├── 9047.md │ │ ├── 9088.md │ │ ├── 9090.md │ │ ├── 9121.md │ │ ├── 9150.md │ │ ├── 9159.md │ │ ├── 9171.md │ │ ├── 9193.md │ │ ├── 9196.md │ │ ├── 9201.md │ │ ├── 9202.md │ │ ├── 9209.md │ │ ├── 9218.md │ │ ├── 9236.md │ │ ├── 9275.md │ │ ├── 9279.md │ │ ├── 9293.md │ │ ├── 934.md │ │ ├── 9346.md │ │ ├── 9350.md │ │ ├── 9358.docx │ │ ├── 9358.md │ │ ├── 9366.md │ │ ├── 9371.md │ │ ├── 9386.md │ │ ├── 9387.md │ │ ├── 9388.md │ │ ├── 9391.docx │ │ ├── 9391.md │ │ ├── 9420.md │ │ ├── 9445.md │ │ ├── 9452.md │ │ ├── 9467.md │ │ ├── 9472.md │ │ ├── 9475.md │ │ ├── 9478.md │ │ ├── 9481.md │ │ ├── 9516.md │ │ ├── 9517.md │ │ ├── 9555.md │ │ ├── 9569.md │ │ ├── 9576.md │ │ ├── 9579.md │ │ ├── 9585.md │ │ ├── 9586.md │ │ ├── 9597.md │ │ ├── 9603.docx │ │ ├── 9603.md │ │ ├── 9616.md │ │ ├── 9630.md │ │ ├── 9632.md │ │ ├── 9635.md │ │ ├── 9639.md │ │ ├── 9644.md │ │ ├── 9652.md │ │ ├── 9657.md │ │ ├── 9676.md │ │ ├── 9700.md │ │ ├── 9716.md │ │ ├── 9777-b.md │ │ ├── 9777.md │ │ ├── 9792.md │ │ ├── 9797.md │ │ ├── 9805.md │ │ ├── 9807.md │ │ ├── 9809.md │ │ ├── 982.md │ │ ├── 9865.md │ │ ├── 987.md │ │ ├── 9878.md │ │ ├── 9902.md │ │ ├── 9904.md │ │ ├── 9905.md │ │ ├── 9908.md │ │ ├── 9943.md │ │ ├── 9945.md │ │ ├── 9953.md │ │ ├── 9987.md │ │ ├── A.txt │ │ ├── B.txt │ │ ├── C.txt │ │ ├── D.txt │ │ ├── abbrevs │ │ ├── adjacent_latex_blocks.md │ │ ├── advanced-optical-materials.csl │ │ ├── alerts.md │ │ ├── american-medical-association.csl │ │ ├── annales.csl │ │ ├── apa.csl │ │ ├── archeologie-medievale.csl │ │ ├── ascii.md │ │ ├── author-in-text-move-note.md │ │ ├── averroes.bib │ │ ├── bar-endinput.tex │ │ ├── bar.tex │ │ ├── biblatex-266.md │ │ ├── biblatex-aksin.md │ │ ├── biblatex-almendro.md │ │ ├── biblatex-angenendt.md │ │ ├── biblatex-aristotle-anima.md │ │ ├── biblatex-aristotle-physics.md │ │ ├── biblatex-aristotle-poetics.md │ │ ├── biblatex-aristotle-rhetoric.md │ │ ├── biblatex-article.md │ │ ├── biblatex-augustine.md │ │ ├── biblatex-averroes-bland.md │ │ ├── biblatex-averroes-hannes.md │ │ ├── biblatex-averroes-hercz.md │ │ ├── biblatex-baez-article.md │ │ ├── biblatex-baez-online.md │ │ ├── biblatex-basic.md │ │ ├── biblatex-bertram.md │ │ ├── biblatex-bibstring-resolution.md │ │ ├── biblatex-book-averroes.md │ │ ├── biblatex-book-coleridge.md │ │ ├── biblatex-book-title-maintitle-series.md │ │ ├── biblatex-book-vazques-de-parga.md │ │ ├── biblatex-brandt.md │ │ ├── biblatex-britannica.md │ │ ├── biblatex-chiu.md │ │ ├── biblatex-cicero.md │ │ ├── biblatex-cms.md │ │ ├── biblatex-coleridge.md │ │ ├── biblatex-companion.md │ │ ├── biblatex-cotton.md │ │ ├── biblatex-crossref-inbook-mvbook.md │ │ ├── biblatex-crossref-nested.md │ │ ├── biblatex-ctan.md │ │ ├── biblatex-dates.md │ │ ├── biblatex-doody.md │ │ ├── biblatex-edtf-date.md │ │ ├── biblatex-escapedquotes.md │ │ ├── biblatex-formatting.md │ │ ├── biblatex-gaonkar-in.md │ │ ├── biblatex-gaonkar.md │ │ ├── biblatex-geer.md │ │ ├── biblatex-gerhardt.md │ │ ├── biblatex-gillies.md │ │ ├── biblatex-glashow.md │ │ ├── biblatex-gonzalez.md │ │ ├── biblatex-hammond.md │ │ ├── biblatex-herrmann.md │ │ ├── biblatex-hyman.md │ │ ├── biblatex-iliad.md │ │ ├── biblatex-inbook-title-booktitle-maintitle-series-2.md │ │ ├── biblatex-inbook-title-booktitle-maintitle-series.md │ │ ├── biblatex-inbook.md │ │ ├── biblatex-incollection-2.md │ │ ├── biblatex-incollection.md │ │ ├── biblatex-inproceedings.md │ │ ├── biblatex-issue288.md │ │ ├── biblatex-itzhaki.md │ │ ├── biblatex-jaffe.md │ │ ├── biblatex-jcg.md │ │ ├── biblatex-kant-kpv.md │ │ ├── biblatex-kant-ku.md │ │ ├── biblatex-kastenholz.md │ │ ├── biblatex-knuth-ct-a.md │ │ ├── biblatex-knuth-ct-b.md │ │ ├── biblatex-knuth-ct-c.md │ │ ├── biblatex-knuth-ct-d.md │ │ ├── biblatex-knuth-ct-e.md │ │ ├── biblatex-knuth-ct-related.md │ │ ├── biblatex-knuth-ct.md │ │ ├── biblatex-kowalik.md │ │ ├── biblatex-kullback-related.md │ │ ├── biblatex-kullback-reprint.md │ │ ├── biblatex-kullback.md │ │ ├── biblatex-laufenberg.md │ │ ├── biblatex-loh.md │ │ ├── biblatex-malinowski.md │ │ ├── biblatex-manual.md │ │ ├── biblatex-markey.md │ │ ├── biblatex-maron.md │ │ ├── biblatex-massa.md │ │ ├── biblatex-moore-related.md │ │ ├── biblatex-moore.md │ │ ├── biblatex-moraux.md │ │ ├── biblatex-murray.md │ │ ├── biblatex-nietzsche-historie.md │ │ ├── biblatex-nietzsche-ksa.md │ │ ├── biblatex-nietzsche-ksa1.md │ │ ├── biblatex-online.md │ │ ├── biblatex-options-url-false-doi-false.md │ │ ├── biblatex-padhye.md │ │ ├── biblatex-patent.md │ │ ├── biblatex-periodical.md │ │ ├── biblatex-piccato.md │ │ ├── biblatex-pines.md │ │ ├── biblatex-quotes.md │ │ ├── biblatex-reese.md │ │ ├── biblatex-report.md │ │ ├── biblatex-salam.md │ │ ├── biblatex-sarfraz.md │ │ ├── biblatex-shore.md │ │ ├── biblatex-sigfridsson.md │ │ ├── biblatex-sorace.md │ │ ├── biblatex-spiegelberg.md │ │ ├── biblatex-springer.md │ │ ├── biblatex-strings.md │ │ ├── biblatex-test-case-conversion.md │ │ ├── biblatex-textnormal.md │ │ ├── biblatex-thesis.md │ │ ├── biblatex-title-and-shorttitle.md │ │ ├── biblatex-vangennep-related.md │ │ ├── biblatex-vangennep-trans.md │ │ ├── biblatex-vangennep.md │ │ ├── biblatex-vazques-de-parga-mvbook.md │ │ ├── biblatex-vazques-de-parga.md │ │ ├── biblatex-video.md │ │ ├── biblatex-vizedom-related.md │ │ ├── biblatex-wassenberg.md │ │ ├── biblatex-weinberg.md │ │ ├── biblatex-westfahl-frontier.md │ │ ├── biblatex-westfahl-space.md │ │ ├── biblatex-wilde.md │ │ ├── biblatex-worman.md │ │ ├── biblatex-yoon.md │ │ ├── biblio.bib │ │ ├── bibtex-basic.md │ │ ├── bioethics.csl │ │ ├── bits-book-meta.md │ │ ├── bits-book-part-wrapper-meta.md │ │ ├── bits-book-part-wrapper.md │ │ ├── bits-book.md │ │ ├── bits-index-elements.md │ │ ├── bits-legend.md │ │ ├── bits-named-boook-parts.md │ │ ├── bits-title-display-as.md │ │ ├── bits-title-supress.md │ │ ├── bits-toc-elements.md │ │ ├── chap1/ │ │ │ └── text.md │ │ ├── chap2/ │ │ │ └── text.md │ │ ├── chicago-annotated-bibliography.csl │ │ ├── chicago-author-date-with-original-date-and-status.csl │ │ ├── chicago-fullnote-bibliography.csl │ │ ├── chicago-note-bibliography.csl │ │ ├── chinese-gb7714-2005-numeric.csl │ │ ├── cite-in-inline-note.md │ │ ├── citeproc-17.md │ │ ├── citeproc-20.md │ │ ├── citeproc-7a.md │ │ ├── citeproc-7b.md │ │ ├── citeproc-87.md │ │ ├── citeproc-author-in-text-suffix.md │ │ ├── csv.md │ │ ├── defaults-inheritance-1.md │ │ ├── defaults-inheritance-2.md │ │ ├── defaults-inheritance-3.md │ │ ├── defaults1.yaml │ │ ├── defaults2.yaml │ │ ├── defaults3.yaml │ │ ├── defaults4.yaml │ │ ├── defaults5.yaml │ │ ├── defaults6.yaml │ │ ├── defaults7.yaml │ │ ├── defaults8.yaml │ │ ├── defaults9.yaml │ │ ├── din-1505-2.csl │ │ ├── docbook-bibliography.md │ │ ├── dokuwiki-quote.md │ │ ├── dots.md │ │ ├── duplicate_attributes.md │ │ ├── emoji.md │ │ ├── empty_paragraphs.md │ │ ├── figures-context.md │ │ ├── figures-fb2.md │ │ ├── figures-haddock.md │ │ ├── figures-html.md │ │ ├── figures-jats.md │ │ ├── figures-jira.md │ │ ├── figures-latex.md │ │ ├── figures-markdown.md │ │ ├── figures-mediawiki.md │ │ ├── figures-org.md │ │ ├── figures-rst.md │ │ ├── figures-texinfo.md │ │ ├── figures-textile.md │ │ ├── figures-xwiki.md │ │ ├── figures-zimwiki.md │ │ ├── file1.txt │ │ ├── file2.txt │ │ ├── gfm.md │ │ ├── harvard-university-of-kent.csl │ │ ├── hspace.md │ │ ├── html-read-figure.md │ │ ├── html-trim-definition-list-terms.md │ │ ├── html-writer-a-in-a.md │ │ ├── hyphenat.md │ │ ├── ieee.csl │ │ ├── ifstrequal.md │ │ ├── indented-fences.md │ │ ├── input-with-endinput.md │ │ ├── issue160.csl │ │ ├── issue437.csl │ │ ├── issue58.csl │ │ ├── jabberwocky.md │ │ ├── jats-figure-alt-text.md │ │ ├── latex-center.md │ │ ├── latex-color.md │ │ ├── latex-command-comment.md │ │ ├── latex-fontawesome.md │ │ ├── latex-math-trailing-space.md │ │ ├── latex-tabular-column-specs.md │ │ ├── le-tapuscrit-note.csl │ │ ├── lettrine.md │ │ ├── lists-inside-definition.md │ │ ├── locators.csl │ │ ├── lstlisting.md │ │ ├── macro-defs-in-preamble.md │ │ ├── macros.md │ │ ├── make-section-column-divs.md │ │ ├── man-defines.md │ │ ├── md-abbrevs.md │ │ ├── mdoc-An.md │ │ ├── mdoc-Bd-unfilled.md │ │ ├── mdoc-Bl-column.md │ │ ├── mdoc-Bl-tag.md │ │ ├── mediawiki_behavior_switches.md │ │ ├── mmd-metadata.md │ │ ├── modern-humanities-research-association.csl │ │ ├── multiple-metadata-blocks.md │ │ ├── nested-spanlike.md │ │ ├── nested-table-to-asciidoc-6942.md │ │ ├── newif.md │ │ ├── oscola.csl │ │ ├── pandoc-citeproc-118.md │ │ ├── pandoc-citeproc-119.md │ │ ├── pandoc-citeproc-13.md │ │ ├── pandoc-citeproc-136.md │ │ ├── pandoc-citeproc-14.md │ │ ├── pandoc-citeproc-152.md │ │ ├── pandoc-citeproc-160.md │ │ ├── pandoc-citeproc-175.md │ │ ├── pandoc-citeproc-197.md │ │ ├── pandoc-citeproc-213.md │ │ ├── pandoc-citeproc-25.md │ │ ├── pandoc-citeproc-250.md │ │ ├── pandoc-citeproc-27.md │ │ ├── pandoc-citeproc-292.md │ │ ├── pandoc-citeproc-301.md │ │ ├── pandoc-citeproc-307.md │ │ ├── pandoc-citeproc-31.md │ │ ├── pandoc-citeproc-312.md │ │ ├── pandoc-citeproc-320.md │ │ ├── pandoc-citeproc-320a.md │ │ ├── pandoc-citeproc-322.md │ │ ├── pandoc-citeproc-325.md │ │ ├── pandoc-citeproc-327.md │ │ ├── pandoc-citeproc-338.md │ │ ├── pandoc-citeproc-351.md │ │ ├── pandoc-citeproc-356.md │ │ ├── pandoc-citeproc-360.md │ │ ├── pandoc-citeproc-361.md │ │ ├── pandoc-citeproc-365.md │ │ ├── pandoc-citeproc-371.md │ │ ├── pandoc-citeproc-38.md │ │ ├── pandoc-citeproc-386.md │ │ ├── pandoc-citeproc-392.md │ │ ├── pandoc-citeproc-399.md │ │ ├── pandoc-citeproc-401.md │ │ ├── pandoc-citeproc-408.md │ │ ├── pandoc-citeproc-416.md │ │ ├── pandoc-citeproc-437.md │ │ ├── pandoc-citeproc-47.md │ │ ├── pandoc-citeproc-51.md │ │ ├── pandoc-citeproc-53.md │ │ ├── pandoc-citeproc-57.md │ │ ├── pandoc-citeproc-58.md │ │ ├── pandoc-citeproc-61.md │ │ ├── pandoc-citeproc-64.md │ │ ├── pandoc-citeproc-65.md │ │ ├── pandoc-citeproc-68.md │ │ ├── pandoc-citeproc-7.md │ │ ├── pandoc-citeproc-70.md │ │ ├── pandoc-citeproc-75.md │ │ ├── pandoc-citeproc-76.md │ │ ├── pandoc-citeproc-77.md │ │ ├── pandoc-citeproc-82.md │ │ ├── pandoc-citeproc-87.md │ │ ├── pandoc-citeproc-chicago-author-date.md │ │ ├── pandoc-citeproc-chicago-fullnote-bibliography.md │ │ ├── pandoc-citeproc-ieee.md │ │ ├── pandoc-citeproc-locators-delimited.md │ │ ├── pandoc-citeproc-locators-integrated.md │ │ ├── pandoc-citeproc-move-period-inside-quote.md │ │ ├── pandoc-citeproc-no-author.md │ │ ├── pandoc-citeproc-number-of-volumes.md │ │ ├── pandoc-citeproc-page-range.md │ │ ├── parse-raw.md │ │ ├── pdfstandard.md │ │ ├── refs.md │ │ ├── reset-citation-positions.md │ │ ├── rst-links.md │ │ ├── rst-writer-gridtable-if-rowspans.md │ │ ├── rst_block_subst.md │ │ ├── sage-harvard.csl │ │ ├── science.csl │ │ ├── section-divs.md │ │ ├── setext-fenced-div.md │ │ ├── shift-heading-level-by.md │ │ ├── short-caption.md │ │ ├── sloppypar.md │ │ ├── smart.md │ │ ├── style399.csl │ │ ├── sub-file-chapter-1.tex │ │ ├── sub-file-chapter-2.tex │ │ ├── svg.md │ │ ├── table-with-cell-align.md │ │ ├── table-with-column-span.md │ │ ├── tabularx.md │ │ ├── tasklist.md │ │ ├── tex-group.md │ │ ├── three.txt │ │ ├── toc.md │ │ ├── translations.md │ │ ├── typst-hs-80.md │ │ ├── typst-image-alt.md │ │ ├── typst-images.md │ │ ├── typst-property-output.md │ │ ├── unicode-collation.md │ │ ├── vancouver.csl │ │ ├── vars-and-metadata.md │ │ ├── video-audio.md │ │ ├── wikilinks_title_after_pipe.md │ │ ├── wikilinks_title_before_pipe.md │ │ ├── write18.md │ │ ├── yaml-metadata-blocks.md │ │ ├── yaml-metadata.yaml │ │ ├── yaml-with-chomp.md │ │ └── zeitschrift-fur-kunstgeschichte.csl │ ├── creole-reader.native │ ├── creole-reader.txt │ ├── djot-reader.djot │ ├── djot-reader.native │ ├── docbook-chapter.docbook │ ├── docbook-chapter.native │ ├── docbook-reader.docbook │ ├── docbook-reader.native │ ├── docbook-xref.docbook │ ├── docbook-xref.native │ ├── docx/ │ │ ├── 0_level_headers.docx │ │ ├── 0_level_headers.native │ │ ├── adjacent_codeblocks.docx │ │ ├── adjacent_codeblocks.native │ │ ├── already_auto_ident.docx │ │ ├── already_auto_ident.native │ │ ├── alternate_document_path.docx │ │ ├── alternate_document_path.native │ │ ├── anchor_header_after_anchor.docx │ │ ├── anchor_header_after_anchor.native │ │ ├── block_quotes.docx │ │ ├── block_quotes.native │ │ ├── char_styles.docx │ │ ├── char_styles.native │ │ ├── codeblock.docx │ │ ├── codeblock.native │ │ ├── comments.docx │ │ ├── comments.native │ │ ├── comments_no_comments.native │ │ ├── comments_warning.docx │ │ ├── compact-style-removal.docx │ │ ├── compact-style-removal.native │ │ ├── cross_reference.docx │ │ ├── cross_reference.native │ │ ├── custom-style-no-styles.native │ │ ├── custom-style-preserve.native │ │ ├── custom-style-reference.docx │ │ ├── custom-style-roundtrip-end.native │ │ ├── custom-style-with-styles.native │ │ ├── custom_style.native │ │ ├── deep_normalize.docx │ │ ├── deep_normalize.native │ │ ├── definition_list.docx │ │ ├── definition_list.native │ │ ├── diagram.docx │ │ ├── diagram.native │ │ ├── document-properties-short-desc.native │ │ ├── document-properties.native │ │ ├── drop_cap.docx │ │ ├── drop_cap.native │ │ ├── dummy_item_after_list_item.docx │ │ ├── dummy_item_after_list_item.native │ │ ├── dummy_item_after_paragraph.docx │ │ ├── dummy_item_after_paragraph.native │ │ ├── empty_field.docx │ │ ├── empty_field.native │ │ ├── enumerated_headings.docx │ │ ├── enumerated_headings.native │ │ ├── german-reference.docx │ │ ├── german_styled_lists.docx │ │ ├── german_styled_lists.native │ │ ├── golden/ │ │ │ ├── block_quotes.docx │ │ │ ├── codeblock.docx │ │ │ ├── comments.docx │ │ │ ├── custom_style_no_reference.docx │ │ │ ├── custom_style_preserve.docx │ │ │ ├── custom_style_reference.docx │ │ │ ├── definition_list.docx │ │ │ ├── document-properties-short-desc.docx │ │ │ ├── document-properties.docx │ │ │ ├── headers.docx │ │ │ ├── image.docx │ │ │ ├── inline_code.docx │ │ │ ├── inline_formatting.docx │ │ │ ├── inline_images.docx │ │ │ ├── link_in_notes.docx │ │ │ ├── links.docx │ │ │ ├── lists.docx │ │ │ ├── lists_9994.docx │ │ │ ├── lists_continuing.docx │ │ │ ├── lists_div_bullets.docx │ │ │ ├── lists_multiple_initial.docx │ │ │ ├── lists_restarting.docx │ │ │ ├── nested_anchors_in_header.docx │ │ │ ├── notes.docx │ │ │ ├── raw-blocks.docx │ │ │ ├── raw-bookmarks.docx │ │ │ ├── table_one_row.docx │ │ │ ├── table_with_list_cell.docx │ │ │ ├── tables-default-widths.docx │ │ │ ├── tables.docx │ │ │ ├── tables_separated_with_rawblock.docx │ │ │ ├── task_list.docx │ │ │ ├── track_changes_deletion.docx │ │ │ ├── track_changes_insertion.docx │ │ │ ├── track_changes_move.docx │ │ │ ├── track_changes_scrubbed_metadata.docx │ │ │ ├── unicode.docx │ │ │ └── verbatim_subsuper.docx │ │ ├── hanging_indent.docx │ │ ├── hanging_indent.native │ │ ├── headers.docx │ │ ├── headers.native │ │ ├── i18n_blocks.docx │ │ ├── i18n_blocks.native │ │ ├── image.docx │ │ ├── image_no_embed.native │ │ ├── image_no_embed_writer.native │ │ ├── image_vml.docx │ │ ├── image_vml.native │ │ ├── image_vml_as_object.docx │ │ ├── image_vml_as_object.native │ │ ├── image_with_textbox_caption.docx │ │ ├── image_with_textbox_caption.native │ │ ├── image_writer_test.native │ │ ├── inline_code.docx │ │ ├── inline_code.native │ │ ├── inline_formatting.docx │ │ ├── inline_formatting.native │ │ ├── inline_formatting_writer.native │ │ ├── inline_images.docx │ │ ├── inline_images.native │ │ ├── inline_images_writer.native │ │ ├── inline_images_writer_test.native │ │ ├── instrText_hyperlink.docx │ │ ├── instrText_hyperlink.native │ │ ├── link_in_notes.docx │ │ ├── link_in_notes.native │ │ ├── links.docx │ │ ├── links.native │ │ ├── links_writer.native │ │ ├── lists-compact.docx │ │ ├── lists-compact.native │ │ ├── lists.docx │ │ ├── lists.native │ │ ├── lists_9994.native │ │ ├── lists_continuing.docx │ │ ├── lists_continuing.native │ │ ├── lists_div_bullets.native │ │ ├── lists_level_override.docx │ │ ├── lists_level_override.native │ │ ├── lists_multiple_initial.native │ │ ├── lists_restarting.docx │ │ ├── lists_restarting.native │ │ ├── lists_sublist_reset.docx │ │ ├── lists_sublist_reset.native │ │ ├── lists_writer.native │ │ ├── mendeley_citations.docx │ │ ├── mendeley_citations_minus.native │ │ ├── mendeley_citations_plus.native │ │ ├── metadata.docx │ │ ├── metadata.native │ │ ├── metadata_after_normal.docx │ │ ├── metadata_after_normal.native │ │ ├── nested_anchors_in_header.docx │ │ ├── nested_anchors_in_header.native │ │ ├── nested_instrText.docx │ │ ├── nested_instrText.native │ │ ├── nested_sdt.docx │ │ ├── nested_sdt.native │ │ ├── nested_smart_tags.docx │ │ ├── nested_smart_tags.native │ │ ├── normalize.docx │ │ ├── normalize.native │ │ ├── notes.docx │ │ ├── notes.native │ │ ├── numbered_header.docx │ │ ├── numbered_header.native │ │ ├── overlapping_targets.docx │ │ ├── overlapping_targets.native │ │ ├── pageref.docx │ │ ├── pageref.native │ │ ├── paragraph_insertion_deletion.docx │ │ ├── paragraph_insertion_deletion_accept.native │ │ ├── paragraph_insertion_deletion_all.native │ │ ├── paragraph_insertion_deletion_reject.native │ │ ├── raw-blocks.native │ │ ├── raw-bookmarks.native │ │ ├── relative_indentation_blockquotes.docx │ │ ├── relative_indentation_blockquotes.native │ │ ├── sdt_elements.docx │ │ ├── sdt_elements.native │ │ ├── sdt_in_footnote.docx │ │ ├── sdt_in_footnote.native │ │ ├── special_punctuation.docx │ │ ├── special_punctuation.native │ │ ├── table_captions_no_field.docx │ │ ├── table_captions_no_field.native │ │ ├── table_captions_with_field.docx │ │ ├── table_captions_with_field.native │ │ ├── table_gridbefore.docx │ │ ├── table_gridbefore.native │ │ ├── table_header_rowspan.docx │ │ ├── table_header_rowspan.native │ │ ├── table_one_header_row.docx │ │ ├── table_one_header_row.native │ │ ├── table_one_row.docx │ │ ├── table_one_row.native │ │ ├── table_variable_width.docx │ │ ├── table_variable_width.native │ │ ├── table_with_list_cell.docx │ │ ├── table_with_list_cell.native │ │ ├── tables-default-widths.native │ │ ├── tables.docx │ │ ├── tables.native │ │ ├── tables_separated_with_rawblock.native │ │ ├── tabs.docx │ │ ├── tabs.native │ │ ├── task_list.docx │ │ ├── task_list.native │ │ ├── text_in_shape_format.docx │ │ ├── text_in_shape_format.native │ │ ├── textbox_image.docx │ │ ├── textbox_image.native │ │ ├── textbox_image_duplicate_encoding.docx │ │ ├── textbox_image_duplicate_encoding.native │ │ ├── track_changes_deletion.docx │ │ ├── track_changes_deletion_accept.native │ │ ├── track_changes_deletion_all.native │ │ ├── track_changes_deletion_reject.native │ │ ├── track_changes_insertion.docx │ │ ├── track_changes_insertion_accept.native │ │ ├── track_changes_insertion_all.native │ │ ├── track_changes_insertion_reject.native │ │ ├── track_changes_move.docx │ │ ├── track_changes_move_accept.native │ │ ├── track_changes_move_all.native │ │ ├── track_changes_move_reject.native │ │ ├── track_changes_scrubbed_metadata.docx │ │ ├── track_changes_scrubbed_metadata.native │ │ ├── trailing_spaces_in_formatting.docx │ │ ├── trailing_spaces_in_formatting.native │ │ ├── trim_last_inline.docx │ │ ├── trim_last_inline.native │ │ ├── unicode.docx │ │ ├── unicode.native │ │ ├── unused_anchors.docx │ │ ├── unused_anchors.native │ │ ├── verbatim_subsuper.docx │ │ ├── verbatim_subsuper.native │ │ ├── zotero_citations.docx │ │ ├── zotero_citations_minus.native │ │ └── zotero_citations_plus.native │ ├── dokuwiki_external_images.dokuwiki │ ├── dokuwiki_external_images.native │ ├── dokuwiki_inline_formatting.dokuwiki │ ├── dokuwiki_inline_formatting.native │ ├── dokuwiki_multiblock_table.dokuwiki │ ├── dokuwiki_multiblock_table.native │ ├── endnotexml-reader.native │ ├── endnotexml-reader.xml │ ├── epub/ │ │ ├── epub2_cover.epub │ │ ├── epub2_no_cover.epub │ │ ├── epub2_picture.epub │ │ ├── features.epub │ │ ├── features.native │ │ ├── formatting.epub │ │ ├── formatting.native │ │ ├── img.epub │ │ ├── img_no_cover.epub │ │ ├── wasteland.epub │ │ └── wasteland.native │ ├── fb2/ │ │ ├── basic.fb2 │ │ ├── basic.markdown │ │ ├── images-embedded.fb2 │ │ ├── images-embedded.html │ │ ├── images.fb2 │ │ ├── images.markdown │ │ ├── math.fb2 │ │ ├── math.markdown │ │ ├── meta.fb2 │ │ ├── meta.markdown │ │ ├── reader/ │ │ │ ├── emphasis.fb2 │ │ │ ├── emphasis.native │ │ │ ├── epigraph.fb2 │ │ │ ├── epigraph.native │ │ │ ├── meta.fb2 │ │ │ ├── meta.native │ │ │ ├── notes.fb2 │ │ │ ├── notes.native │ │ │ ├── poem.fb2 │ │ │ ├── poem.native │ │ │ ├── titles.fb2 │ │ │ └── titles.native │ │ ├── titles.fb2 │ │ └── titles.markdown │ ├── haddock-reader.haddock │ ├── haddock-reader.native │ ├── html-reader.html │ ├── html-reader.native │ ├── insert │ ├── ipynb/ │ │ ├── mime.ipynb │ │ ├── mime.native │ │ ├── mime.out.ipynb │ │ ├── rank.ipynb │ │ ├── rank.out.html │ │ ├── simple.in.native │ │ ├── simple.ipynb │ │ └── simple.out.native │ ├── jats-reader.native │ ├── jats-reader.xml │ ├── jira-reader.jira │ ├── jira-reader.native │ ├── latex-reader.latex │ ├── latex-reader.native │ ├── lhs-test-markdown.native │ ├── lhs-test.fragment.html+lhs │ ├── lhs-test.html │ ├── lhs-test.html+lhs │ ├── lhs-test.latex │ ├── lhs-test.latex+lhs │ ├── lhs-test.markdown │ ├── lhs-test.markdown+lhs │ ├── lhs-test.native │ ├── lhs-test.rst │ ├── lhs-test.rst+lhs │ ├── man-reader.man │ ├── man-reader.native │ ├── markdown-citations.native │ ├── markdown-citations.txt │ ├── markdown-reader-more.native │ ├── markdown-reader-more.txt │ ├── mediawiki-reader.native │ ├── mediawiki-reader.wiki │ ├── odt/ │ │ ├── markdown/ │ │ │ ├── blockquote2.md │ │ │ ├── bold.md │ │ │ ├── citation.md │ │ │ ├── endnote.md │ │ │ ├── externalLink.md │ │ │ ├── footnote.md │ │ │ ├── formula.md │ │ │ ├── headers.md │ │ │ ├── horizontalRule.md │ │ │ ├── image.md │ │ │ ├── imageIndex.md │ │ │ ├── imageWithCaption.md │ │ │ ├── italic.md │ │ │ ├── listBlocks.md │ │ │ ├── paragraph.md │ │ │ ├── strikeout.md │ │ │ ├── trackedChanges.md │ │ │ └── underlined.md │ │ ├── native/ │ │ │ ├── blockquote.native │ │ │ ├── image.native │ │ │ ├── imageIndex.native │ │ │ ├── imageRelative.native │ │ │ ├── imageWithCaption.native │ │ │ ├── inlinedCode.native │ │ │ ├── listContinueNumbering.native │ │ │ ├── listContinueNumbering2.native │ │ │ ├── orderedListHeader.native │ │ │ ├── orderedListMixed.native │ │ │ ├── orderedListRoman.native │ │ │ ├── orderedListSimple.native │ │ │ ├── preformattedText.native │ │ │ ├── preformattedTextParentStyle.native │ │ │ ├── referenceToChapter.native │ │ │ ├── referenceToListItem.native │ │ │ ├── referenceToText.native │ │ │ ├── simpleTable.native │ │ │ ├── simpleTableWithCaption.native │ │ │ ├── simpleTableWithHeader.native │ │ │ ├── simpleTableWithMultipleHeaderRows.native │ │ │ ├── sourceText.native │ │ │ ├── tab.native │ │ │ ├── tableWithContents.native │ │ │ ├── tableWithSpans.native │ │ │ ├── textMixedStyles.native │ │ │ ├── unicode.native │ │ │ ├── unorderedList.native │ │ │ └── unorderedListHeader.native │ │ └── odt/ │ │ ├── blockquote.odt │ │ ├── blockquote2.odt │ │ ├── bold.odt │ │ ├── citation.odt │ │ ├── endnote.odt │ │ ├── expression.odt │ │ ├── expressionUnevaluated.odt │ │ ├── externalLink.odt │ │ ├── footnote.odt │ │ ├── formula.odt │ │ ├── headers.odt │ │ ├── hiddenTextByStyle.odt │ │ ├── hiddenTextByVariable.odt │ │ ├── horizontalRule.odt │ │ ├── image.odt │ │ ├── imageIndex.odt │ │ ├── imageRelative.odt │ │ ├── imageWithCaption.odt │ │ ├── inlinedCode.odt │ │ ├── italic.odt │ │ ├── listBlocks.odt │ │ ├── listContinueNumbering.odt │ │ ├── listContinueNumbering2.odt │ │ ├── orderedListHeader.odt │ │ ├── orderedListMixed.odt │ │ ├── orderedListRoman.odt │ │ ├── orderedListSimple.odt │ │ ├── paragraph.odt │ │ ├── preformattedText.odt │ │ ├── preformattedTextParentStyle.odt │ │ ├── referenceAllInOne.odt │ │ ├── referenceToChapter.odt │ │ ├── referenceToListItem.odt │ │ ├── referenceToText.odt │ │ ├── simpleTable.odt │ │ ├── simpleTableWithCaption.odt │ │ ├── simpleTableWithHeader.odt │ │ ├── simpleTableWithMultipleHeaderRows.odt │ │ ├── sourceText.odt │ │ ├── strikeout.odt │ │ ├── tab.odt │ │ ├── table.odt │ │ ├── tableWithCaption.odt │ │ ├── tableWithContents.odt │ │ ├── tableWithSpans.odt │ │ ├── textMixedStyles.odt │ │ ├── trackedChanges.odt │ │ ├── underlined.odt │ │ ├── unicode.odt │ │ ├── unorderedList.odt │ │ ├── unorderedListHeader.odt │ │ └── variable.odt │ ├── opml-reader.native │ ├── opml-reader.opml │ ├── org-select-tags.native │ ├── org-select-tags.org │ ├── pipe-tables.native │ ├── pipe-tables.txt │ ├── pod-reader.native │ ├── pod-reader.pod │ ├── pptx/ │ │ ├── background-image/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── blanks/ │ │ │ ├── just-speaker-notes/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ ├── nbsp-in-body/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ └── nbsp-in-heading/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── code/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── code-custom/ │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── comparison/ │ │ │ ├── both-columns/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ ├── extra-image/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ ├── extra-text/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ ├── non-text-first/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ └── one-column/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── content-with-caption/ │ │ │ ├── heading-text-image/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ ├── image-text/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ └── text-image/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── document-properties/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── document-properties-short-desc/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── endnotes/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── endnotes-toc/ │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── footer/ │ │ │ ├── basic/ │ │ │ │ ├── output.pptx │ │ │ │ └── reference.pptx │ │ │ ├── fixed-date/ │ │ │ │ ├── output.pptx │ │ │ │ └── reference.pptx │ │ │ ├── higher-slide-number/ │ │ │ │ ├── output.pptx │ │ │ │ └── reference.pptx │ │ │ ├── input.native │ │ │ └── no-title-slide/ │ │ │ ├── output.pptx │ │ │ └── reference.pptx │ │ ├── images/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── incremental-lists/ │ │ │ ├── with-flag/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ └── without-flag/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── inline-formatting/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── layouts/ │ │ │ ├── deleted.pptx │ │ │ ├── input.native │ │ │ └── moved.pptx │ │ ├── list-level/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── lists/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── metadata-speaker-notes/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── pauses/ │ │ │ └── without-incremental/ │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── raw-ooxml/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── reference-deleted-layouts.pptx │ │ ├── reference-depth.pptx │ │ ├── reference-moved-layouts.pptx │ │ ├── reference-no-slides/ │ │ │ ├── add-slides/ │ │ │ │ ├── input.native │ │ │ │ └── output.pptx │ │ │ └── with-notes/ │ │ │ ├── input.native │ │ │ └── output.pptx │ │ ├── reference-no-slides.pptx │ │ ├── remove-empty-slides/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── single-column/ │ │ │ ├── image/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ └── text/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── slide-breaks/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── slide-breaks-slide-level-1/ │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── slide-breaks-toc/ │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── slide-level-0/ │ │ │ ├── h1-h2-with-table/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ ├── h1-with-image/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ ├── h1-with-table/ │ │ │ │ ├── input.native │ │ │ │ ├── output.pptx │ │ │ │ └── templated.pptx │ │ │ └── h2-with-image/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── speaker-notes/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── speaker-notes-after-metadata/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── speaker-notes-afterheader/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── speaker-notes-afterseps/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── start-numbering-at/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ ├── tables/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ └── two-column/ │ │ ├── all-text/ │ │ │ ├── input.native │ │ │ ├── output.pptx │ │ │ └── templated.pptx │ │ └── text-and-image/ │ │ ├── input.native │ │ ├── output.pptx │ │ └── templated.pptx │ ├── pptx-reader/ │ │ ├── basic.native │ │ └── basic.pptx │ ├── rst-reader.native │ ├── rst-reader.rst │ ├── rtf/ │ │ ├── accent.native │ │ ├── accent.rtf │ │ ├── bookmark.native │ │ ├── bookmark.rtf │ │ ├── footnote.native │ │ ├── footnote.rtf │ │ ├── formatting.native │ │ ├── formatting.rtf │ │ ├── heading.native │ │ ├── heading.rtf │ │ ├── image.native │ │ ├── image.rtf │ │ ├── link.native │ │ ├── link.rtf │ │ ├── list_complex.native │ │ ├── list_complex.rtf │ │ ├── list_simple.native │ │ ├── list_simple.rtf │ │ ├── table_error_codes.native │ │ ├── table_error_codes.rtf │ │ ├── table_simple.native │ │ ├── table_simple.rtf │ │ ├── unicode.native │ │ └── unicode.rtf │ ├── s5-basic.html │ ├── s5-fancy.html │ ├── s5-fragment.html │ ├── s5-inserts.html │ ├── s5.native │ ├── tables/ │ │ ├── nordics.html4 │ │ ├── nordics.html5 │ │ ├── nordics.jats_archiving │ │ ├── nordics.latex │ │ ├── nordics.markdown │ │ ├── nordics.mediawiki │ │ ├── nordics.native │ │ ├── nordics.typst │ │ ├── planets.html4 │ │ ├── planets.html5 │ │ ├── planets.jats_archiving │ │ ├── planets.latex │ │ ├── planets.markdown │ │ ├── planets.mediawiki │ │ ├── planets.native │ │ ├── planets.typst │ │ ├── students.html4 │ │ ├── students.html5 │ │ ├── students.jats_archiving │ │ ├── students.latex │ │ ├── students.markdown │ │ ├── students.mediawiki │ │ ├── students.native │ │ └── students.typst │ ├── tables-rstsubset.native │ ├── tables.asciidoc │ ├── tables.asciidoc_legacy │ ├── tables.bbcode │ ├── tables.context │ ├── tables.djot │ ├── tables.docbook4 │ ├── tables.docbook5 │ ├── tables.dokuwiki │ ├── tables.fb2 │ ├── tables.haddock │ ├── tables.html4 │ ├── tables.html5 │ ├── tables.icml │ ├── tables.jats_archiving │ ├── tables.jats_articleauthoring │ ├── tables.jats_publishing │ ├── tables.jira │ ├── tables.latex │ ├── tables.man │ ├── tables.markdown │ ├── tables.markua │ ├── tables.mediawiki │ ├── tables.ms │ ├── tables.muse │ ├── tables.native │ ├── tables.opendocument │ ├── tables.org │ ├── tables.plain │ ├── tables.rst │ ├── tables.rtf │ ├── tables.tei │ ├── tables.texinfo │ ├── tables.textile │ ├── tables.txt │ ├── tables.typst │ ├── tables.vimdoc │ ├── tables.xwiki │ ├── tables.zimwiki │ ├── test-pandoc.hs │ ├── testsuite.native │ ├── testsuite.txt │ ├── textile-reader.native │ ├── textile-reader.textile │ ├── tikiwiki-reader.native │ ├── tikiwiki-reader.tikiwiki │ ├── twiki-reader.native │ ├── twiki-reader.twiki │ ├── txt2tags.native │ ├── txt2tags.t2t │ ├── typst-reader.native │ ├── typst-reader.typ │ ├── undergradmath.typ │ ├── vimdoc/ │ │ ├── definition-lists.markdown │ │ ├── definition-lists.vimdoc │ │ ├── headers-numbered.vimdoc │ │ ├── headers.markdown │ │ ├── headers.vimdoc │ │ ├── vim-online-doc.markdown │ │ └── vim-online-doc.vimdoc │ ├── vimwiki-reader.native │ ├── vimwiki-reader.wiki │ ├── writer.asciidoc │ ├── writer.asciidoc_legacy │ ├── writer.bbcode │ ├── writer.context │ ├── writer.djot │ ├── writer.docbook4 │ ├── writer.docbook5 │ ├── writer.dokuwiki │ ├── writer.fb2 │ ├── writer.haddock │ ├── writer.html4 │ ├── writer.html5 │ ├── writer.icml │ ├── writer.jats_archiving │ ├── writer.jats_articleauthoring │ ├── writer.jats_publishing │ ├── writer.jira │ ├── writer.latex │ ├── writer.man │ ├── writer.markdown │ ├── writer.markua │ ├── writer.mediawiki │ ├── writer.ms │ ├── writer.muse │ ├── writer.native │ ├── writer.opendocument │ ├── writer.opml │ ├── writer.org │ ├── writer.plain │ ├── writer.rst │ ├── writer.rtf │ ├── writer.tei │ ├── writer.texinfo │ ├── writer.textile │ ├── writer.typst │ ├── writer.vimdoc │ ├── writer.xwiki │ ├── writer.zimwiki │ ├── writers-lang-and-dir.context │ ├── writers-lang-and-dir.latex │ ├── writers-lang-and-dir.native │ └── xlsx-reader/ │ ├── basic.native │ └── basic.xlsx ├── tools/ │ ├── .editorconfig │ ├── build-and-upload-api-docs.sh │ ├── build-arm.sh │ ├── changelog-helper.sh │ ├── changes_template.html │ ├── cliptree.gvpr │ ├── diff-zip.sh │ ├── extract-changes.lua │ ├── github-upload.sh │ ├── latex-package-dependencies.lua │ ├── moduledeps.lua │ ├── pandoc-template-mode.el │ ├── pandoc-xml.dtd │ ├── pandoc-xml.rnc │ ├── pandoc-xml.rng │ ├── pandoc-xml.xsd │ ├── parseTimings.pl │ ├── update-lua-module-docs.lua │ ├── update-readme.lua │ ├── update-translations.py │ ├── validate-docx.sh │ └── validate-docx2.sh ├── wasm/ │ ├── LICENSE │ ├── Makefile │ ├── examples/ │ │ ├── bibtex-to-csljson/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── csv-table-to-org/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── custom-template/ │ │ │ ├── custom.tpl │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── docx-with-equations-to-latex/ │ │ │ ├── equations.docx │ │ │ └── options.json │ │ ├── hello-world/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── highlighted-code-to-html/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── ipynb-to-rtf/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── latex-to-docbook-with-mathml/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── latex-with-macros-to-restructured-text/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── lua-filters/ │ │ │ ├── count_words.lua │ │ │ ├── emph_to_allcaps.lua │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── man-page-to-context/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── markdown-citations-to-plain-with-csl-style/ │ │ │ ├── le-tapuscrit-note.csl │ │ │ ├── options.json │ │ │ ├── refs.bib │ │ │ └── stdin │ │ ├── markdown-to-docbook-with-citations/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── markdown-to-revealjs-slides/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── markdown-to-rst/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── mediawiki-to-docx-with-equations/ │ │ │ ├── options.json │ │ │ └── stdin │ │ ├── ris-to-formatted-markdown-bibliography/ │ │ │ ├── options.json │ │ │ └── stdin │ │ └── rst-table-from-yaml-data/ │ │ ├── custom.rst │ │ ├── options.json │ │ ├── species.rst │ │ └── stdin │ ├── index.html │ ├── index.js │ ├── pandoc.js │ └── patches/ │ ├── conduit-extra.patch │ ├── lua.patch │ ├── memory.patch │ ├── splitmix.patch │ ├── streaming-commons.patch │ └── xml-conduit.patch ├── weeder.toml ├── windows/ │ ├── AdvancedWelcomeEulaDlg_Custom.wxs │ ├── Makefile │ ├── Pandoc-en-us.wxl │ ├── WixUI_Advanced_Custom.wxs │ └── pandoc.wxs └── xml-light/ └── Text/ └── Pandoc/ └── XML/ ├── Light/ │ ├── Output.hs │ ├── Proc.hs │ └── Types.hs └── Light.hs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .cirrus.yml ================================================ task: name: macos_arm64 alias: macos_arm64 trigger_type: manual timeout_in: 90m macos_instance: image: ghcr.io/cirruslabs/macos-runner:sonoma brew_script: - brew update - brew install ghc@9.10 cabal-install env: PATH: /opt/homebrew/opt/ghc@9.10/bin:${PATH} cabal_store_cache: folder: ~/.cabal/store fingerprint_key: macos_2023_03_12 deps_script: - cabal update - cabal build all -fembed_data_files -fhttp -fserver -flua --dependencies-only upload_caches: - cabal_store install_script: - sh macos/make_macos_release.sh macos_arm64_artifacts: path: ./macos-arm64/** task: name: linux_arm64 alias: linux_arm64 trigger_type: manual timeout_in: 90m arm_container: image: quay.io/benz0li/ghc-musl:9.10 cpu: 4 memory: 12G env: CABALOPTS: -f-export-dynamic -fembed_data_files -fhttp -fserver -flua --enable-executable-static -j4 GHCOPTS: -j4 +RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread cabal_store_cache: folder: ~/.cabal/store fingerprint_key: linux_arm64_2023_03_11 deps_script: - cabal update - cabal build --dependencies-only $CABALOPTS --ghc-options="$GHCOPTS" pandoc-cli upload_caches: - cabal_store package_script: - bash linux/make_artifacts.sh - cabal freeze && cat cabal.project.freeze linux_arm64_artifacts: path: ./linux-arm64/** task: name: linux_amd64 alias: linux_amd64 trigger_type: manual timeout_in: 90m container: image: quay.io/benz0li/ghc-musl:9.10 cpu: 4 memory: 12G env: CABALOPTS: -f-export-dynamic -fembed_data_files -fhttp -fserver -flua --enable-executable-static -j4 GHCOPTS: -j4 +RTS -A256m -RTS -split-sections -optc-Os -optl=-pthread cabal_store_cache: folder: ~/.cabal/store fingerprint_key: linux_amd64_2023_03_12 deps_script: - cabal update - cabal build --dependencies-only $CABALOPTS --ghc-options="$GHCOPTS" pandoc-cli upload_caches: - cabal_store package_script: - cabal freeze && cat cabal.project.freeze - bash linux/make_artifacts.sh linux_amd64_artifacts: path: ./linux-amd64/** ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 end_of_line = lf indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true [*.{zip,docx}] charset = end_of_line = indent_style = indent_size = insert_final_newline = trim_trailing_whitespace = [*.{markdown,md}] trim_trailing_whitespace = false [test/*] insert_final_newline = false trim_trailing_whitespace = false ================================================ FILE: .gitattributes ================================================ test/fb2/reader/* -text pandoc-lua-engine/test/*.custom -text pandoc-lua-engine/test/*.txt -text ================================================ FILE: .gitignore ================================================ /*.* /build-artifacts* /dist-newstyle /dist /ctags /LOG /TAGS /bench-* !.circleci/** !.cirrus.yml !.envrc !.editorconfig !.gitattributes !.github/** !.gitignore !.hlint.yaml !.mailmap !.stylish-haskell.yaml !AUTHORS.md !BUGS !CITATION.cff !CONTRIBUTING.md !COPYING.md !COPYRIGHT !INSTALL.md !MANUAL.txt !Makefile !README.md !README.template !RELEASE-CHECKLIST-TEMPLATE.org !SECURITY.md !Setup.hs !cabal.project !changelog.md !pandoc.cabal !hie.yaml !flake.nix !flake.lock !default.nix !release.nix !shell.nix !stack.yaml !weeder.toml !app/** !benchmark/** !citeproc/** !data/** !doc/** !linux/** !macos/** !man/** !pandoc-lua-engine/** !pandoc-server/** !pandoc-cli/** !src/** !test/** !tools/** !trypandoc/** !xml-light/** !windows/** *.bkp *.orig *.o *.hi *.dyn_o *.dyn_hi *~ .*sw? .DS_Store .stack-work ================================================ FILE: .hlint.yaml ================================================ # HLint configuration file # https://github.com/ndmitchell/hlint ########################## # Specify additional command line arguments # - arguments: [--color=auto, --cpp-ansi] # Ignore some builtin hints # - ignore: {name: "Avoid lambda"} - ignore: {name: "Use bimap"} - ignore: {name: "Use void"} - ignore: {name: "Eta reduce"} - ignore: {name: "Evaluate"} - ignore: {name: "Reduce duplication"} # TODO: could be more fine-grained - ignore: {name: "Use &&&"} - ignore: {name: "Use String"} - ignore: {name: "Use camelCase"} - ignore: {name: "Use fmap"} # specific for GHC 7.8 compat - ignore: {name: "Use isDigit"} - ignore: {name: "Use <&>"} - ignore: name: "Monad law, left identity" within: Text.Pandoc.App.OutputSettings - ignore: name: "Move brackets to avoid $" within: Text.Pandoc.Writers.CslJson - ignore: name: "Redundant <$>" within: - Text.Pandoc.Readers.Docx.Parse - Text.Pandoc.Writers.MediaWiki - Text.Pandoc.Writers.OpenDocument - Text.Pandoc.Writers.Powerpoint.Output - Text.Pandoc.Writers.Powerpoint.Presentation - ignore: name: "Redundant return" within: Text.Pandoc.Citeproc.BibTeX # With recent hlint this rule has given false positives on TH - ignore: name: "Redundant bracket" - ignore: name: "Use <$>" within: - Text.Pandoc.Readers.LaTeX - Text.Pandoc.Readers.Markdown - ignore: name: "Use camelCase" within: - Tests.Writers.Docbook - Tests.Writers.Native - Text.Pandoc.Citeproc - Text.Pandoc.Extensions - Text.Pandoc.Lua.Marshaling.Version - Text.Pandoc.Lua.Module.Pandoc - Text.Pandoc.Lua.Module.Utils - Text.Pandoc.Readers.Odt.ContentReader - Text.Pandoc.Readers.Odt.Namespaces - ignore: name: "Use forM_" within: - Text.Pandoc.Readers.DocBook - ignore: name: "Use Just" within: - Text.Pandoc.Citeproc.MetaValue - Text.Pandoc.Readers.Odt.ContentReader - Text.Pandoc.Writers.Roff - ignore: name: "Use list comprehension" within: Text.Pandoc.Citeproc.BibTeX - ignore: name: "Use list literal pattern" within: Text.Pandoc.Citeproc.MetaValue # TODO: check - ignore: name: "Use second" within: - Text.Pandoc.Citeproc.BibTeX - Text.Pandoc.Citeproc.Locator # TODO: check - ignore: name: "Use sortOn" within: Text.Pandoc.Writers.OpenDocument - ignore: name: "Use tuple-section" within: - Text.Pandoc.Readers.EPUB - Text.Pandoc.ImageSize - Text.Pandoc.Readers.Markdown - Text.Pandoc.Readers.RST # fromRight is only in base >= 4.10 - ignore: name: "Use fromRight" # Define some custom infix operators # - fixity: infixr 3 ~^#^~ ================================================ FILE: .mailmap ================================================ Adam Brandizzi Albert Krewinkel Albert Krewinkel Albert Krewinkel Albert Krewinkel Alexander Krotov Alex Ivkin Alex Ivkin Andrew Dunning Andrew Dunning Andrew Newman Clare Macrae Hugo Roy Hugo Roy John MacFarlane John MacFarlane John MacFarlane Jose Luis Duran Marc Schreiber Masayoshi Takahashi Matthew Pickering Mauro Bieg Mauro Bieg Mauro Bieg Mauro Bieg Mauro Bieg Nils Carlson Nils Carlson Robin Lambertz Roland Hieber Sebastian Talmon <35015406+stalmon@users.noreply.github.com> Sergei Trofimovich Sergei Trofimovich Stefan Björk Thomas Hodgson Timm Albers Yan Pashkovsky ================================================ FILE: .stylish-haskell.yaml ================================================ # stylish-haskell configuration file # ================================== # The stylish-haskell tool is mainly configured by specifying steps. These steps # are a list, so they have an order, and one specific step may appear more than # once (if needed). Each file is processed by these steps in the given order. steps: # Convert some ASCII sequences to their Unicode equivalents. This is disabled # by default. # - unicode_syntax: # # In order to make this work, we also need to insert the UnicodeSyntax # # language pragma. If this flag is set to true, we insert it when it's # # not already present. You may want to disable it if you configure # # language extensions using some other method than pragmas. Default: # # true. # add_language_pragma: true # Align the right hand side of some elements. This is quite conservative # and only applies to statements where each element occupies a single # line. - simple_align: cases: true top_level_patterns: true records: true # Import cleanup - imports: # There are different ways we can align names and lists. # # - global: Align the import names and import list throughout the entire # file. # # - file: Like global, but don't add padding when there are no qualified # imports in the file. # # - group: Only align the imports per group (a group is formed by adjacent # import lines). # # - none: Do not perform any alignment. # # Default: global. align: none # Following options affect only import list alignment. # # List align has following options: # # - after_alias: Import list is aligned with end of import including # 'as' and 'hiding' keywords. # # > import qualified Data.List as List (concat, foldl, foldr, head, # > init, last, length) # # - with_alias: Import list is aligned with start of alias or hiding. # # > import qualified Data.List as List (concat, foldl, foldr, head, # > init, last, length) # # - new_line: Import list starts always on new line. # # > import qualified Data.List as List # > (concat, foldl, foldr, head, init, last, length) # # Default: after_alias list_align: after_alias # Long list align style takes effect when import is too long. This is # determined by 'columns' setting. # # - inline: This option will put as much specs on same line as possible. # # - new_line: Import list will start on new line. # # - new_line_multiline: Import list will start on new line when it's # short enough to fit to single line. Otherwise it'll be multiline. # # - multiline: One line per import list entry. # Type with constructor list acts like single import. # # > import qualified Data.Map as M # > ( empty # > , singleton # > , ... # > , delete # > ) # # Default: inline long_list_align: inline # List padding determines indentation of import list on lines after import. # This option affects 'list_align' and 'long_list_align'. list_padding: 4 # Separate lists option affects formatting of import list for type # or class. The only difference is single space between type and list # of constructors, selectors and class functions. # # - true: There is single space between Foldable type and list of it's # functions. # # > import Data.Foldable (Foldable (fold, foldl, foldMap)) # # - false: There is no space between Foldable type and list of it's # functions. # # > import Data.Foldable (Foldable(fold, foldl, foldMap)) # # Default: true separate_lists: true # Language pragmas - language_pragmas: # We can generate different styles of language pragma lists. # # - vertical: Vertical-spaced language pragmas, one per line. # # - compact: A more compact style. # # - compact_line: Similar to compact, but wrap each line with # `{-#LANGUAGE #-}'. # # Default: vertical. style: vertical # Align affects alignment of closing pragma brackets. # # - true: Brackets are aligned in same column. # # - false: Brackets are not aligned together. There is only one space # between actual import and closing bracket. # # Default: true align: true # stylish-haskell can detect redundancy of some language pragmas. If this # is set to true, it will remove those redundant pragmas. Default: true. remove_redundant: true # Replace tabs by spaces. This is disabled by default. # - tabs: # # Number of spaces to use for each tab. Default: 8, as specified by the # # Haskell report. # spaces: 8 # Remove trailing whitespace - trailing_whitespace: {} # A common setting is the number of columns (parts of) code will be wrapped # to. Different steps take this into account. Default: 80. columns: 80 # By default, line endings are converted according to the OS. You can override # preferred format here. # # - native: Native newline format. CRLF on Windows, LF on other OSes. # # - lf: Convert to LF ("\n"). # # - crlf: Convert to CRLF ("\r\n"). # # Default: native. newline: native # Sometimes, language extensions are specified in a cabal file or from the # command line instead of using language pragmas in the file. stylish-haskell # needs to be aware of these, so it can parse the file correctly. # # No language extensions are enabled by default. # language_extensions: # - TemplateHaskell # - QuasiQuotes ================================================ FILE: AUTHORS.md ================================================ # Contributors - Aaron Wolen - Adelar da Silva Queiróz - Agriya Khetarpal - Agustín Martín Barbero - Akash Patel - Akos Marton - Albert Krewinkel - Albert Lei - Alex Ivkin - Alex Toldaiev - Alex Vong - Alexander Batischev - Alexander Kondratskiy - Alexander Krotov - Alexander Sulfrian - Alexander V Vershilov - Alexandre Franke - Alfred Wechselberger - Amar Al-Zubaidi - Amir Dekel - Amneesh Singh - Amogh Rathore - Amy de Buitléir - Anabra - Anders Waldenborg - Andreas Deininger - Andreas Lööw - Andreas Scherer - Andres Freund - Andrew Dunning - Andy Morris - Aner Lucero - Anti-Distinctlyminty - Antoine Latter - Anton Antich - Anton Melnikov - Antonio Terceiro - Arata Mizuki - Arata Mizuki - Arfon Smith - Arlo O'Keeffe - Artem Pelenitsyn - Artyom Kazak - Asliddinbek Azizovich - B. Scott Michel - Bastien Dumont - Ben Firshman - Ben Gamari - Ben Steinberg - Beni Cherniavsky-Paskin - Benjamin Bray - Benjamin Esham - Benjamin Wuethrich - Benoit Schweblin - Benson Muite - Bjorn Buckwalter - Blake Eryx - Bodigrim - Bradley Kuhn - Brent Yorgey - Brian Leung - Bryan O'Sullivan - Caleb McDaniel - Caleb Mclennan - Calvin Beck - Carlos Scheidegger - Carlos Sosa - Carsten Allefeld - Castedo Ellerman - Cécile Chemin - Cédric Couralet - Cezary Drożak - Chandrahas77 - Charanjit Singh - Charles Tapley Hoyt - Charlotte Koch - Chris Black - Chris Callison-Burch - Christian Conkle - Christian Christiansen - Christian Despres - Christoffer Ackelman - Christoffer Sawicki - Christophe Dervieux - Christopher Kenny - Clare Macrae - Clint Adams - Conal Elliott - Cormac Relf - Craig S. Bosma - Damien Clochard - Daniel Bergey - Daniel Kessler - Daniel Maslowski - Daniel T. Staal - Daniele D'Orazio - David A Roberts - David Lazar - David Martschenko - David Röthlisberger - Denis Laxalde - Denis Maier - Derek Chen-Becker - Diego Balseiro - Diogo Almiro - Dimitri Sabadie - Dimitris Apostolou - Dmitry Pogodin - Dmitry Volodin - Douglas Calvert - Edwin Török - Eigil Rischel - Elliot Bobrow - Emanuel Evans - Emmanuel Ferdman - Emerson Harkin - Emily Bourke - Emily Eisenberg - Eric Kow - Eric Schrijver - Eric Seidel - Erik Post - Erik Rask - Ethan Riley - Étienne Bersac - Evan Silberman - Even Brenden - Ezwal - Fabián Heredia Montiel - Félix Baylac-Jacqué - Felix Yan - Florian Beeres - Florian Eitel - Florian Klink - Florian Kohrt - FoxChillz - Francesco Mazzoli - Francesco Occhipinti - François Gannaz - Frank Seifferth - Frederik Elwert - Freiric Barral - Freirich Raabe - Frerich Raabe - Fyodor Sheremetyev - Gabor Pali - Gabriel Lewertowski - Gavin Beatty - George Stagg - Georgi Lyubenov - GHyman83 - Gokul Rajiv - Gordon Woodhull - Gottfried Haider - Greg Maslov - Greg Rundlett - Grégory Bataille - Guriy Samarin - Gwern Branwen - Hamish Mackenzie - Hans-Peter Deifel - Hendrik Erz - Heiko Schlittermann - Henrik Tramberend - Henry de Valence - Herwig Stuetz - Hikaru Ibayashi - Hos Es - Hubert Plociniczak - Iacobus1983 - Ian Max Andolina - Igor Khorlo - Igor Pashev - Ilona Silverwood - Ilya V. Portnov - Ivan Panchenko - Ivan Trubach - Ivar de Bruin - Ivo Clarysse - Jaap de Jong - Jacob Larkin - James J Balamuta - J. B. Rainsberger - J. Lewis Muir - Jackson Schuster - Jaehwang Jung - Jaime Marquínez Ferrándiz - Jake Zimmerman - Jakob Voß - James Aspnes - James P. Ascher - James Barlow - James Scott-Brown - Jamie F. Olson - Jan Larres - Jan Schulz - Jan Tojnar - Jannik Buhr - Jan-Otto Kröpke - Jared Lander - Jason Ronallo - Jeff Arnold - Jeff Runningen - Jens Getreu - Jens Petersen - Jens Oehlschlägel - Jesse Hathaway - Jeremie Knuesel - Jérémy Bobbio - Jeroen de Haas - Jerry Sky - Jesse Rosenthal - Jez Cope - Joe Hermaszewski - Joe Hillenbrand - John KetzerX - John Luke Bentley - John MacFarlane - John Muccigrosso - John Purnell - Jonas Scholl - Jonas Smedegaard - Jonathan - Jonathan Daugherty - Jonathan Dönszelmann - Jose Luis Duran - José de Mattos Neto - Josef Svenningsson - Joseph C. Sible - Julia Diaz - Julien Cretel - Julien Dutant - Juliette Fourcot - Juliusz Gonera - Justin Bogner - Justin Wood - Karl Pettersson - Keiichiro Shikano - Kelsey Hightower - Kevin Broch - Kolen Cheung - Konstantin Zudov - Kristof Bastiaensen - Krystof Beuermann - Lars-Dominik Braun - Laurent P. René de Cotret - Lawrence Chonavel - Leif Metcalf - Leo Heitmann Ruiz - Leonard Rosenthol - Lila - Link Swanson - Loïc Grobol - Lorenzo - Lucas Escot - Lucas Viana - Lucas V. R - Luis Rivera - Luke Plant - Manolis Stamatogiannakis - Marc Schreiber - Marcin Serwin - Mario Lang - Mark Gardner - Mark Szepieniec - Mark Wright - Martin Joerg - Martin Linn - Martin Michlmayr - Martín Pozo - Masataka Ogawa - Masayoshi Takahashi - Matej Kollar - Mathias Schenner - Mathias Walter - Mathieu Boespflug - Mathieu Duponchelle - Matt Dodson - Matthew Doty - Matthew Eddey - Matthew Pickering - Matthias C. M. Troffaes - Mauro Bieg - Max Bolingbroke - Max Heller - Max Rydahl Andersen - Merijn Verstraaten - Michael Beaumont - Michael Chladek - Michael Hoffmann - Michael McClurg - Michael Peyton Jones - Michael Reed - Michael Snoyman - Michael Stahl - Michael Thompson - Mickaël Canouil - Mike Tzou - Mikołaj Machowski - Milan Bracke - MinRK - Mohamed Akram - Morgan Willcock - Morton Fox - Nathan Gass - Naveen - Neil Mayhew - Nick Bart - Nick Berendsen - Nick Fleisher - Nicolas Kaiser - Niklas Eicker - Nikolai Korobeinikov - Nikolay Yakimov - Nils Carlson - Nixon Enraght-Moony - Noah Malmed - Nokome Bentley - Norwid Behrnd - OCzarnecki - Ola Wolska - Ole Martin Ruud - Oliver Fabel - Oliver Matthews - Olivier Benz - Olivier Dossmann - Ophir Lifshitz - Or Neeman - OvidiusCicero - Owen McGrath - Pablo Rodríguez - Pascal Wagler - Pau RE - Paul Rivier - Paul Tilley - Paulo Tanimoto - Pavol Otto - Per Christian Gaustad - Pete Ryland - Peter Briggs - Peter Fabinksi - Peter Wang - Philip Pesca - Philippe Ombredanne - Phillip Alday - Pranesh Prakash - Prat - Prayag Verma - Puneeth Chaganti - Quinn - R. N. West - Ralf Stephan - Raniere Silva - Raymond Berger - Raymond Ehlers - Recai Oktaş - Repetitive - Reuben Thomas - Rowan Rodrik van der Molen - Roland Hieber - Roman Beránek - Ruqi - RyanGlScott - Ryan Gibb - S.P.H - Salim B - Sam S. Almahri - Sam May - Samuel Tardieu - Saumel Lemmenmeier - Santiago Zarate - Sascha Wilde - Scott Morrison - Sean Soon - Sebastian Talmon - Sebbones - Sen-wen Deng - Sergei Trofimovich - Sergey Astanin - Seth Speaks - Shahbaz Youssefi - Shaun Attfield - Shim Myeongseob - Shin Sang-jae - Sidarth Kapur - Sidney Mau - Sidharth Kapur - Simon Hengel - Simon Schuster - Siphalor - Stefan Dresselhaus - Stephan Daus - Stephan Meijer - Stephen Altamirano - Stephen Huan - Stephen Reindl - Stéphane Guillou - Sukil Etxenike - Sukka - Sumit Sahrawat - Suraj Patil - Sven Wick - TEC - Tarik Graba - Tatiana Porras - Terence Eden - Thenaesh Elango - Thomas Hodgson - Thomas Soeiro - Thomas Weißschuh - Tiago-Manzato - Tim Lin - Tim Stewart - Tim Wisotzki - Timm Albers - Timothy Humphries - Tiziano Müller - Todd Sifleet - Tomas Dahlqvist - TomBen - Tom Leese - Toni Dietze - Tristan Stenner - Tristan de Cacqueray - Tristano Ajmone - Tuong Nguyen Manh - Uli Köhler - Urs Liska - Václav Haisman - Václav Zeman - Vaibhav Sagar - Vanessa McHale - Vasily Alferov - Veratyr - Viktor Kronvall - Vincent - Vlad Hanciuta - Vladimir Alexiev - Waldir Pimenta - Wandmalfarbe - Wentao Han - Wikiwide - William Lupton - William Rusnack - Winnie Hellmann - 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 - bumper314 - chinapedia - cholonam - closeobserve - csforste - d-dorazio - damon-sava-stanley - dbecher-ito - ebiim - ech0 - etclub - favonia - guqicun - har7an - harabat - hseg - infinity0x - jeongminkim-islab - josch - kaizshang91 - lawcho - lifeunleaded - lux-lth - luz paz - lwolfsonkin - massifrg - mbracke - mbrackeantidot - mh4ckt3mh4ckt1c4s - mjfs - mt_caret - nbehrnd - niszet - nkalvi - nuew - obcat - oltolm - oquechy - pacien - perro tuerto - piq9117 - priiduonu - qerub - quasicomputational - reptee - ricnorr - robabla - roblabla - rodja.trappe - rski - samuel-weinhardt - sdhoward - shreevatsa.public - takahashim - taotieren - tecosaur - tgkokk - the-solipsist - thomjur - thsutton - thron7 - timo-a - vijayphoenix - vkraven - wgevaert - wiefling - willj-dev - wuffi - λx.x ================================================ FILE: BUGS ================================================ To view a list of known bugs, or to enter a bug report, please use Pandoc's issue tracker: . See also CONTRIBUTING.md. ================================================ FILE: CITATION.cff ================================================ cff-version: 1.2.0 title: Pandoc message: "If you use this software, please cite it as below." type: software url: "https://github.com/jgm/pandoc" authors: - given-names: John family-names: MacFarlane email: jgm@berkeley.edu orcid: 'https://orcid.org/0000-0003-2557-9090' - given-names: Albert family-names: Krewinkel email: tarleb+github@moltkeplatz.de orcid: '0000-0002-9455-0796' - given-names: Jesse family-names: Rosenthal email: jrosenthal@jhu.edu ================================================ FILE: CONTRIBUTING.md ================================================ Contributing to pandoc ====================== Welcome to pandoc! Very soon after its beginnings in 2006, pandoc has been influenced, improved, and modified, by users, devs, and newcomers alike. The project thrives on its active community. It is great to have you here. How can I help? --------------- There are many ways in which you can support pandoc. Here are a few ideas: * Participate in online discussions. The [discussion forum] is a good place for this. * Help with questions. Every request that is answered by the wider community frees time for programming contributors. This will speed up development of new features and issue fixes. Don't underestimate your knowledge, please share it! Good places to help are the [discussion forum], Q/A sites like StackOverflow, community forums (e.g. [RStudio][RStudio Community], [Zettlr][Zettlr Forum]), and, for technical questions, the GitHub [issue tracker]. * Write or improve documentation. If you ran into a problem which took more time to figure out than expected, please consider to save other users from the same experience. People writing the documentation tend to lack an outside view, so please help provide one. Good documentation is both difficult and extremely important. The official docs are not the only place for documentation. Pandoc also has a [Wiki][pandoc wiki]. Private blogs can serve as documentation just as the official manual can. * Contribute code. No matter whether it's a small fix in a format template or a huge lump of Haskell code: help is welcome. It's usually a good idea to talk about the plans early, as this can prevent unnecessary work. See below for more information. * Last but not least: consider funding the development and maintenance of pandoc financially. You can find sponsor buttons on the [pandoc website] and the [GitHub repository][GitHub repo]. A rich ecosystem of libraries, editors, filters, and templates has developed around pandoc; conversely, pandoc builds and depends on a large number of libraries. Contributing to any of these projects is another way that can help to ensure stability, and to keep pushing the boundaries of what is possible with pandoc. [RStudio Community]: https://community.rstudio.com/ [Zettlr Forum]: https://forum.zettlr.com/ [pandoc wiki]: https://github.com/jgm/pandoc/wiki [pandoc website]: https://pandoc.org [GitHub repo]: https://github.com/jgm/pandoc Have a question? ---------------- Ask on the [discussion forum]. Found a bug? ------------ Bug reports are welcome! Please report all bugs on pandoc's GitHub [issue tracker]. Before you submit a bug report, search the [open issues] *and* [closed issues] to make sure the issue hasn't come up before. Also, check the [User's Guide] and [FAQs] for anything relevant. Make sure you can reproduce the bug with the [latest released version] of pandoc---or, even better, the development version, since the bug may have been fixed since the last release. [Nightly builds] are available, so you don't need to compile from source to test against the development version. (To fetch a nightly, visit the link, click the topmost "Nightly" in the table, then choose your platform under "Artifacts." Note that you must be logged in with a GitHub account.) Your report should give detailed, *reproducible* instructions, including * the pandoc version (check using `pandoc -v`) * the exact command line used * the exact input used * the output received * the output you expected instead A small test case (just a few lines) is ideal. If your input is large, try to whittle it down to a *minimum working example*. Out of scope? ------------- A less than perfect conversion does not necessarily mean there's a bug in pandoc. Quoting from the MANUAL: > Because pandoc's intermediate representation of a document is less > expressive than many of the formats it converts between, one should > not expect perfect conversions between every format and every other. > Pandoc attempts to preserve the structural elements of a document, but > not formatting details such as margin size. And some document elements, > such as complex tables, may not fit into pandoc's simple document > model. While conversions from pandoc's Markdown to all formats aspire > to be perfect, conversions from formats more expressive than pandoc's > Markdown can be expected to be lossy. For example, both `docx` and `odt` formats can represent margin size, but because pandoc's internal document model does not contain a representation of margin size, this information will be lost on converting from docx to `odt`. (You can, however, customize margin size using `--reference-doc`.) So before submitting a bug report, consider whether it might be "out of scope." If it concerns a feature of documents that isn't representable in pandoc's Markdown, then it very likely is. (If in doubt, you can always ask on the [discussion forum].) Fixing bugs from the issue tracker ---------------------------------- Almost all the bugs on the issue tracker have one or more associated tags. These are used to indicate the *complexity* and *nature* of a bug. There is not yet a way to indicate priority. An up to date summary of issues can be found on [GitHub labels]. * [good first issue] — The perfect starting point for new contributors. The issue is generic and can be resolved without deep knowledge of the code base. * [enhancement] — A feature which would be desirable. We recommend you discuss any proposed enhancement on the [discussion forum] before writing code. * [bug] — A problem which needs to be fixed. * [complexity:low] — The fix should only be a couple of lines. * [complexity:high] — The fix might require structural changes or in depth knowledge of the code base. * [new:reader] — A request to add a new input format. * [new:writer] — A request to add a new output format. * [docs] — A discrepancy, or ambiguity in the documentation. * [status:in-progress] — Someone is actively working on or planning to work on the ticket. * [status:more-discussion-needed] — It is unclear what the correct approach to solving the ticket is. Before starting on tickets such as this it would be advisable to post on the ticket. * [status:more-info-needed] — We require more information from a user before we can classify a report properly. Issues related to a specific format are tagged accordingly, e.g. feature request or bug reports related to Markdown are labelled with [format:markdown]. Have an idea for a new feature? ------------------------------- First, search the [discussion forum] and the issue tracker (both [open issues] *and* [closed issues]) to make sure that the idea has not been discussed before. Explain the rationale for the feature you're requesting. Why would this feature be useful? Consider also any possible drawbacks, including backwards compatibility, new library dependencies, and performance issues. Features are very rarely "implement and forget", as all code must be maintained. This is especially relevant for large or complex contributions. It is helpful to be sympathetic to that fact, and to communicate future plans and availability clearly. Any potential new feature is best discussed on the [discussion forum] before opening an issue. Patches and pull requests ------------------------- Patches and pull requests are welcome. Before you put time into a nontrivial patch, it is a good idea to discuss it on the [discussion forum], especially if it is for a new feature (rather than fixing a bug). Please follow these guidelines: 1. Each patch (commit) should make a single logical change (fix a bug, add a feature, clean up some code, add documentation). Everything related to that change should be included (including tests and documentation), and nothing unrelated should be included. 2. The first line of the commit message should be a short description of the whole commit (ideally <= 50 characters). Then there should be a blank line, followed by a more detailed description of the change. 3. Follow the stylistic conventions you find in the existing pandoc code. Use spaces, not tabs, and wrap code to 80 columns. Always include type signatures for top-level functions. Consider installing [EditorConfig], this will help you to follow the coding style prevalent in pandoc. 4. Your code should compile without warnings (`-Wall` clean). 5. Run the tests to make sure your code does not introduce new bugs. (See below under [Tests](#tests).) All tests should pass. 6. It is a good idea to add test cases for the bug you are fixing. (See below under [Tests](#tests).) If you are adding a new writer or reader, you must include tests. 7. If you are adding a new feature, include updates to `MANUAL.txt`. 8. All code must be released under the general license governing pandoc (GPL v2). 9. It is better not to introduce new dependencies. Dependencies on 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 we support ghc versions 9.6 and higher. All pull requests and commits are tested automatically on GitHub Actions. Tests ----- Tests can be run as follows: cabal install --only-dependencies --enable-tests cabal configure --enable-tests cabal build cabal test or, if you're using [stack], stack setup stack test The test program is `test/test-pandoc.hs`. To run particular tests (pattern-matching on their names), use the `-p` option: cabal install pandoc --enable-tests cabal test --test-options='-p markdown' Or with stack: stack test --test-arguments='-p markdown' It is often helpful to add `-j4` (run tests in parallel) and `--hide-successes` (don't clutter output with successes) to the test arguments as well. Collecting all options in a `cabal.project.local` file in the project's root directory can help to keep `cabal` commands short. E.g.: flags: +embed_data_files tests: True test-show-details: direct test-options: -j4 --hide-successes If you add a new feature to pandoc, please add tests as well, following the pattern of the existing tests. The test suite code is in `test/test-pandoc.hs`. If you are adding a new reader or writer, it is probably easiest to add some data files to the `test` directory, and modify `test/Tests/Old.hs`. Otherwise, it is better to modify the module under the `test/Tests` hierarchy corresponding to the pandoc module you are changing. Alternatively, you may add a "command test" to the `/test/command/` hierarchy, following the pattern of the tests there. These test files should have a meaningful name, which can include the issue number and/or the feature that's being tested. For example, `5474-tables.md` refers to both issue and feature. You can rebuild the golden tests in `tests/` by passing `--accept` to the test script. (If you're using stack, `stack test --test-arguments "--accept"`; or `make TESTARGS=--accept`). Then check the changed golden files for accuracy, and commit the changes. For docx or pptx tests, open the files in Word or Powerpoint to ensure that they weren't corrupted and that they had the expected result, and mention the Word/Powerpoint version and OS in your commit comment. Code style ---------- Pandoc uses [hlint] to identify opportunities for code improvements like redundant brackets or unnecessary `Language` extensions. However, sometimes there are cases where there are good reasons to use code different from what hlint proposes. In these cases, the respective warning should be disabled in the file `.hlint.yaml`. There should be no errors when running `hlint .`; this is checked by the continuous integration (CI) setup. It is recommended that contributors check their code with a local hlint installation, but relying on the CI is fine, too. A good way to ensure no new warnings are introduced is to use a Git [pre-commit hook] which runs hlint on all updated Haskell files before creating a commit: #!/bin/sh git diff --diff-filter=MA --cached --name-only | grep '\.hs$' | \ xargs hlint --hint .hlint.yaml (If you are using GNU `xargs`, add the `-r` option immediately after `xargs`.) Saving this to `.git/hooks/pre-commit`, and making the script executable, will prevent accidental introduction of potentially problematic code. Benchmarks ---------- To run benchmarks with cabal: cabal configure --enable-benchmarks cabal build cabal bench With stack: stack bench Using the REPL -------------- With a recent version of cabal, you can do `cabal repl` and get a ghci REPL for working with pandoc. With [stack], use `stack ghci`. We recommend using the following `.ghci` file (which can be placed in the source directory): :set -fobject-code :set -XTypeSynonymInstances :set -XScopedTypeVariables :set -XOverloadedStrings Profiling --------- To diagnose a performance issue with parsing, first try using the `--trace` option. This will give you a record of when block parsers succeed, so you can spot backtracking issues. To use the GHC profiler with cabal: cabal clean cabal install --enable-library-profiling --enable-executable-profiling pandoc +RTS -p -RTS [file]... less pandoc.prof With stack: stack clean stack install --profile pandoc +RTS -p -RTS [file]... less pandoc.prof The code -------- Pandoc has a publicly accessible git repository on GitHub: . To get a local copy of the source: git clone https://github.com/jgm/pandoc.git The source for the main pandoc program is `pandoc.hs`. The source for the pandoc library is in `src/`, the source for the tests is in `test/`, and the source for the benchmarks is in `benchmark/`. The modules `Text.Pandoc.Definition`, `Text.Pandoc.Builder`, and `Text.Pandoc.Generic` are in a separate library `pandoc-types`. The code can be found in . To build pandoc, you will need a working installation of the [Haskell platform]. The library is structured as follows: - `Text.Pandoc` is a top-level module that exports what is needed by most users of the library. Any patches that add new readers or writers will need to make changes here, too. - `Text.Pandoc.Definition` (in `pandoc-types`) defines the types used for representing a pandoc document. - `Text.Pandoc.Builder` (in `pandoc-types`) provides functions for building pandoc documents programmatically. - `Text.Pandoc.Generics` (in `pandoc-types`) provides functions allowing you to promote functions that operate on parts of pandoc documents to functions that operate on whole pandoc documents, walking the tree automatically. - `Text.Pandoc.Readers.*` are the readers, and `Text.Pandoc.Writers.*` are the writers. - `Text.Pandoc.Citeproc.*` contain the code for citation handling, including an interface to the [citeproc] library. - `Text.Pandoc.Data` is used to embed data files when the `embed_data_files` cabal flag is used. - `Text.Pandoc.Emoji` is a thin wrapper around [emojis]. - `Text.Pandoc.Highlighting` contains the interface to the skylighting library, which is used for code syntax highlighting. - `Text.Pandoc.ImageSize` is a utility module containing functions for calculating image sizes from the contents of image files. - `Text.Pandoc.MIME` contains functions for associating MIME types with extensions. - `Text.Pandoc.Options` defines reader and writer options. - `Text.Pandoc.PDF` contains functions for producing a PDF from a LaTeX source. - `Text.Pandoc.Parsing` contains parsing functions used in multiple readers. the needs of pandoc. - `Text.Pandoc.SelfContained` contains functions for making an HTML file "self-contained," by importing remotely linked images, CSS, and JavaScript and turning them into `data:` URLs. - `Text.Pandoc.Shared` is a grab-bag of shared utility functions. - `Text.Pandoc.Writers.Shared` contains utilities used in writers only. - `Text.Pandoc.Slides` contains functions for splitting a markdown document into slides, using the conventions described in the MANUAL. - `Text.Pandoc.Templates` defines pandoc's templating system. - `Text.Pandoc.UTF8` contains functions for converting text to and from UTF8 bytestrings (strict and lazy). - `Text.Pandoc.Asciify` contains functions to derive ascii versions of identifiers that use accented characters. - `Text.Pandoc.UUID` contains functions for generating UUIDs. - `Text.Pandoc.XML` contains functions for formatting XML. Adding a new command-line option -------------------------------- To add a new command-line option, you'll need to make changes in several places: - `MANUAL.txt` -- documentation for new option, both in the list of options and in the section on defaults files. - `Text.Pandoc.App.Opt` -- new constructor for Opt and default value - `Text.Pandoc.App.CommandLineOptions` -- the option parser - `Text.Pandoc.App` or `Text.Pandoc.App.OutputSettings` -- handle the new option - possibly in pandoc-server: `Text.Pandoc.Server` -- handle the new option If your change requires a new field for ReaderOptions or WriterOptions, you'll also need to - `Text.Pandoc.Options` -- type change and default value - in pandoc-lua-engine: Text.Pandoc.Lua.Marshal.WriterOptions and/or Text.Pandoc.Lua.Marshal.ReaderOptions Lua filters ----------- 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). [open issues]: https://github.com/jgm/pandoc/issues [closed issues]: https://github.com/jgm/pandoc/issues?q=is%3Aissue+is%3Aclosed [latest released version]: https://github.com/jgm/pandoc/releases/latest [Nightly builds]: https://github.com/jgm/pandoc/actions?query=workflow%3ANightly [pandoc-discuss]: https://groups.google.com/group/pandoc-discuss [issue tracker]: https://github.com/jgm/pandoc/issues [User's Guide]: https://pandoc.org/MANUAL.html [FAQs]: https://pandoc.org/faqs.html [EditorConfig]: https://editorconfig.org/ [Haskell platform]: https://www.haskell.org/platform/ [hlint]: https://hackage.haskell.org/package/hlint [citeproc]: https://hackage.haskell.org/package/citeproc [emojis]: https://hackage.haskell.org/package/emojis [hsb2hs]: https://hackage.haskell.org/package/hsb2hs [pre-commit hook]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks [GitHub labels]: https://github.com/jgm/pandoc/labels [good first issue]:https://github.com/jgm/pandoc/labels/good%20first%20issue [enhancement]: https://github.com/jgm/pandoc/labels/enhancement [bug]: https://github.com/jgm/pandoc/labels/bug [complexity:low]: https://github.com/jgm/pandoc/labels/complexity:low [complexity:high]: https://github.com/jgm/pandoc/labels/complexity:high [docs]: https://github.com/jgm/pandoc/labels/docs [format:markdown]: https://github.com/jgm/pandoc/labels/format:markdown [new:reader]: https://github.com/jgm/pandoc/labels/new:reader [new:writer]: https://github.com/jgm/pandoc/labels/new:writer [status:in-progress]: https://github.com/jgm/pandoc/labels/status:in-progress [status:more-discussion-needed]: https://github.com/jgm/pandoc/labels/status:more-discussion-needed [status:more-info-needed]: https://github.com/jgm/pandoc/labels/status:more-info-needed [stack]: https://github.com/commercialhaskell/stack [discussion forum]: https://github.com/jgm/pandoc/discussions ================================================ FILE: COPYING.md ================================================ ### GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ### Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION **0.** This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. **1.** You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. **2.** You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: **a)** You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. **b)** You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. **c)** If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. **3.** You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: **a)** Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, **b)** Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, **c)** Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. **4.** You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. **5.** You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. **6.** Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. **7.** If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. **8.** If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. **9.** The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. **10.** If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. **NO WARRANTY** **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### END OF TERMS AND CONDITIONS ### How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands \`show w' and \`show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than \`show w' and \`show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl.html) instead of this License. ================================================ FILE: COPYRIGHT ================================================ Pandoc Copyright (C) 2006-2024 John MacFarlane With the exceptions noted below, this code is released under the [GPL], version 2 or later: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . The GNU General Public License is available in the file COPYING.md in the source distribution. On Debian systems, the complete text of the GPL can be found in `/usr/share/common-licenses/GPL`. [GPL]: https://www.gnu.org/copyleft/gpl.html The complete source code for pandoc version X.Y.Z is available at and at . Pandoc includes some code with different copyrights, or subject to different licenses. The copyright and license statements for these sources are included below. All are GPL-compatible licenses. ---------------------------------------------------------------------- The modules in the `pandoc-types` repository (Text.Pandoc.Definition, Text.Pandoc.Builder, Text.Pandoc.Generics, Text.Pandoc.JSON, Text.Pandoc.Walk) are licensed under the BSD 3-clause license: Copyright (c) 2006-2024, John MacFarlane All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of John MacFarlane nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- Pandoc's templates (in `data/templates`) are dual-licensed as either GPL (v2 or higher, same as pandoc) or (at your option) the BSD 3-clause license. Copyright (c) 2014--2024, John MacFarlane ---------------------------------------------------------------------- src/Text/Pandoc/Writers/Muse.hs Copyright (C) 2017-2020 Alexander Krotov Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Writers/Texinfo.hs Copyright (C) 2008-2024 John MacFarlane and Peter Wang Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Writers/OpenDocument.hs Copyright (C) 2008-2024 Andrea Rossato and John MacFarlane Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Writers/Org.hs Copyright (C) 2010-2024 Puneeth Chaganti, John MacFarlane, and Albert Krewinkel Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Writers/ZimWiki.hs Copyright (C) 2017 Alex Ivkin Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/Docx.hs src/Text/Pandoc/Readers/Docx/* Copyright (C) 2014-2020 Jesse Rosenthal Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/Textile.hs Copyright (C) 2010-2024 Paul Rivier and John MacFarlane Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/TikiWiki.hs Copyright (C) 2017 Robin Lee Powell Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/JATS.hs Copyright (C) 2017-2018 Hamish Mackenzie Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/EPUB.hs Copyright (C) 2014-2024 Matthew Pickering and John MacFarlane Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/Org.hs src/Text/Pandoc/Readers/Org/* test/Tests/Readers/Org/* Copyright (C) 2014-2024 Albert Krewinkel Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- pandoc-lua-engine/src/Text/Pandoc/Lua.hs pandoc-lua-engine/src/Text/Pandoc/Lua/* pandoc-lua-engine/test/lua/* Copyright (C) 2017--2024 Albert Krewinkel and John MacFarlane Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/Jira.hs src/Text/Pandoc/Writers/Jira.hs test/Tests/Readers/Jira.hs Copyright (C) 2019--2024 Albert Krewinkel Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/FB2.hs Copyright (C) 2018--2019 Alexander Krotov Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- The dzslides template contains JavaScript and CSS from Paul Rouget's dzslides template. https://github.com/paulrouget/dzslides Released under the Do What the Fuck You Want To Public License. ------------------------------------------------------------------------ Pandoc embeds a Lua interpreter (via hslua). Copyright © 1994--2023 Lua.org, PUC-Rio. 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. ------------------------------------------------------------------------ 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. ================================================ FILE: INSTALL.md ================================================ # Installing pandoc The simplest way to get the latest pandoc release is to use the installer. Download the latest installer For alternative ways to install pandoc, see below under the heading for your operating system. **Note**: the statically linked Pandoc binaries provided by us (or those available on Conda Forge) have a limitation. They are unable to utilise Lua filters that rely on Lua modules written in C. If you require the functionality offered by these filters, please consider an alternative method of installation. ## Windows There is a package installer at pandoc's [download page]. This will install pandoc, replacing older versions, and update your path to include the directory where pandoc's binaries are installed. If you prefer not to use the msi installer, we also provide a zip file that contains pandoc's binaries and documentation. Simply unzip this file and move the binaries to a directory of your choice. Alternatively, you can install pandoc using [Chocolatey](https://chocolatey.org): choco install pandoc Chocolatey can also install other software that integrates with Pandoc. For example, to install `rsvg-convert` (from [librsvg], covering formats without SVG support), [Python] (to use Pandoc filters), and [MiKTeX] (to typeset PDFs with [LaTeX]): choco install rsvg-convert python miktex Or, you can install pandoc using [winget](https://github.com/microsoft/winget-pkgs): winget install --source winget --exact --id JohnMacFarlane.Pandoc Or, you can install Pandoc using [Conda forge]. Using multiple installation methods can result in two separate installations of pandoc; it is recommended to properly uninstall pandoc before switching to an alternative installation method. By default, Pandoc creates PDFs using LaTeX. We recommend installing it via [MiKTeX]. With the option `--pdf-engine`, you however can specify other programs for this task. ## macOS There is a package installer at pandoc's [download page]. If you later want to uninstall the package, you can do so by downloading [this script][uninstaller] and running it with `perl uninstall-pandoc.pl`. Alternatively, you can install pandoc using [Homebrew](https://brew.sh): brew install pandoc Homebrew can also install other software that integrates with Pandoc. For example, to install [librsvg] (its `rsvg-convert` covers formats without SVG support), [Python] (to use Pandoc filters), and [BasicTeX] (to typeset PDFs with [LaTeX]): brew install librsvg python homebrew/cask/basictex Note: On unsupported versions of macOS (more than three releases old), Homebrew installs from source, which takes additional time and disk space for the `ghc` compiler and dependent Haskell libraries. You can also install pandoc using [MacPorts]: port install pandoc Or, you can install Pandoc using [Conda forge]. We also provide a zip file containing the binaries and man pages, for those who prefer not to use the installer. Simply unzip the file and move the binaries and man pages to whatever directory you like. By default, Pandoc creates PDFs using LaTeX. Because a full [MacTeX] installation uses four gigabytes of disk space, we recommend [BasicTeX] or [TinyTeX](https://yihui.org/tinytex/) and using the `tlmgr` tool to install additional packages as needed. If you receive errors warning of fonts not found: tlmgr install collection-fontsrecommended With the option `--pdf-engine`, you however can specify other programs for this task. ## Linux Check whether the pandoc version in your package manager is not outdated. Pandoc is in the [Debian], [Ubuntu], [Slackware], [Arch], [Fedora], [NixOS], [openSUSE], [gentoo] and [Void] repositories. To get the latest release, we provide a binary package for amd64 architecture on the **[download page]**. The executable is statically linked and has no dynamic dependencies or dependencies on external data files. Both a tarball and a deb installer are provided. To install the deb: sudo dpkg -i $DEB where `$DEB` is the path to the downloaded deb. This will install the `pandoc` executable and man page. If you use an RPM-based distro, you may be able to install the deb from our download page using `alien`. On any distro, you may install from the tarball into `$DEST` (say, `/usr/local/` or `$HOME/.local`) by doing tar xvzf $TGZ --strip-components 1 -C $DEST where `$TGZ` is the path to the downloaded zipped tarball. For Pandoc versions before 2.0, which don't provide a tarball, try instead ar p $DEB data.tar.gz | tar xvz --strip-components 2 -C $DEST Or, you can install Pandoc using [Conda forge]. You can also install from source, using the instructions below under [Compiling from source]. By default, Pandoc creates PDFs using LaTeX. We recommend installing [TeX Live](https://www.tug.org/texlive/) via your package manager. (On Debian/Ubuntu, `apt-get install texlive`.) With the option `--pdf-engine`, you however can specify other programs for this task. ## Chrome OS On Chrome OS, pandoc can be installed using the [chromebrew](https://github.com/skycocker/chromebrew) package manager with the command: ```sh crew install pandoc ``` This will automatically build and configure pandoc for the specific device you are using. ## BSD Pandoc is in the [NetBSD], [FreeBSD], and [OpenBSD ports] repositories. ## Conda Forge You can install Pandoc using a [Conda Forge](https://anaconda.org/conda-forge/pandoc) tool, like [Conda](https://conda.pydata.org/docs/intro.html), [[Micro]Mamba](https://mamba.readthedocs.io/en/latest/index.html) or [Pixi](https://prefix.dev). Conda forge also includes multiple LaTeX and other relevant packages for Pandoc (including `pandoc-citeproc`, `pandoc-plot`, `rsvg-convert` via `librsvg` etc.). **Note:** conda forge installs a statically-linked executable. conda install -c conda-forge pandoc pixi global install pandoc micromamba install pandoc ## Docker The official Docker images for pandoc can be found at and at [dockerhub](https://hub.docker.com/). The [pandoc/core](https://hub.docker.com/r/pandoc/core) image contains `pandoc`. The [pandoc/latex](https://hub.docker.com/r/pandoc/latex) image also contains the minimal LaTeX installation needed to produce PDFs using pandoc. To run pandoc using Docker, converting `README.md` to `README.pdf`: docker run --rm --volume "`pwd`:/data" --user `id -u`:`id -g` pandoc/latex README.md -o README.pdf ## GitHub Actions Pandoc can be run through [GitHub Actions](https://github.com/features/actions). For some examples, see . ## GitLab CI/CD Pandoc can be run through [GitLab CI/CD]. For some examples, see . ## Compiling from source If for some reason a binary package is not available for your platform, or if you want to hack on pandoc or use a non-released version, you can install from source. ### Getting the pandoc source code Source tarballs can be found at . For example, to fetch the source for version 1.17.0.3: wget https://hackage.haskell.org/package/pandoc-1.17.0.3/pandoc-1.17.0.3.tar.gz tar xvzf pandoc-1.17.0.3.tar.gz cd pandoc-1.17.0.3 Or you can fetch the development code by cloning the repository: git clone https://github.com/jgm/pandoc cd pandoc Note: there may be times when the development code is broken or depends on other libraries which must be installed separately. Unless you really know what you're doing, install the last released version. ### Quick stack method The easiest way to build pandoc from source is to use [stack][stack]: 1. Install [stack][stack]. Note that Pandoc requires stack >= 1.7.0. 2. stack setup stack install pandoc-cli `stack setup` will automatically download the ghc compiler if you don't have it. `stack install` will install the `pandoc` executable into `~/.local/bin`, which you should add to your `PATH`. This process will take a while, and will consume a considerable amount of disk space. ### Quick cabal method 1. Install [ghcup](https://www.haskell.org/ghcup/install/). This will give you `ghc` and `cabal`. 2. Update your package database: cabal update 3. Use `cabal` to install pandoc and its dependencies: cabal install pandoc-cli This procedure will install the released version of pandoc, which will be downloaded automatically from HackageDB. The `pandoc` executable will be symlinked in`$HOME/.cabal/bin` on linux/unix/macOS and in `%APPDATA%\cabal\bin` on Windows. Make sure this directory is in your path. To specify a custom install directory, use `--installdir`. To have the executable copied instead of symlinked, use `--install-method=copy`. If you want to install a modified or development version of pandoc instead, switch to the source directory before running the above command -- cabal will use the local code for all projects mentioned in the `cabal.project`. 4. You should now be able to run `pandoc`: pandoc --help 5. Cabal does not install the `pandoc.1` man page, but you can copy it from the `man/` directory of the source code to `/usr/local/share/man/man1/` or wherever man pages go on your system. ### Custom cabal method This is a step-by-step procedure that offers maximal control over the build and installation. Most users should use the quick install, but this information may be of use to packagers. For more details, see the [Cabal User's Guide]. These instructions assume that the pandoc source directory is your working directory. You will need cabal version 2.0 or higher. 1. Install dependencies: cabal update cabal build --only-dependencies 2. Configure: cabal configure --prefix=DIR --bindir=DIR --libdir=DIR \ --datadir=DIR --libsubdir=DIR --datasubdir=DIR --docdir=DIR \ --htmldir=DIR --program-prefix=PREFIX --program-suffix=SUFFIX \ --mandir=DIR --flags=FLAGSPEC --enable-tests All of the options have sensible defaults that can be overridden as needed. `FLAGSPEC` is a list of Cabal configuration flags, optionally preceded by a `-` (to force the flag to `false`), and separated by spaces. `pandoc`'s flags include: - `embed_data_files`: embed all data files into the binary (default no). This is helpful if you want to create a relocatable binary. `pandoc-cli`'s flags include: - `lua`: compile in support for Lua filters and custom writers. - `server`: compile in support for running in HTTP server mode when the executable is renamed (or symlinked as) `pandoc-server`. 3. Build: cabal build cabal test 4. Build API documentation: cabal haddock --html-location=URL --hyperlink-source 5. Install cabal install pandoc-cli ### Creating a relocatable binary It is possible to compile pandoc such that the data files pandoc uses are embedded in the binary. The resulting binary can be run from any directory and is completely self-contained. With cabal, add `-fembed_data_files` to the `cabal configure` or `cabal build` commands. With stack, use `--flag pandoc:embed_data_files`. ### Running tests Pandoc comes with an automated test suite. To run with cabal, `cabal test`; to run with stack, `stack test`. To run particular tests (pattern-matching on their names), use the `-p` option: cabal test --test-options='-p markdown' Or with stack: stack test --test-arguments='-p markdown' It is often helpful to add `-j4` (run tests in parallel) and `--hide-successes` (don't clutter output with successes) to the test arguments as well. If you add a new feature to pandoc, please add tests as well, following the pattern of the existing tests. The test suite code is in `test/test-pandoc.hs`. If you are adding a new reader or writer, it is probably easiest to add some data files to the `test` directory, and modify `test/Tests/Old.hs`. Otherwise, it is better to modify the module under the `test/Tests` hierarchy corresponding to the pandoc module you are changing. ### Running benchmarks To build and run the benchmarks: cabal configure --enable-benchmarks && cabal build cabal bench or with stack: stack bench To use a smaller sample size so the benchmarks run faster: cabal bench --benchmark-options='-s 20' To run just the markdown benchmarks: cabal bench --benchmark-options='markdown' [Arch]: https://archlinux.org/packages/?q=pandoc [Cabal User's Guide]: https://cabal.readthedocs.io/ [Debian]: https://packages.debian.org/search?keywords=pandoc [Fedora]: https://packages.fedoraproject.org/pkgs/pandoc/pandoc/ [FreeBSD]: https://www.freshports.org/textproc/hs-pandoc/ [GHC]: https://www.haskell.org/ghc/ [GitLab CI/CD]: https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/ [MacPorts]: https://ports.macports.org/port/pandoc/ [MacTeX]: https://tug.org/mactex/ [OpenBSD ports]: https://cvsweb.openbsd.org/ports/textproc/pandoc/ [BasicTeX]: https://www.tug.org/mactex/morepackages.html [LaTeX]: https://www.latex-project.org [MiKTeX]: https://miktex.org/ [librsvg]: https://wiki.gnome.org/Projects/LibRsvg [Python]: https://www.python.org [NetBSD]: https://pkgsrc.se/converters/pandoc [NixOS]: https://search.nixos.org/packages?query=pandoc [Slackware]: https://www.slackbuilds.org/result/?search=pandoc&sv= [Ubuntu]: https://packages.ubuntu.com/search?keywords=pandoc [download page]: https://github.com/jgm/pandoc/releases/latest [gentoo]: https://packages.gentoo.org/package/app-text/pandoc [haskell repository]: https://wiki.archlinux.org/index.php/Haskell_Package_Guidelines#.5Bhaskell.5D [openSUSE]: https://software.opensuse.org/package/pandoc [source tarball]: https://hackage.haskell.org/package/pandoc [stack]: https://docs.haskellstack.org/en/stable/install_and_upgrade.html [cabal-install]: https://hackage.haskell.org/package/cabal-install [Void]: https://voidlinux.org/packages/?arch=x86_64&q=pandoc [uninstaller]: https://raw.githubusercontent.com/jgm/pandoc/main/macos/uninstall-pandoc.pl ================================================ FILE: MANUAL.txt ================================================ --- title: Pandoc User's Guide author: John MacFarlane date: 2026-03-19 --- # Synopsis `pandoc` [*options*] [*input-file*]... # Description Pandoc is a [Haskell] library for converting from one markup format to another, and a command-line tool that uses this library. Pandoc can convert between numerous markup and word processing formats, including, but not limited to, various flavors of [Markdown], [HTML], [LaTeX] and [Word docx]. For the full lists of input and output formats, see the `--from` and `--to` [options below][General options]. Pandoc can also produce [PDF] output: see [creating a PDF], below. Pandoc's enhanced version of Markdown includes syntax for [tables], [definition lists], [metadata blocks], [footnotes], [citations], [math], and much more. See below under [Pandoc's Markdown]. Pandoc has a modular design: it consists of a set of readers, which parse text in a given format and produce a native representation of the document (an _abstract syntax tree_ or AST), and a set of writers, which convert this native representation into a target format. Thus, adding an input or output format requires only adding a reader or writer. Users can also run custom [pandoc filters] to modify the intermediate AST. Because pandoc's intermediate representation of a document is less expressive than many of the formats it converts between, one should not expect perfect conversions between every format and every other. Pandoc attempts to preserve the structural elements of a document, but not formatting details such as margin size. And some document elements, such as complex tables, may not fit into pandoc's simple document model. While conversions from pandoc's Markdown to all formats aspire to be perfect, conversions from formats more expressive than pandoc's Markdown can be expected to be lossy. ## Using pandoc If no *input-files* are specified, input is read from *stdin*. Output goes to *stdout* by default. For output to a file, use the `-o`/`--output` option: pandoc -o output.html input.txt By default, pandoc produces a document fragment. To produce a standalone document (e.g. a valid HTML file including `` and ``), use the `-s` or `--standalone` flag: pandoc -s -o output.html input.txt For more information on how standalone documents are produced, see [Templates] below. If multiple input files are given, pandoc will concatenate them all (with blank lines between them) before parsing. (Use `--file-scope` to parse files individually.) ## Specifying formats The format of the input and output can be specified explicitly using command-line options. The input format can be specified using the `-f/--from` option, the output format using the `-t/--to` option. Thus, to convert `hello.txt` from Markdown to LaTeX, you could type: pandoc -f markdown -t latex hello.txt To convert `hello.html` from HTML to Markdown: pandoc -f html -t markdown hello.html Supported input and output formats are listed below under [Options] (see `-f` for input formats and `-t` for output formats). You can also use `pandoc --list-input-formats` and `pandoc --list-output-formats` to print lists of supported formats. If the input or output format is not specified explicitly, pandoc will attempt to guess it from the extensions of the filenames. Thus, for example, pandoc -o hello.tex hello.txt will convert `hello.txt` from Markdown to LaTeX. If no output file is specified (so that output goes to *stdout*), or if the output file's extension is unknown, the output format will default to HTML. If no input file is specified (so that input comes from *stdin*), or if the input files' extensions are unknown, the input format will be assumed to be Markdown. ## Character encoding Pandoc uses the UTF-8 character encoding for both input and output. If your local character encoding is not UTF-8, you should pipe input and output through [`iconv`]: iconv -t utf-8 input.txt | pandoc | iconv -f utf-8 Note that in some output formats (such as HTML, LaTeX, ConTeXt, RTF, OPML, DocBook, and Texinfo), information about the character encoding is included in the document header, which will only be included if you use the `-s/--standalone` option. [`iconv`]: https://www.gnu.org/software/libiconv/ ## Creating a PDF To produce a PDF, specify an output file with a `.pdf` extension: pandoc test.txt -o test.pdf By default, pandoc will use LaTeX to create the PDF, which requires that a LaTeX engine be installed (see `--pdf-engine` below). Alternatively, pandoc can use ConTeXt, roff ms, or HTML as an intermediate format. To do this, specify an output file with a `.pdf` extension, as before, but add the `--pdf-engine` option or `-t context`, `-t html`, or `-t ms` to the command line. The tool used to generate the PDF from the intermediate format may be specified using `--pdf-engine`. You can control the PDF style using variables, depending on the intermediate format used: see [variables for LaTeX], [variables for ConTeXt], [variables for `wkhtmltopdf`], [variables for ms]. When HTML is used as an intermediate format, the output can be styled using `--css`. To debug the PDF creation, it can be useful to look at the intermediate representation: instead of `-o test.pdf`, use for example `-s -o test.tex` to output the generated LaTeX. You can then test it with `pdflatex test.tex`. When using LaTeX, the following packages need to be available (they are included with all recent versions of [TeX Live]): [`amsfonts`], [`amsmath`], [`lm`], [`unicode-math`], [`iftex`], [`listings`] (if the `--listings` option is used), [`fancyvrb`], [`longtable`], [`booktabs`], [`multirow`] (if the document contains a table with cells that cross multiple rows), [`graphicx`] (if the document contains images), [`bookmark`], [`xcolor`], [`soul`], [`geometry`] (with the `geometry` variable set), [`setspace`] (with `linestretch`), and [`babel`] (with `lang`). If `CJKmainfont` is set, [`xeCJK`] is needed if `xelatex` is used, else [`luatexja`] is needed if `lualatex` is used. [`framed`] is required if code is highlighted in a scheme that use a colored background. The use of `xelatex` or `lualatex` as the PDF engine requires [`fontspec`]. `lualatex` uses [`selnolig`] and [`lua-ul`]. `xelatex` uses [`bidi`] (with the `dir` variable set). If the `mathspec` variable is set, `xelatex` will use [`mathspec`] instead of [`unicode-math`]. The [`csquotes`] package will be used for [typography] if the `csquotes` variable or metadata field is set to a true value. The [`natbib`], [`biblatex`], [`bibtex`], and [`biber`] packages can optionally be used for [citation rendering]. If math with `\cancel`, `\bcancel`, or `\xcancel` is used, the [`cancel`] package is needed. The following packages will be used to improve output quality if present, but pandoc does not require them to be present: [`upquote`] (for straight quotes in verbatim environments), [`microtype`] (for better spacing adjustments), [`parskip`] (for better inter-paragraph spaces), [`xurl`] (for better line breaks in URLs), and [`footnotehyper`] or [`footnote`] (to allow footnotes in tables). [TeX Live]: https://www.tug.org/texlive/ [`amsfonts`]: https://ctan.org/pkg/amsfonts [`amsmath`]: https://ctan.org/pkg/amsmath [`babel`]: https://ctan.org/pkg/babel [`biber`]: https://ctan.org/pkg/biber [`biblatex`]: https://ctan.org/pkg/biblatex [`bibtex`]: https://ctan.org/pkg/bibtex [`bidi`]: https://ctan.org/pkg/bidi [`bookmark`]: https://ctan.org/pkg/bookmark [`booktabs`]: https://ctan.org/pkg/booktabs [`csquotes`]: https://ctan.org/pkg/csquotes [`fancyvrb`]: https://ctan.org/pkg/fancyvrb [`fontspec`]: https://ctan.org/pkg/fontspec [`footnote`]: https://ctan.org/pkg/footnote [`footnotehyper`]: https://ctan.org/pkg/footnotehyper [`framed`]: https://ctan.org/pkg/framed [`geometry`]: https://ctan.org/pkg/geometry [`graphicx`]: https://ctan.org/pkg/graphicx [`cancel`]: https://ctan.org/pkg/cancel [`hyperref`]: https://ctan.org/pkg/hyperref [`iftex`]: https://ctan.org/pkg/iftex [`listings`]: https://ctan.org/pkg/listings [`lm`]: https://ctan.org/pkg/lm [`lua-ul`]: https://ctan.org/pkg/lua-ul [`luatexja`]: https://ctan.org/pkg/luatexja [`longtable`]: https://ctan.org/pkg/longtable [`mathspec`]: https://ctan.org/pkg/mathspec [`microtype`]: https://ctan.org/pkg/microtype [`multirow`]: https://ctan.org/pkg/multirow [`natbib`]: https://ctan.org/pkg/natbib [`parskip`]: https://ctan.org/pkg/parskip [`polyglossia`]: https://ctan.org/pkg/polyglossia [`prince`]: https://www.princexml.com/ [`setspace`]: https://ctan.org/pkg/setspace [`soul`]: https://ctan.org/pkg/soul [`unicode-math`]: https://ctan.org/pkg/unicode-math [`upquote`]: https://ctan.org/pkg/upquote [`weasyprint`]: https://weasyprint.org [`wkhtmltopdf`]: https://wkhtmltopdf.org [`xcolor`]: https://ctan.org/pkg/xcolor [`xeCJK`]: https://ctan.org/pkg/xecjk [`xurl`]: https://ctan.org/pkg/xurl [`selnolig`]: https://ctan.org/pkg/selnolig ## Reading from the Web Instead of an input file, an absolute URI may be given. In this case pandoc will fetch the content using HTTP: pandoc -f html -t markdown https://www.fsf.org It is possible to supply a custom User-Agent string or other header when requesting a document from a URL: pandoc -f html -t markdown --request-header User-Agent:"Mozilla/5.0" \ https://www.fsf.org # Options ## General options {.options} `-f` *FORMAT*, `-r` *FORMAT*, `--from=`*FORMAT*, `--read=`*FORMAT* : Specify input format. *FORMAT* can be: ::: {#input-formats} - `asciidoc` ([AsciiDoc] markup) - `bibtex` ([BibTeX] bibliography) - `biblatex` ([BibLaTeX] bibliography) - `bits` ([BITS] XML, alias for `jats`) - `commonmark` ([CommonMark] Markdown) - `commonmark_x` ([CommonMark] Markdown with extensions) - `creole` ([Creole 1.0]) - `csljson` ([CSL JSON] bibliography) - `csv` ([CSV] table) - `tsv` ([TSV] table) - `djot` ([Djot markup]) - `docbook` ([DocBook]) - `docx` ([Word docx]) - `dokuwiki` ([DokuWiki markup]) - `endnotexml` ([EndNote XML bibliography]) - `epub` ([EPUB]) - `fb2` ([FictionBook2] e-book) - `gfm` ([GitHub-Flavored Markdown]), or the deprecated and less accurate `markdown_github`; use [`markdown_github`](#markdown-variants) only if you need extensions not supported in [`gfm`](#markdown-variants). - `haddock` ([Haddock markup]) - `html` ([HTML]) - `ipynb` ([Jupyter notebook]) - `jats` ([JATS] XML) - `jira` ([Jira]/Confluence wiki markup) - `json` (JSON version of native AST) - `latex` ([LaTeX]) - `markdown` ([Pandoc's Markdown]) - `markdown_mmd` ([MultiMarkdown]) - `markdown_phpextra` ([PHP Markdown Extra]) - `markdown_strict` (original unextended [Markdown]) - `mediawiki` ([MediaWiki markup]) - `man` ([roff man]) - `mdoc` ([mdoc] manual page markup) - `muse` ([Muse]) - `native` (native Haskell) - `odt` ([OpenDocument text document][ODT]) - `opml` ([OPML]) - `org` ([Emacs Org mode]) - `pod` (Perl's [Plain Old Documentation]) - `pptx` ([PowerPoint]) - `ris` ([RIS] bibliography) - `rtf` ([Rich Text Format]) - `rst` ([reStructuredText]) - `t2t` ([txt2tags]) - `textile` ([Textile]) - `tikiwiki` ([TikiWiki markup]) - `twiki` ([TWiki markup]) - `typst` ([typst]) - `vimwiki` ([Vimwiki]) - `xlsx` ([Excel spreadsheet][XLSX]) - `xml` (XML version of native AST) - the path of a custom Lua reader, see [Custom readers and writers] below ::: Extensions can be individually enabled or disabled by appending `+EXTENSION` or `-EXTENSION` to the format name. See [Extensions] below, for a list of extensions and their names. See `--list-input-formats` and `--list-extensions`, below. `-t` *FORMAT*, `-w` *FORMAT*, `--to=`*FORMAT*, `--write=`*FORMAT* : Specify output format. *FORMAT* can be: ::: {#output-formats} - `ansi` (text with [ANSI escape codes], for terminal viewing) - `asciidoc` (modern [AsciiDoc] as interpreted by [AsciiDoctor]) - `asciidoc_legacy` ([AsciiDoc] as interpreted by [`asciidoc-py`]). - `asciidoctor` (deprecated synonym for `asciidoc`) - `bbcode` [BBCode] - `bbcode_fluxbb` [BBCode (FluxBB)] - `bbcode_phpbb` [BBCode (phpBB)] - `bbcode_steam` [BBCode (Steam)] - `bbcode_hubzilla` [BBCode (Hubzilla)] - `bbcode_xenforo` [BBCode (xenForo)] - `beamer` ([LaTeX beamer][`beamer`] slide show) - `bibtex` ([BibTeX] bibliography) - `biblatex` ([BibLaTeX] bibliography) - `chunkedhtml` (zip archive of multiple linked HTML files) - `commonmark` ([CommonMark] Markdown) - `commonmark_x` ([CommonMark] Markdown with extensions) - `context` ([ConTeXt]) - `csljson` ([CSL JSON] bibliography) - `djot` ([Djot markup]) - `docbook` or `docbook4` ([DocBook] 4) - `docbook5` (DocBook 5) - `docx` ([Word docx]) - `dokuwiki` ([DokuWiki markup]) - `epub` or `epub3` ([EPUB] v3 book) - `epub2` (EPUB v2) - `fb2` ([FictionBook2] e-book) - `gfm` ([GitHub-Flavored Markdown]), or the deprecated and less accurate `markdown_github`; use [`markdown_github`](#markdown-variants) only if you need extensions not supported in [`gfm`](#markdown-variants). - `haddock` ([Haddock markup]) - `html` or `html5` ([HTML], i.e. [HTML5]/XHTML [polyglot markup]) - `html4` ([XHTML] 1.0 Transitional) - `icml` ([InDesign ICML]) - `ipynb` ([Jupyter notebook]) - `jats_archiving` ([JATS] XML, Archiving and Interchange Tag Set) - `jats_articleauthoring` ([JATS] XML, Article Authoring Tag Set) - `jats_publishing` ([JATS] XML, Journal Publishing Tag Set) - `jats` (alias for `jats_archiving`) - `jira` ([Jira]/Confluence wiki markup) - `json` (JSON version of native AST) - `latex` ([LaTeX]) - `man` ([roff man]) - `markdown` ([Pandoc's Markdown]) - `markdown_mmd` ([MultiMarkdown]) - `markdown_phpextra` ([PHP Markdown Extra]) - `markdown_strict` (original unextended [Markdown]) - `markua` ([Markua]) - `mediawiki` ([MediaWiki markup]) - `ms` ([roff ms]) - `muse` ([Muse]) - `native` (native Haskell) - `odt` ([OpenDocument text document][ODT]) - `opml` ([OPML]) - `opendocument` ([OpenDocument XML]) - `org` ([Emacs Org mode]) - `pdf` ([PDF]) - `plain` (plain text) - `pptx` ([PowerPoint] slide show) - `rst` ([reStructuredText]) - `rtf` ([Rich Text Format]) - `texinfo` ([GNU Texinfo]) - `textile` ([Textile]) - `slideous` ([Slideous] HTML and JavaScript slide show) - `slidy` ([Slidy] HTML and JavaScript slide show) - `dzslides` ([DZSlides] HTML5 + JavaScript slide show) - `revealjs` ([reveal.js] HTML5 + JavaScript slide show) - `s5` ([S5] HTML and JavaScript slide show) - `tei` ([TEI Simple]) - `typst` ([typst]) - `vimdoc` ([Vimdoc]) - `xml` (XML version of native AST) - `xwiki` ([XWiki markup]) - `zimwiki` ([ZimWiki markup]) - the path of a custom Lua writer, see [Custom readers and writers] below ::: Note that `odt`, `docx`, `epub`, and `pdf` output will not be directed to *stdout* unless forced with `-o -`. Extensions can be individually enabled or disabled by appending `+EXTENSION` or `-EXTENSION` to the format name. See [Extensions] below, for a list of extensions and their names. See `--list-output-formats` and `--list-extensions`, below. `-o` *FILE*, `--output=`*FILE* : Write output to *FILE* instead of *stdout*. If *FILE* is `-`, output will go to *stdout*, even if a non-textual format (`docx`, `odt`, `epub2`, `epub3`) is specified. If the output format is `chunkedhtml` and *FILE* has no extension, then instead of producing a `.zip` file pandoc will create a directory *FILE* and unpack the zip archive there (unless *FILE* already exists, in which case an error will be raised). `--data-dir=`*DIRECTORY* : Specify the user data directory to search for pandoc data files. If this option is not specified, the default user data directory will be used. On \*nix and macOS systems this will be the `pandoc` subdirectory of the XDG data directory (by default, `$HOME/.local/share`, overridable by setting the `XDG_DATA_HOME` environment variable). If that directory does not exist and `$HOME/.pandoc` exists, it will be used (for backwards compatibility). On Windows the default user data directory is `%APPDATA%\pandoc`. You can find the default user data directory on your system by looking at the output of `pandoc --version`. Data files placed in this directory (for example, `reference.odt`, `reference.docx`, `epub.css`, `templates`) will override pandoc's normal defaults. (Note that the user data directory is not created by pandoc, so you will need to create it yourself if you want to make use of it.) `-d` *FILE*, `--defaults=`*FILE* : Specify a set of default option settings. *FILE* is a YAML 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 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` : Generate a bash completion script. To enable bash completion with pandoc, add this to your `.bashrc`: eval "$(pandoc --bash-completion)" `--verbose` : Give verbose debugging output. `--quiet` : Suppress warning messages. `--fail-if-warnings[=true|false]` : Exit with error status if there are any warnings. `--log=`*FILE* : Write log messages in machine-readable JSON format to *FILE*. All messages above DEBUG level will be written, regardless of verbosity settings (`--verbose`, `--quiet`). `--list-input-formats` : List supported input formats, one per line. `--list-output-formats` : List supported output formats, one per line. `--list-extensions`[`=`*FORMAT*] : List supported extensions for *FORMAT*, one per line, preceded by a `+` or `-` indicating whether it is enabled by default in *FORMAT*. If *FORMAT* is not specified, defaults for pandoc's Markdown are given. `--list-highlight-languages` : List supported languages for syntax highlighting, one per line. `--list-highlight-styles` : List supported styles for syntax highlighting, one per line. See `--syntax-highlighting`. `-v`, `--version` : Print version. `-h`, `--help` : Show usage message. [ANSI escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code [Markdown]: https://daringfireball.net/projects/markdown/ [CommonMark]: https://commonmark.org [PHP Markdown Extra]: https://michelf.ca/projects/php-markdown/extra/ [GitHub-Flavored Markdown]: https://help.github.com/articles/github-flavored-markdown/ [MultiMarkdown]: https://fletcherpenney.net/multimarkdown/ [reStructuredText]: https://docutils.sourceforge.io/docs/ref/rst/introduction.html [S5]: https://meyerweb.com/eric/tools/s5/ [Slidy]: https://www.w3.org/Talks/Tools/Slidy2/ [Slideous]: https://goessner.net/articles/slideous/ [HTML]: https://www.w3.org/html/ [HTML5]: https://html.spec.whatwg.org/ [polyglot markup]: https://www.w3.org/TR/html-polyglot/ [XHTML]: https://www.w3.org/TR/xhtml1/ [LaTeX]: https://www.latex-project.org/ [`beamer`]: https://ctan.org/pkg/beamer [Beamer User's Guide]: http://mirrors.ctan.org/macros/latex/contrib/beamer/doc/beameruserguide.pdf [ConTeXt]: https://www.contextgarden.net/ [Rich Text Format]: https://en.wikipedia.org/wiki/Rich_Text_Format [DocBook]: https://docbook.org [Djot markup]: https://djot.net [JATS]: https://jats.nlm.nih.gov [BITS]: https://jats.nlm.nih.gov/extensions/bits/ [Jira]: https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all [txt2tags]: https://txt2tags.org [EPUB]: http://idpf.org/epub [OPML]: https://opml.org/spec2.opml [OpenDocument XML]: https://www.oasis-open.org/2021/06/16/opendocument-v1-3-oasis-standard-published/ [ODT]: https://en.wikipedia.org/wiki/OpenDocument [Plain Old Documentation]: https://perldoc.perl.org/perlpod [Textile]: https://textile-lang.com [MediaWiki markup]: https://www.mediawiki.org/wiki/Help:Formatting [DokuWiki markup]: https://www.dokuwiki.org/dokuwiki [ZimWiki markup]: https://zim-wiki.org/manual/Help/Wiki_Syntax.html [XWiki markup]: https://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/Features/XWikiSyntax/ [XLSX]: https://en.wikipedia.org/wiki/Microsoft_Excel#File_formats [Vimdoc]: https://vimhelp.org/helphelp.txt.html#help-writing [TWiki markup]: https://twiki.org/cgi-bin/view/TWiki/TextFormattingRules [TikiWiki markup]: https://doc.tiki.org/Wiki-Syntax-Text#The_Markup_Language_Wiki-Syntax [Haddock markup]: https://www.haskell.org/haddock/doc/html/ch03s08.html [Creole 1.0]: http://www.wikicreole.org/wiki/Creole1.0 [CSV]: https://tools.ietf.org/html/rfc4180 [TSV]: https://www.iana.org/assignments/media-types/text/tab-separated-values [roff man]: https://man.cx/groff_man(7) [roff ms]: https://man.cx/groff_ms(7) [Haskell]: https://www.haskell.org [GNU Texinfo]: https://www.gnu.org/software/texinfo/ [RIS]: https://en.wikipedia.org/wiki/RIS_(file_format) [Emacs Org mode]: https://orgmode.org [AsciiDoc]: https://asciidoc.org/ [AsciiDoctor]: https://asciidoctor.org/ [`asciidoc-py`]: https://github.com/asciidoc-py/asciidoc-py [DZSlides]: https://paulrouget.com/dzslides/ [Word docx]: https://en.wikipedia.org/wiki/Office_Open_XML [PDF]: https://www.adobe.com/pdf/ [reveal.js]: https://revealjs.com/ [FictionBook2]: http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1 [Jupyter notebook]: https://nbformat.readthedocs.io/en/latest/ [InDesign ICML]: https://web.archive.org/web/20211006210211/https://wwwimages.adobe.com/www.adobe.com/content/dam/acom/en/devnet/indesign/sdk/cs6/idml/idml-cookbook.pdf [TEI Simple]: https://github.com/TEIC/TEI-Simple [Muse]: https://amusewiki.org/library/manual [PowerPoint]: https://en.wikipedia.org/wiki/Microsoft_PowerPoint [Vimwiki]: https://vimwiki.github.io [CSL JSON]: https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html [BibTeX]: https://ctan.org/pkg/bibtex [BibLaTeX]: https://ctan.org/pkg/biblatex [Markua]: https://leanpub.com/markua/read [EndNote XML bibliography]: https://support.clarivate.com/Endnote/s/article/EndNote-XML-Document-Type-Definition [typst]: https://typst.app [mdoc]: https://mandoc.bsd.lv/man/mdoc.7.html [BBCode]: https://www.bbcode.org/reference.php [BBCode (FluxBB)]: https://web.archive.org/web/20210623155046/https://fluxbb.org/forums/help.php#bbcode [BBCode (Hubzilla)]: https://hubzilla.org/help/member/bbcode [BBCode (Steam)]: https://steamcommunity.com/comment/ForumTopic/formattinghelp [BBCode (phpBB)]: https://www.phpbb.com/community/help/bbcode [BBCode (xenForo)]: https://www.xenfocus.com/community/help/bb-codes/ ## Reader options {.options} `--shift-heading-level-by=`*NUMBER* : Shift heading levels by a positive or negative integer. For example, with `--shift-heading-level-by=-1`, level 2 headings become level 1 headings, and level 3 headings become level 2 headings. Headings cannot have a level less than 1, so a heading that would be shifted below level 1 becomes a regular paragraph. Exception: with a shift of -N, a level-N heading at the beginning of the document replaces the metadata title. `--shift-heading-level-by=-1` is a good choice when converting HTML or Markdown documents that use an initial level-1 heading for the document title and level-2+ headings for sections. `--shift-heading-level-by=1` may be a good choice for converting Markdown documents that use level-1 headings for sections to HTML, since pandoc uses a level-1 heading to render the document title. `--base-header-level=`*NUMBER* : *Deprecated. Use `--shift-heading-level-by`=X instead, where X = NUMBER - 1.* Specify the base level for headings (defaults to 1). `--indented-code-classes=`*CLASSES* : Specify classes to use for indented code blocks---for example, `perl,numberLines` or `haskell`. Multiple classes may be separated by spaces or commas. `--default-image-extension=`*EXTENSION* : Specify a default extension to use when image paths/URLs have no extension. This allows you to use the same source for formats that require different kinds of images. Currently this option only affects the Markdown and LaTeX readers. `--file-scope[=true|false]` : Parse each file individually before combining for multifile documents. This will allow footnotes in different files with the same identifiers to work as expected. If this option is set, footnotes and links will not work across files. Reading binary files (docx, odt, epub) implies `--file-scope`. If two or more files are processed using `--file-scope`, prefixes based on the filenames will be added to identifiers in order to disambiguate them, and internal links will be adjusted accordingly. For example, a header with identifier `foo` in `subdir/file1.txt` will have its identifier changed to `subdir__file1.txt__foo`. `-F` *PROGRAM*, `--filter=`*PROGRAM* : Specify an executable to be used as a filter transforming the pandoc AST after the input is parsed and before the output is written. The executable should read JSON from stdin and write JSON to stdout. The JSON must be formatted like pandoc's own JSON input and output. The name of the output format will be passed to the filter as the first argument. Hence, pandoc --filter ./caps.py -t latex is equivalent to pandoc -t json | ./caps.py latex | pandoc -f json -t latex The latter form may be useful for debugging filters. Filters may be written in any language. `Text.Pandoc.JSON` exports `toJSONFilter` to facilitate writing filters in Haskell. Those who would prefer to write filters in python can use the module [`pandocfilters`], installable from PyPI. There are also pandoc filter libraries in [PHP], [perl], and [JavaScript/node.js]. In order of preference, pandoc will look for filters in 1. a specified full or relative path (executable or non-executable), 2. `$DATADIR/filters` (executable or non-executable) where `$DATADIR` is the user data directory (see `--data-dir`, above), 3. `$PATH` (executable only). Filters, Lua-filters, and citeproc processing are applied in the order specified on the command line. `-L` *SCRIPT*, `--lua-filter=`*SCRIPT* : Transform the document in a similar fashion as JSON filters (see `--filter`), but use pandoc's built-in Lua filtering system. The given Lua script is expected to return a list of Lua filters which will be applied in order. Each Lua filter must contain element-transforming functions indexed by the name of the AST element on which the filter function should be applied. The `pandoc` Lua module provides helper functions for element creation. It is always loaded into the script's Lua environment. See the [Lua filters documentation] for further details. In order of preference, pandoc will look for Lua filters in 1. a specified full or relative path, 2. `$DATADIR/filters` where `$DATADIR` is the user data directory (see `--data-dir`, above). Filters, Lua filters, and citeproc processing are applied in the order specified on the command line. `-M` *KEY*[`=`*VAL*], `--metadata=`*KEY*[`:`*VAL*] : Set the metadata field *KEY* to the value *VAL*. A value specified on the command line overrides a value specified in the document using [YAML metadata blocks][Extension: `yaml_metadata_block`]. Values will be parsed as YAML boolean or string values. If no value is specified, the value will be treated as Boolean true. Like `--variable`, `--metadata` causes template variables to be set. But unlike `--variable`, `--metadata` affects the metadata of the underlying document (which is accessible from filters and may be printed in some output formats) and metadata values will be escaped when inserted into the template. `--metadata-file=`*FILE* : Read metadata from the supplied YAML (or JSON) file. This option can be used with every input format, but string scalars in the metadata file will always be parsed as Markdown. (If the input format is Markdown or a Markdown variant, then the same variant will be used to parse the metadata file; if it is a non-Markdown format, pandoc's default Markdown extensions will be used.) This option can be used repeatedly to include multiple metadata files; values in files specified later on the command line will be preferred over those specified in earlier files. Metadata values specified inside the document, or by using `-M`, overwrite values specified with this option. The file will be searched for first in the working directory, and then in the `metadata` subdirectory of the user data directory (see `--data-dir`). `-p`, `--preserve-tabs[=true|false]` : Preserve tabs instead of converting them to spaces. (By default, pandoc converts tabs to spaces before parsing its input.) Note that this will only affect tabs in literal code spans and code blocks. Tabs in regular text are always treated as spaces. `--tab-stop=`*NUMBER* : Specify the number of spaces per tab (default is 4). `--track-changes=accept`|`reject`|`all` : Specifies what to do with insertions, deletions, and comments produced by the MS Word "Track Changes" feature. `accept` (the default) processes all the insertions and deletions. `reject` ignores them. Both `accept` and `reject` ignore comments. `all` includes all insertions, deletions, and comments, wrapped in spans with `insertion`, `deletion`, `comment-start`, and `comment-end` classes, respectively. The author and time of change is included. `all` is useful for scripting: only accepting changes from a certain reviewer, say, or before a certain date. If a paragraph is inserted or deleted, `track-changes=all` produces a span with the class `paragraph-insertion`/`paragraph-deletion` before the affected paragraph break. This option only affects the docx reader. `--extract-media=`*DIR*|*FILE*`.zip` : Extract images and other media contained in or linked from the source document to the path *DIR*, creating it if necessary, and adjust the images references in the document so they point to the extracted files. Media are downloaded, read from the file system, or extracted from a binary container (e.g. docx), as needed. The original file paths are used if they are relative paths not containing `..`. Otherwise filenames are constructed from the SHA1 hash of the contents. 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 one to a line. If this option is not specified, pandoc will read the data file `abbreviations` from the user data directory or fall back on a system default. To see the system default, use `pandoc --print-default-data-file=abbreviations`. The only use pandoc makes of this list is in the Markdown reader. Strings found in this list will be followed by a nonbreaking space, and the period will not produce sentence-ending space in formats like LaTeX. The strings may not contain spaces. `--trace[=true|false]` : Print diagnostic output tracing parser progress to stderr. This option is intended for use by developers in diagnosing performance issues. [`pandocfilters`]: https://github.com/jgm/pandocfilters [PHP]: https://github.com/vinai/pandocfilters-php [perl]: https://metacpan.org/pod/Pandoc::Filter [JavaScript/node.js]: https://github.com/mvhenderson/pandoc-filter-node [Lua filters documentation]: https://pandoc.org/lua-filters.html ## General writer options {.options} `-s`, `--standalone` : Produce output with an appropriate header and footer (e.g. a standalone HTML, LaTeX, TEI, or RTF file, not a fragment). This option is set automatically for `pdf`, `epub`, `epub3`, `fb2`, `docx`, and `odt` output. For `native` output, this option causes metadata to be included; otherwise, metadata is suppressed. `--template=`*FILE*|*URL* : Use the specified file as a custom template for the generated document. Implies `--standalone`. See [Templates], below, for a description of template syntax. If the template is not found, pandoc will search for it in the `templates` subdirectory of the user data directory (see `--data-dir`). If no extension is specified and an extensionless template is not found, pandoc will look for a template with an extension corresponding to the writer, so that `--template=special` looks for `special.html` for HTML output. If this option is not used, a default template appropriate for the output format will be used (see `-D/--print-default-template`). `-V` *KEY*[`=`*VAL*], `--variable=`*KEY*[`=`*VAL*] : Set the template variable *KEY* to the string value *VAL* when rendering the document in standalone mode. Either `:` or `=` may be used to separate *KEY* from *VAL*. If no *VAL* is specified, the key will be given the value `true`. Structured values (lists, maps) cannot be assigned using this option, but they can be assigned in the `variables` section of a [defaults file][Defaults files] or using the `--variable-json` option. If the variable already has a *list* value, the value will be added to the list. If it already has another kind of value, it will be made into a list containing the previous and the new value. For example, `-V keyword=Joe -V author=Sue` makes `author` contain a list of strings: `Joe` and `Sue`. `--variable-json=`*KEY*[`=`:*JSON*] : Set the template variable *KEY* to the value specified by a JSON string (this may be a boolean, a string, a list, or a mapping; a number will be treated as a string). For example, `--variable-json foo=false` will give `foo` the boolean false value, while `--variable-json foo='"false"'` will give it the string value `"false"`. Either `:` or `=` may be used to separate *KEY* from *VAL*. If the variable already has a value, this value will be replaced. `--sandbox[=true|false]` : Run pandoc in a sandbox, limiting IO operations in readers and writers to reading the files specified on the command line. Note that this option does not limit IO operations by filters or in the production of PDF documents. But it does offer security against, for example, disclosure of files through the use of `include` directives. Anyone using pandoc on untrusted user input should use this option. Note: some readers and writers (e.g., `docx`) need access to data files. If these are stored on the file system, then pandoc will not be able to find them when run in `--sandbox` mode and will raise an error. For these applications, we recommend using a pandoc binary compiled with the `embed_data_files` option, which causes the data files to be baked into the binary instead of being stored on the file system. `-D` *FORMAT*, `--print-default-template=`*FORMAT* : Print the system default template for an output *FORMAT*. (See `-t` for a list of possible *FORMAT*s.) Templates in the user data directory are ignored. This option may be used with `-o`/`--output` to redirect output to a file, but `-o`/`--output` must come before `--print-default-template` on the command line. Note that some of the default templates use partials, for example `styles.html`. To print the partials, use `--print-default-data-file`: for example, `--print-default-data-file=templates/styles.html`. `--print-default-data-file=`*FILE* : Print a system default data file. Files in the user data directory are ignored. This option may be used with `-o`/`--output` to redirect output to a file, but `-o`/`--output` must come before `--print-default-data-file` on the command line. `--eol=crlf`|`lf`|`native` : Manually specify line endings: `crlf` (Windows), `lf` (macOS/Linux/UNIX), or `native` (line endings appropriate to the OS on which pandoc is being run). The default is `native`. `--dpi`=*NUMBER* : Specify the default dpi (dots per inch) value for conversion from pixels to inch/centimeters and vice versa. (Technically, the correct term would be ppi: pixels per inch.) The default is 96dpi. When images contain information about dpi internally, the encoded value is used instead of the default specified by this option. `--wrap=auto`|`none`|`preserve` : Determine how text is wrapped in the output (the source code, not the rendered version). With `auto` (the default), pandoc will attempt to wrap lines to the column width specified by `--columns` (default 72). With `none`, pandoc will not wrap lines at all. With `preserve`, pandoc will attempt to preserve the wrapping from the source document (that is, where there are nonsemantic newlines in the source, there will be nonsemantic newlines in the output as well). In `ipynb` output, this option affects wrapping of the contents of Markdown cells. `--columns=`*NUMBER* : Specify length of lines in characters. This affects text wrapping in the generated source code (see `--wrap`). It also affects calculation of column widths for plain text tables (see [Tables] below). `--toc[=true|false]`, `--table-of-contents[=true|false]` : Include an automatically generated table of contents (or, in the case of `latex`, `context`, `docx`, `odt`, `opendocument`, `rst`, or `ms`, an instruction to create one) in the output document. This option has no effect unless `-s/--standalone` is used, and it has no effect on `man`, `docbook4`, `docbook5`, or `jats` output. Note that if you are producing a PDF via `ms` and using (the default) `pdfroff` as a `--pdf-engine`, the table of contents will appear at the beginning of the document, before the title. If you would prefer it to be at the end of the document, use the option `--pdf-engine-opt=--no-toc-relocation`. If `groff` is used as the `--pdf-engine`, the table of contents will always appear at the end of the document. `--toc-depth=`*NUMBER* : Specify the number of section levels to include in the table of contents. The default is 3 (which means that level-1, 2, and 3 headings will be listed in the contents). `--lof[=true|false]`, `--list-of-figures[=true|false]` : Include an automatically generated list of figures (or, in some formats, an instruction to create one) in the output document. This option has no effect unless `-s/--standalone` is used, and it only has an effect on `latex`, `context`, and `docx` output. `--lot[=true|false]`, `--list-of-tables[=true|false]` : Include an automatically generated list of tables (or, in some formats, an instruction to create one) in the output document. This option has no effect unless `-s/--standalone` is used, and it only has an effect on `latex`, `context`, and `docx` output. `--strip-comments[=true|false]` : Strip out HTML comments in the Markdown or Textile source, rather than passing them on to Markdown, Textile or HTML output as raw HTML. This does not apply to HTML comments inside raw HTML blocks when the `markdown_in_html_blocks` extension is not set. `--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 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. The default for HTML, EPUB, Docx, Ms, Man, and LaTeX output is 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`, and `tango`. For more information on syntax highlighting in pandoc, see [Syntax highlighting], below. See also `--list-highlight-styles`. Instead of a *STYLE* name, a JSON file with extension `.theme` may be supplied. This will be parsed as a KDE syntax highlighting theme and (if valid) used as the highlighting style. To generate the JSON version of an existing style, use `--print-highlight-style`. `--no-highlight` : *Deprecated, use `--syntax-highlighting=none` instead.* Disables syntax highlighting for code blocks and inlines, even when a language attribute is given. `--highlight-style=`*STYLE*|*FILE* : _Deprecated, use `--syntax-highlighting=`*STYLE*|*FILE* instead._ Specifies the coloring style to be used in highlighted source code. `--print-highlight-style=`*STYLE*|*FILE* : Prints a JSON version of a highlighting style, which can be modified, saved with a `.theme` extension, and used with `--syntax-highlighting`. This option may be used with `-o`/`--output` to redirect output to a file, but `-o`/`--output` must come before `--print-highlight-style` on the command line. `--syntax-definition=`*FILE* : Instructs pandoc to load a KDE XML syntax definition file, which will be used for syntax highlighting of appropriately marked code blocks. This can be used to add support for new languages or to use altered syntax definitions for existing languages. This option may be repeated to add multiple syntax definitions. `-H` *FILE*, `--include-in-header=`*FILE*|*URL* : Include contents of *FILE*, verbatim, at the end of the header. This can be used, for example, to include special CSS or JavaScript in HTML documents. This option can be used repeatedly to include multiple files in the header. They will be included in the order specified. Implies `--standalone`. `-B` *FILE*, `--include-before-body=`*FILE*|*URL* : Include contents of *FILE*, verbatim, at the beginning of the document body (e.g. after the `` tag in HTML, or the `\begin{document}` command in LaTeX). This can be used to include navigation bars or banners in HTML documents. This option can be used repeatedly to include multiple files. They will be included in the order specified. Implies `--standalone`. Note that if the output format is `odt`, this file must be in OpenDocument XML format suitable for insertion into the body of the document, and if the output is `docx`, this file must be in appropriate OpenXML format. `-A` *FILE*, `--include-after-body=`*FILE*|*URL* : Include contents of *FILE*, verbatim, at the end of the document body (before the `` tag in HTML, or the `\end{document}` command in LaTeX). This option can be used repeatedly to include multiple files. They will be included in the order specified. Implies `--standalone`. Note that if the output format is `odt`, this file must be in OpenDocument XML format suitable for insertion into the body of the document, and if the output is `docx`, this file must be in appropriate OpenXML format. `--resource-path=`*SEARCHPATH* : List of paths to search for images and other resources. The paths should be separated by `:` on Linux, UNIX, and macOS systems, and by `;` on Windows. If `--resource-path` is not specified, the default resource path is the working directory. Note that, if `--resource-path` is specified, the working directory must be explicitly listed or it will not be searched. For example: `--resource-path=.:test` will search the working directory and the `test` subdirectory, in that order. This option can be used repeatedly. Search path components that come later on the command line will be searched before those that come earlier, so `--resource-path foo:bar --resource-path baz:bim` is equivalent to `--resource-path baz:bim:foo:bar`. Note that this option only has an effect when pandoc itself needs to find an image (e.g., in producing a PDF or docx, or when `--embed-resources` is used.) It will not cause image paths to be rewritten in other cases (e.g., when pandoc is generating LaTeX or HTML). `--request-header=`*NAME*`:`*VAL* : Set the request header *NAME* to the value *VAL* when making HTTP requests (for example, when a URL is given on the command line, or when resources used in a document must be downloaded). If you're behind a proxy, you also need to set the environment variable `http_proxy` to `http://...`. `--no-check-certificate[=true|false]` : Disable the certificate verification to allow access to unsecure HTTP resources (for example when the certificate is no longer valid or self signed). ## Options affecting specific writers {.options} `--self-contained[=true|false]` : *Deprecated synonym for `--embed-resources --standalone`.* `--embed-resources[=true|false]` : Produce a standalone HTML file with no external dependencies, using `data:` URIs to incorporate the contents of linked scripts, stylesheets, images, and videos. The resulting file should be "self-contained," in the sense that it needs no external files and no net access to be displayed properly by a browser. This option works only with HTML output formats, including `html4`, `html5`, `html+lhs`, `html5+lhs`, `s5`, `slidy`, `slideous`, `dzslides`, and `revealjs`. Scripts, images, and stylesheets at absolute URLs will be downloaded; those at relative URLs will be sought relative to the working directory (if the first source file is local) or relative to the base URL (if the first source file is remote). Elements with the attribute `data-external="1"` will be left alone; the documents they link to will not be incorporated in the document. Limitation: resources that are loaded dynamically through JavaScript cannot be incorporated; as a result, fonts may be missing when `--mathjax` is used, and some advanced features (e.g. zoom or speaker notes) may not work in an offline "self-contained" `reveal.js` slide show. For SVG images, `img` tags with `data:` URIs are used, unless the image has the class `inline-svg`, in which case an inline SVG element is inserted. This approach is recommended when there are many occurrences of the same SVG in a document, as `` elements will be used to reduce duplication. `--link-images[=true|false]` : Include links to images instead of embedding the images in ODT. (This option currently only affects ODT output.) `--html-q-tags[=true|false]` : Use `` tags for quotes in HTML. (This option only has an effect if the `smart` extension is enabled for the input format used.) `--ascii[=true|false]` : Use only ASCII characters in output. Currently supported for XML and HTML formats (which use entities instead of UTF-8 when this option is selected), CommonMark, gfm, and Markdown (which use entities), roff man and ms (which use hexadecimal escapes), and to a limited degree LaTeX (which uses standard commands for accented characters when possible). `--reference-links[=true|false]` : Use reference-style links, rather than inline links, in writing Markdown or reStructuredText. By default inline links are used. The placement of link references is affected by the `--reference-location` option. `--reference-location=block`|`section`|`document` : Specify whether footnotes (and references, if `reference-links` is set) are placed at the end of the current (top-level) block, the current section, or the document. The default is `document`. Currently this option only affects the `markdown`, `muse`, `html`, `epub`, `slidy`, `s5`, `slideous`, `dzslides`, and `revealjs` writers. In slide formats, specifying `--reference-location=section` will cause notes to be rendered at the bottom of a slide. `--figure-caption-position=above`|`below` : Specify whether figure captions go above or below figures (default is `below`). This option only affects HTML, LaTeX, Docx, ODT, and Typst output. `--table-caption-position=above`|`below` : Specify whether table captions go above or below tables (default is `above`). This option only affects HTML, LaTeX, Docx, ODT, and Typst output. `--markdown-headings=setext`|`atx` : Specify whether to use ATX-style (`#`-prefixed) or Setext-style (underlined) headings for level 1 and 2 headings in Markdown output. (The default is `atx`.) ATX-style headings are always used for levels 3+. This option also affects Markdown cells in `ipynb` output. `--list-tables[=true|false]` : Render tables as list tables in RST output. `--top-level-division=default`|`section`|`chapter`|`part` : Treat top-level headings as the given division type in LaTeX, ConTeXt, DocBook, and TEI output. The hierarchy order is part, chapter, then section; all headings are shifted such that the top-level heading becomes the specified type. The default behavior is to determine the best division type via heuristics: unless other conditions apply, `section` is chosen. When the `documentclass` variable is set to `report`, `book`, or `memoir` (unless the `article` option is specified), `chapter` is implied as the setting for this option. If `beamer` is the output format, specifying either `chapter` or `part` will cause top-level headings to become `\part{..}`, while second-level headings remain as their default type. In Docx output, this option adds section breaks before first-level headings if `chapter` is selected, and before first- and second-level headings if `part` is selected. Footnote numbers will restart with each section break unless the reference doc modifies this. `-N`, `--number-sections=[true|false]` : Number section headings in LaTeX, ConTeXt, HTML, Docx, ms, or EPUB output. By default, sections are not numbered. Sections with class `unnumbered` will never be numbered, even if `--number-sections` is specified. `--number-offset=`*NUMBER*[`,`*NUMBER*`,`*...*] : Offsets for section heading numbers. The first number is added to the section number for level-1 headings, the second for level-2 headings, and so on. So, for example, if you want the first level-1 heading in your document to be numbered "6" instead of "1", specify `--number-offset=5`. If your document starts with a level-2 heading which you want to be numbered "1.5", specify `--number-offset=1,4`. `--number-offset` only directly affects the number of the first section heading in a document; subsequent numbers increment in the normal way. Implies `--number-sections`. Currently this feature only affects HTML and Docx output. `--listings[=true|false]` : *Deprecated, use `--syntax-highlighting=idiomatic` or `--syntax-highlighting=default` instead. Use the [`listings`] package for LaTeX code blocks. The package does not support multi-byte encoding for source code. To handle UTF-8 you would need to use a custom template. This issue is fully documented here: [Encoding issue with the listings package]. `-i`, `--incremental[=true|false]` : Make list items in slide shows display incrementally (one by one). The default is for lists to be displayed all at once. `--slide-level=`*NUMBER* : Specifies that headings with the specified level create slides (for `beamer`, `revealjs`, `pptx`, `s5`, `slidy`, `slideous`, `dzslides`). Headings above this level in the hierarchy are used to divide the slide show into sections; headings below this level create subheads within a slide. Valid values are 0-6. If a slide level of 0 is specified, slides will not be split automatically on headings, and horizontal rules must be used to indicate slide boundaries. If a slide level is not specified explicitly, the slide level will be set automatically based on the contents of the document; see [Structuring the slide show]. `--section-divs[=true|false]` : Wrap sections in `
` tags (or `
` tags for `html4`), and attach identifiers to the enclosing `
` (or `
`) rather than the heading itself (see [Heading identifiers], below). This option only affects HTML output (and does not affect HTML slide formats). `--email-obfuscation=none`|`javascript`|`references` : Specify a method for obfuscating `mailto:` links in HTML documents. `none` leaves `mailto:` links as they are. `javascript` obfuscates them using JavaScript. `references` obfuscates them by printing their letters as decimal or hexadecimal character references. The default is `none`. `--id-prefix=`*STRING* : Specify a prefix to be added to all identifiers and internal links in HTML and DocBook output, and to footnote numbers in Markdown and Haddock output. This is useful for preventing duplicate identifiers when generating fragments to be included in other pages. `-T` *STRING*, `--title-prefix=`*STRING* : Specify *STRING* as a prefix at the beginning of the title that appears in the HTML header (but not in the title as it appears at the beginning of the HTML body). Implies `--standalone`. `-c` *URL*, `--css=`*URL* : Link to a CSS style sheet. This option can be used repeatedly to include multiple files. They will be included in the order specified. This option only affects HTML (including HTML slide shows) and EPUB output. It should be used together with `-s/--standalone`, because the link to the stylesheet goes in the document header. A stylesheet is required for generating EPUB. If none is provided using this option (or the `css` or `stylesheet` metadata fields), pandoc will look for a file `epub.css` in the user data directory (see `--data-dir`). If it is not found there, sensible defaults will be used. [`--reference-doc=`*FILE*|*URL*]{#option--reference-doc} : Use the specified file as a style reference in producing a docx or ODT file. Docx : For best results, the reference docx should be a modified version of a docx file produced using pandoc. The contents of the reference docx are ignored, but its stylesheets and document properties (including margins, page size, header, and footer) are used in the new docx. If no reference docx is specified on the command line, pandoc will look for a file `reference.docx` in the user data directory (see `--data-dir`). If this is not found either, sensible defaults will be used. To produce a custom `reference.docx`, first get a copy of the default `reference.docx`: `pandoc -o custom-reference.docx --print-default-data-file reference.docx`. Then open `custom-reference.docx` in Word, modify the styles as you wish, and save the file. For best results, do not make changes to this file other than modifying the styles used by pandoc: Paragraph styles: - Normal - Body Text - First Paragraph - Compact - Title - Subtitle - Author - Date - Abstract - AbstractTitle - Bibliography - Heading 1 - Heading 2 - Heading 3 - Heading 4 - Heading 5 - Heading 6 - Heading 7 - Heading 8 - Heading 9 - Block Text [for block quotes] - Footnote Block Text [for block quotes in footnotes] - Source Code - Footnote Text - Definition Term - Definition - Caption - Table Caption - Image Caption - Figure - Captioned Figure - TOC Heading Character styles: - Default Paragraph Font - Verbatim Char - Footnote Reference - Hyperlink - Section Number Table style: - Table ODT : For best results, the reference ODT should be a modified version of an ODT produced using pandoc. The contents of the reference ODT are ignored, but its stylesheets are used in the new ODT. If no reference ODT is specified on the command line, pandoc will look for a file `reference.odt` in the user data directory (see `--data-dir`). If this is not found either, sensible defaults will be used. To produce a custom `reference.odt`, first get a copy of the default `reference.odt`: `pandoc -o custom-reference.odt --print-default-data-file reference.odt`. Then open `custom-reference.odt` in LibreOffice, modify the styles as you wish, and save the file. PowerPoint : Templates included with Microsoft PowerPoint 2013 (either with `.pptx` or `.potx` extension) are known to work, as are most templates derived from these. The specific requirement is that the template should contain layouts with the following names (as seen within PowerPoint): - Title Slide - Title and Content - Section Header - Two Content - Comparison - Content with Caption - Blank For each name, the first layout found with that name will be used. If no layout is found with one of the names, pandoc will output a warning and use the layout with that name from the default reference doc instead. (How these layouts are used is described in [PowerPoint layout choice](#powerpoint-layout-choice).) All templates included with a recent version of MS PowerPoint will fit these criteria. (You can click on `Layout` under the `Home` menu to check.) You can also modify the default `reference.pptx`: first run `pandoc -o custom-reference.pptx --print-default-data-file reference.pptx`, and then modify `custom-reference.pptx` in MS PowerPoint (pandoc will use the layouts with the names listed above). `--split-level=`*NUMBER* : Specify the heading level at which to split an EPUB or chunked HTML document into separate files. The default is to split into chapters at level-1 headings. In the case of EPUB, this option only affects the internal composition of the EPUB, not the way chapters and sections are displayed to users. Some readers may be slow if the chapter files are too large, so for large documents with few level-1 headings, one might want to use a chapter level of 2 or 3. For chunked HTML, this option determines how much content goes in each "chunk." `--chunk-template=`*PATHTEMPLATE* : Specify a template for the filenames in a `chunkedhtml` document. In the template, `%n` will be replaced by the chunk number (padded with leading 0s to 3 digits), `%s` with the section number of the chunk, `%h` with the heading text (with formatting removed), `%i` with the section identifier. For example, `section-%s-%i.html` might be resolved to `section-1.1-introduction.html`. The characters `/` and `\` are not allowed in chunk templates and will be ignored. The default is `%s-%i.html`. `--epub-chapter-level=`*NUMBER* : *Deprecated synonym for `--split-level`.* `--epub-cover-image=`*FILE* : Use the specified image as the EPUB cover. It is recommended that the image be less than 1000px in width and height. Note that in a Markdown source document you can also specify `cover-image` in a YAML metadata block (see [EPUB Metadata], below). `--epub-title-page=true`|`false` : Determines whether a the title page is included in the EPUB (default is `true`). `--epub-metadata=`*FILE* : Look in the specified XML file for metadata for the EPUB. The file should contain a series of [Dublin Core elements]. For example: Creative Commons es-AR By default, pandoc will include the following metadata elements: `` (from the document title), `` (from the document authors), `` (from the document date, which should be in [ISO 8601 format]), `` (from the `lang` variable, or, if is not set, the locale), and `` (a randomly generated UUID). Any of these may be overridden by elements in the metadata file. Note: if the source document is Markdown, a YAML metadata block in the document can be used instead. See below under [EPUB Metadata]. `--epub-embed-font=`*FILE* : Embed the specified font in the EPUB. This option can be repeated to embed multiple fonts. Wildcards can also be used: for example, `DejaVuSans-*.ttf`. However, if you use wildcards on the command line, be sure to escape them or put the whole filename in single quotes, to prevent them from being interpreted by the shell. To use the embedded fonts, you will need to add declarations like the following to your CSS (see `--css`): @font-face { font-family: DejaVuSans; font-style: normal; font-weight: normal; src:url("../fonts/DejaVuSans-Regular.ttf"); } @font-face { font-family: DejaVuSans; font-style: normal; font-weight: bold; src:url("../fonts/DejaVuSans-Bold.ttf"); } @font-face { font-family: DejaVuSans; font-style: italic; font-weight: normal; src:url("../fonts/DejaVuSans-Oblique.ttf"); } @font-face { font-family: DejaVuSans; font-style: italic; font-weight: bold; src:url("../fonts/DejaVuSans-BoldOblique.ttf"); } body { font-family: "DejaVuSans"; } `--epub-subdirectory=`*DIRNAME* : Specify the subdirectory in the OCF container that is to hold the EPUB-specific contents. The default is `EPUB`. To put the EPUB contents in the top level, use an empty string. `--ipynb-output=all|none|best` : Determines how ipynb output cells are treated. `all` means that all of the data formats included in the original are preserved. `none` means that the contents of data cells are omitted. `best` causes pandoc to try to pick the richest data block in each output cell that is compatible with the output format. The default is `best`. `--pdf-engine=`*PROGRAM* : Use the specified engine when producing PDF output. Valid values are `pdflatex`, `lualatex`, `xelatex`, `latexmk`, `tectonic`, `wkhtmltopdf`, `weasyprint`, `pagedjs-cli`, `prince`, `context`, `groff`, `pdfroff`, and `typst`. If the engine is not in your PATH, the full path of the engine may be specified here. If this option is not specified, pandoc uses the following defaults depending on the output format specified using `-t/--to`: - `-t latex` or none: `pdflatex` (other options: `xelatex`, `lualatex`, `tectonic`, `latexmk`) - `-t context`: `context` - `-t html`: `weasyprint` (other options: `prince`, `wkhtmltopdf`, `pagedjs-cli`; see [print-css.rocks](https://print-css.rocks) for a good introduction to PDF generation from HTML/CSS) - `-t ms`: `pdfroff` - `-t typst`: `typst` This option is normally intended to be used when a PDF file is specified as `-o/--output`. However, it may still have an effect when other output formats are requested. For example, `ms` output will include `.pdfhref` macros only if a `--pdf-engine` is selected, and the macros will be differently encoded depending on whether `groff` or `pdfroff` is specified. `--pdf-engine-opt=`*STRING* : Use the given string as a command-line argument to the `pdf-engine`. For example, to use a persistent directory `foo` for `latexmk`'s auxiliary files, use `--pdf-engine-opt=-outdir=foo`. Note that no check for duplicate options is done. [Dublin Core elements]: https://www.dublincore.org/specifications/dublin-core/dces/ [ISO 8601 format]: https://www.w3.org/TR/NOTE-datetime [Encoding issue with the listings package]: https://en.wikibooks.org/wiki/LaTeX/Source_Code_Listings#Encoding_issue ## Citation rendering {.options} `-C`, `--citeproc` : Process the citations in the file, replacing them with rendered citations and adding a bibliography. Citation processing will not take place unless bibliographic data is supplied, either through an external file specified using the `--bibliography` option or the `bibliography` field in metadata, or via a `references` section in metadata containing a list of citations in CSL YAML format with Markdown formatting. The style is controlled by a [CSL] stylesheet specified using the `--csl` option or the `csl` field in metadata. (If no stylesheet is specified, the `chicago-author-date` style will be used by default.) The citation processing transformation may be applied before or after filters or Lua filters (see `--filter`, `--lua-filter`): these transformations are applied in the order they appear on the command line. For more information, see the section on [Citations]. Note: if this option is specified, the `citations` extension will be disabled automatically in the writer, to ensure that the citeproc-generated citations will be rendered instead of the format's own citation syntax. `--bibliography=`*FILE* : Set the `bibliography` field in the document's metadata to *FILE*, overriding any value set in the metadata. If you supply this argument multiple times, each *FILE* will be added to bibliography. If *FILE* is a URL, it will be fetched via HTTP. If *FILE* is not found relative to the working directory, it will be sought in the resource path (see `--resource-path`). `--csl=`*FILE* : Set the `csl` field in the document's metadata to *FILE*, overriding any value set in the metadata. (This is equivalent to `--metadata csl=FILE`.) If *FILE* is a URL, it will be fetched via HTTP. If *FILE* is not found relative to the working directory, it will be sought in the resource path (see `--resource-path`) and finally in the `csl` subdirectory of the pandoc user data directory. `--citation-abbreviations=`*FILE* : Set the `citation-abbreviations` field in the document's metadata to *FILE*, overriding any value set in the metadata. (This is equivalent to `--metadata citation-abbreviations=FILE`.) If *FILE* is a URL, it will be fetched via HTTP. If *FILE* is not found relative to the working directory, it will be sought in the resource path (see `--resource-path`) and finally in the `csl` subdirectory of the pandoc user data directory. `--natbib` : Use [`natbib`] for citations in LaTeX output. This option is not for use with the `--citeproc` option or with PDF output. It is intended for use in producing a LaTeX file that can be processed with [`bibtex`]. `--biblatex` : Use [`biblatex`] for citations in LaTeX output. This option is not for use with the `--citeproc` option or with PDF output. It is intended for use in producing a LaTeX file that can be processed with [`bibtex`] or [`biber`]. ## Math rendering in HTML {.options} The default is to render TeX math as far as possible using Unicode characters. Formulas are put inside a `span` with `class="math"`, so that they may be styled differently from the surrounding text if needed. However, this gives acceptable results only for basic math, usually you will want to use `--mathjax` or another of the following options. `--mathjax`[`=`*URL*] : Use [MathJax] to display embedded TeX math in HTML output. TeX math will be put between `\(...\)` (for inline math) or `\[...\]` (for display math) and wrapped in `` tags with class `math`. Then the MathJax JavaScript will render it. The *URL* should point to the `MathJax.js` load script. If a *URL* is not provided, a link to the Cloudflare CDN will be inserted. `--mathml` : Convert TeX math to [MathML] (in `epub3`, `docbook4`, `docbook5`, `jats`, `html4` and `html5`). This is the default in `odt` output. MathML is supported natively by the main web browsers and select e-book readers. `--webtex`[`=`*URL*] : Convert TeX formulas to `` tags that link to an external script that converts formulas to images. The formula will be URL-encoded and concatenated with the URL provided. For SVG images you can for example use `--webtex https://latex.codecogs.com/svg.latex?`. If no URL is specified, the CodeCogs URL generating PNGs will be used (`https://latex.codecogs.com/png.latex?`). Note: the `--webtex` option will affect Markdown output as well as HTML, which is useful if you're targeting a version of Markdown without native math support. `--katex`[`=`*URL*] : Use [KaTeX] to display embedded TeX math in HTML output. The *URL* is the base URL for the KaTeX library. That directory should contain a `katex.min.js` and a `katex.min.css` file. If a *URL* is not provided, a link to the KaTeX CDN will be inserted. `--gladtex` : Enclose TeX math in `` tags in HTML output. The resulting HTML can then be processed by [GladTeX] to produce SVG images of the typeset formulas and an HTML file with these images embedded. pandoc -s --gladtex input.md -o myfile.htex gladtex -d image_dir myfile.htex # produces myfile.html and images in image_dir [MathML]: https://www.w3.org/Math/ [MathJax]: https://www.mathjax.org [KaTeX]: https://github.com/Khan/KaTeX [GladTeX]: https://humenda.github.io/GladTeX/ ## Options for wrapper scripts {.options} `--dump-args[=true|false]` : Print information about command-line arguments to *stdout*, then exit. This option is intended primarily for use in wrapper scripts. The first line of output contains the name of the output file specified with the `-o` option, or `-` (for *stdout*) if no output file was specified. The remaining lines contain the command-line arguments, one per line, in the order they appear. These do not include regular pandoc options and their arguments, but do include any options appearing after a `--` separator at the end of the line. `--ignore-args[=true|false]` : Ignore command-line arguments (for use in wrapper scripts). Regular pandoc options are not ignored. Thus, for example, pandoc --ignore-args -o foo.html -s foo.txt -- -e latin1 is equivalent to pandoc -o foo.html -s # Exit codes If pandoc completes successfully, it will return exit code 0. Nonzero exit codes have the following meanings: Code Error ----- ------------------------------------ 1 PandocIOError 3 PandocFailOnWarningError 4 PandocAppError 5 PandocTemplateError 6 PandocOptionError 21 PandocUnknownReaderError 22 PandocUnknownWriterError 23 PandocUnsupportedExtensionError 24 PandocCiteprocError 25 PandocBibliographyError 31 PandocEpubSubdirectoryError 43 PandocPDFError 44 PandocXMLError 47 PandocPDFProgramNotFoundError 61 PandocHttpError 62 PandocShouldNeverHappenError 63 PandocSomeError 64 PandocParseError 66 PandocMakePDFError 67 PandocSyntaxMapError 83 PandocFilterError 84 PandocLuaError 89 PandocNoScriptingEngine 91 PandocMacroLoop 92 PandocUTF8DecodingError 93 PandocIpynbDecodingError 94 PandocUnsupportedCharsetError 95 PandocInputNotTextError 97 PandocCouldNotFindDataFileError 98 PandocCouldNotFindMetadataFileError 99 PandocResourceNotFound ----- ------------------------------------ # Defaults files The `--defaults` option may be used to specify a package 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 one line: ``` yaml 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: ``` yaml csl: ${HOME}/mycsldir/special.csl ``` `${USERDATA}` may also be used; this will always resolve to the user data directory that is current when the defaults file is parsed, regardless of the setting of the environment variable `USERDATA`. `${.}` will resolve to the directory containing the defaults file itself. This allows you to refer to resources contained in that directory: ``` yaml epub-cover-image: ${.}/cover.jpg epub-metadata: ${.}/meta.xml resource-path: - . # the working directory from which pandoc is run - ${.}/images # the images subdirectory of the directory # containing this defaults file ``` This environment variable interpolation syntax *only* works in fields that expect file paths. Defaults files can be placed in the `defaults` subdirectory of the user data directory and used from any directory. For example, one could create a file specifying defaults for writing letters, save it as `letter.yaml` in the `defaults` subdirectory of the user data directory, and then invoke these defaults from any directory using `pandoc --defaults letter` or `pandoc -dletter`. When multiple defaults are used, their contents will be combined. Note that, where command-line arguments may be repeated (`--metadata-file`, `--css`, `--include-in-header`, `--include-before-body`, `--include-after-body`, `--variable`, `--metadata`, `--syntax-definition`), the values specified on the command line will combine with values specified in the defaults file, rather than replacing them. The following tables show the mapping between the command line and defaults file entries. +----------------------------------+-----------------------------------+ | command line | defaults file | +:=================================+:==================================+ | ``` | ``` yaml | | foo.md | input-file: foo.md | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | foo.md bar.md | input-files: | | | - foo.md | | | - bar.md | | ``` | ``` | +----------------------------------+-----------------------------------+ The value of `input-files` may be left empty to indicate input from stdin, and it can be an empty sequence `[]` for no input. ## General options +----------------------------------+-----------------------------------+ | command line | defaults file | +:=================================+:==================================+ | ``` | ``` yaml | | --from markdown+emoji | from: markdown+emoji | | ``` | ``` | | | ``` yaml | | | reader: markdown+emoji | | | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --to markdown+hard_line_breaks | to: markdown+hard_line_breaks | | ``` | ``` | | | ``` yaml | | | writer: markdown+hard_line_breaks | | | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --output foo.pdf | output-file: foo.pdf | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --output - | output-file: | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --data-dir dir | data-dir: dir | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --defaults file | defaults: | | ``` | - file | | | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --verbose | verbosity: INFO | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --quiet | verbosity: ERROR | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --fail-if-warnings | fail-if-warnings: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --sandbox | sandbox: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --log=FILE | log-file: FILE | | ``` | ``` | +----------------------------------+-----------------------------------+ Options specified in a defaults file itself always have priority over those in another file included with a `defaults:` entry. `verbosity` can have the values `ERROR`, `WARNING`, or `INFO`. ## Reader options +----------------------------------+-----------------------------------+ | command line | defaults file | +:=================================+:==================================+ | ``` | ``` yaml | | --shift-heading-level-by -1 | shift-heading-level-by: -1 | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --indented-code-classes python | indented-code-classes: | | | - python | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --default-image-extension ".jpg" | default-image-extension: '.jpg' | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --file-scope | file-scope: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --citeproc \ | filters: | | --lua-filter count-words.lua \ | - citeproc | | --filter special.lua | - count-words.lua | | | - type: json | | | path: special.lua | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --metadata key=value \ | metadata: | | --metadata key2 | key: value | | | key2: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --metadata-file meta.yaml | metadata-files: | | | - meta.yaml | | ``` | ``` | | | ``` yaml | | | metadata-file: meta.yaml | | | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --preserve-tabs | preserve-tabs: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --tab-stop 8 | tab-stop: 8 | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --track-changes accept | track-changes: accept | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --extract-media dir | extract-media: dir | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --abbreviations abbrevs.txt | abbreviations: abbrevs.txt | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --trace | trace: true | | ``` | ``` | +----------------------------------+-----------------------------------+ Metadata values specified in a defaults file are parsed as literal string text, not Markdown. Filters will be assumed to be Lua filters if they have the `.lua` extension, and JSON filters otherwise. But the filter type can also be specified explicitly, as shown. Filters are run in the order specified. To include the built-in citeproc filter, use either `citeproc` or `{type: citeproc}`. ## General writer options +----------------------------------+-----------------------------------+ | command line | defaults file | +:=================================+:==================================+ | ``` | ``` yaml | | --standalone | standalone: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --template letter | template: letter | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --variable key=val \ | variables: | | --variable key2 | key: val | | | key2: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --eol nl | eol: nl | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --dpi 300 | dpi: 300 | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --wrap preserve | wrap: "preserve" | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --columns 72 | columns: 72 | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --table-of-contents | table-of-contents: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --toc | toc: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --toc-depth 3 | toc-depth: 3 | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --strip-comments | strip-comments: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --no-highlight | syntax-highlighting: 'none' | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --syntax-highlighting kate | syntax-highlighting: kate | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --syntax-definition mylang.xml | syntax-definitions: | | | - mylang.xml | | ``` | ``` | | | ``` yaml | | | syntax-definition: mylang.xml | | | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --include-in-header inc.tex | include-in-header: | | | - inc.tex | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --include-before-body inc.tex | include-before-body: | | | - inc.tex | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --include-after-body inc.tex | include-after-body: | | | - inc.tex | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --resource-path .:foo | resource-path: ['.','foo'] | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --request-header foo:bar | request-headers: | | | - ["User-Agent", "Mozilla/5.0"] | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --no-check-certificate | no-check-certificate: true | | ``` | ``` | +----------------------------------+-----------------------------------+ ## Options affecting specific writers +----------------------------------+-----------------------------------+ | command line | defaults file | +:=================================+:==================================+ | ``` | ``` yaml | | --self-contained | self-contained: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --link-images | link-images: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --html-q-tags | html-q-tags: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --ascii | ascii: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --reference-links | reference-links: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --reference-location block | reference-location: block | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --figure-caption-position=above | figure-caption-position: above | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --table-caption-position=below | table-caption-position: below | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --markdown-headings atx | markdown-headings: atx | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --list-tables | list-tables: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --top-level-division chapter | top-level-division: chapter | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --number-sections | number-sections: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --number-offset=1,4 | number-offset: \[1,4\] | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --listings | listings: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --list-of-figures | list-of-figures: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --lof | lof: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --list-of-tables | list-of-tables: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --lot | lot: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --incremental | incremental: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --slide-level 2 | slide-level: 2 | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --section-divs | section-divs: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --email-obfuscation references | email-obfuscation: references | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --id-prefix ch1 | identifier-prefix: ch1 | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --title-prefix MySite | title-prefix: MySite | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --css styles/screen.css \ | css: | | --css styles/special.css | - styles/screen.css | | | - styles/special.css | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --reference-doc my.docx | reference-doc: my.docx | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --epub-cover-image cover.jpg | epub-cover-image: cover.jpg | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --epub-title-page=false | epub-title-page: false | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --epub-metadata meta.xml | epub-metadata: meta.xml | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --epub-embed-font special.otf \ | epub-fonts: | | --epub-embed-font headline.otf | - special.otf | | | - headline.otf | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --split-level 2 | split-level: 2 | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --chunk-template="%i.html" | chunk-template: "%i.html" | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --epub-subdirectory="" | epub-subdirectory: '' | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --ipynb-output best | ipynb-output: best | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --pdf-engine xelatex | pdf-engine: xelatex | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --pdf-engine-opt=--shell-escape | pdf-engine-opts: | | | - '-shell-escape' | | ``` | ``` | | | ``` yaml | | | pdf-engine-opt: '-shell-escape' | | | ``` | +----------------------------------+-----------------------------------+ ## Citation rendering +----------------------------------+-----------------------------------+ | command line | defaults file | +:=================================+:==================================+ | ``` | ``` yaml | | --citeproc | citeproc: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --bibliography logic.bib | bibliography: logic.bib | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --csl ieee.csl | csl: ieee.csl | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --citation-abbreviations ab.json | citation-abbreviations: ab.json | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --natbib | cite-method: natbib | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --biblatex | cite-method: biblatex | | ``` | ``` | +----------------------------------+-----------------------------------+ `cite-method` can be `citeproc`, `natbib`, or `biblatex`. This only affects LaTeX output. If you want to use citeproc to format citations, you should also set 'citeproc: true'. If you need control over when the citeproc processing is done relative to other filters, you should instead use `citeproc` in the list of `filters` (see [Reader options](#reader-options-1)). ## Math rendering in HTML +----------------------------------+-----------------------------------+ | command line | defaults file | +:=================================+:==================================+ | ``` | ``` yaml | | --mathjax | html-math-method: | | | method: mathjax | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --mathml | html-math-method: | | | method: mathml | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --webtex | html-math-method: | | | method: webtex | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --katex | html-math-method: | | | method: katex | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --gladtex | html-math-method: | | | method: gladtex | | ``` | ``` | +----------------------------------+-----------------------------------+ In addition to the values listed above, `method` can have the value `plain`. If the command line option accepts a URL argument, an `url:` field can be added to `html-math-method:`. ## Options for wrapper scripts +----------------------------------+-----------------------------------+ | command line | defaults file | +:=================================+:==================================+ | ``` | ``` yaml | | --dump-args | dump-args: true | | ``` | ``` | +----------------------------------+-----------------------------------+ | ``` | ``` yaml | | --ignore-args | ignore-args: true | | ``` | ``` | +----------------------------------+-----------------------------------+ # Templates When the `-s/--standalone` option is used, pandoc uses a template to add header and footer material that is needed for a self-standing document. To see the default template that is used, just type pandoc -D *FORMAT* where *FORMAT* is the name of the output format. A custom template can be specified using the `--template` option. You can also override the system default templates for a given output format *FORMAT* by putting a file `templates/default.*FORMAT*` in the user data directory (see `--data-dir`, above). *Exceptions:* - For `odt` output, customize the `default.opendocument` template. - For `docx` output, customize the `default.openxml` template. - For `pdf` output, customize the `default.latex` template (or the `default.context` template, if you use `-t context`, or the `default.ms` template, if you use `-t ms`, or the `default.html` template, if you use `-t html`). - `pptx` has no template. Note that `docx`, `odt`, and `pptx` output can also be customized using `--reference-doc`. Use a reference doc to adjust the styles in your document; use a template to handle variable interpolation and customize the presentation of metadata, the position of the table of contents, boilerplate text, etc. Templates contain *variables*, which allow for the inclusion of arbitrary information at any point in the file. They may be set at the command line using the `-V/--variable` option. If a variable is not set, pandoc will look for the key in the document's metadata, which can be set using either [YAML metadata blocks][Extension: `yaml_metadata_block`] or with the `-M/--metadata` option. In addition, some variables are given default values by pandoc. See [Variables] below for a list of variables used in pandoc's default templates. If you use custom templates, you may need to revise them as pandoc changes. We recommend tracking the changes in the default templates, and modifying your custom templates accordingly. An easy way to do this is to fork the [pandoc-templates] repository and merge in changes after each pandoc release. [pandoc-templates]: https://github.com/jgm/pandoc-templates ## Template syntax ### Comments Anything between the sequence `$--` and the end of the line will be treated as a comment and omitted from the output. ### Delimiters To mark variables and control structures in the template, either `$`...`$` or `${`...`}` may be used as delimiters. The styles may also be mixed in the same template, but the opening and closing delimiter must match in each case. The opening delimiter may be followed by one or more spaces or tabs, which will be ignored. The closing delimiter may be preceded by one or more spaces or tabs, which will be ignored. To include a literal `$` in the document, use `$$`. ### Interpolated variables A slot for an interpolated variable is a variable name surrounded by matched delimiters. Variable names must begin with a letter and can contain letters, numbers, `_`, `-`, and `.`. The keywords `it`, `if`, `else`, `endif`, `for`, `sep`, and `endfor` may not be used as variable names. Examples: ``` $foo$ $foo.bar.baz$ $foo_bar.baz-bim$ $ foo $ ${foo} ${foo.bar.baz} ${foo_bar.baz-bim} ${ foo } ``` Variable names with periods are used to get at structured variable values. So, for example, `employee.salary` will return the value of the `salary` field of the object that is the value of the `employee` field. - If the value of the variable is a simple value, it will be rendered verbatim. (Note that no escaping is done; the assumption is that the calling program will escape the strings appropriately for the output format.) - If the value is a list, the values will be concatenated. - If the value is a map, the string `true` will be rendered. - Every other value will be rendered as the empty string. ### Conditionals A conditional begins with `if(variable)` (enclosed in matched delimiters) and ends with `endif` (enclosed in matched delimiters). It may optionally contain an `else` (enclosed in matched delimiters). The `if` section is used if `variable` has a true value, otherwise the `else` section is used (if present). The following values count as true: - any map - any array containing at least one true value - any nonempty string - boolean True Note that in YAML metadata (and metadata specified on the command line using `-M/--metadata`), unquoted `true` and `false` will be interpreted as Boolean values. But a variable specified on the command line using `-V/--variable` will always be given a string value. Hence a conditional `if(foo)` will be triggered if you use `-V foo=false`, but not if you use `-M foo=false`. Examples: ``` $if(foo)$bar$endif$ $if(foo)$ $foo$ $endif$ $if(foo)$ part one $else$ part two $endif$ ${if(foo)}bar${endif} ${if(foo)} ${foo} ${endif} ${if(foo)} ${ foo.bar } ${else} no foo! ${endif} ``` The keyword `elseif` may be used to simplify complex nested conditionals: ``` $if(foo)$ XXX $elseif(bar)$ YYY $else$ ZZZ $endif$ ``` ### For loops A for loop begins with `for(variable)` (enclosed in matched delimiters) and ends with `endfor` (enclosed in matched delimiters). - If `variable` is an array, the material inside the loop will be evaluated repeatedly, with `variable` being set to each value of the array in turn, and concatenated. - If `variable` is a map, the material inside will be set to the map. - If the value of the associated variable is not an array or a map, a single iteration will be performed on its value. Examples: ``` $for(foo)$$foo$$sep$, $endfor$ $for(foo)$ - $foo.last$, $foo.first$ $endfor$ ${ for(foo.bar) } - ${ foo.bar.last }, ${ foo.bar.first } ${ endfor } $for(mymap)$ $it.name$: $it.office$ $endfor$ ``` You may optionally specify a separator between consecutive values using `sep` (enclosed in matched delimiters). The material between `sep` and the `endfor` is the separator. ``` ${ for(foo) }${ foo }${ sep }, ${ endfor } ``` Instead of using `variable` inside the loop, the special anaphoric keyword `it` may be used. ``` ${ for(foo.bar) } - ${ it.last }, ${ it.first } ${ endfor } ``` ### Partials Partials (subtemplates stored in different files) may be included by using the name of the partial, followed by `()`, for example: ``` ${ styles() } ``` Partials will be sought in the directory containing the main template. The file name will be assumed to have the same extension as the main template if it lacks an extension. When calling the partial, the full name including file extension can also be used: ``` ${ styles.html() } ``` (If a partial is not found in the directory of the template and the template path is given as a relative path, it will also be sought in the `templates` subdirectory of the user data directory.) Partials may optionally be applied to variables using a colon: ``` ${ date:fancy() } ${ articles:bibentry() } ``` If `articles` is an array, this will iterate over its values, applying the partial `bibentry()` to each one. So the second example above is equivalent to ``` ${ for(articles) } ${ it:bibentry() } ${ endfor } ``` Note that the anaphoric keyword `it` must be used when iterating over partials. In the above examples, the `bibentry` partial should contain `it.title` (and so on) instead of `articles.title`. Final newlines are omitted from included partials. Partials may include other partials. A separator between values of an array may be specified in square brackets, immediately after the variable name or partial: ``` ${months[, ]} ${articles:bibentry()[; ]} ``` The separator in this case is literal and (unlike with `sep` in an explicit `for` loop) cannot contain interpolated variables or other template directives. ### Nesting To ensure that content is "nested," that is, subsequent lines indented, use the `^` directive: ``` $item.number$ $^$$item.description$ ($item.price$) ``` In this example, if `item.description` has multiple lines, they will all be indented to line up with the first line: ``` 00123 A fine bottle of 18-year old Oban whiskey. ($148) ``` To nest multiple lines to the same level, align them with the `^` directive in the template. For example: ``` $item.number$ $^$$item.description$ ($item.price$) (Available til $item.sellby$.) ``` will produce ``` 00123 A fine bottle of 18-year old Oban whiskey. ($148) (Available til March 30, 2020.) ``` If a variable occurs by itself on a line, preceded by whitespace and not followed by further text or directives on the same line, and the variable's value contains multiple lines, it will be nested automatically. ### Breakable spaces Normally, spaces in the template itself (as opposed to values of the interpolated variables) are not breakable, but they can be made breakable in part of the template by using the `~` keyword (ended with another `~`). ``` $~$This long line may break if the document is rendered with a short line length.$~$ ``` ### Pipes A pipe transforms the value of a variable or partial. Pipes are specified using a slash (`/`) between the variable name (or partial) and the pipe name. Example: ``` $for(name)$ $name/uppercase$ $endfor$ $for(metadata/pairs)$ - $it.key$: $it.value$ $endfor$ $employee:name()/uppercase$ ``` Pipes may be chained: ``` $for(employees/pairs)$ $it.key/alpha/uppercase$. $it.name$ $endfor$ ``` Some pipes take parameters: ``` |----------------------|------------| $for(employee)$ $it.name.first/uppercase/left 20 "| "$$it.name.salary/right 10 " | " " |"$ $endfor$ |----------------------|------------| ``` Currently the following pipes are predefined: - `pairs`: Converts a map or array to an array of maps, each with `key` and `value` fields. If the original value was an array, the `key` will be the array index, starting with 1. - `uppercase`: Converts text to uppercase. - `lowercase`: Converts text to lowercase. - `length`: Returns the length of the value: number of characters for a textual value, number of elements for a map or array. - `reverse`: Reverses a textual value or array, and has no effect on other values. - `first`: Returns the first value of an array, if applied to a non-empty array; otherwise returns the original value. - `last`: Returns the last value of an array, if applied to a non-empty array; otherwise returns the original value. - `rest`: Returns all but the first value of an array, if applied to a non-empty array; otherwise returns the original value. - `allbutlast`: Returns all but the last value of an array, if applied to a non-empty array; otherwise returns the original value. - `chomp`: Removes trailing newlines (and breakable space). - `nowrap`: Disables line wrapping on breakable spaces. - `alpha`: Converts textual values that can be read as an integer into lowercase alphabetic characters `a..z` (mod 26). This can be used to get lettered enumeration from array indices. To get uppercase letters, chain with `uppercase`. - `roman`: Converts textual values that can be read as an integer into lowercase roman numerals. This can be used to get lettered enumeration from array indices. To get uppercase roman, chain with `uppercase`. - `left n "leftborder" "rightborder"`: Renders a textual value in a block of width `n`, aligned to the left, with an optional left and right border. Has no effect on other values. This can be used to align material in tables. Widths are positive integers indicating the number of characters. Borders are strings inside double quotes; literal `"` and `\` characters must be backslash-escaped. - `right n "leftborder" "rightborder"`: Renders a textual value in a block of width `n`, aligned to the right, and has no effect on other values. - `center n "leftborder" "rightborder"`: Renders a textual value in a block of width `n`, aligned to the center, and has no effect on other values. ## Variables ### Metadata variables `title`, `author`, `date` : allow identification of basic aspects of the document. Included in PDF metadata through LaTeX and ConTeXt. These can be set through a [pandoc title block][Extension: `pandoc_title_block`], which allows for multiple authors, or through a [YAML metadata block][Extension: `yaml_metadata_block`]: --- author: - Aristotle - Peter Abelard ... Note that if you just want to set PDF or HTML metadata, without including a title block in the document itself, you can set the `title-meta`, `author-meta`, and `date-meta` variables. (By default these are set automatically, based on `title`, `author`, and `date`.) The page title in HTML is set by `pagetitle`, which is equal to `title` by default. `subtitle` : document subtitle, included in HTML, EPUB, LaTeX, ConTeXt, and docx documents `abstract` : document summary, included in HTML, LaTeX, ConTeXt, AsciiDoc, and docx documents `abstract-title` : title of abstract, currently used only in HTML, EPUB, docx, and Typst. This will be set automatically to a localized value, depending on `lang`, but can be manually overridden. `keywords` : list of keywords to be included in HTML, PDF, ODT, pptx, docx and AsciiDoc metadata; repeat as for `author`, above `subject` : document subject, included in ODT, PDF, docx, EPUB, and pptx metadata `description` : document description, included in ODT, docx and pptx metadata. Some applications show this as `Comments` metadata. `category` : document category, included in docx and pptx metadata Additionally, any root-level string metadata, not included in ODT, docx or pptx metadata is added as a *custom property*. The following [YAML] metadata block for instance: --- title: 'This is the title' subtitle: "This is the subtitle" author: - Author One - Author Two description: | This is a long description. It consists of two paragraphs ... will include `title`, `author` and `description` as standard document properties and `subtitle` as a custom property when converting to docx, ODT or pptx. ### Language variables `lang` : identifies the main language of the document using IETF language tags (following the [BCP 47] standard), such as `en` or `en-GB`. The [Language subtag lookup] tool can look up or verify these tags. This affects most formats, and controls hyphenation in PDF output when using LaTeX (through [`babel`] and [`polyglossia`]) or ConTeXt. Use native pandoc [Divs and Spans] with the `lang` attribute to switch the language: --- lang: en-GB ... Text in the main document language (British English). ::: {lang=fr-CA} > Cette citation est écrite en français canadien. ::: More text in English. ['Zitat auf Deutsch.']{lang=de} `dir` : the base script direction, either `rtl` (right-to-left) or `ltr` (left-to-right). For bidirectional documents, native pandoc `span`s and `div`s with the `dir` attribute (value `rtl` or `ltr`) can be used to override the base direction in some output formats. This may not always be necessary if the final renderer (e.g. the browser, when generating HTML) supports the [Unicode Bidirectional Algorithm]. When using LaTeX for bidirectional documents, only the `xelatex` engine is fully supported (use `--pdf-engine=xelatex`). [BCP 47]: https://tools.ietf.org/html/bcp47 [Unicode Bidirectional Algorithm]: https://www.w3.org/International/articles/inline-bidi-markup/uba-basics [Language subtag lookup]: https://r12a.github.io/app-subtags/ ### Variables for HTML `document-css` : Enables inclusion of most of the [CSS] in the `styles.html` [partial](#partials) (have a look with `pandoc --print-default-data-file=templates/styles.html`). Unless you use `--css`, this variable is set to `true` by default. You can disable it with e.g. `pandoc -M document-css=false`. `mainfont` : sets the CSS `font-family` property on the `html` element. `fontsize` : sets the base CSS `font-size`, which you'd usually set to e.g. `20px`, but it also accepts `pt` (12pt = 16px in most browsers). `fontcolor` : sets the CSS `color` property on the `html` element. `linkcolor` : sets the CSS `color` property on all links. `monofont` : sets the CSS `font-family` property on `code` elements. `monobackgroundcolor` : sets the CSS `background-color` property on `code` elements and adds extra padding. `linestretch` : sets the CSS `line-height` property on the `html` element, which is preferred to be unitless. `maxwidth` : sets the CSS `max-width` property (default is 36em). `backgroundcolor` : sets the CSS `background-color` property on the `html` element. `margin-left`, `margin-right`, `margin-top`, `margin-bottom` : sets the corresponding CSS `padding` properties on the `body` element. To override or extend some [CSS] for just one document, include for example: --- header-includes: | --- [CSS]: https://developer.mozilla.org/en-US/docs/Learn/CSS ### Variables for HTML math `classoption` : when using `--katex`, you can render display math equations flush left using [YAML metadata](#layout) or with `-M classoption=fleqn`. ### Variables for HTML slides These affect HTML output when [producing slide shows with pandoc](#slide-shows). `institute` : author affiliations: can be a list when there are multiple authors `revealjs-url` : base URL for reveal.js documents (defaults to `https://unpkg.com/reveal.js@^5`) `s5-url` : base URL for S5 documents (defaults to `s5/default`) `slidy-url` : base URL for Slidy documents (defaults to `https://www.w3.org/Talks/Tools/Slidy2`) `slideous-url` : base URL for Slideous documents (defaults to `slideous`) `title-slide-attributes` : additional attributes for the title slide of reveal.js slide shows. See [background in reveal.js, beamer, and pptx] 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`. [reveal.js configuration options]: https://revealjs.com/config/ ### Variables for Beamer slides These variables change the appearance of PDF slides using [`beamer`]. `aspectratio` : slide aspect ratio (`43` for 4:3 [default], `169` for 16:9, `1610` for 16:10, `149` for 14:9, `141` for 1.41:1, `54` for 5:4, `32` for 3:2) `beameroption` : add extra beamer option with `\setbeameroption{}` `institute` : author affiliations: can be a list when there are multiple authors `logo` : logo image for slides `logooptions` : options for logo image (e.g., `width`, `height`) `navigation` : controls navigation symbols (default is `empty` for no navigation symbols; other valid values are `frame`, `vertical`, and `horizontal`) `section-titles` : enables "title pages" for new sections (default is true) `theme`, `colortheme`, `fonttheme`, `innertheme`, `outertheme` : beamer themes `themeoptions`, `colorthemeoptions`, `fontthemeoptions`, `innerthemeoptions`, `outerthemeoptions` : options for LaTeX beamer themes (lists) `titlegraphic` : image for title slide: can be a list `titlegraphicoptions` : options for title slide image (e.g., `width`, `height`) `shorttitle`, `shortsubtitle`, `shortauthor`, `shortinstitute`, `shortdate` : some beamer themes use short versions of the title, subtitle, author, institute, date ### Variables for PowerPoint These variables control the visual aspects of a slide show that are not easily controlled via templates. `monofont` : font to use for code. ### Variables for LaTeX Pandoc uses these variables when [creating a PDF] with a LaTeX engine. #### Layout `block-headings` : make `\paragraph` and `\subparagraph` (fourth- and fifth-level headings, or fifth- and sixth-level with book classes) free-standing rather than run-in; requires further formatting to distinguish from `\subsubsection` (third- or fourth-level headings). Instead of using this option, [KOMA-Script] can adjust headings more extensively: --- documentclass: scrartcl header-includes: | \RedeclareSectionCommand[ beforeskip=-10pt plus -2pt minus -1pt, afterskip=1sp plus -1sp minus 1sp, font=\normalfont\itshape]{paragraph} \RedeclareSectionCommand[ beforeskip=-10pt plus -2pt minus -1pt, afterskip=1sp plus -1sp minus 1sp, font=\normalfont\scshape, indent=0pt]{subparagraph} ... `classoption` : option for document class, e.g. `oneside`; repeat for multiple options: --- classoption: - twocolumn - landscape ... `documentclass` : document class: usually one of the standard classes, [`article`], [`book`], and [`report`]; the [KOMA-Script] equivalents, `scrartcl`, `scrbook`, and `scrreprt`, which default to smaller margins; or [`memoir`] `geometry` : option for [`geometry`] package, e.g. `margin=1in`; repeat for multiple options: --- geometry: - top=30mm - left=20mm - heightrounded ... `shorthands` : Enable language-specific shorthands when loading `babel`. (By default, pandoc includes `shorthands=off` when loading `babel`, disabling language-specific shorthands.) `hyperrefoptions` : option for [`hyperref`] package, e.g. `linktoc=all`; repeat for multiple options: --- hyperrefoptions: - linktoc=all - pdfwindowui - pdfpagemode=FullScreen ... `indent` : if true, pandoc will use document class settings for indentation (the default LaTeX template otherwise removes indentation and adds space between paragraphs) `linestretch` : adjusts line spacing using the [`setspace`] package, e.g. `1.25`, `1.5` `margin-left`, `margin-right`, `margin-top`, `margin-bottom` : sets margins if `geometry` is not used (otherwise `geometry` overrides these) `pagestyle` : control `\pagestyle{}`: the default article class supports `plain` (default), `empty` (no running heads or page numbers), and `headings` (section titles in running heads) `papersize` : paper size, e.g. `letter`, `a4` `secnumdepth` : numbering depth for sections (with `--number-sections` option or `numbersections` variable) `beamerarticle` : produce an article from Beamer slides. Note: if you set this variable, you must specify the beamer writer but use the default *LaTeX* template: for example, `pandoc -Vbeamerarticle -t beamer --template default.latex`. `handout` : produce a handout version of Beamer slides (with overlays condensed into single slides) `csquotes` : load `csquotes` package and use `\enquote` or `\enquote*` for quoted text. `csquotesoptions` : options to use for `csquotes` package (repeat for multiple options). `babeloptions` : options to pass to the babel package (may be repeated for multiple options). This defaults to `provide=*` if the main language isn't a European language written with Latin or Cyrillic script or Vietnamese. Most users will not need to adjust the default setting. #### Fonts `fontenc` : allows font encoding to be specified through `fontenc` package (with `pdflatex`); default is `T1` (see [LaTeX font encodings guide]) `fontfamily` : font package for use with `pdflatex`: [TeX Live] includes many options, documented in the [LaTeX Font Catalogue]. The default is [Latin Modern][`lm`]. `fontfamilyoptions` : options for package used as `fontfamily`; repeat for multiple options. For example, to use the Libertine font with proportional lowercase (old-style) figures through the [`libertinus`] package: --- fontfamily: libertinus fontfamilyoptions: - osf - p ... `fontsize` : font size for body text. The standard classes allow 10pt, 11pt, and 12pt. To use another size, set `documentclass` to one of the [KOMA-Script] classes, such as `scrartcl` or `scrbook`. `mainfont`, `sansfont`, `monofont`, `mathfont`, `CJKmainfont`, `CJKsansfont`, `CJKmonofont` : font families for use with `xelatex` or `lualatex`: take the name of any system font, using the [`fontspec`] package. `CJKmainfont` uses the [`xecjk`] package if `xelatex` is used, or the [`luatexja`] package if `lualatex` is used. `mainfontoptions`, `sansfontoptions`, `monofontoptions`, `mathfontoptions`, `CJKoptions`, `luatexjapresetoptions` : options to use with `mainfont`, `sansfont`, `monofont`, `mathfont`, `CJKmainfont` in `xelatex` and `lualatex`. Allow for any choices available through [`fontspec`]; repeat for multiple options. For example, to use the [TeX Gyre] version of Palatino with lowercase figures: --- mainfont: TeX Gyre Pagella mainfontoptions: - Numbers=Lowercase - Numbers=Proportional ... `mainfontfallback`, `sansfontfallback`, `monofontfallback` : fonts to try if a glyph isn't found in `mainfont`, `sansfont`, or `monofont` respectively. These are lists. The font name must be followed by a colon and optionally a set of options, for example: --- mainfontfallback: - "FreeSans:" - "NotoColorEmoji:mode=harf" ... Font fallbacks currently only work with `lualatex`. `babelfonts` : a map of Babel language names (e.g. `chinese`) to the font to be used with the language: --- babelfonts: chinese-hant: "Noto Serif CJK TC" russian: "Noto Serif" ... `microtypeoptions` : options to pass to the microtype package #### Links `colorlinks` : add color to link text; automatically enabled if any of `linkcolor`, `filecolor`, `citecolor`, `urlcolor`, or `toccolor` are set `boxlinks` : add visible box around links (has no effect if `colorlinks` is set) `linkcolor`, `filecolor`, `citecolor`, `urlcolor`, `toccolor` : color for internal links, external links, citation links, linked URLs, and links in table of contents, respectively: uses options allowed by [`xcolor`], including the `dvipsnames`, `svgnames`, and `x11names` lists `links-as-notes` : causes links to be printed as footnotes `urlstyle` : style for URLs (e.g., `tt`, `rm`, `sf`, and, the default, `same`) #### Front matter `lof`, `lot` : include list of figures, list of tables (can also be set using `--lof/--list-of-figures`, `--lot/--list-of-tables`) `thanks` : contents of acknowledgments footnote after document title `toc` : include table of contents (can also be set using `--toc/--table-of-contents`) `toc-depth` : level of section to include in table of contents #### BibLaTeX Bibliographies These variables function when using BibLaTeX for [citation rendering]. `biblatexoptions` : list of options for biblatex `biblio-style` : bibliography style, when used with `--natbib` and `--biblatex` `biblio-title` : bibliography title, when used with `--natbib` and `--biblatex` `bibliography` : bibliography to use for resolving references `natbiboptions` : list of options for natbib #### Other `pdf-trailer-id` : the PDF trailer ID; must be two PDF byte strings if set, conventionally with 16 bytes each. E.g., `<00112233445566778899aabbccddeeff> <00112233445566778899aabbccddeeff>`. 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 [TeX Gyre]: http://www.gust.org.pl/projects/e-foundry/tex-gyre [`article`]: https://ctan.org/pkg/article [`book`]: https://ctan.org/pkg/book [`libertinus`]: https://ctan.org/pkg/libertinus [`memoir`]: https://ctan.org/pkg/memoir [`report`]: https://ctan.org/pkg/report ### Variables for ConTeXt Pandoc uses these variables when [creating a PDF] with ConTeXt. `fontsize` : font size for body text (e.g. `10pt`, `12pt`) `headertext`, `footertext` : text to be placed in running header or footer (see [ConTeXt Headers and Footers]); repeat up to four times for different placement `indenting` : controls indentation of paragraphs, e.g. `yes,small,next` (see [ConTeXt Indentation]); repeat for multiple options `interlinespace` : adjusts line spacing, e.g. `4ex` (using [`setupinterlinespace`]); repeat for multiple options `layout` : options for page margins and text arrangement (see [ConTeXt Layout]); repeat for multiple options `linkcolor`, `contrastcolor` : color for links outside and inside a page, e.g. `red`, `blue` (see [ConTeXt Color]) `linkstyle` : typeface style for links, e.g. `normal`, `bold`, `slanted`, `boldslanted`, `type`, `cap`, `small` `lof`, `lot` : include list of figures, list of tables `mainfont`, `sansfont`, `monofont`, `mathfont` : font families: take the name of any system font (see [ConTeXt Font Switching]) `mainfontfallback`, `sansfontfallback`, `monofontfallback` : list of fonts to try, in order, if a glyph is not found in the main font. Use `\definefallbackfamily`-compatible font name syntax. Emoji fonts are unsupported. `margin-left`, `margin-right`, `margin-top`, `margin-bottom` : sets margins, if `layout` is not used (otherwise `layout` overrides these) `pagenumbering` : page number style and location (using [`setuppagenumbering`]); repeat for multiple options `papersize` : paper size, e.g. `letter`, `A4`, `landscape` (see [ConTeXt Paper Setup]); repeat for multiple options `pdfa` : adds to the preamble the setup necessary to generate PDF/A of the type specified, e.g. `1a:2005`, `2a`. If no type is specified (i.e. the value is set to True, by e.g. `--metadata=pdfa` or `pdfa: true` in a YAML metadata block), `1b:2005` will be used as default, for reasons of backwards compatibility. Using `--variable=pdfa` without specified value is not supported. To successfully generate PDF/A the required ICC color profiles have to be available and the content and all included files (such as images) have to be standard-conforming. The ICC profiles and output intent may be specified using the variables `pdfaiccprofile` and `pdfaintent`. See also [ConTeXt PDFA] for more details. `pdfaiccprofile` : when used in conjunction with `pdfa`, specifies the ICC profile to use in the PDF, e.g. `default.cmyk`. If left unspecified, `sRGB.icc` is used as default. May be repeated to include multiple profiles. Note that the profiles have to be available on the system. They can be obtained from [ConTeXt ICC Profiles]. `pdfaintent` : when used in conjunction with `pdfa`, specifies the output intent for the colors, e.g. `ISO coated v2 300\letterpercent\space (ECI)` If left unspecified, `sRGB IEC61966-2.1` is used as default. `toc` : include table of contents (can also be set using `--toc/--table-of-contents`) `urlstyle` : typeface style for links without link text, e.g. `normal`, `bold`, `slanted`, `boldslanted`, `type`, `cap`, `small` `whitespace` : spacing between paragraphs, e.g. `none`, `small` (using [`setupwhitespace`]) `includesource` : include all source documents as file attachments in the PDF file [ConTeXt Paper Setup]: https://wiki.contextgarden.net/Document_layout_and_layers/Paper_setup [ConTeXt Layout]: https://wiki.contextgarden.net/Document_layout_and_layers/Tutorials [ConTeXt Font Switching]: https://wiki.contextgarden.net/Characters_words_and_fonts/Tutorials [ConTeXt Color]: https://wiki.contextgarden.net/Color [ConTeXt Headers and Footers]: https://wiki.contextgarden.net/Document_layout_and_layers/Headers_and_footers [ConTeXt Indentation]: https://wiki.contextgarden.net/Text_blocks/Typography/Indentation [ConTeXt PDFA]: https://wiki.contextgarden.net/Input_and_compilation/PDF/PDFA [ConTeXt ICC Profiles]: https://wiki.contextgarden.net/Input_and_compilation/PDF/PDFX#ICC_profiles [`setupwhitespace`]: https://wiki.contextgarden.net/Command/setupwhitespace [`setupinterlinespace`]: https://wiki.contextgarden.net/Command/setupinterlinespace [`setuppagenumbering`]: https://wiki.contextgarden.net/Command/setuppagenumbering ### Variables for `wkhtmltopdf` Pandoc uses these variables when [creating a PDF] with [`wkhtmltopdf`]. The `--css` option also affects the output. `footer-html`, `header-html` : add information to the header and footer `margin-left`, `margin-right`, `margin-top`, `margin-bottom` : set the page margins `papersize` : sets the PDF paper size ### Variables for man pages `adjusting` : adjusts text to left (`l`), right (`r`), center (`c`), or both (`b`) margins `footer` : footer in man pages `header` : header in man pages `section` : section number in man pages ### Variables for Texinfo `version` : version of software (used in title and title page) `filename` : name of info file to be generated (defaults to a name based on the texi filename) ### Variables for Typst `template` : Typst template to use (relative path only). `margin` : A dictionary with the fields defined in the Typst documentation: `x`, `y`, `top`, `bottom`, `left`, `right`. `papersize` : Paper size: `a4`, `us-letter`, etc. `mainfont` : Name of system font to use for the main font. `fontsize` : Font size (e.g., `12pt`). `section-numbering` : Schema to use for numbering sections, e.g. `1.A.1`. `page-numbering` : Schema to use for numbering pages, e.g. `1` or `i`, or an empty string to omit page numbering. `columns` : Number of columns for body text. `thanks` : contents of acknowledgments footnote after document title `mathfont`, `codefont` : Name of system font to use for math and code, respectively. `linestretch` : adjusts line spacing, e.g. `1.25`, `1.5` `linkcolor`, `filecolor`, `citecolor` : color for external links, internal links, and citation links, respectively: expects a hexadecimal color code ### Variables for ms `fontfamily` : `A` (Avant Garde), `B` (Bookman), `C` (Helvetica), `HN` (Helvetica Narrow), `P` (Palatino), or `T` (Times New Roman). This setting does not affect source code, which is always displayed using monospace Courier. These built-in fonts are limited in their coverage of characters. Additional fonts may be installed using the script [`install-font.sh`] provided by Peter Schaffter and documented in detail on [his web site][ms-font-steps]. `indent` : paragraph indent (e.g. `2m`) `lineheight` : line height (e.g. `12p`) `pointsize` : point size (e.g. `10p`) [`install-font.sh`]: https://www.schaffter.ca/mom/bin/install-font.sh [ms-font-steps]: https://www.schaffter.ca/mom/momdoc/appendices.html#steps ### Variables set automatically Pandoc sets these variables automatically in response to [options] or document contents; users can also modify them. These vary depending on the output format, and include the following: `body` : body of document `date-meta` : the `date` variable converted to ISO 8601 YYYY-MM-DD, included in all HTML based formats (dzslides, epub, html, html4, html5, revealjs, s5, slideous, slidy). The recognized formats for `date` are: `mm/dd/yyyy`, `mm/dd/yy`, `yyyy-mm-dd` (ISO 8601), `dd MM yyyy` (e.g. either `02 Apr 2018` or `02 April 2018`), `MM dd, yyyy` (e.g. `Apr. 02, 2018` or `April 02, 2018), `yyyy[mm[dd]]` (e.g. `20180402, `201804` or `2018`). `header-includes` : contents specified by `-H/--include-in-header` (may have multiple values) `include-before` : contents specified by `-B/--include-before-body` (may have multiple values) `include-after` : contents specified by `-A/--include-after-body` (may have multiple values) `meta-json` : JSON representation of all of the document's metadata. Field values are transformed to the selected output format. `numbersections` : non-null value if `-N/--number-sections` was specified `sourcefile`, `outputfile` : source and destination filenames, as given on the command line. `sourcefile` can also be a list if input comes from multiple files, or empty if input is from stdin. You can use the following snippet in your template to distinguish them: $if(sourcefile)$ $for(sourcefile)$ $sourcefile$ $endfor$ $else$ (stdin) $endif$ Similarly, `outputfile` can be `-` if output goes to the terminal. If you need absolute paths, use e.g. `$curdir$/$sourcefile$`. `pdf-engine` : name of PDF engine if provided using `--pdf-engine`, or the default engine for the format if PDF output is requested. `curdir` : working directory from which pandoc is run. `pandoc-version` : pandoc version. `toc` : non-null value if `--toc/--table-of-contents` was specified `toc-title` : title of table of contents (works only with EPUB, HTML, revealjs, opendocument, odt, docx, pptx, beamer, LaTeX). Note that in docx and pptx a custom `toc-title` will be picked up from metadata, but cannot be set as a variable. [pandoc-templates]: https://github.com/jgm/pandoc-templates # Extensions The behavior of some of the readers and writers can be adjusted by enabling or disabling various extensions. An extension can be enabled by adding `+EXTENSION` to the format name and disabled by adding `-EXTENSION`. For example, `--from markdown_strict+footnotes` is strict Markdown with footnotes enabled, while `--from markdown-footnotes-pipe_tables` is pandoc's Markdown without footnotes or pipe tables. The Markdown reader and writer make by far the most use of extensions. Extensions only used by them are therefore covered in the section [Pandoc's Markdown] below (see [Markdown variants] for `commonmark` and `gfm`). In the following, extensions that also work for other formats are covered. Note that Markdown extensions added to the `ipynb` format affect Markdown cells in Jupyter notebooks (as do command-line options like `--markdown-headings`). ## Typography ### Extension: `smart` ### Interpret straight quotes as curly quotes, `---` as em-dashes, `--` as en-dashes, and `...` as ellipses. Nonbreaking spaces are inserted after certain abbreviations, such as "Mr." This extension can be enabled/disabled for the following formats: input formats : `markdown`, `commonmark`, `latex`, `mediawiki`, `org`, `rst`, `twiki`, `html` output formats : `markdown`, `latex`, `context`, `org`, `rst` enabled by default in : `markdown`, `latex`, `context` (both input and output) Note: If you are *writing* Markdown, then the `smart` extension has the reverse effect: what would have been curly quotes comes out straight. In LaTeX, `smart` means to use the standard TeX ligatures for quotation marks (` `` ` and ` '' ` for double quotes, `` ` `` and `` ' `` for single quotes) and dashes (`--` for en-dash and `---` for em-dash). If `smart` is disabled, then in reading LaTeX pandoc will parse these characters literally. In writing LaTeX, enabling `smart` tells pandoc to use the ligatures when possible; if `smart` is disabled pandoc will use unicode quotation mark and dash characters. ## Headings and sections ### Extension: `auto_identifiers` ### A heading without an explicitly specified identifier will be automatically assigned a unique identifier based on the heading text. This extension can be enabled/disabled for the following formats: input formats : `markdown`, `latex`, `rst`, `mediawiki`, `textile` output formats : `markdown`, `muse` enabled by default in : `markdown`, `muse` The default algorithm used to derive the identifier from the heading text is: - Remove all formatting, links, etc. - Remove all footnotes. - Remove all non-alphanumeric characters, except underscores, hyphens, and periods. - Replace all spaces and newlines with hyphens. - Convert all alphabetic characters to lowercase. - Remove everything up to the first letter (identifiers may not begin with a number or punctuation mark). - If nothing is left after this, use the identifier `section`. Thus, for example, Heading Identifier ------------------------------- ---------------------------- `Heading identifiers in HTML` `heading-identifiers-in-html` `Maître d'hôtel` `maître-dhôtel` `*Dogs*?--in *my* house?` `dogs--in-my-house` `[HTML], [S5], or [RTF]?` `html-s5-or-rtf` `3. Applications` `applications` `33` `section` These rules should, in most cases, allow one to determine the identifier from the heading text. The exception is when several headings have the same text; in this case, the first will get an identifier as described above; the second will get the same identifier with `-1` appended; the third with `-2`; and so on. (However, a different algorithm is used if `gfm_auto_identifiers` is enabled; see below.) These identifiers are used to provide link targets in the table of contents generated by the `--toc|--table-of-contents` option. They also make it easy to provide links from one section of a document to another. A link to this section, for example, might look like this: See the section on [heading identifiers](#heading-identifiers-in-html-latex-and-context). Note, however, that this method of providing links to sections works only in HTML, LaTeX, and ConTeXt formats. If the `--section-divs` option is specified, then each section will be wrapped in a `section` (or a `div`, if `html4` was specified), and the identifier will be attached to the enclosing `
` (or `
`) tag rather than the heading itself. This allows entire sections to be manipulated using JavaScript or treated differently in CSS. ### Extension: `ascii_identifiers` ### Causes the identifiers produced by `auto_identifiers` to be pure ASCII. Accents are stripped off of accented Latin letters, and non-Latin letters are omitted. ### Extension: `gfm_auto_identifiers` ### Changes the algorithm used by `auto_identifiers` to conform to GitHub's method. Spaces are converted to dashes (`-`), uppercase characters to lowercase characters, and punctuation characters other than `-` and `_` are removed. Emojis are replaced by their names. ## Math Input The extensions [`tex_math_dollars`](#extension-tex_math_dollars), [`tex_math_gfm`](#extension-tex_math_gfm), [`tex_math_single_backslash`](#extension-tex_math_single_backslash), and [`tex_math_double_backslash`](#extension-tex_math_double_backslash) are described in the section about Pandoc's Markdown. However, they can also be used with HTML input. This is handy for reading web pages formatted using MathJax, for example. ## Raw HTML/TeX The following extensions are described in more detail in their respective sections of [Pandoc's Markdown]: - [`raw_html`](#extension-raw_html) allows HTML elements which are not representable in pandoc's AST to be parsed as raw HTML. By default, this is disabled for HTML input. - [`raw_tex`](#extension-raw_tex) allows raw LaTeX, TeX, and ConTeXt to be included in a document. This extension can be enabled/disabled for the following formats (in addition to `markdown`): input formats : `latex`, `textile`, `html` (environments, `\ref`, and `\eqref` only), `ipynb` output formats : `textile`, `commonmark` Note: as applied to `ipynb`, `raw_html` and `raw_tex` affect not only raw TeX in Markdown cells, but data with mime type `text/html` in output cells. Since the `ipynb` reader attempts to preserve the richest possible outputs when several options are given, you will get best results if you disable `raw_html` and `raw_tex` when converting to formats like `docx` which don't allow raw `html` or `tex`. - [`native_divs`](#extension-native_divs) causes HTML `div` elements to be parsed as native pandoc Div blocks. If you want them to be parsed as raw HTML, use `-f html-native_divs+raw_html`. - [`native_spans`](#extension-native_spans) causes HTML `span` elements to be parsed as native pandoc Span inlines. If you want them to be parsed as raw HTML, use `-f html-native_spans+raw_html`. If you want to drop all `div`s and `span`s when converting HTML to Markdown, you can use `pandoc -f html-native_divs-native_spans -t markdown`. ## Literate Haskell support ### Extension: `literate_haskell` ### Treat the document as literate Haskell source. This extension can be enabled/disabled for the following formats: input formats : `markdown`, `rst`, `latex` output formats : `markdown`, `rst`, `latex`, `html` If you append `+lhs` (or `+literate_haskell`) to one of the formats above, pandoc will treat the document as literate Haskell source. This means that - In Markdown input, "bird track" sections will be parsed as Haskell code rather than block quotations. Text between `\begin{code}` and `\end{code}` will also be treated as Haskell code. For ATX-style headings the character '=' will be used instead of '#'. - In Markdown output, code blocks with classes `haskell` and `literate` will be rendered using bird tracks, and block quotations will be indented one space, so they will not be treated as Haskell code. In addition, headings will be rendered setext-style (with underlines) rather than ATX-style (with '#' characters). (This is because ghc treats '#' characters in column 1 as introducing line numbers.) - In restructured text input, "bird track" sections will be parsed as Haskell code. - In restructured text output, code blocks with class `haskell` will be rendered using bird tracks. - In LaTeX input, text in `code` environments will be parsed as Haskell code. - In LaTeX output, code blocks with class `haskell` will be rendered inside `code` environments. - In HTML output, code blocks with class `haskell` will be rendered with class `literatehaskell` and bird tracks. Examples: pandoc -f markdown+lhs -t html reads literate Haskell source formatted with Markdown conventions and writes ordinary HTML (without bird tracks). pandoc -f markdown+lhs -t html+lhs writes HTML with the Haskell code in bird tracks, so it can be copied and pasted as literate Haskell source. Note that GHC expects the bird tracks in the first column, so indented literate code blocks (e.g. inside an itemized environment) will not be picked up by the Haskell compiler. ## Other extensions ### Extension: `empty_paragraphs` ### Allows empty paragraphs. By default empty paragraphs are omitted. This extension can be enabled/disabled for the following formats: input formats : `docx`, `html` output formats : `docx`, `odt`, `opendocument`, `html`, `latex` ### Extension: `native_numbering` ### Enables native numbering of figures and tables. Enumeration starts at 1. This extension can be enabled/disabled for the following formats: output formats : `odt`, `opendocument`, `docx` ### Extension: `xrefs_name` ### Links to headings, figures and tables inside the document are substituted with cross-references that will use the name or caption of the referenced item. The original link text is replaced once the generated document is refreshed. This extension can be combined with `xrefs_number` in which case numbers will appear before the name. Text in cross-references is only made consistent with the referenced item once the document has been refreshed. This extension can be enabled/disabled for the following formats: output formats : `odt`, `opendocument` ### Extension: `xrefs_number` ### Links to headings, figures and tables inside the document are substituted with cross-references that will use the number of the referenced item. The original link text is discarded. This extension can be combined with `xrefs_name` in which case the name or caption numbers will appear after the number. For the `xrefs_number` to be useful heading numbers must be enabled in the generated document, also table and figure captions must be enabled using for example the `native_numbering` extension. Numbers in cross-references are only visible in the final document once it has been refreshed. This extension can be enabled/disabled for the following formats: output formats : `odt`, `opendocument` ### Extension: `styles` ### {#ext-styles} When converting from docx, add `custom-styles` attributes for all docx styles, regardless of whether pandoc understands the meanings of these styles. Because attributes cannot be added directly to paragraphs or text in the pandoc AST, paragraph styles will cause Divs to be created and character styles will cause Spans to be created to hold the attributes. (Table styles will be added to the Table elements directly.) This extension can be used with [docx custom styles](#custom-styles). input formats : `docx` ### Extension: `amuse` ### In the `muse` input format, this enables Text::Amuse extensions to Emacs Muse markup. ### Extension: `raw_markdown` ### In the `ipynb` input format, this causes Markdown cells to be included as raw Markdown blocks (allowing lossless round-tripping) rather than being parsed. Use this only when you are targeting `ipynb` or a Markdown-based output format. ### Extension: `citations` (typst) {#typst-citations} When the `citations` extension is enabled in `typst` (as it is by default), `typst` citations will be parsed as native pandoc citations, and native pandoc citations will be rendered as `typst` citations. ### Extension: `citations` (org) {#org-citations} When the `citations` extension is enabled in `org`, org-cite and org-ref style citations will be parsed as native pandoc citations, and org-cite citations will be used to render native pandoc citations. [org-cite]: https://orgmode.org/manual/Citations.html [org-ref]: https://github.com/jkitchin/org-ref ### Extension: `citations` (docx) {#docx-citations} When `citations` is enabled in `docx`, citations inserted by Zotero or Mendeley or EndNote plugins will be parsed as native pandoc citations. (Otherwise, the formatted citations generated by the bibliographic software will be parsed as regular text.) ### Extension: `fancy_lists` (org) {#org-fancy-lists} Some aspects of [Pandoc's Markdown fancy lists](#extension-fancy_lists) are also accepted in `org` input, mimicking the option `org-list-allow-alphabetical` in Emacs. As in Org Mode, enabling this extension allows lowercase and uppercase alphabetical markers for ordered lists to be parsed in addition to arabic ones. Note that for Org, this does not include roman numerals or the `#` placeholder that are enabled by the extension in Pandoc's Markdown. ### Extension: `element_citations` ### In the `jats` output formats, this causes reference items to be replaced with `` elements. These elements are not influenced by CSL styles, but all information on the item is included in tags. ### Extension: `ntb` ### In the `context` output format this enables the use of [Natural Tables (TABLE)](https://wiki.contextgarden.net/TABLE) instead of the default [Extreme Tables (xtables)](https://wiki.contextgarden.net/xtables). Natural tables allow more fine-grained global customization but come at a performance penalty compared to extreme tables. ### Extension: `smart_quotes` (org) ### Interpret straight quotes as curly quotes during parsing. When *writing* Org, then the `smart_quotes` extension has the reverse effect: what would have been curly quotes comes out straight. This extension is implied if `smart` is enabled. ### Extension: `special_strings` (org) ### Interpret `---` as em-dashes, `--` as en-dashes, `\-` as shy hyphen, and `...` as ellipses. This extension is implied if `smart` is enabled. ### Extension: `tagging` ### {#extension--tagging} Enabling this extension with `context` output will produce markup suitable for the production of tagged PDFs. This includes additional markers for paragraphs and alternative markup for emphasized text. The `emphasis-command` template variable is set if the extension is enabled. # Pandoc's Markdown Pandoc understands an extended and slightly revised version of John Gruber's [Markdown] syntax. This document explains the syntax, noting differences from original Markdown. Except where noted, these differences can be suppressed by using the `markdown_strict` format instead of `markdown`. Extensions can be enabled or disabled to specify the behavior more granularly. They are described in the following. See also [Extensions] above, for extensions that work also on other formats. ## Philosophy Markdown is designed to be easy to write, and, even more importantly, easy to read: > A Markdown-formatted document should be publishable as-is, as plain > text, without looking like it's been marked up with tags or formatting > instructions.\ > -- [John Gruber](https://daringfireball.net/projects/markdown/syntax#philosophy) This principle has guided pandoc's decisions in finding syntax for tables, footnotes, and other extensions. There is, however, one respect in which pandoc's aims are different from the original aims of Markdown. Whereas Markdown was originally designed with HTML generation in mind, pandoc is designed for multiple output formats. Thus, while pandoc allows the embedding of raw HTML, it discourages it, and provides other, non-HTMLish ways of representing important document elements like definition lists, tables, mathematics, and footnotes. ## Paragraphs A paragraph is one or more lines of text followed by one or more blank lines. Newlines are treated as spaces, so you can reflow your paragraphs as you like. If you need a hard line break, put two or more spaces at the end of a line. ### Extension: `escaped_line_breaks` ### A backslash followed by a newline is also a hard line break. Note: in multiline and grid table cells, this is the only way to create a hard line break, since trailing spaces in the cells are ignored. ## Headings There are two kinds of headings: Setext and ATX. ### Setext-style headings ### A setext-style heading is a line of text "underlined" with a row of `=` signs (for a level-one heading) or `-` signs (for a level-two heading): A level-one heading =================== A level-two heading ------------------- The heading text can contain inline formatting, such as emphasis (see [Inline formatting], below). ### ATX-style headings ### An ATX-style heading consists of one to six `#` signs and a line of text, optionally followed by any number of `#` signs. The number of `#` signs at the beginning of the line is the heading level: ## A level-two heading ### A level-three heading ### As with setext-style headings, the heading text can contain formatting: # A level-one heading with a [link](/url) and *emphasis* ### Extension: `blank_before_header` ### Original Markdown syntax does not require a blank line before a heading. Pandoc does require this (except, of course, at the beginning of the document). The reason for the requirement is that it is all too easy for a `#` to end up at the beginning of a line by accident (perhaps through line wrapping). Consider, for example: I like several of their flavors of ice cream: #22, for example, and #5. ### Extension: `space_in_atx_header` ### Many Markdown implementations do not require a space between the opening `#`s of an ATX heading and the heading text, so that `#5 bolt` and `#hashtag` count as headings. With this extension, pandoc does require the space. ### Heading identifiers ### See also the [`auto_identifiers` extension](#extension-auto_identifiers) above. ### Extension: `header_attributes` ### Headings can be assigned attributes using this syntax at the end of the line containing the heading text: {#identifier .class .class key=value key=value} Thus, for example, the following headings will all be assigned the identifier `foo`: # My heading {#foo} ## My heading ## {#foo} My other heading {#foo} --------------- (This syntax is compatible with [PHP Markdown Extra].) Note that although this syntax allows assignment of classes and key/value attributes, writers generally don't use all of this information. Identifiers, classes, and key/value attributes are used in HTML and HTML-based formats such as EPUB and slidy. Identifiers are used for labels and link anchors in the LaTeX, ConTeXt, Textile, Jira markup, and AsciiDoc writers. Headings with the class `unnumbered` will not be numbered, even if `--number-sections` is specified. A single hyphen (`-`) in an attribute context is equivalent to `.unnumbered`, and preferable in non-English documents. So, # My heading {-} is just the same as # My heading {.unnumbered} If the `unlisted` class is present in addition to `unnumbered`, the heading will not be included in a table of contents. (Currently this feature is only implemented for certain formats: those based on LaTeX and HTML, PowerPoint, and RTF.) ### Extension: `implicit_header_references` ### Pandoc behaves as if reference links have been defined for each heading. So, to link to a heading # Heading identifiers in HTML you can simply write [Heading identifiers in HTML] or [Heading identifiers in HTML][] or [the section on heading identifiers][heading identifiers in HTML] instead of giving the identifier explicitly: [Heading identifiers in HTML](#heading-identifiers-in-html) If there are multiple headings with identical text, the corresponding reference will link to the first one only, and you will need to use explicit links to link to the others, as described above. Like regular reference links, these references are case-insensitive. Explicit link reference definitions always take priority over implicit heading references. So, in the following example, the link will point to `bar`, not to `#foo`: # Foo [foo]: bar See [foo] ## Block quotations Markdown uses email conventions for quoting blocks of text. A block quotation is one or more paragraphs or other block elements (such as lists or headings), with each line preceded by a `>` character and an optional space. (The `>` need not start at the left margin, but it should not be indented more than three spaces.) > This is a block quote. This > paragraph has two lines. > > 1. This is a list inside a block quote. > 2. Second item. A "lazy" form, which requires the `>` character only on the first line of each block, is also allowed: > This is a block quote. This paragraph has two lines. > 1. This is a list inside a block quote. 2. Second item. Among the block elements that can be contained in a block quote are other block quotes. That is, block quotes can be nested: > This is a block quote. > > > A block quote within a block quote. If the `>` character is followed by an optional space, that space will be considered part of the block quote marker and not part of the indentation of the contents. Thus, to put an indented code block in a block quote, you need five spaces after the `>`: > code ### Extension: `blank_before_blockquote` ### Original Markdown syntax does not require a blank line before a block quote. Pandoc does require this (except, of course, at the beginning of the document). The reason for the requirement is that it is all too easy for a `>` to end up at the beginning of a line by accident (perhaps through line wrapping). So, unless the `markdown_strict` format is used, the following does not produce a nested block quote in pandoc: > This is a block quote. >> Not nested, since `blank_before_blockquote` is enabled by default ## Verbatim (code) blocks ### Indented code blocks ### A block of text indented four spaces (or one tab) is treated as verbatim text: that is, special characters do not trigger special formatting, and all spaces and line breaks are preserved. For example, if (a > 3) { moveShip(5 * gravity, DOWN); } The initial (four space or one tab) indentation is not considered part of the verbatim text, and is removed in the output. Note: blank lines in the verbatim text need not begin with four spaces. ### Fenced code blocks ### ### Extension: `fenced_code_blocks` ### In addition to standard indented code blocks, pandoc supports *fenced* code blocks. These begin with a row of three or more tildes (`~`) and end with a row of tildes that must be at least as long as the starting row. Everything between these lines is treated as code. No indentation is necessary: ~~~~~~~ if (a > 3) { moveShip(5 * gravity, DOWN); } ~~~~~~~ Like regular code blocks, fenced code blocks must be separated from surrounding text by blank lines. If the code itself contains a row of tildes or backticks, just use a longer row of tildes or backticks at the start and end: ~~~~~~~~~~~~~~~~ ~~~~~~~~~~ code including tildes ~~~~~~~~~~ ~~~~~~~~~~~~~~~~ ### Extension: `backtick_code_blocks` ### Same as `fenced_code_blocks`, but uses backticks (`` ` ``) instead of tildes (`~`). ### Extension: `fenced_code_attributes` ### Optionally, you may attach attributes to fenced or backtick code block using this syntax: ~~~~ {#mycode .haskell .numberLines startFrom="100"} qsort [] = [] qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here `mycode` is an identifier, `haskell` and `numberLines` are classes, and `startFrom` is an attribute with value `100`. Some output formats can use this information to do syntax highlighting. Currently, the only output formats that use this information are HTML, LaTeX, Docx, Ms, and PowerPoint. If highlighting is supported for your output format and language, then the code block above will appear highlighted, with numbered lines. (To see which languages are supported, type `pandoc --list-highlight-languages`.) Otherwise, the code block above will appear as follows:
      
      ...
      
    
The `numberLines` (or `number-lines`) class will cause the lines of the code block to be numbered, starting with `1` or the value of the `startFrom` attribute. The `lineAnchors` (or `line-anchors`) class will cause the lines to be clickable anchors in HTML output. A shortcut form can also be used for specifying the language of the code block: ```haskell qsort [] = [] ``` This is equivalent to: ``` {.haskell} qsort [] = [] ``` This shortcut form may be combined with attributes: ```haskell {.numberLines} qsort [] = [] ``` Which is equivalent to: ``` {.haskell .numberLines} qsort [] = [] ``` If the `fenced_code_attributes` extension is disabled, but input contains class attribute(s) for the code block, the first class attribute will be printed after the opening fence as a bare word. To prevent all highlighting, use the `--syntax-highlighting=none` option. To set the highlighting style or method, use `--syntax-highlighting`. For more information on highlighting, see [Syntax highlighting], below. ## Line blocks ### Extension: `line_blocks` ### A line block is a sequence of lines beginning with a vertical bar (`|`) followed by a space. The division into lines will be preserved in the output, as will any leading spaces; otherwise, the lines will be formatted as Markdown. This is useful for verse and addresses: | The limerick packs laughs anatomical | In space that is quite economical. | But the good ones I've seen | So seldom are clean | And the clean ones so seldom are comical | 200 Main St. | Berkeley, CA 94718 The lines can be hard-wrapped if needed, but the continuation line must begin with a space. | The Right Honorable Most Venerable and Righteous Samuel L. Constable, Jr. | 200 Main St. | Berkeley, CA 94718 Inline formatting (such as emphasis) is allowed in the content (though it can't cross line boundaries). Block-level formatting (such as block quotes or lists) is not recognized. This syntax is borrowed from [reStructuredText]. ## Lists ### Bullet lists ### A bullet list is a list of bulleted list items. A bulleted list item begins with a bullet (`*`, `+`, or `-`). Here is a simple example: * one * two * three This will produce a "compact" list. If you want a "loose" list, in which each item is formatted as a paragraph, put spaces between the items: * one * two * three The bullets need not be flush with the left margin; they may be indented one, two, or three spaces. The bullet must be followed by whitespace. List items look best if subsequent lines are flush with the first line (after the bullet): * here is my first list item. * and my second. But Markdown also allows a "lazy" format: * here is my first list item. * and my second. ### Block content in list items ### A list item may contain multiple paragraphs and other block-level content. However, subsequent paragraphs must be preceded by a blank line and indented to line up with the first non-space content after the list marker. * First paragraph. Continued. * Second paragraph. With a code block, which must be indented eight spaces: { code } Exception: if the list marker is followed by an indented code block, which must begin 5 spaces after the list marker, then subsequent paragraphs must begin two columns after the last character of the list marker: * code continuation paragraph List items may include other lists. In this case the preceding blank line is optional. The nested list must be indented to line up with the first non-space character after the list marker of the containing list item. * fruits + apples - macintosh - red delicious + pears + peaches * vegetables + broccoli + chard As noted above, Markdown allows you to write list items "lazily," instead of indenting continuation lines. However, if there are multiple paragraphs or other blocks in a list item, the first line of each must be indented. + A lazy, lazy, list item. + Another one; this looks bad but is legal. Second paragraph of second list item. ### Ordered lists ### Ordered lists work just like bulleted lists, except that the items begin with enumerators rather than bullets. In original Markdown, enumerators are decimal numbers followed by a period and a space. The numbers themselves are ignored, so there is no difference between this list: 1. one 2. two 3. three and this one: 5. one 7. two 1. three ### Extension: `fancy_lists` ### Unlike original Markdown, pandoc allows ordered list items to be marked with uppercase and lowercase letters and roman numerals, in addition to Arabic numerals. List markers may be enclosed in parentheses or followed by a single right-parenthesis or period. They must be separated from the text that follows by at least one space, and, if the list marker is a capital letter with a period, by at least two spaces.[^2] [^2]: The point of this rule is to ensure that normal paragraphs starting with people's initials, like B. Russell won a Nobel Prize (but not for "On Denoting"). do not get treated as list items. This rule will not prevent (C) 2007 Joe Smith from being interpreted as a list item. In this case, a backslash escape can be used: (C\) 2007 Joe Smith The `fancy_lists` extension also allows '`#`' to be used as an ordered list marker in place of a numeral: #. one #. two Note: the '`#`' ordered list marker doesn't work with `commonmark`. ### Extension: `startnum` ### Pandoc also pays attention to the type of list marker used, and to the starting number, and both of these are preserved where possible in the output format. Thus, the following yields a list with numbers followed by a single parenthesis, starting with 9, and a sublist with lowercase roman numerals: 9) Ninth 10) Tenth 11) Eleventh i. subone ii. subtwo iii. subthree Pandoc will start a new list each time a different type of list marker is used. So, the following will create three lists: (2) Two (5) Three 1. Four * Five If default list markers are desired, use `#.`: #. one #. two #. three ### Extension: `task_lists` ### Pandoc supports task lists, using the syntax of GitHub-Flavored Markdown. - [ ] an unchecked task list item - [x] checked item ### Definition lists ### ### Extension: `definition_lists` ### Pandoc supports definition lists, using the syntax of [PHP Markdown Extra] with some extensions.[^3] Term 1 : Definition 1 Term 2 with *inline markup* : Definition 2 { some code, part of Definition 2 } Third paragraph of definition 2. Each term must fit on one line, which may optionally be followed by a blank line, and must be followed by one or more definitions. A definition begins with a colon or tilde, which may be indented one or two spaces. A term may have multiple definitions, and each definition may consist of one or more block elements (paragraph, code block, list, etc.), each indented four spaces or one tab stop. The body of the definition (not including the first line) should be indented four spaces. However, as with other Markdown lists, you can "lazily" omit indentation except at the beginning of a paragraph or other block element: Term 1 : Definition with lazy continuation. Second paragraph of the definition. If you leave space before the definition (as in the example above), the text of the definition will be treated as a paragraph. In some output formats, this will mean greater spacing between term/definition pairs. For a more compact definition list, omit the space before the definition: Term 1 ~ Definition 1 Term 2 ~ Definition 2a ~ Definition 2b Note that space between items in a definition list is required. [^3]: I have been influenced by the suggestions of [David Wheeler](https://justatheory.com/2009/02/modest-markdown-proposal/). ### Numbered example lists ### ### Extension: `example_lists` ### The special list marker `@` can be used for sequentially numbered examples. The first list item with a `@` marker will be numbered '1', the next '2', and so on, throughout the document. The numbered examples need not occur in a single list; each new list using `@` will take up where the last stopped. So, for example: (@) My first example will be numbered (1). (@) My second example will be numbered (2). Explanation of examples. (@) My third example will be numbered (3). Numbered examples can be labeled and referred to elsewhere in the document: (@good) This is a good example. As (@good) illustrates, ... The label can be any string of alphanumeric characters, underscores, or hyphens. Continuation paragraphs in example lists must always be indented four spaces, regardless of the length of the list marker. That is, example lists always behave as if the `four_space_rule` extension is set. This is because example labels tend to be long, and indenting content to the first non-space character after the label would be awkward. You can repeat an earlier numbered example by re-using its label: (@foo) Sample sentence. Intervening text... This theory can explain the case we saw earlier (repeated): (@foo) Sample sentence. This only works reliably, though, if the repeated item is in a list by itself, because each numbered example list will be numbered continuously from its starting number. ### Ending a list ### What if you want to put an indented code block after a list? - item one - item two { my code block } Trouble! Here pandoc (like other Markdown implementations) will treat `{ my code block }` as the second paragraph of item two, and not as a code block. To "cut off" the list after item two, you can insert some non-indented content, like an HTML comment, which won't produce visible output in any format: - item one - item two { my code block } You can use the same trick if you want two consecutive lists instead of one big list: 1. one 2. two 3. three 1. uno 2. dos 3. tres ## Horizontal rules A line containing a row of three or more `*`, `-`, or `_` characters (optionally separated by spaces) produces a horizontal rule: * * * * --------------- We strongly recommend that horizontal rules be separated from surrounding text by blank lines. If a horizontal rule is not followed by a blank line, pandoc may try to interpret the lines that follow as a YAML metadata block or a table. ## Tables Four kinds of tables may be used. The first three kinds presuppose the use of a fixed-width font, such as Courier. The fourth kind can be used with proportionally spaced fonts, as it does not require lining up columns. ### Extension: `table_captions` ### A caption may optionally be provided with all 4 kinds of tables (as illustrated in the examples below). A caption is a paragraph beginning with the string `Table:` (or `table:` or just `:`), which will be stripped off. It may appear either before or after the table. ### Extension: `simple_tables` ### Simple tables look like this: Right Left Center Default ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 Table: Demonstration of simple table syntax. The header and table rows must each fit on one line. Column alignments are determined by the position of the header text relative to the dashed line below it:[^4] - If the dashed line is flush with the header text on the right side but extends beyond it on the left, the column is right-aligned. - If the dashed line is flush with the header text on the left side but extends beyond it on the right, the column is left-aligned. - If the dashed line extends beyond the header text on both sides, the column is centered. - If the dashed line is flush with the header text on both sides, the default alignment is used (in most cases, this will be left). [^4]: This scheme is due to Michel Fortin, who proposed it on the [Markdown discussion list](http://six.pairlist.net/pipermail/markdown-discuss/2005-March/001097.html). The table must end with a blank line, or a line of dashes followed by a blank line. The column header row may be omitted, provided a dashed line is used to end the table. For example: ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 ------- ------ ---------- ------- When the header row is omitted, column alignments are determined on the basis of the first line of the table body. So, in the tables above, the columns would be right, left, center, and right aligned, respectively. ### Extension: `multiline_tables` ### Multiline tables allow header and table rows to span multiple lines of text (but cells that span multiple columns or rows of the table are not supported). Here is an example: ------------------------------------------------------------- Centered Default Right Left Header Aligned Aligned Aligned ----------- ------- --------------- ------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. ------------------------------------------------------------- Table: Here's the caption. It, too, may span multiple lines. These work like simple tables, but with the following differences: - They must begin with a row of dashes, before the header text (unless the header row is omitted). - They must end with a row of dashes, then a blank line. - The rows must be separated by blank lines. In multiline tables, the table parser pays attention to the widths of the columns, and the writers try to reproduce these relative widths in the output. So, if you find that one of the columns is too narrow in the output, try widening it in the Markdown source. The header may be omitted in multiline tables as well as simple tables: ----------- ------- --------------- ------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. ----------- ------- --------------- ------------------------- : Here's a multiline table without a header. It is possible for a multiline table to have just one row, but the row should be followed by a blank line (and then the row of dashes that ends the table), or the table may be interpreted as a simple table. ### Extension: `grid_tables` ### Grid tables look like this: : Sample grid table. +---------------+---------------+--------------------+ | Fruit | Price | Advantages | +===============+===============+====================+ | Bananas | $1.34 | - built-in wrapper | | | | - bright color | +---------------+---------------+--------------------+ | Oranges | $2.10 | - cures scurvy | | | | - tasty | +---------------+---------------+--------------------+ The row of `=`s separates the header from the table body, and can be omitted for a headerless table. The cells of grid tables may contain arbitrary block elements (multiple paragraphs, code blocks, lists, etc.). Cells can span multiple columns or rows: +---------------------+----------+ | Property | Earth | +=============+=======+==========+ | | min | -89.2 °C | | Temperature +-------+----------+ | 1961-1990 | mean | 14 °C | | +-------+----------+ | | max | 56.7 °C | +-------------+-------+----------+ A table header may contain more than one row: +---------------------+-----------------------+ | Location | Temperature 1961-1990 | | | in degree Celsius | | +-------+-------+-------+ | | min | mean | max | +=====================+=======+=======+=======+ | Antarctica | -89.2 | N/A | 19.8 | +---------------------+-------+-------+-------+ | Earth | -89.2 | 14 | 56.7 | +---------------------+-------+-------+-------+ Alignments can be specified as with pipe tables, by putting colons at the boundaries of the separator line after the header: +---------------+---------------+--------------------+ | Right | Left | Centered | +==============:+:==============+:==================:+ | Bananas | $1.34 | built-in wrapper | +---------------+---------------+--------------------+ For headerless tables, the colons go on the top line instead: +--------------:+:--------------+:------------------:+ | Right | Left | Centered | +---------------+---------------+--------------------+ A table foot can be defined by enclosing it with separator lines that use `=` instead of `-`: +---------------+---------------+ | Fruit | Price | +===============+===============+ | Bananas | $1.34 | +---------------+---------------+ | Oranges | $2.10 | +===============+===============+ | Sum | $3.44 | +===============+===============+ The foot must always be placed at the very bottom of the table. Grid tables can be created easily using Emacs' table-mode (`M-x table-insert`). ### Extension: `pipe_tables` ### Pipe tables look like this: | Right | Left | Default | Center | |------:|:-----|---------|:------:| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | : Demonstration of pipe table syntax. The syntax is identical to [PHP Markdown Extra tables]. The beginning and ending pipe characters are optional, but pipes are required between all columns. The colons indicate column alignment as shown. The header cannot be omitted. To simulate a headerless table, include a header with blank cells. Since the pipes indicate column boundaries, columns need not be vertically aligned, as they are in the above example. So, this is a perfectly legal (though ugly) pipe table: fruit| price -----|-----: apple|2.05 pear|1.37 orange|3.09 The cells of pipe tables cannot contain block elements like paragraphs and lists, and cannot span multiple lines. If any line of the Markdown source is longer than the column width (see `--columns`), then the table will take up the full text width and the cell contents will wrap, with the relative cell widths determined by the number of dashes in the line separating the table header from the table body. (For example `---|-` would make the first column 3/4 and the second column 1/4 of the full text width.) On the other hand, if no lines are wider than column width, then cell contents will not be wrapped, and the cells will be sized to their contents. Note: pandoc also recognizes pipe tables of the following form, as can be produced by Emacs' orgtbl-mode: | One | Two | |-----+-------| | my | table | | is | nice | The difference is that `+` is used instead of `|`. Other orgtbl features are not supported. In particular, to get non-default column alignment, you'll need to add colons as above. [PHP Markdown Extra tables]: https://michelf.ca/projects/php-markdown/extra/#table ### Extension: `table_attributes` ### Attributes may be attached to tables by including them at the end of the caption. (For the syntax, see [`header_attributes`][Extension: `header_attributes`].) : Here's the caption. {#ident .class key="value"} ## Metadata blocks ### Extension: `pandoc_title_block` ### If the file begins with a title block % title % author(s) (separated by semicolons) % date it will be parsed as bibliographic information, not regular text. (It will be used, for example, in the title of standalone LaTeX or HTML output.) The block may contain just a title, a date and an author, or all three elements. If you want to include an author but no title, or a title and a date but no author, you need a blank line: ``` % % Author ``` ``` % My title % % June 15, 2006 ``` The title may occupy multiple lines, but continuation lines must begin with leading space, thus: ``` % My title on multiple lines ``` If a document has multiple authors, the authors may be put on separate lines with leading space, or separated by semicolons, or both. So, all of the following are equivalent: ``` % Author One Author Two ``` ``` % Author One; Author Two ``` ``` % Author One; Author Two ``` The date must fit on one line. All three metadata fields may contain standard inline formatting (italics, links, footnotes, etc.). Title blocks will always be parsed, but they will affect the output only when the `--standalone` (`-s`) option is chosen. In HTML output, titles will appear twice: once in the document head---this is the title that will appear at the top of the window in a browser---and once at the beginning of the document body. The title in the document head can have an optional prefix attached (`--title-prefix` or `-T` option). The title in the body appears as an H1 element with class "title", so it can be suppressed or reformatted with CSS. If a title prefix is specified with `-T` and no title block appears in the document, the title prefix will be used by itself as the HTML title. The man page writer extracts a title, man page section number, and other header and footer information from the title line. The title is assumed to be the first word on the title line, which may optionally end with a (single-digit) section number in parentheses. (There should be no space between the title and the parentheses.) Anything after this is assumed to be additional footer and header text. A single pipe character (`|`) should be used to separate the footer text from the header text. Thus, % PANDOC(1) will yield a man page with the title `PANDOC` and section 1. % PANDOC(1) Pandoc User Manuals will also have "Pandoc User Manuals" in the footer. % PANDOC(1) Pandoc User Manuals | Version 4.0 will also have "Version 4.0" in the header. ### Extension: `yaml_metadata_block` ### A [YAML] metadata block is a valid YAML object, delimited by a line of three hyphens (`---`) at the top and a line of three hyphens (`---`) or three dots (`...`) at the bottom. The initial line `---` must not be followed by a blank line. A YAML metadata block may occur anywhere in the document, but if it is not at the beginning, it must be preceded by a blank line. (Note that JSON may be used as well, because JSON is a subset of YAML.) Note that, because of the way pandoc concatenates input files when several are provided, you may also keep the metadata in a separate YAML file and pass it to pandoc as an argument, along with your Markdown files: pandoc chap1.md chap2.md chap3.md metadata.yaml -s -o book.html Just be sure that the YAML file begins with `---` and ends with `---` or `...`. Alternatively, you can use the `--metadata-file` option. Using that approach however, you cannot reference content (like footnotes) from the main Markdown input document. Metadata will be taken from the fields of the YAML object and added to any existing document metadata. Metadata can contain lists and objects (nested arbitrarily), but all string scalars will be interpreted as Markdown. Fields with names ending in an underscore will be ignored by pandoc. (They may be given a role by external processors.) Field names must not be interpretable as YAML numbers or boolean values (so, for example, `yes`, `True`, and `15` cannot be used as field names). A document may contain multiple metadata blocks. If two metadata blocks attempt to set the same field, the value from the second block will be taken. Each metadata block is handled internally as an independent YAML document. This means, for example, that any YAML anchors defined in a block cannot be referenced in another block. When pandoc is used with `-t markdown` to create a Markdown document, a YAML metadata block will be produced only if the `-s/--standalone` option is used. All of the metadata will appear in a single block at the beginning of the document. Note that [YAML] escaping rules must be followed. Thus, for example, if a title contains a colon, it must be quoted, and if it contains a backslash escape, then it must be ensured that it is not treated as a [YAML escape sequence]. The pipe character (`|`) can be used to begin an indented block that will be interpreted literally, without need for escaping. This form is necessary when the field contains blank lines or block-level formatting: --- title: 'This is the title: it contains a colon' author: - Author One - Author Two keywords: [nothing, nothingness] abstract: | This is the abstract. It consists of two paragraphs. ... The literal block after the `|` must be indented relative to the line containing the `|`. If it is not, the YAML will be invalid and pandoc will not interpret it as metadata. For an overview of the complex rules governing YAML, see the [Wikipedia entry on YAML syntax]. Template variables will be set automatically from the metadata. Thus, for example, in writing HTML, the variable `abstract` will be set to the HTML equivalent of the Markdown in the `abstract` field:

This is the abstract.

It consists of two paragraphs.

Variables can contain arbitrary YAML structures, but the template must match this structure. The `author` variable in the default templates expects a simple list or string, but can be changed to support more complicated structures. The following combination, for example, would add an affiliation to the author if one is given: --- title: The document title author: - name: Author One affiliation: University of Somewhere - name: Author Two affiliation: University of Nowhere ... To use the structured authors in the example above, you would need a custom template: $for(author)$ $if(author.name)$ $author.name$$if(author.affiliation)$ ($author.affiliation$)$endif$ $else$ $author$ $endif$ $endfor$ Raw content to include in the document's header may be specified using `header-includes`; however, it is important to mark up this content as raw code for a particular output format, using the [`raw_attribute` extension](#extension-raw_attribute), or it will be interpreted as Markdown. For example: header-includes: - | ```{=latex} \let\oldsection\section \renewcommand{\section}[1]{\clearpage\oldsection{#1}} ``` Note: the `yaml_metadata_block` extension works with `commonmark` as well as `markdown` (and it is enabled by default in `gfm` and `commonmark_x`). However, in these formats the following restrictions apply: - The YAML metadata block must occur at the beginning of the document (and there can be only one). If multiple files are given as arguments to pandoc, only the first can be a YAML metadata block. - The leaf nodes of the YAML structure are parsed in isolation from each other and from the rest of the document. So, for example, you can't use a reference link in these contexts if the link definition is somewhere else in the document. [YAML escape sequence]: https://yaml.org/spec/1.2/spec.html#id2776092 [Wikipedia entry on YAML syntax]: https://en.wikipedia.org/wiki/YAML#Syntax ## Backslash escapes ### Extension: `all_symbols_escapable` ### Except inside a code block or inline code, any punctuation or space character preceded by a backslash will be treated literally, even if it would normally indicate formatting. Thus, for example, if one writes *\*hello\** one will get *hello* instead of hello This rule is easier to remember than original Markdown's rule, which allows only the following characters to be backslash-escaped: \`*_{}[]()>#+-.! (However, if the `markdown_strict` format is used, the original Markdown rule will be used.) A backslash-escaped space is parsed as a nonbreaking space. In TeX output, it will appear as `~`. In HTML and XML output, it will appear as a literal unicode nonbreaking space character (note that it will thus actually look "invisible" in the generated HTML source; you can still use the `--ascii` command-line option to make it appear as an explicit entity). A backslash-escaped newline (i.e. a backslash occurring at the end of a line) is parsed as a hard line break. It will appear in TeX output as `\\` and in HTML as `
`. This is a nice alternative to Markdown's "invisible" way of indicating hard line breaks using two trailing spaces on a line. Backslash escapes do not work in verbatim contexts. ## Inline formatting ### Emphasis ### To *emphasize* some text, surround it with `*`s or `_`, like this: This text is _emphasized with underscores_, and this is *emphasized with asterisks*. Double `*` or `_` produces **strong emphasis**: This is **strong emphasis** and __with underscores__. A `*` or `_` character surrounded by spaces, or backslash-escaped, will not trigger emphasis: This is * not emphasized *, and \*neither is this\*. ### Extension: `intraword_underscores` ### Because `_` is sometimes used inside words and identifiers, pandoc does not interpret a `_` surrounded by alphanumeric characters as an emphasis marker. If you want to emphasize just part of a word, use `*`: feas*ible*, not feas*able*. ### Strikeout ### ### Extension: `strikeout` ### To strike out a section of text with a horizontal line, begin and end it with `~~`. Thus, for example, This ~~is deleted text.~~ ### Superscripts and subscripts ### ### Extension: `superscript`, `subscript` ### Superscripts may be written by surrounding the superscripted text by `^` characters; subscripts may be written by surrounding the subscripted text by `~` characters. Thus, for example, H~2~O is a liquid. 2^10^ is 1024. The text between `^...^` or `~...~` may not contain spaces or newlines. If the superscripted or subscripted text contains spaces, these spaces must be escaped with backslashes. (This is to prevent accidental superscripting and subscripting through the ordinary use of `~` and `^`, and also bad interactions with footnotes.) Thus, if you want the letter P with 'a cat' in subscripts, use `P~a\ cat~`, not `P~a cat~`. ### Verbatim ### To make a short span of text verbatim, put it inside backticks: What is the difference between `>>=` and `>>`? If the verbatim text includes a backtick, use double backticks: Here is a literal backtick `` ` ``. (The spaces after the opening backticks and before the closing backticks will be ignored.) The general rule is that a verbatim span starts with a string of consecutive backticks (optionally followed by a space) and ends with a string of the same number of backticks (optionally preceded by a space). Note that backslash-escapes (and other Markdown constructs) do not work in verbatim contexts: This is a backslash followed by an asterisk: `\*`. ### Extension: `inline_code_attributes` ### Attributes can be attached to verbatim text, just as with [fenced code blocks]: `<$>`{.haskell} ### Underline ### To underline text, use the `underline` class: [Underline]{.underline} Or, without the `bracketed_spans` extension (but with `native_spans`): Underline This will work in all output formats that support underline. ### Small caps ### To write small caps, use the `smallcaps` class: [Small caps]{.smallcaps} Or, without the `bracketed_spans` extension: Small caps For compatibility with other Markdown flavors, CSS is also supported: Small caps This will work in all output formats that support small caps. ### Highlighting ### To highlight text, use the `mark` class: [Mark]{.mark} Or, without the `bracketed_spans` extension (but with `native_spans`): Mark This will work in all output formats that support highlighting. ## Math ### Extension: `tex_math_dollars` ### Anything between two `$` characters will be treated as TeX math. The opening `$` must have a non-space character immediately to its right, while the closing `$` must have a non-space character immediately to its left, and must not be followed immediately by a digit. Thus, `$20,000 and $30,000` won't parse as math. If for some reason you need to enclose text in literal `$` characters, backslash-escape them and they won't be treated as math delimiters. For display math, use `$$` delimiters. (In this case, the delimiters may be separated from the formula by whitespace. However, there can be no blank lines between the opening and closing `$$` delimiters.) TeX math will be printed in all output formats. How it is rendered depends on the output format: LaTeX ~ It will appear verbatim surrounded by `\(...\)` (for inline math) or `\[...\]` (for display math). Markdown, Emacs Org mode, ConTeXt, ZimWiki ~ It will appear verbatim surrounded by `$...$` (for inline math) or `$$...$$` (for display math). XWiki ~ It will appear verbatim surrounded by `{{formula}}..{{/formula}}`. reStructuredText ~ It will be rendered using an [interpreted text role `:math:`]. AsciiDoc ~ For AsciiDoc output math will appear verbatim surrounded by `latexmath:[...]`. For `asciidoc_legacy` the bracketed material will also include inline or display math delimiters. Texinfo ~ It will be rendered inside a `@math` command. roff man, Jira markup ~ It will be rendered verbatim without `$`'s. MediaWiki, DokuWiki ~ It will be rendered inside `` tags. Textile ~ It will be rendered inside `` tags. RTF, OpenDocument ~ It will be rendered, if possible, using Unicode characters, and will otherwise appear verbatim. ODT ~ It will be rendered, if possible, using MathML. DocBook ~ If the `--mathml` flag is used, it will be rendered using MathML in an `inlineequation` or `informalequation` tag. Otherwise it will be rendered, if possible, using Unicode characters. Docx and PowerPoint ~ It will be rendered using OMML math markup. FictionBook2 ~ If the `--webtex` option is used, formulas are rendered as images using CodeCogs or other compatible web service, downloaded and embedded in the e-book. Otherwise, they will appear verbatim. HTML, Slidy, DZSlides, S5, EPUB ~ The way math is rendered in HTML will depend on the command-line options selected. Therefore see [Math rendering in HTML] above. [interpreted text role `:math:`]: https://docutils.sourceforge.io/docs/ref/rst/roles.html#math ## Raw HTML ### Extension: `raw_html` ### Markdown allows you to insert raw HTML (or DocBook) anywhere in a document (except verbatim contexts, where `<`, `>`, and `&` are interpreted literally). (Technically this is not an extension, since standard Markdown allows it, but it has been made an extension so that it can be disabled if desired.) The raw HTML is passed through unchanged in HTML, S5, Slidy, Slideous, DZSlides, EPUB, Markdown, CommonMark, Emacs Org mode, and Textile output, and suppressed in other formats. For a more explicit way of including raw HTML in a Markdown document, see the [`raw_attribute` extension][Extension: `raw_attribute`]. In the CommonMark format, if `raw_html` is enabled, superscripts, subscripts, strikeouts and small capitals will be represented as HTML. Otherwise, plain-text fallbacks will be used. Note that even if `raw_html` is disabled, tables will be rendered with HTML syntax if they cannot use pipe syntax. ### Extension: `markdown_in_html_blocks` ### Original Markdown allows you to include HTML "blocks": blocks of HTML between balanced tags that are separated from the surrounding text with blank lines, and start and end at the left margin. Within these blocks, everything is interpreted as HTML, not Markdown; so (for example), `*` does not signify emphasis. Pandoc behaves this way when the `markdown_strict` format is used; but by default, pandoc interprets material between HTML block tags as Markdown. Thus, for example, pandoc will turn
*one* [a link](https://google.com)
into
one a link
whereas `Markdown.pl` will preserve it as is. There is one exception to this rule: text between ` HTML """) ``` ## Image This image ![image](myimage.png) will be included as a cell attachment. ```` If you want to add cell attributes, group cells differently, or add output to code cells, then you need to include divs to indicate the structure. You can use either [fenced divs][Extension: `fenced_divs`] or [native divs][Extension: `native_divs`] for this. Here is an example: ```` :::::: {.cell .markdown} # Lorem **Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus bibendum felis dictum sodales. :::::: :::::: {.cell .code execution_count=1} ``` {.python} print("hello") ``` ::: {.output .stream .stdout} ``` hello ``` ::: :::::: :::::: {.cell .code execution_count=2} ``` {.python} from IPython.display import HTML HTML(""" HTML """) ``` ::: {.output .execute_result execution_count=2} ```{=html} HTML hello ``` ::: :::::: ```` If you include raw HTML or TeX in an output cell, use the [raw attribute](#extension-raw_attribute), as shown in the last cell of the example above. Although pandoc can process "bare" raw HTML and TeX, the result is often interspersed raw elements and normal textual elements, and in an output cell pandoc expects a single, connected raw block. To avoid using raw HTML or TeX except when marked explicitly using raw attributes, we recommend specifying the extensions `-raw_html-raw_tex+raw_attribute` when translating between Markdown and ipynb notebooks. Note that options and extensions that affect reading and writing of Markdown will also affect Markdown cells in ipynb notebooks. For example, `--wrap=preserve` will preserve soft line breaks in Markdown cells; `--markdown-headings=setext` will cause Setext-style headings to be used; and `--preserve-tabs` will prevent tabs from being turned to spaces. # Vimdoc Vimdoc writer generates Vim help files and makes use of the following metadata variables: ``` yaml abstract: "A short description" author: Author title: Title # Vimdoc-specific filename: "definition-lists.txt" vimdoc-prefix: pandoc ``` Complete header requires `abstract`, `author`, `title` and `filename` to be set. Compiling file with such metadata produces the following file (assumes `--standalone`, see [Templates]): ``` vimdoc *definition-lists.txt* A short description Title by Author Type |gO| to see the table of contents. [...] vim:tw=72:sw=4:ts=4:ft=help:norl:et: ``` If `vimdoc-prefix` is set, all non-command tags are prefixed with its value, it is used to prevent tag collision: all headers have a tag (either inferred or explicit) and multiple help pages can have the same header names, therefore collision is to be expected. Let our input be the following markdown file: ``` markdown ## Header `:[range]Fnl {expr}`{#:Fnl} : Evaluates {expr} or range `vim.b`{#vim.b} : Buffer-scoped (`:h b:`) variables for the current buffer. Invalid or unset key returns `nil`. Can be indexed with an integer to access variables for a specific buffer. [Span]{#span} : generic inline container for phrasing content, which does not inherently represent anything. ``` Convert it to vimdoc: ``` vimdoc ------------------------------------------------------------------------ Header *header* :[range]Fnl {expr} *:Fnl* Evaluates {expr} or range `vim.b` *vim.b* Buffer-scoped (|b:|) variables for the current buffer. Invalid or unset key returns `nil`. Can be indexed with an integer to access variables for a specific buffer. Span *span* generic inline container for phrasing content, which does not inherently represent anything. ``` Convert it to vimdoc with metadata variable set (e.g. with `-M vimdoc-prefix=pandoc`) ``` vimdoc ------------------------------------------------------------------------ Header *pandoc-header* :[range]Fnl {expr} *:Fnl* Evaluates {expr} or range `vim.b` *pandoc-vim.b* Buffer-scoped (|b:|) variables for the current buffer. Invalid or unset key returns `nil`. Can be indexed with an integer to access variables for a specific buffer. Span *pandoc-span* generic inline container for phrasing content, which does not inherently represent anything. ``` `vim.b` and `Span` got their prefixes but not `:Fnl` because ex-commands (those starting with `:`) don't get a prefix, since they are considered unique across help pages. In both cases `:help b:` became reference `|b:|` (also works with `:h b:`). Links pointing to either or become references too. Vim traditionally wraps at 78, but Pandoc defaults to 72. Use `--columns 78` to match Vim. # Syntax highlighting Pandoc will automatically highlight syntax in [fenced code blocks] that are marked with a language name. The Haskell library [skylighting] is used for highlighting. Currently highlighting is supported only for HTML, EPUB, Docx, Ms, Man, Typst, and LaTeX/PDF output. To see a list of language names that pandoc will recognize, type `pandoc --list-highlight-languages`. The color scheme can be selected using the `--syntax-highlighting` option. The default color scheme is `pygments`, which imitates the default color scheme used by the Python library pygments (though pygments is not actually used to do the highlighting). To see a list of highlight styles, type `pandoc --list-highlight-styles`. If you are not satisfied with the predefined styles, you can use `--print-highlight-style` to generate a JSON `.theme` file which can be modified and used as the argument to `--syntax-highlighting`. To get a JSON version of the `pygments` style, for example: pandoc -o my.theme --print-highlight-style pygments Then edit `my.theme` and use it like this: pandoc --syntax-highlighting my.theme If you are not satisfied with the built-in highlighting, or you want to highlight a language that isn't supported, you can use the `--syntax-definition` option to load a [KDE-style XML syntax definition file](https://docs.kde.org/stable5/en/kate/katepart/highlight.html). Before writing your own, have a look at KDE's [repository of syntax definitions](https://github.com/KDE/syntax-highlighting/tree/master/data/syntax). If you receive an error that pandoc "Could not read highlighting theme", check that the JSON file is encoded with UTF-8 and has no Byte-Order Mark (BOM). 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 Custom styles can be used in the docx, odt and ICML formats. ## Output By default, pandoc's odt, docx and ICML output applies a predefined set of styles for blocks such as paragraphs and block quotes, and uses largely default formatting (italics, bold) for inlines. This will work for most purposes, especially alongside a [reference doc](#option--reference-doc) file. However, if you need to apply your own styles to blocks, or match a preexisting set of styles, pandoc allows you to define custom styles for blocks and text using `div`s and `span`s, respectively. If you define a Div, Span, or Table with the attribute `custom-style`, pandoc will apply your specified style to the contained elements (with the exception of elements whose function depends on a style, like headings, code blocks, block quotes, or links). So, for example, using the `bracketed_spans` syntax, [Get out]{custom-style="Emphatically"}, he said. would produce a file with "Get out" styled with character style `Emphatically`. Similarly, using the `fenced_divs` syntax, Dickinson starts the poem simply: ::: {custom-style="Poetry"} | A Bird came down the Walk--- | He did not know I saw--- ::: would style the two contained lines with the `Poetry` paragraph style. Styles will be defined in the output file as inheriting from normal text (docx) or Default Paragraph Style (odt), if the styles are not yet in your [reference doc](#option--reference-doc). If they are already defined, pandoc will not alter the definition. This feature allows for greatest customization in conjunction with [pandoc filters]. If you want all paragraphs after block quotes to be indented, you can write a filter to apply the styles necessary. If you want all italics to be transformed to the `Emphasis` character style (perhaps to change their color), you can write a filter which will transform all italicized inlines to inlines within an `Emphasis` custom-style `span`. For docx or odt output, you don't need to enable any extensions for custom styles to work. For icml output, you can also set an `object-style` in images: ![Image with object style](myImage.jpg){object-style="fixedSizeImage"} In InDesign you'll see that object style given to the image, and you'll be able to customize it, or load its definition from a template of yours. [pandoc filters]: https://pandoc.org/filters.html ## Input The docx reader, by default, only reads those styles that it can convert into pandoc elements, either by direct conversion or interpreting the derivation of the input document's styles. By enabling the [`styles` extension](#ext-styles) in the docx reader (`-f docx+styles`), you can produce output that maintains the styles of the input document, using the `custom-style` class. A `custom-style` attribute will be added for each style. Divs will be created to hold the paragraph styles, and Spans to hold the character styles. Table styles will be applied directly to the Table. For example, using the `custom-style-reference.docx` file in the test directory, we have the following different outputs: Without the `+styles` extension: $ pandoc test/docx/custom-style-reference.docx -f docx -t markdown This is some text. This is text with an *emphasized* text style. And this is text with a **strengthened** text style. > Here is a styled paragraph that inherits from Block Text. And with the extension: $ pandoc test/docx/custom-style-reference.docx -f docx+styles -t markdown ::: {custom-style="First Paragraph"} This is some text. ::: ::: {custom-style="Body Text"} This is text with an [emphasized]{custom-style="Emphatic"} text style. And this is text with a [strengthened]{custom-style="Strengthened"} text style. ::: ::: {custom-style="My Block Style"} > Here is a styled paragraph that inherits from Block Text. ::: With these custom styles, you can use your input document as a reference-doc while creating docx output (see below), and maintain the same styles in your input and output files. # Custom readers and writers Pandoc can be extended with custom readers and writers written in [Lua]. (Pandoc includes a Lua interpreter, so Lua need not be installed separately.) To use a custom reader or writer, simply specify the path to the Lua script in place of the input or output format. For example: pandoc -t data/sample.lua pandoc -f my_custom_markup_language.lua -t latex -s If the script is not found relative to the working directory, it will be sought in the `custom` subdirectory of the user data directory (see `--data-dir`). A custom reader is a Lua script that defines one function, Reader, which takes a string as input and returns a Pandoc AST. See the [Lua filters documentation] for documentation of the functions that are available for creating pandoc AST elements. For parsing, the [lpeg] parsing library is available by default. To see a sample custom reader: pandoc --print-default-data-file creole.lua If you want your custom reader to have access to reader options (e.g. the tab stop setting), you give your Reader function a second `options` parameter. A custom writer is a Lua script that defines a function that specifies how to render each element in a Pandoc AST. See the [djot-writer.lua] for a full-featured example. Note that custom writers have no default template. If you want to use `--standalone` with a custom writer, you will need to specify a template manually using `--template` or add a new default template with the name `default.NAME_OF_CUSTOM_WRITER.lua` to the `templates` subdirectory of your user data directory (see [Templates]). [Lua]: https://www.lua.org [lpeg]: http://www.inf.puc-rio.br/~roberto/lpeg/ [djot-writer.lua]: https://github.com/jgm/djot.lua/blob/main/djot-writer.lua # Reproducible builds Some of the document formats pandoc targets (such as EPUB, docx, and ODT) include build timestamps in the generated document. That means that the files generated on successive builds will differ, even if the source does not. To avoid this, set the `SOURCE_DATE_EPOCH` environment variable, and the timestamp will be taken from it instead of the current time. `SOURCE_DATE_EPOCH` should contain an integer unix timestamp (specifying the number of seconds since midnight UTC January 1, 1970). For reproducible builds with LaTeX, you can either specify the `pdf-trailer-id` in the metadata or leave it undefined, in which case pandoc will create a trailer-id based on a hash of the `SOURCE_DATE_EPOCH` and the document's contents. Some document formats also include a unique identifier. For EPUB, this can be set explicitly by setting the `identifier` metadata field (see [EPUB Metadata], above). # Accessible PDFs and PDF archiving standards PDF is a flexible format, and using PDF in certain contexts requires additional conventions. For example, PDFs are not accessible by default; they define how characters are placed on a page but do not contain semantic information on the content. However, it is possible to generate accessible PDFs, which use tagging to add semantic information to the document. Pandoc defaults to LaTeX to generate PDF. 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 is commonly used in combination with these standards to ensure best results. Note, however, that standard compliance depends on many things, including the colorspace of embedded images. Pandoc cannot check this, and external programs must be used to ensure that generated PDFs are in compliance. ## 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 ConTeXt always produces tagged PDFs, but the quality depends on the input. The default ConTeXt markup generated by pandoc is optimized for readability and reuse, not tagging. Enable the [`tagging`](#extension--tagging) format extension to force markup that is optimized for tagging. For example: pandoc -t context+tagging doc.md -o doc.pdf A recent `context` version should be used, as older versions contained a bug that lead to invalid PDF metadata. ## WeasyPrint The HTML-based engine WeasyPrint includes experimental support for PDF/A and PDF/UA since version 57. Tagged PDFs can created with pandoc --pdf-engine=weasyprint \ --pdf-engine-opt=--pdf-variant=pdf/ua-1 ... The feature is experimental and standard compliance should not be assumed. ## Prince XML The non-free HTML-to-PDF converter `prince` has extensive support for various PDF standards as well as tagging. E.g.: pandoc --pdf-engine=prince \ --pdf-engine-opt=--tagged-pdf ... See the prince documentation for more info. ## Typst Typst 0.12 can produce PDF/A-2b: pandoc --pdf-engine=typst --pdf-engine-opt=--pdf-standard=a-2b ... ## Word Processors Word processors like LibreOffice and MS Word can also be used to generate standardized and tagged PDF output. Pandoc does not support direct conversions via these tools. However, pandoc can convert a document to a `docx` or `odt` file, which can then be opened and converted to PDF with the respective word processor. See the documentation for [Word][word-accessible-pdfs] and [LibreOffice][lo-pdf-export]. [word-accessible-pdfs]: https://support.microsoft.com/en-us/office/create-accessible-pdfs-064625e0-56ea-4e16-ad71-3aa33bb4b7ed [lo-pdf-export]: https://help.libreoffice.org/latest/en-US/text/shared/01/ref_pdf_export_general.html # Running pandoc as a web server If you rename (or symlink) the pandoc executable to `pandoc-server`, or if you call pandoc with `server` as the first argument, it will start up a web server with a JSON API. This server exposes most of the conversion functionality of pandoc. For full documentation, see the [pandoc-server] man page. If you rename (or symlink) the pandoc executable to `pandoc-server.cgi`, it will function as a CGI program exposing the same API as `pandoc-server`. `pandoc-server` is designed to be maximally secure; it uses Haskell's type system to provide strong guarantees that no I/O will be performed on the server during pandoc conversions. [pandoc-server]: https://github.com/jgm/pandoc/blob/master/doc/pandoc-server.md # Running pandoc as a Lua interpreter Calling the pandoc executable under the name `pandoc-lua` or with `lua` as the first argument will make it function as a standalone Lua interpreter. The behavior is mostly identical to that of the [standalone `lua` executable][lua standalone], version 5.4. All `pandoc.*` packages, as well as the packages `re` and `lpeg`, are available via global variables. Furthermore, the globals `PANDOC_VERSION`, `PANDOC_STATE`, and `PANDOC_API_VERSION` are set at startup. For full documentation, see the [pandoc-lua] man page. [lua standalone]: https://www.lua.org/manual/5.4/manual.html#7 [pandoc-lua]: https://github.com/jgm/pandoc/blob/master/doc/pandoc-lua.md # A note on security 1. Although pandoc itself will not create or modify any files other than those you explicitly ask it create (with the exception of temporary files used in producing PDFs), a filter or custom writer could in principle do anything on your file system. Please audit filters and custom writers very carefully before using them. 2. Several input formats (including LaTeX, Org, RST, and Typst) support `include` directives that allow the contents of a file to be included in the output. An untrusted attacker could use these to view the contents of files on the file system. (Using the `--sandbox` option can protect against this threat.) 3. Several output formats (including RTF, FB2, HTML with `--self-contained`, EPUB, Docx, and ODT) will embed encoded or raw images into the output file. An untrusted attacker could exploit this to view the contents of non-image files on the file system. (Using the `--sandbox` option can protect against this threat, but will also prevent including images in these formats.) 4. In reading HTML files, pandoc will attempt to include the contents of `iframe` elements by fetching content from the local file or URL specified by `src`. If untrusted HTML is processed on a server, this has the potential to reveal anything readable by the process running the server. Using the `-f html+raw_html` will mitigate this threat by causing the whole `iframe` to be parsed as a raw HTML block. Using `--sandbox` will also protect against the threat. 5. If your application uses pandoc as a Haskell library (rather than shelling out to the executable), it is possible to use it in a mode that fully isolates pandoc from your file system, by running the pandoc operations in the `PandocPure` monad. See the document [Using the pandoc API](https://pandoc.org/using-the-pandoc-api.html) for more details. (This corresponds to the use of the `--sandbox` option on the command line.) 6. Pandoc's parsers can exhibit pathological performance on some corner cases. It is wise to put any pandoc operations under a timeout, to avoid DOS attacks that exploit these issues. If you are using the pandoc executable, you can add the command line options `+RTS -M512M -RTS` (for example) to limit the heap size to 512MB. Note that the `commonmark` parser (including `commonmark_x` and `gfm`) is much less vulnerable to pathological performance than the `markdown` parser, so it is a better choice when processing untrusted input. 7. The HTML generated by pandoc is not guaranteed to be safe. If `raw_html` is enabled for the Markdown input, users can inject arbitrary HTML. Even if `raw_html` is disabled, users can include dangerous content in URLs and attributes. To be safe, you should run all HTML generated from untrusted user input through an HTML sanitizer. # Authors Copyright 2006--2024 John MacFarlane (jgm@berkeley.edu). Released under the [GPL], version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) For a full list of contributors, see the file AUTHORS.md in the pandoc source code. [GPL]: https://www.gnu.org/copyleft/gpl.html "GNU General Public License" [YAML]: https://yaml.org/spec/1.2/spec.html "YAML v1.2 Spec" ================================================ FILE: Makefile ================================================ VERSION?=$(shell grep '^[Vv]ersion:' pandoc.cabal | awk '{print $$2;}') PANDOC_CLI_VERSION?=$(shell grep '^[Vv]ersion:' pandoc-cli/pandoc-cli.cabal | awk '{print $$2;}') SOURCEFILES?=$(shell git ls-tree -r main --name-only src pandoc-cli pandoc-server pandoc-lua-engine | grep "\.hs$$") PANDOCSOURCEFILES?=$(shell git ls-tree -r main --name-only src | grep "\.hs$$") DOCKERIMAGE=quay.io/benz0li/ghc-musl:9.10 TIMESTAMP=$(shell date "+%Y%m%d_%H%M") LATESTBENCH=$(word 1,$(shell ls -t bench_*.csv 2>/dev/null)) BASELINE?=$(LATESTBENCH) ROOT?=Text.Pandoc ifeq ($(BASELINE),) BASELINECMD= else BASELINECMD=--baseline $(BASELINE) endif CABALOPTS?=--disable-optimization -f-export-dynamic -fhttp --ghc-option=-fwrite-ide-info --ghc-option=-fdiagnostics-color=always --ghc-option=-j WEBSITE=../../web/pandoc.org REVISION?=1 BENCHARGS?=--csv bench_$(TIMESTAMP).csv $(BASELINECMD) --timeout=6 +RTS -T --nonmoving-gc -RTS $(if $(PATTERN),--pattern "$(PATTERN)",) pandoc=$(shell cabal list-bin $(CABALOPTS) pandoc-cli) OPTIMIZE_WASM?=1 all: build test binpath ## build executable and run tests .PHONY: all build: ## build executable cabal build \ $(CABALOPTS) pandoc-cli .PHONY: build prof: ## build with profiling and optimizations cabal build --enable-profiling all .PHONY: prof binpath: ## print path of built pandoc executable @cabal list-bin -v0 $(CABALOPTS) pandoc-cli .PHONY: binpath ghcid: ## run ghcid ghcid -c 'cabal repl pandoc' .PHONY: ghcid repl: ## run cabal repl cabal repl $(CABALOPTS) pandoc .PHONY: repl linecounts: ## print line counts for each module @wc -l $(SOURCEFILES) | sort -n .PHONY: linecounts # Note: to accept current results of golden tests, # make test TESTARGS='--accept' test: ## unoptimized build and run tests with cabal cabal test \ $(CABALOPTS) \ --test-options="--hide-successes --ansi-tricks=false $(TESTARGS)" all .PHONY: test quick-stack: ## unoptimized build and tests with stack stack install \ --system-ghc --flag 'pandoc:embed_data_files' \ --fast \ --test \ --test-arguments='-j4 --hide-successes --ansi-tricks=false $(TESTARGS)' .PHONY: quick-stack prerelease: validate-epub README.md fix_spacing check-cabal check-stack checkdocs man check-version-sync check-changelog check-manversion uncommitted_changes ## prerelease checks .PHONY: prerelease uncommitted_changes: ! git diff | grep '.' .PHONY: uncommitted_changes authors: ## prints unique authors since last released version git log --pretty=format:"%an" $$(git tag -l | grep '[^0-9]' | sort | tail -1)..HEAD | sort | uniq | while read -r; do grep -i -q "^- $$REPLY" AUTHORS.md || echo $$REPLY ; done check-stack: $$HOME/.local/bin/stack-lint-extra-deps # check that stack.yaml dependencies are up to date ! grep 'git:' stack.yaml # use only released versions .PHONY: check-stack check-cabal: git-files.txt sdist-files.txt @echo "Checking to see if all committed test/data files are in sdist." diff -u $^ @for pkg in . pandoc-lua-engine pandoc-server pandoc-cli; \ do \ pushd $$pkg ; \ cabal check --ignore=missing-upper-bounds ; \ cabal outdated ; \ popd ; \ done ! grep 'git:' cabal.project # use only released versions .PHONY: check-cabal check-version-sync: @echo "Checking for match between pandoc and pandoc-cli versions" [ $(VERSION) == $(PANDOC_CLI_VERSION) ] @echo "Checking that pandoc-cli depends on this version of pandoc" grep 'pandoc == $(VERSION)' pandoc-cli/pandoc-cli.cabal .PHONY: check-version-sync check-changelog: @echo "Checking for changelog entry for this version" rg '## pandoc $(VERSION) \(\d\d\d\d-\d\d-\d\d\)' changelog.md .PHONY: check-changelog check-manversion: @echo "Checking version number in man pages" grep '"pandoc $(VERSION)"' "pandoc-cli/man/pandoc.1" grep '"pandoc $(VERSION)"' "pandoc-cli/man/pandoc-server.1" grep '"pandoc $(VERSION)"' "pandoc-cli/man/pandoc-lua.1" .PHONY: check-manversion checkdocs: @echo "Checking for tabs in manual." ! rg -n -e '\t' \ MANUAL.txt changelog.md doc/pandoc-server.md doc/pandoc-lua.md .PHONY: checkdocs bench: ## build and run benchmarks cabal bench --benchmark-options='$(BENCHARGS)' 2>&1 | tee "bench_$(TIMESTAMP).txt" .PHONY: bench reformat: ## reformat with stylish-haskell for f in $(SOURCEFILES); do echo $$f; stylish-haskell -i $$f ; done .PHONY: reformat lint: ## run hlint hlint --report=hlint.html $(SOURCEFILES) || open hlint.html .PHONY: lint fix_spacing: ## fix trailing newlines and spaces @ERRORS=0; echo "Checking for spacing errors..." && for f in $(SOURCEFILES); do printf '%s\n' "`cat $$f`" | sed -e 's/ *$$//' > $$f.tmp; diff -u $$f $$f.tmp || ERRORS=1; mv $$f.tmp $$f; done; [ $$ERRORS -eq 0 ] || echo "Spacing errors have been fixed; please commit the changes."; exit $$ERRORS .PHONY: fix_spacing changes_github: ## copy this release's changes in gfm @$(pandoc) --lua-filter tools/extract-changes.lua changelog.md -t gfm --wrap=none --template tools/changes_template.html | sed -e 's/\\#/#/g' .PHONY: changes_github man: pandoc-cli/man/pandoc.1 pandoc-cli/man/pandoc-server.1 pandoc-cli/man/pandoc-lua.1 ## build man pages .PHONY: man latex-package-dependencies: ## print packages used by default latex template $(pandoc) lua tools/latex-package-dependencies.lua .PHONY: latex-package-dependencies coverage: ## code coverage information cabal test \ --ghc-option=-fhpc \ $(CABALOPTS) \ --test-options="--hide-successes --ansi-tricks=false $(TESTARGS)" hpc markup --destdir=coverage test/test-pandoc.tix open coverage/hpc_index.html .PHONY: coverage transitive-deps: ## print transitive dependencies cabal-plan topo | sort | sed -e 's/-[0-9]\..*//' .PHONY: transitive-deps debpkg: ## create linux package docker run -v `pwd`:/mnt \ -v `pwd`/linux/artifacts:/artifacts \ --user $(id -u):$(id -g) \ -e REVISION=$(REVISION) \ -e CABALOPTS="-f-export-dynamic -fembed_data_files -fserver -flua --enable-executable-static -j4 --ghc-option=-j4 --ghc-option=-split-sections --ghc-option=-optc-Os --ghc-option=-optl=-pthread" \ -w /mnt \ --memory=0 \ --rm \ $(DOCKERIMAGE) \ bash \ /mnt/linux/make_artifacts.sh .PHONY: debpkg pandoc-cli/man/pandoc.1: MANUAL.txt man/pandoc.1.before man/pandoc.1.after pandoc.cabal $(pandoc) $< -f markdown -t man -s \ --lua-filter man/manfilter.lua \ --include-before-body man/pandoc.1.before \ --include-after-body man/pandoc.1.after \ --metadata author="" \ --variable section="1" \ --variable title="pandoc" \ --variable header='Pandoc User\[cq]s Guide' \ --variable footer="pandoc $(VERSION)" \ -o $@ pandoc-cli/man/%.1: doc/%.md pandoc.cabal $(pandoc) $< -f markdown -t man -s \ --lua-filter man/manfilter.lua \ --metadata author="" \ --variable section="1" \ --variable title="$(basename $(notdir $@))" \ --variable header='Pandoc User\[cq]s Guide' \ --variable footer="pandoc $(VERSION)" \ --include-after-body man/pandoc.1.after \ -o $@ README.md: README.template MANUAL.txt tools/update-readme.lua $(pandoc) --lua-filter tools/update-readme.lua \ --reference-location=section -t gfm $< -o $@ doc/lua-filters.md: tools/update-lua-module-docs.lua ## update lua-filters.md module docs cabal run pandoc-cli -- \ --standalone \ --reference-links \ --columns=66 \ --from=$< \ --output=$@ \ $@ .PHONY: doc/lua-filters.md download_stats: ## print download stats from GitHub releases curl https://api.github.com/repos/jgm/pandoc/releases | \ jq -r '.[] | .assets | .[] | "\(.download_count)\t\(.name)"' .PHONY: download_stats pandoc-templates: ## update pandoc-templates repo rm ../pandoc-templates/default.* ; \ cp data/templates/* ../pandoc-templates/ ; \ pushd ../pandoc-templates/ && \ git add * && \ git commit -m "Updated templates for pandoc $(VERSION)" && \ popd .PHONY: pandoc-templates update-website: ## update website and upload make -C $(WEBSITE) update make -C $(WEBSITE) make -C $(WEBSITE) upload .PHONY: update-website update-translations: ## update data/translations from Babel and Polyglossia python3 tools/update-translations.py .PHONY: update-translations validate-docx-golden-tests: ## validate docx golden tests against schema which xmllint || ("xmllint is required" && exit 1) test -d ./docx-validator || \ (git clone https://github.com/devoidfury/docx-validator && \ cd docx-validator && patch -p1 <../wml.xsd.patch) sh ./tools/validate-docx.sh test/docx/golden/*.docx .PHONY: validate-docx-golden-tests validate-docx-golden-tests2: ## validate docx golden tests using OOXMLValidator which dotnet || ("dotnet is required" && exit 1) which jq || ("jq is required" && exit 1) test -d ./OOXML-Validator || \ (git clone https://github.com/mikeebowen/OOXML-Validator.git \ && cd OOXML-Validator && dotnet build --configuration=Release) sh ./tools/validate-docx2.sh test/docx/golden/ .PHONY: validate-docx-golden-tests2 node_modules/.bin/ace: npm install @daisy/ace validate-epub: node_modules/.bin/ace ## generate an epub and validate it with epubcheck and ace which epubcheck || exit 1 tmp=$$(mktemp -d) && \ for epubver in 2 3; do \ file=$$tmp/ver$$epubver.epub ; \ $(pandoc) test/epub/wasteland.epub --epub-cover=test/lalune.jpg -Mtitle="The Wasteland" --resource-path test/epub -t epub$$epubver -o $$file --number-sections --toc --quiet && \ echo $$file && \ epubcheck $$file || exit 1 ; \ done && \ ./node_modules/.bin/ace $$tmp/ver3.epub -o ace-report-v2 --force modules.csv: $(PANDOCSOURCEFILES) @rg '^import.*Text\.Pandoc\.' --with-filename $^ \ | rg -v 'Text\.Pandoc\.(Definition|Builder|Walk|Generic)' \ | sort \ | uniq \ | sed -e 's/src\///' \ | sed -e 's/\//\./g' \ | sed -e 's/\.hs:import *\(qualified *\)*\([^ ]*\).*/,\2/' \ > $@ modules.dot: modules.csv @echo "digraph G {" > $@ @echo "overlap=\"scale\"" >> $@ @sed -e 's/\([^,]*\),\(.*\)/ "\1" -> "\2";/' $< >> $@ @echo "}" >> $@ # To get the module dependencies of Text.Pandoc.Parsing: # make modules.pdf ROOT=Text.Pandoc.Parsing modules.pdf: modules.dot gvpr -f tools/cliptree.gvpr -a '"$(ROOT)"' $< | dot -Tpdf > $@ # make moduledeps ROOT=Text.Pandoc.Parsing moduledeps: modules.csv ## Print transitive dependencies of a module ROOT @echo "$(ROOT)" @$(pandoc) lua tools/moduledeps.lua transitive $(ROOT) | sort .PHONY: moduledeps clean: ## clean up cabal clean .PHONY: clean .PHONY: .FORCE sdist-files.txt: .FORCE cabal sdist --list-only | sed 's/\.\///' | grep '^\(test\|data\)/' | sort > $@ git-files.txt: .FORCE git ls-tree -r --name-only HEAD | grep '^\(test\|data\)/' | sort > $@ help: ## display this help @echo "Targets:" @grep -E '^[ a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-16s %s\n", $$1, $$2}' @echo @echo "Environment variables with default values:" @printf "%-16s%s\n" "CABALOPTS" "$(CABALOPTS)" @printf "%-16s%s\n" "TESTARGS" "$(TESTARGS)" @printf "%-16s%s\n" "BASELINE" "$(BASELINE)" @printf "%-16s%s\n" "REVISION" "$(REVISION)" .PHONY: help release-checklist: release-checklist-$(VERSION).org .PHONY: release-checklist release-checklist-$(VERSION).org: RELEASE-CHECKLIST-TEMPLATE.org sed -e 's/RELEASE_VERSION/$(VERSION)/g' $< > $@ hie.yaml: ## regenerate hie.yaml gen-hie > $@ .PHONY: hie.yaml pandoc.wasm: ## build pandoc.wasm -rm $@ wasm32-wasi-cabal update wasm32-wasi-cabal build pandoc-cli ifeq ($(OPTIMIZE_WASM),1) echo "Optimizing (this may take a long time, to avoid, set OPTIMIZE_WASM=0)..." wasm-opt -Oz $$(wasm32-wasi-cabal list-bin pandoc-cli | tail -1) -o $@ else echo "Copying unoptimized pandoc.wasm..." cp "$$(wasm32-wasi-cabal list-bin pandoc-cli | tail -1)" "$@" endif .PHONY: pandoc.wasm ================================================ FILE: README.md ================================================ # Pandoc [![github release](https://img.shields.io/github/release/jgm/pandoc.svg?label=current+release)](https://github.com/jgm/pandoc/releases) [![hackage release](https://img.shields.io/hackage/v/pandoc.svg?label=hackage)](https://hackage.haskell.org/package/pandoc) [![homebrew](https://img.shields.io/homebrew/v/pandoc.svg)](https://formulae.brew.sh/formula/pandoc) [![stackage LTS package](https://stackage.org/package/pandoc/badge/lts)](https://www.stackage.org/lts/package/pandoc) [![CI tests](https://github.com/jgm/pandoc/workflows/CI%20tests/badge.svg)](https://github.com/jgm/pandoc/actions) [![license](https://img.shields.io/badge/license-GPLv2+-lightgray.svg)](https://www.gnu.org/licenses/gpl.html) [![pandoc-discuss on google groups](https://img.shields.io/badge/pandoc-discuss-red.svg?style=social)](https://groups.google.com/forum/#!forum/pandoc-discuss) ## The universal markup converter Pandoc is a [Haskell](https://haskell.org) library for converting from one markup format to another, and a command-line tool that uses this library. It can convert *from*
- `asciidoc` ([AsciiDoc](https://asciidoc.org/) markup) - `bibtex` ([BibTeX](https://ctan.org/pkg/bibtex) bibliography) - `biblatex` ([BibLaTeX](https://ctan.org/pkg/biblatex) bibliography) - `bits` ([BITS](https://jats.nlm.nih.gov/extensions/bits/) XML, alias for `jats`) - `commonmark` ([CommonMark](https://commonmark.org) Markdown) - `commonmark_x` ([CommonMark](https://commonmark.org) Markdown with extensions) - `creole` ([Creole 1.0](http://www.wikicreole.org/wiki/Creole1.0)) - `csljson` ([CSL JSON](https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html) bibliography) - `csv` ([CSV](https://tools.ietf.org/html/rfc4180) table) - `tsv` ([TSV](https://www.iana.org/assignments/media-types/text/tab-separated-values) table) - `djot` ([Djot markup](https://djot.net)) - `docbook` ([DocBook](https://docbook.org)) - `docx` ([Word docx](https://en.wikipedia.org/wiki/Office_Open_XML)) - `dokuwiki` ([DokuWiki markup](https://www.dokuwiki.org/dokuwiki)) - `endnotexml` ([EndNote XML bibliography](https://support.clarivate.com/Endnote/s/article/EndNote-XML-Document-Type-Definition)) - `epub` ([EPUB](http://idpf.org/epub)) - `fb2` ([FictionBook2](http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1) e-book) - `gfm` ([GitHub-Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/)), or the deprecated and less accurate `markdown_github`; use [`markdown_github`](https://pandoc.org/MANUAL.html#markdown-variants) only if you need extensions not supported in [`gfm`](https://pandoc.org/MANUAL.html#markdown-variants). - `haddock` ([Haddock markup](https://www.haskell.org/haddock/doc/html/ch03s08.html)) - `html` ([HTML](https://www.w3.org/html/)) - `ipynb` ([Jupyter notebook](https://nbformat.readthedocs.io/en/latest/)) - `jats` ([JATS](https://jats.nlm.nih.gov) XML) - `jira` ([Jira](https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all)/Confluence wiki markup) - `json` (JSON version of native AST) - `latex` ([LaTeX](https://www.latex-project.org/)) - `markdown` ([Pandoc’s Markdown](https://pandoc.org/MANUAL.html#pandocs-markdown)) - `markdown_mmd` ([MultiMarkdown](https://fletcherpenney.net/multimarkdown/)) - `markdown_phpextra` ([PHP Markdown Extra](https://michelf.ca/projects/php-markdown/extra/)) - `markdown_strict` (original unextended [Markdown](https://daringfireball.net/projects/markdown/)) - `mediawiki` ([MediaWiki markup](https://www.mediawiki.org/wiki/Help:Formatting)) - `man` ([roff man](https://man.cx/groff_man(7))) - `mdoc` ([mdoc](https://mandoc.bsd.lv/man/mdoc.7.html) manual page markup) - `muse` ([Muse](https://amusewiki.org/library/manual)) - `native` (native Haskell) - `odt` ([OpenDocument text document](https://en.wikipedia.org/wiki/OpenDocument)) - `opml` ([OPML](https://opml.org/spec2.opml)) - `org` ([Emacs Org mode](https://orgmode.org)) - `pod` (Perl’s [Plain Old Documentation](https://perldoc.perl.org/perlpod)) - `pptx` ([PowerPoint](https://en.wikipedia.org/wiki/Microsoft_PowerPoint)) - `ris` ([RIS](https://en.wikipedia.org/wiki/RIS_(file_format)) bibliography) - `rtf` ([Rich Text Format](https://en.wikipedia.org/wiki/Rich_Text_Format)) - `rst` ([reStructuredText](https://docutils.sourceforge.io/docs/ref/rst/introduction.html)) - `t2t` ([txt2tags](https://txt2tags.org)) - `textile` ([Textile](https://textile-lang.com)) - `tikiwiki` ([TikiWiki markup](https://doc.tiki.org/Wiki-Syntax-Text#The_Markup_Language_Wiki-Syntax)) - `twiki` ([TWiki markup](https://twiki.org/cgi-bin/view/TWiki/TextFormattingRules)) - `typst` ([typst](https://typst.app)) - `vimwiki` ([Vimwiki](https://vimwiki.github.io)) - `xlsx` ([Excel spreadsheet](https://en.wikipedia.org/wiki/Microsoft_Excel#File_formats)) - `xml` (XML version of native AST) - the path of a custom Lua reader, see [Custom readers and writers](https://pandoc.org/MANUAL.html#custom-readers-and-writers) below
It can convert *to*
- `ansi` (text with [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code), for terminal viewing) - `asciidoc` (modern [AsciiDoc](https://asciidoc.org/) as interpreted by [AsciiDoctor](https://asciidoctor.org/)) - `asciidoc_legacy` ([AsciiDoc](https://asciidoc.org/) as interpreted by [`asciidoc-py`](https://github.com/asciidoc-py/asciidoc-py)). - `asciidoctor` (deprecated synonym for `asciidoc`) - `bbcode` [BBCode](https://www.bbcode.org/reference.php) - `bbcode_fluxbb` [BBCode (FluxBB)](https://web.archive.org/web/20210623155046/https://fluxbb.org/forums/help.php#bbcode) - `bbcode_phpbb` [BBCode (phpBB)](https://www.phpbb.com/community/help/bbcode) - `bbcode_steam` [BBCode (Steam)](https://steamcommunity.com/comment/ForumTopic/formattinghelp) - `bbcode_hubzilla` [BBCode (Hubzilla)](https://hubzilla.org/help/member/bbcode) - `bbcode_xenforo` [BBCode (xenForo)](https://www.xenfocus.com/community/help/bb-codes/) - `beamer` ([LaTeX beamer](https://ctan.org/pkg/beamer) slide show) - `bibtex` ([BibTeX](https://ctan.org/pkg/bibtex) bibliography) - `biblatex` ([BibLaTeX](https://ctan.org/pkg/biblatex) bibliography) - `chunkedhtml` (zip archive of multiple linked HTML files) - `commonmark` ([CommonMark](https://commonmark.org) Markdown) - `commonmark_x` ([CommonMark](https://commonmark.org) Markdown with extensions) - `context` ([ConTeXt](https://www.contextgarden.net/)) - `csljson` ([CSL JSON](https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html) bibliography) - `djot` ([Djot markup](https://djot.net)) - `docbook` or `docbook4` ([DocBook](https://docbook.org) 4) - `docbook5` (DocBook 5) - `docx` ([Word docx](https://en.wikipedia.org/wiki/Office_Open_XML)) - `dokuwiki` ([DokuWiki markup](https://www.dokuwiki.org/dokuwiki)) - `epub` or `epub3` ([EPUB](http://idpf.org/epub) v3 book) - `epub2` (EPUB v2) - `fb2` ([FictionBook2](http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1) e-book) - `gfm` ([GitHub-Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/)), or the deprecated and less accurate `markdown_github`; use [`markdown_github`](https://pandoc.org/MANUAL.html#markdown-variants) only if you need extensions not supported in [`gfm`](https://pandoc.org/MANUAL.html#markdown-variants). - `haddock` ([Haddock markup](https://www.haskell.org/haddock/doc/html/ch03s08.html)) - `html` or `html5` ([HTML](https://www.w3.org/html/), i.e. [HTML5](https://html.spec.whatwg.org/)/XHTML [polyglot markup](https://www.w3.org/TR/html-polyglot/)) - `html4` ([XHTML](https://www.w3.org/TR/xhtml1/) 1.0 Transitional) - `icml` ([InDesign ICML](https://web.archive.org/web/20211006210211/https://wwwimages.adobe.com/www.adobe.com/content/dam/acom/en/devnet/indesign/sdk/cs6/idml/idml-cookbook.pdf)) - `ipynb` ([Jupyter notebook](https://nbformat.readthedocs.io/en/latest/)) - `jats_archiving` ([JATS](https://jats.nlm.nih.gov) XML, Archiving and Interchange Tag Set) - `jats_articleauthoring` ([JATS](https://jats.nlm.nih.gov) XML, Article Authoring Tag Set) - `jats_publishing` ([JATS](https://jats.nlm.nih.gov) XML, Journal Publishing Tag Set) - `jats` (alias for `jats_archiving`) - `jira` ([Jira](https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all)/Confluence wiki markup) - `json` (JSON version of native AST) - `latex` ([LaTeX](https://www.latex-project.org/)) - `man` ([roff man](https://man.cx/groff_man(7))) - `markdown` ([Pandoc’s Markdown](https://pandoc.org/MANUAL.html#pandocs-markdown)) - `markdown_mmd` ([MultiMarkdown](https://fletcherpenney.net/multimarkdown/)) - `markdown_phpextra` ([PHP Markdown Extra](https://michelf.ca/projects/php-markdown/extra/)) - `markdown_strict` (original unextended [Markdown](https://daringfireball.net/projects/markdown/)) - `markua` ([Markua](https://leanpub.com/markua/read)) - `mediawiki` ([MediaWiki markup](https://www.mediawiki.org/wiki/Help:Formatting)) - `ms` ([roff ms](https://man.cx/groff_ms(7))) - `muse` ([Muse](https://amusewiki.org/library/manual)) - `native` (native Haskell) - `odt` ([OpenDocument text document](https://en.wikipedia.org/wiki/OpenDocument)) - `opml` ([OPML](https://opml.org/spec2.opml)) - `opendocument` ([OpenDocument XML](https://www.oasis-open.org/2021/06/16/opendocument-v1-3-oasis-standard-published/)) - `org` ([Emacs Org mode](https://orgmode.org)) - `pdf` ([PDF](https://www.adobe.com/pdf/)) - `plain` (plain text) - `pptx` ([PowerPoint](https://en.wikipedia.org/wiki/Microsoft_PowerPoint) slide show) - `rst` ([reStructuredText](https://docutils.sourceforge.io/docs/ref/rst/introduction.html)) - `rtf` ([Rich Text Format](https://en.wikipedia.org/wiki/Rich_Text_Format)) - `texinfo` ([GNU Texinfo](https://www.gnu.org/software/texinfo/)) - `textile` ([Textile](https://textile-lang.com)) - `slideous` ([Slideous](https://goessner.net/articles/slideous/) HTML and JavaScript slide show) - `slidy` ([Slidy](https://www.w3.org/Talks/Tools/Slidy2/) HTML and JavaScript slide show) - `dzslides` ([DZSlides](https://paulrouget.com/dzslides/) HTML5 + JavaScript slide show) - `revealjs` ([reveal.js](https://revealjs.com/) HTML5 + JavaScript slide show) - `s5` ([S5](https://meyerweb.com/eric/tools/s5/) HTML and JavaScript slide show) - `tei` ([TEI Simple](https://github.com/TEIC/TEI-Simple)) - `typst` ([typst](https://typst.app)) - `vimdoc` ([Vimdoc](https://vimhelp.org/helphelp.txt.html#help-writing)) - `xml` (XML version of native AST) - `xwiki` ([XWiki markup](https://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/Features/XWikiSyntax/)) - `zimwiki` ([ZimWiki markup](https://zim-wiki.org/manual/Help/Wiki_Syntax.html)) - the path of a custom Lua writer, see [Custom readers and writers](https://pandoc.org/MANUAL.html#custom-readers-and-writers) below
Pandoc can also produce PDF output via LaTeX, Groff ms, or HTML. Pandoc’s enhanced version of Markdown includes syntax for tables, definition lists, metadata blocks, footnotes, citations, math, and much more. See the User’s Manual below under [Pandoc’s Markdown](https://pandoc.org/MANUAL.html#pandocs-markdown). Pandoc has a modular design: it consists of a set of readers, which parse text in a given format and produce a native representation of the document (an *abstract syntax tree* or AST), and a set of writers, which convert this native representation into a target format. Thus, adding an input or output format requires only adding a reader or writer. Users can also run custom pandoc filters to modify the intermediate AST (see the documentation for [filters](https://pandoc.org/filters.html) and [Lua filters](https://pandoc.org/lua-filters.html)). Because pandoc’s intermediate representation of a document is less expressive than many of the formats it converts between, one should not expect perfect conversions between every format and every other. Pandoc attempts to preserve the structural elements of a document, but not formatting details such as margin size. And some document elements, such as complex tables, may not fit into pandoc’s simple document model. While conversions from pandoc’s Markdown to all formats aspire to be perfect, conversions from formats more expressive than pandoc’s Markdown can be expected to be lossy. ## Installing Here’s [how to install pandoc](INSTALL.md). ## Documentation Pandoc’s website contains a full [User’s Guide](https://pandoc.org/MANUAL.html). It is also available [here](MANUAL.txt) as pandoc-flavored Markdown. The website also contains some [examples of the use of pandoc](https://pandoc.org/demos.html), a limited [online demo](https://pandoc.org/try), and a [WebAssembly-based online demo](https://pandoc.org/app). ## Contributing Pull requests, bug reports, and feature requests are welcome. Please make sure to read [the contributor guidelines](CONTRIBUTING.md) before opening a new issue. ## License © 2006-2024 John MacFarlane (jgm@berkeley.edu). Released under the [GPL](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html "GNU General Public License"), version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) ================================================ FILE: README.template ================================================ Pandoc ====== [![github release](https://img.shields.io/github/release/jgm/pandoc.svg?label=current+release)](https://github.com/jgm/pandoc/releases) [![hackage release](https://img.shields.io/hackage/v/pandoc.svg?label=hackage)](https://hackage.haskell.org/package/pandoc) [![homebrew](https://img.shields.io/homebrew/v/pandoc.svg)](https://formulae.brew.sh/formula/pandoc) [![stackage LTS package](https://stackage.org/package/pandoc/badge/lts)](https://www.stackage.org/lts/package/pandoc) [![CI tests](https://github.com/jgm/pandoc/workflows/CI%20tests/badge.svg)](https://github.com/jgm/pandoc/actions) [![license](https://img.shields.io/badge/license-GPLv2+-lightgray.svg)](https://www.gnu.org/licenses/gpl.html) [![pandoc-discuss on google groups](https://img.shields.io/badge/pandoc-discuss-red.svg?style=social)](https://groups.google.com/forum/#!forum/pandoc-discuss) The universal markup converter ------------------------------ Pandoc is a [Haskell] library for converting from one markup format to another, and a command-line tool that uses this library. It can convert *from* ::: {#input-formats} ::: It can convert *to* ::: {#output-formats} ::: Pandoc can also produce PDF output via LaTeX, Groff ms, or HTML. Pandoc's enhanced version of Markdown includes syntax for tables, definition lists, metadata blocks, footnotes, citations, math, and much more. See the User's Manual below under [Pandoc's Markdown](https://pandoc.org/MANUAL.html#pandocs-markdown). Pandoc has a modular design: it consists of a set of readers, which parse text in a given format and produce a native representation of the document (an _abstract syntax tree_ or AST), and a set of writers, which convert this native representation into a target format. Thus, adding an input or output format requires only adding a reader or writer. Users can also run custom pandoc filters to modify the intermediate AST (see the documentation for [filters](https://pandoc.org/filters.html) and [Lua filters](https://pandoc.org/lua-filters.html)). Because pandoc's intermediate representation of a document is less expressive than many of the formats it converts between, one should not expect perfect conversions between every format and every other. Pandoc attempts to preserve the structural elements of a document, but not formatting details such as margin size. And some document elements, such as complex tables, may not fit into pandoc's simple document model. While conversions from pandoc's Markdown to all formats aspire to be perfect, conversions from formats more expressive than pandoc's Markdown can be expected to be lossy. Installing ---------- Here's [how to install pandoc](INSTALL.md). Documentation ------------- Pandoc’s website contains a full [User’s Guide](https://pandoc.org/MANUAL.html). It is also available [here](MANUAL.txt) as pandoc-flavored Markdown. The website also contains some [examples of the use of pandoc](https://pandoc.org/demos.html), a limited [online demo](https://pandoc.org/try), and a [WebAssembly-based online demo](https://pandoc.org/app). Contributing ------------ Pull requests, bug reports, and feature requests are welcome. Please make sure to read [the contributor guidelines](CONTRIBUTING.md) before opening a new issue. License ------- © 2006-2024 John MacFarlane (jgm@berkeley.edu). Released under the [GPL], version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) [GPL]: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html "GNU General Public License" [Haskell]: https://haskell.org ================================================ FILE: RELEASE-CHECKLIST-TEMPLATE.org ================================================ #+STARTUP: content #+PROPERTY: :header-args: :results verbatim ** pandoc RELEASE_VERSION release checklist *** TODO Check [[https://github.com/jgm/pandoc/issues?q=state%3Aopen%20label%3A%22priority%3Ahigh%22%20][priority-high]] tag *** TODO Release any prerelease packages in [[./cabal.project]] *** TODO [[./pandoc.cabal]] - bump version number *** TODO [[./pandoc-cli/pandoc-cli.cabal]] - bump version, sync pandoc version #+begin_src sh git log $(git describe --tags --abbrev=0)..HEAD --oneline pandoc-cli #+end_src *** TODO [[./pandoc-lua-engine/pandoc-lua-engine.cabal]] - bump version? #+begin_src sh git log $(git describe --tags --abbrev=0)..HEAD --oneline pandoc-lua-engine #+end_src *** TODO [[./pandoc-server/pandoc-server.cabal]] - bump version? #+begin_src sh git log $(git describe --tags --abbrev=0)..HEAD --oneline pandoc-server #+end_src *** TODO Update [[./MANUAL.txt]] date and rebuild man pages #+begin_src sh :results silent NEWDATE=$(date -I) sed -i '' -e "/^---$/,/^---$/s/^date:.*$/date: $NEWDATE/" MANUAL.txt make man #+end_src *** TODO Finalize [[./changelog.md]] #+begin_src sh :results output file :file LOG.md git log $(git describe --tags --abbrev=0)..HEAD --mailmap --reverse --format=format:' * %s%n %aN%n%w(78,4,4)%b' | sed -e '/^ *John MacFarlane$/d' | sed -e 's/ *$//' #+end_src *** TODO prerelease checks #+begin_src sh :results output verbatim make prerelease #+end_src *** TODO Update [[./AUTHORS.md]] #+begin_src sh :results output list org make authors #+end_src *** TODO Run [[https://github.com/jgm/pandoc/actions/workflows/release-candidate.yml][release candidate workflow]] on GitHub #+begin_src sh :var ghtoken=(jgm-authinfo-get "api.github.com" "jgm_pandoc_release") curl -L \ -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $ghtoken"\ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/jgm/pandoc/actions/workflows/release-candidate.yml/dispatches \ -d '{"ref":"main"}' #+end_src *** TODO Run [[https://cirrus-ci.com][release candidate workflows]] manually on cirrus-ci.com *** TODO If it builds successfully, download artifacts from GitHub: Windows and intel mac and pandoc.wasm (rename as pandoc-VERSION.wasm) from Cirrus-ci.com: linux amd64 and arm64 and m1 mac *** TODO Use 'make' in macos artifact to sign code *** TODO Install pandoc from package So website will have the correct executable. *** TODO Update website #+begin_src sh make update-website #+end_src *** TODO Tag release in git: - use X.Y for pandoc - pandoc-cli-X.Y - if needed: pandoc-server-X.Y - if needed: pandoc-lua-engine-X.Y *** TODO Upload packages to Hackage: #+NAME: changed-packages #+begin_src sh :results silent echo pandoc echo pandoc-cli for package in pandoc-lua-engine pandoc-server; do lines=$(git log $(git describe --tags --abbrev=0)..HEAD --oneline $package | wc -l) if ! [ $lines -eq 0 ]; then echo $package; fi done #+end_src - pandoc - pandoc-cli and if changed: - pandoc-server - pandoc-lua-engine *** TODO make pandoc-templates #+begin_src sh make pandoc-templates pushd ~/src/pandoc-templates git tag RELEASE_VERSION git push git push --tags popd #+end_src *** TODO Copy binary to server, install it #+begin_src # example: cd RELEASE_VERSION tar xvzf pandoc-RELEASE_VERSION-linux-amd64.tar.gz scp pandoc-RELEASE_VERSION/bin/pandoc website:cgi-bin/pandoc-server.cgi #+end_src *** TODO Upload pandoc.wasm #+begin_src sh make pandoc.wasm cd wasm make upload #+end_src *** TODO create release announcement and add to GH release announcement #+NAME: relann #+begin_src elisp :results value file :file relann-RELEASE_VERSION "I'm pleased to announce the release of pandoc RELEASE_VERSION, available in the usual places: Binary packages & changelog: https://github.com/jgm/pandoc/releases/tag/RELEASE_VERSION Source & API documentation: http://hackage.haskell.org/package/pandoc-RELEASE_VERSION Description of release. Any API changes. Thanks to all who contributed, especially new contributors ... " #+end_src *** TODO Add [[https://github.com/jgm/pandoc/releases/][release on GitHub]] #+begin_src sh :var announcement=relann :results output literal echo '```' cat relann-RELEASE_VERSION echo '```' echo '' make changes_github #+end_src *** TODO Announce on [[mailto:pandoc-announce@googlegroups.com][pandoc-announce]] ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions Only the most recent version of pandoc is supported with security updates. ## Reporting a Vulnerability To report a vulnerability, email the maintainer, jgm@berkeley.edu. But first please read the section of the manual entitled [A note on security](https://pandoc.org/MANUAL.html#a-note-on-security), which describes some security guarantees pandoc does NOT make. ================================================ FILE: benchmark/benchmark-pandoc.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2012-2024 John MacFarlane This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} import Text.Pandoc import Text.Pandoc.MIME import Control.DeepSeq (force) import Control.Monad.Except (throwError) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString as B import qualified Data.Text as T import Test.Tasty.Bench -- import Gauge import qualified Data.ByteString.Lazy as BL import Data.Maybe (mapMaybe) import Data.List (sortOn) import Text.Pandoc.Format (FlavoredFormat(..)) readerBench :: Pandoc -> T.Text -> Maybe Benchmark readerBench _ name | name `elem` ["bibtex", "biblatex", "csljson"] = Nothing readerBench doc name = either (const Nothing) Just $ runPure $ do (rdr, rexts) <- getReader $ FlavoredFormat name mempty (wtr, wexts) <- getWriter $ FlavoredFormat name mempty tmpl <- Just <$> compileDefaultTemplate name case (rdr, wtr) of (TextReader r, TextWriter w) -> do inp <- w def{ writerWrapText = WrapAuto , writerExtensions = wexts , writerTemplate = tmpl } doc return $ bench (T.unpack name) $ nf (either (error . show) id . runPure . r def) inp (ByteStringReader r, ByteStringWriter w) -> do inp <- w def{ writerWrapText = WrapAuto , writerExtensions = wexts , writerTemplate = tmpl } doc return $ bench (T.unpack name) $ nf (either (error . show) id . runPure . r def{readerExtensions = rexts}) inp _ -> throwError $ PandocSomeError $ "text/bytestring format mismatch: " <> name getImages :: IO [(FilePath, MimeType, BL.ByteString)] getImages = do ll <- B.readFile "test/lalune.jpg" mv <- B.readFile "test/movie.jpg" return [("lalune.jpg", "image/jpg", BL.fromStrict ll) ,("movie.jpg", "image/jpg", BL.fromStrict mv)] writerBench :: [(FilePath, MimeType, BL.ByteString)] -> Pandoc -> T.Text -> Maybe Benchmark writerBench _ _ name | name `elem` ["bibtex", "biblatex", "csljson"] = Nothing writerBench imgs doc name = either (const Nothing) Just $ runPure $ do (wtr, wexts) <- getWriter $ FlavoredFormat name mempty case wtr of TextWriter writerFun -> return $ bench (T.unpack name) $ nf (\d -> either (error . show) id $ runPure $ do mapM_ (\(fp,mt,bs) -> insertMedia fp (Just mt) bs) imgs writerFun def{ writerExtensions = wexts} d) doc ByteStringWriter writerFun -> return $ bench (T.unpack name) $ nf (\d -> either (error . show) id $ runPure $ do mapM_ (\(fp,mt,bs) -> insertMedia fp (Just mt) bs) imgs writerFun def{ writerExtensions = wexts} d) doc main :: IO () main = do inp <- UTF8.toText <$> B.readFile "test/testsuite.txt" let opts = def let doc = either (error . show) force $ runPure $ readMarkdown opts inp defaultMain [ env getImages $ \imgs -> bgroup "writers" $ mapMaybe (writerBench imgs doc . fst) (sortOn fst writers :: [(T.Text, Writer PandocPure)]) , bgroup "readers" $ mapMaybe (readerBench doc . fst) (sortOn fst readers :: [(T.Text, Reader PandocPure)]) ] ================================================ FILE: cabal.project ================================================ packages: . pandoc-lua-engine pandoc-server pandoc-cli constraints: skylighting-format-blaze-html >= 0.1.2, skylighting-format-context >= 0.1.0.2, auto-update >= 0.2.6, crypton >= 1.1.1 package pandoc flags: +embed_data_files +http if arch(wasm32) tests: False package atomic-counter flags: +no-cmm package aeson flags: -ordered-keymap package crypton ghc-options: -optc-DARGON2_NO_THREADS package digest flags: -pkg-config package lua flags: +cross-compile ghc-options: -optc-mllvm -optc-wasm-enable-sjlj -optc-D_WASI_EMULATED_PROCESS_CLOCKS -optc-D_WASI_EMULATED_SIGNAL -optc-DLUA_STUB_TMPNAM -optc-DLUA_STUB_TMPFILE -optc-DLUA_STUB_SYSTEM package pandoc flags: +embed_data_files -http package pandoc-cli flags: +lua -repl -server ld-options: -lwasi-emulated-process-clocks -lwasi-emulated-signal -lsetjmp package pandoc-lua-engine flags: -repl allow-newer: all:base, all:binary, all:bytestring, all:containers, all:ghc-bignum, all:template-haskell, all:text, all:time source-repository-package type: git location: https://github.com/snoyberg/conduit.git tag: 6b98f070fea09a3bf0a5d0897a2e27e3aa91c8fe subdir: conduit-extra post-checkout-command: sh -c "patch -N -p1 < ../../../wasm/patches/conduit-extra.patch" source-repository-package type: git location: https://github.com/jappeace/ram.git tag: 6e49475ae7b4b3545923407388690234d838dc45 post-checkout-command: sh -c "patch -N -p1 < ../../../wasm/patches/memory.patch" source-repository-package type: git location: https://github.com/fpco/streaming-commons.git tag: v0.2.3.1 post-checkout-command: sh -c "patch -N -p1 < ../../../wasm/patches/streaming-commons.patch" source-repository-package type: git location: https://github.com/snoyberg/xml.git tag: xml-conduit/1.10.1.0 subdir: xml-conduit post-checkout-command: sh -c "patch -N -p1 < ../../../wasm/patches/xml-conduit.patch" source-repository-package type: git location: https://github.com/haskellari/splitmix.git tag: v0.1.3.2 post-checkout-command: sh -c "patch -N -p1 < ../../../wasm/patches/splitmix.patch" source-repository-package type: git location: https://github.com/hslua/hslua tag: lua-2.3.4 subdir: lua post-checkout-command: sh -c "patch -N -p1 < ../../../wasm/patches/lua.patch" ================================================ FILE: changelog.md ================================================ # Revision history for pandoc ## pandoc 3.9.0.2 (2026-03-19) * Typst template: fix regression introduced in 3.9.0.1 (#11538). ## pandoc 3.9.0.1 (2026-03-17) * WASM GUI: + Don't block everything while pandoc.wasm loads. + Fix bug with Unicode filenames (#11447). + Catch errors loading pandoc. * Docx reader: + Recognize media inside textboxes (#11053, Raymond Berger). + Properly handle media stored with packaged-rooted paths such as `/media` (#11518). This can be produced by the online version of Word. With this change, media stored at an absolute path will be stored with its original name and not given a SHA1-based name. + Preserve non-textbox content when unwrapping textboxes (#11510, #6893, #11412, #5394, #9633). Treat text inside a textbox containing an image as a figure caption. * Typst reader: + Handle bibliography command (#11460). If this is present, a bibliography section is added to the document and the bibliography paths are added to `bibliography` in the metadata. This sets things up for using `--citeproc`. * Textile reader: + Handle block content in cells (#11455). * Docx reader: + Support `w:gridBefore` table row property (#11464, Jan Tojnar). * LaTeX reader: + Support `\footnotemark` and `\footnotetext` (#11450). These commands allow separating the footnote mark from its content, useful in tables, minipages, and other contexts where \footnote cannot be used directly. + Support supertabular environment (#11523, bodigrim). * ODT Reader: + Fix relative linked images (#11369, Tuong Nguyen Manh). + Recognize `Preformatted_20_Text` style Jan Tojnar This is used by LibreOffice, and we will switch to that as well. + Add block smushing logic (Jan Tojnar). + Support Preformatted Text style (#4841, Jan Tojnar). * Markdown reader: + Fix bug with `lists_without_preceding_blankline` (#11534). * Typst writer: + Don't add a carriage return after `\` for hard break (#11446). They are not necessary. Note that they can still be included if you use `--wrap=preserve` and add a newline in your source document. + Improve handling of data: URIs in images. Instead of using an SVG with a link containing the data URI (the solution of #10460), we can now simply produce a `bytes` object with the requisite bytes. Typst figures out the format automatically. + Fix escaping of quotes (#11463). + Include alt attributes on images (mcanouil). + Properly escape `.` after bracketed argument (#11511). * Docx writer: + Don't depend on `extractTarget` from Docx reader. + Fix section breaks with `--top-level-division` (#11482, #10578). * EPUB writer: + Add cover metadata for EPUB3 (#11479). This allows file managers to show cover thumbnails for EPUB3 files as well. + Update allowed values for EPUB3 metadata `identifier.scheme` (#11481, Pascal Wagler). * ODT writer: + Rename inline source class to match LibreOffice (#3390, Jan Tojnar). Rename the text style we use to represent `Code` inlines from `Source_20_Text` to `Source_Text`. This is the same name LibreOffice Writer uses so it will be recognized by the Character Styles section of the Styles menu. + Remove font size from inline source class (Jan Tojnar). This matches what LibreOffice Writer is doing. Also fixes literals inside headings being too small. + Modernize `Preformatted Text` & `Source Text` styles (Jan Tojnar). Presumably, `font-family-generic` and `font-pitch` will allow to find a replacement on systems that do not have `Courier New`. * Markdown writer: + Fix rendering of alerts (#11479). We only properly handled the case where the alert started with a paragraph, but it can start with a list or other block type. + Escape literal `&` that would trigger entity (#11490). * HTML writer: + For revealjs, default `scrollProgress` to `auto` (Christophe Dervieux). * PPTX writer: + Register content type for embedded fonts (#11492). * MediaWiki writer: + Use appropriate syntax for external images (#11494). Note that they will only be rendered as images if an option `$wgAllowExternalImages` is enabled in the MediaWiki instance. * ICML writer: + Support for image object styles (#11498, massifrg). This change allows users to style images in InDesign bysetting the `object-style` attribute in a pandoc Image, which is mapped to the AppliedObjectStyle attribute in the Rectangle element around an Image element in the resulting ICML. * JATS writer: + Improve representation of Divs (Albert Krewinkel). The writer now checks if the element used to represent (non-special) Divs has any `` specific attributes. If it does, the writer keeps wrapping the Div contents in a ``, as it did before. Otherwise, the writer falls back to the more appropriate `

` element or simply unwraps the Div if the wrapping element wouldn't have any attributes. The new behavior gives better semantic results in most cases, as `` should be used for text that "is outside the flow of the narrative text", which doesn't apply to most divs. "Special" divs, like those used to mark sections, are not affected by this change. * Text.Pandoc.Writers.GridTable: + Normalize tables (#8102). Previously, if an invalid table was passed to `toTable`, an array index error could be raised. Normalizing the table forces it into a shape that won't allow this error. * Lua subsystem (Albert Krewinkel): + Add new function `pandoc.types.Sources` (#11441). * LaTeX template: properly handle keywords with commas. These need to be put in an `\xmpquote{..}` command. Closes #11528. * HTML styles template: avoid duplicate code selector. Consolidate two clauses. Closes #11484. * Revealjs template: fix type rendering of scroll-view options (Christophe Dervieux, #11486). * Typst template: + Use both place and block for title (Gordon Woodhull). Otherwise the title will be confined to the left column + Put title block in a conditional (#11529). This avoids an empty block for documents that lack metadata information. The empty block causes problems if `#set page` is used, as it will cause a page break. * Beamer template: add `logooption` variable (#11452, Sidney Mau). * Text.Pandoc.ImageSize: + Correctly handle percentage width, height on SVG (#11530). Previously we were getting image size of 0 when a percentage was specified for width or height on SVG. With this change, we simply ignore these percentages (becaues ImageSize doesn't know the size of the containing element). * Re-add `-threaded` to compile options in pandoc-cli. * Use released djot, asciidoc, texmath, typst. * Allow crypton 1.1, tls 2.2, http-client-tls 0.4. * Require auto-update >= 0.2.6 to fix server on macOS (#11488). * pandoc-cli.cabal: bump base min bound to 4.18 (same as pandoc). * MANUAL.txt: + Note that JSON may be used in YAML metadata blocks (#11525). + Update link for ICML in manual. + Fix outdated OPML spec URL in MANUAL.txt (#11504, Peter Briggs). ## 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 . 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). ``, ``, ``, and `` 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 `

` 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 `` element. Furthermore,
      figure content that isn't allowed as children of `` elements, such as
      raw text, gets wrapped in `

` 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 `` 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). * Add `xlsx` (Microsoft Excel) as an input format (Anton Antich). Each worksheet turns into a section containing a table. * Add `pptx` (PowerPoint) as new input format (Anton Antich). * Add `bbcode` as a new output format (#11242, reptee). Several variants of BBCode are also supported: `bbcode_fluxbb` (FluxBB), `bbcode_phpbb` (phpBB), `bbcode_steam` (Hubzilla), `bbcode_hubzilla` (Hubzilla), and `bbcode_xenforo` (xenForo). * New exported module Text.Pandoc.Readers.AsciiDoc, exporting `readAsciiDoc` [API change]. * New module `Text.Pandoc.Readers.Pptx`, exporting `readPptx` (Anton Antich) [API change]. * New module `Text.Pandoc.Readers.Xlsx`, exporting `readXlsx` (Anton Antich) [API change]. * LaTeX reader: + Revert `\linebreak` as LineBreak (#11272). `\linebreak` is more of a hint, it shouldn't produce a hard break. + Better handling of `\makeatletter` in parsing raw LaTeX (#11270). + Fix spurious paragraph breaks in math environments (#11265, Emmanuel Ferdman). Previously, a math environment with extra space before the `\end` would get rendered with a blank line, which LaTeX treats as a paragraph break. + Change type on `rawLaTeXParser` in Text.Pandoc.LaTeX.Parsing. The preparser doesn't need to return a value. + Fix `rawTeXParser` (#11253). Make macro expansion in raw LaTeX depend on the setting of the `latex_macros` extension. Previously macros were always expanded, even in raw TeX in markdown. In addition, there was previously a bug that caused content to be garbled in certain cases. + Handle `ifstrequal` at a lower level, like the other `if` commands (#11253). + Move `ifstrequal`, `iftoggle`, etc., which were misplaced in `environments`, to `blockCommands`, so these commands work properly. * Docx reader: + Handle REF link instruction (#11296, Ezwal). + Check recursively for caption styles (Albert Krewinkel). The docx reader uses caption styles to identify figures and captioned tables. It now checks for known caption styles in the full styles hierarchy of a paragraph instead of just checking the style directly. This allows to recognize caption styles that are built on top of the basic *caption* style, as is sometimes the case in sophisticated styles. * Markdown reader: + Fix performance issue in links with `'` (#10880). * Typst reader: + Handle document metadata and `#title` (jgm/typst-hs#80). Note that previously, the typst reader never returned document metadata. Now it does, even if the typst document does not contain a `#title` function that would result in actually printing the title block. * Djot reader: + Add Space elements (#11250). Previously we just got big Str elements with spaces included. But many pandoc writers assume that breakable spaces will be Space elements, and this is also required for automatic wrapping. * RST reader: + Correctly handle intraword emphasis (#11309). * Text.Pandoc.Readers: + Export `readAsciiDoc`, `readXlsx`, `readPptx` [API change]. * New module Text.Pandoc.Writers.BBCode, exporting `writeBBCode`, `writeBBCodeSteam`, `writeBBCodeFluxBB`, `writeBBCodePhpBB`, `writeBBCodeHubzilla`, `writeBBCodeXenforo` [API change]. * LaTeX writer: + Make level 1-3 headings work inside blockquotes (#11281, James Barlow). + Remove `split` from list of math environments (#11274). + Improve handling of math environments in tex math (#11266). * HTML writer: + Add reveal.js `scroll` and `scrollSnap` options to writer and template (#10052, Asliddinbek Azizovich). + Use 'defer' when including mathjax script, as recommended in MathJax docs (#11292). * ANSI writer: + Apply row spans in tables (#10149, Tuong Nguyen Manh). The ANSI writer is now able to keep track of row spans and apply them in rows. * Pptx writer: + Handle reference doc without slides (#7536, Tuong Nguyen Manh). * AsciiDoc writer: + Add more table features (#11267, Tuong Nguyen Manh): Row span and column span, footer row, individual horizontal cell alignment. * Typst template: + Fix font for compatibility with typst 0.14, which doesn't permit an empty array for `font` (#11238). + Re-add `columns` to typst template (#11259), fixing a pandoc 3.8 regression. + Fix syntax for bibliography inclusion (#11233, Mickaël Canouil). Previously the syntax was wrong when multiple bibliography files were specified. Typst expects an array. * Text.Pandoc.Writers: + Export `writeBBCode`, `writeBBCodeSteam`, `writeBBCodeFluxBB`, `writeBBCodePhpBB`, `writeBBCodeHubzilla`, `writeBBCodeXenforo` [API change]. * Text.Pandoc.Writers.Shared: + Add functions `insertCurrentSpansAtColumn`, `takePreviousSpansAtColumn` and `decrementTrailingRowSpans` for applying and keeping track of RowSpans over multiple rows (#10149, Tuong Nguyen Manh). [API change] * Text.Pandoc.Logging: + Change message for missing HTML title warning (#11307). Suggest setting the `pagetitle` variable instead of setting `title` in metadata. * Lua subsystem: + Preserve common state of custom Lua readers (Albert Krewinkel). The common state is transferred to Lua when calling a custom Lua reader, and is now also transferred back after the reader has finished. This ensures that info messages, warnings, and mediabag entries are available to the main program and all subsequent processing steps. * Text.Pandoc.PDF: + Avoid converting SVG to PDF when non-TeX PDF engine is used (#11275). This fixes a 3.8 regression, which caused documents with SVGs to raise an error when converted to PDF using WeasyPrint. + Fix a 3.8 regression with typst and smart quotes (#11256). Before 3.8, the default behavior when producing a PDF `-t typst` was to produce smart quotes according to typst's defaults. (This could be defeated by specifying `-t typst-smart`.) This behavior broke in 3.8 because of a change to Text.Pandoc.PDF. This change caused `smart` to be disabled for all formats when producing PDFs, when before it was only disable for TeX-based formats (to avoid bad ligatures). This commit restores the old behavior. Possibly the regression also other affects other non-TeX formats, e.g. HTML. * Text.Pandoc.Shared: + Add functions `allRowsEmpty` and `tableBodiesToRows` from the RST writer for reuse in other writers. (Tuong Nguyen Manh) [API change]. * Text.Pandoc.Citeproc: + Allow formatting in locator to be transmitted to citeproc. We do this indirectly, by rendering the formatting using the HTML tags that citeproc recognizes. Fixes jgm/citeproc#68 and jgm/citeproc#163. Note that formatting is only possible for locators given in the explicit form, surrounded by curly braces. It won't work for implicit locators, since these expect number-like expressions. * New non-exported module Text.Pandoc.Readers.OOXML.Shared containing functions factored out from Text.Pandoc.Readers.Docx.Util (Anton Antich). * Tests: The common file `nativeDiff` has been extracted from the Docx and Pptx text files and put in Tests.Helpers. * Use asciidoc 0.1, djot 0.1.2.4, texmath 0.13.0.2, typst 0.8.1, citeproc 0.12. * MANUAL.txt: + Improve `implicit_figure` documentation (#11082). + Give both forms of options when referring to them (#11306). * Update INSTALL.md (#11271). ## pandoc 3.8.2.1 (2025-10-20) * HTML reader: allow blank space between open and close `iframe`. * RTF reader: improve hyperlink parsing (#11211). * Org reader: + Parse parameter lists on unknown blocks (#11188, Albert Krewinkel). The reader tries to parse the rest of the opening line of a block, e.g., `#+begin_myblock …`, as a parameters list. It first assumes that the parameters are in lisp-style (`:key value`), then alternatively tries to read python-style key-value pairs (`key=value`) and falls back to reading the entire remaining line as a single `parameter` attribute. + Add support for dynamic blocks. * Docx writer: properly handle nested comment spans (#8189, #6959, mourino). * RST writer: Don't use simple tables with RowSpans (#11214, Tuong Nguyen Manh). * Typst writer: Escape open paren after non-space (#11210). This fixes an issue that occurs if an open paren comes right after e.g. `#strong[test]`. * Typst template: ensure that title block is properly centered (#11221). * LaTeX writer/template: small fix for unnumbered tables for compatibility with older LaTeX installations (#11201). Thanks to @priiduonu for the solution. * MANUAL.txt: Fixed missing backtick (#11209, FoxChillz). * Correct anchor references to `pandoc.text` module documentation (#11111, Emmanuel Ferdman). * Fixed golden test regeneration in Docx reader test. * Allow unicode-data 0.8. * Use citeproc 0.11. This fixes a significant performance regression in pandoc 3.8, which was due to a rewrite of the default chicago-author-date.csl file. Performance with `--citeproc` is now on par with what we had in pandoc 3.7, even with the revised Chicago styles. ## pandoc 3.8.2 (2025-10-05) * Markdown reader/writer: implement new `table_attributes` extension (#10884). When `table_attributes` is enabled (as it is by default for pandoc's Markdown), attributes can be attached to a table by including them at the end of the caption. Previously the writer would emit an identifier in this position, but the reader didn't handle it. Now arbitrary attributes are allowed, and they work in both the reader and writer. * Typst writer: don't add superfluous semicolons (#11196). Previously we added semicolons after inline commands not followed by spaces, but mainly this was to deal with one issue: the presence of a semicolon after an inline command, which would be swallowed as a command separator (#9252). This commits adopts an approach that should avoid so many superfluous semicolons: it escapes semicolons that might come right after a command. * Typst template: fix 3.8 regression in which links disappear (#11194). A template change in 3.8 added a show rule for links which causes them to disappear except in special cases. * Text.Pandoc.Parsing: rewrite `oneOfStrings` more efficiently. * LaTeX writer: Fix strikeout in links (#11192, Tuong Nguyen Manh). As in #1294 `\url` and `\href` need to be protected inside an mbox for `soul` commands. * Text.Pandoc.Extensions: Add `Ext_table_attributes` constructor for `Extension` [API change]. * Use released texmath 0.13.0.1. * Update FSF contact information in COPYING (#11183, Bensun Muite). * MANUAL.txt: remove some redundancy (#11178, Reuben Thomas). ## pandoc 3.8.1 (2025-09-29) * New output format `vimdoc` (Vim documentation format) (#11132, reptee). + [API change] Added module Text.Pandoc.Writers.Vimdoc, exporting `writeVimdoc`. * Markdown reader: + Improve superscript/subscript/inline note parsing (#8652). We do not allow inline notes to be followed by `(` or `[`. Otherwise, we parse inline notes before superscripts. Also, the sub/superscript parsers have been adjusted so that they really exclude unescaped spaces (as they did not before, when the spaces occurred in nested inlines). + Fix simple table alignment (#11136, Tuong Nguyen Manh). Take wide characters into account when determining the alignment. * LaTeX reader: + Ignore `\pandocbounded` (#11140). * XML reader: + Parse `` (#11137, massifrg). * Typst reader: + Add support for reading typst pagebreak (#11101, Raymond Berger). The pagebreak is parsed as a HorizontalRule inside a wrapper Div with class `page-break`. * Docx reader: + Handle figures in indented paragraphs (#11028). + Change default for textwidth. This should only be used if `sectPr` is not found. + Properly calculate table column widths (#9837, #11147). Previously we assumed that every table took up the full text width. Now we read the text width from the document's `sectPr`. + Use Tasty.Golden for Docx reader tests. This way we can update them with `--accept`. * RST reader: + Fix regression in simple table parsing (#11150). + SkippedContent warning if table directive contains non-tabular content. + Simple tables: leading space in a cell should not cause the contents to be parsed as a block quote (#11146). + Parse `:alt:` on figure (#11140). Also give a better default if `alt` is not specified, using the stringified caption rather than the filename. + Support col spans for simple tables (Tuong Nguyen Manh). * Markdown writer: + Improve handling of implicit figures (#11140). Allow implicit figures when alt text differs from caption (in this case, we use an image attribute to add the alt). + Use approximate pipe tables when it's the only option (#11128). If we have a table with row/colspans that can rendered as an approximate pipe table (without row/colspans), and no other table format is enabled that could render the table, we fall back to an "approximate" pipe table, with no row/colspans. * RST writer: + Ensure blank line before directives (#11162). + Add col spans for simple tables (#10127, Tuong Nguyen Manh). * OpenDocument writer: + Add missing table elements (#10002, Tuong Nguyen Manh). Add missing header rows after the first one, footer rows as well as TableBody header rows. * Docx writer: + Fix regression (from 3.8) in highlighted code (#11156). * Powerpoint writer: + Handle single column (Tuong Nguyen Manh). * Typst writer: + Fix syntax highlighting (#11171, completes #10525). Previously the native typst highlighting was always used, regardless of the setting of `--syntax-highlighting`. With this change, `--syntax-highlighting=none` and `--syntax-highlighting=` (with skylighting style) will work. * LaTeX writer: + Make beamer footnotes compatible with pauses (#5954). Previously they would appear before the content to which the note was attached, when there were pauses in a slide. + Avoid `\_` in bibliography variable (#11152). + Ensure that unlabelled tables don't increment counter (#11141). + Protect VERB in caption (#11139, Tuong Nguyen Manh). + Don't add links to TOC (#11124, Albert Krewinkel). + Fix strikeouts in beamer title (#11168, Tuong Nguyen Manh). * LaTeX template: Add `shorthands` variable for LaTeX output (#11160). If true, pandoc will allow language-specific shorthands when loading babel. (This is helpful, for example, in getting proper spacing around French punctuation.) * epub.css: Remove coloring for `a, a:visiting` (#11174). This was causing links in iOS books app not to be distinguished in any way (since underlining is not used there). * Text.Pandoc.Parsing: + [API chage] (Tuong Nguyen Manh). New functions `tableWithSpans`, `tableWithSpans'`, `toTableComponentsWithSpans` and `toTableComponentsWithSpans'` take a list of lists of (Blocks, RowSpan, ColSpan) to parse a Table with different RowSpan and ColSpan values accordingly. New helper functions `singleRowSpans` and `singleColumnSpans` help set all RowSpans or ColSpans to be 1 in case the table format only allows setting one or the other. * Text.Pandoc.Class: + Let `fetchItem` fail if the HTTP request is not successful (Albert Krewinkel). HTTP requests that don't return a 200 error code are now treated as an error. This ensures that a warning is triggered when using `--embed-resources` or `--extract-media`. * Text.Pandoc.Writers.Shared: + Add new function `removeLinks` [API change] (Albert Krewinkel). The function converts links to spans. It is used, for example, to avoid nested links. The HTML writer used to put the description of nested links into small caps, but uses a simple *span* now. * Text.Pandoc.Highlighting: export typst functions [API change]. New exported functions `formatTypstBlock`, `formatTypstInline`, `styleToTypst`. * Text.Pandoc.XML: + Add `fetchpriority` to list of HTML attributes (#11176). * Allow unicode-data 0.7. * Use released djot 0.1.2.3. Fixes a bug in which indentation was swallowed in a code block inside a blockquote. ## pandoc 3.8 (2025-09-06) * Add a new input and output format `xml`, exactly representing a Pandoc AST and isomorphic to the existing `native` and `json` formats (massifrg). XML schemas for validation can be found in `tools/pandoc-xml.*`. The format is documented in `doc/xml.md`. Pandoc now defaults to this reader and writer when the `.xml` extension is used. Two new exported modules are added [API change]: Text.Pandoc.Readers.XML, exporting `readXML`, and Text.Pandoc.Writers.XML, exporting `writeXML`. A new unexported module Text.Pandoc.XMLFormat is also added. * Add a new command line option `--syntax-highlighting`; this takes the values `none`, `default`, `idiomatic`, a style name, or a path to a theme file. It replaces the `--no-highlighting`, `--highlighting-style`, and `--listings` options, which will still work but with a deprecation warning. (Albert Krewinkel) * Create directory of output file if it doesn't exist (#11040). * Update `--version` copyright dates (#10961), and use a hardcoded string "pandoc" for the program name in `--version`, per GNU guidelines. * Add `smart_quotes` and `special_strings` extensions (Albert Krewinkel). Currently these only affect `org`. Org mode makes a distinction between smart parsing of quotes, and smart parsing of special strings like `...`. The finer grained control over these features is necessary to truthfully reproduce Emacs Org mode behavior. Special strings are enabled by default, while smart quotes are disabled. * Remove the old `compact_definition_lists` extension. This was neded to preserve backwards compatibility after pandoc 1.12 was released, but at this point we can get rid of it. * Make `-t chunkedhtml -o -` output to stdout (as documented), rather than creating a directory called `-` (#11068). * RST reader: Support multiple header rows (#10338, TuongNM). * LaTeX reader: + Support soft hyphens (Albert Krewinkel). + Parse `\minisec` as unlisted level 6 headings (#10635, Albert Krewinkel). + Support `\ifmmode` (#10915). + Change handling of math environments (#9711, #9296). Certain environments in LaTeX will trigger math mode and can't occur within math mode: e.g., `align` or `equation`. Previously we "downshifted" these, parsing an `align` environment as a Math element with `aligned`, and an `equation` environment as a regular display math element. With this shift, we put these in Math inlines but retain the original environments. texmath and MathJax both handle these environments well. * Typst reader: + Fix addition of image path prefix to use posix separator. + Properly resolve image paths in included files (#11090). + Handle inline-level show rules on block content (#11017). Typst allows things like `smallcaps` to be applied to block-level content like headings. This produces a type mismatch in pandoc, so before processing the output of typst-hs, we transform it, pulling the block-level elements outside of the inline-level elements. * Org reader: + Improve sub- and superscript parsing (Albert Krewinkel). Sub- and superscript must be preceded by a string in Org mode. Some text preceded by space or at the start of a paragraph was previously parsed incorrectly as sub- or superscript. + Allow "greater block" names to contain any non-space char (#4287, Albert Krewinkel). + Accept quoted values as argument values (#8869, Albert Krewinkel). + Recognize "fast access" characters in TODO state definitions (#10990, Ryan Gibb). + Improve org-cite parsing: Handle global prefix and suffix properly. Use all and only the styles mentioned in oc-basic.el. Allow space after `;`. * HTML reader: + Don't drop the initial newline in a `pre` element (#11064). * DocBook reader: + Add rowspan support (#10981, Sean Soon). + Be sensitive to startingnumber attribute on ordered lists (#10912). * POD reader: + Fix named entity lookup (#11015, Evan Silberman). * Man reader: + Support header and footer reader (Sean Soon). * Markdown reader: + Don't confuse a span after an author-in-text citation with a locator. E.g. `@foo [test]{.bar}`. See https://github.com/jgm/pandoc/issues/9080#issuecomment-3221689892. + Make definition lists behave like other lists (#10889). If the `four_space_rule` extension is not enabled, figure out the indentation needed for child blocks dynamically, by looking at the first nonspace content after the `:` marker. Previously the four-space rule was always obeyed. + Fix tight/loose detection for definition lists, to conform to the documentation. * ODT reader: + Support `table-header-rows` (Tuong Nguyen Manh). * Docx reader: + Don't add highlighting if highlight color is "none" (#10900). + Handle strict OpenXML as well as transitional (#7691). + Fix `stringToInteger` (#9184). It previously converted things like `11ccc` to an integer; now it requires that the whole string be parsable as an integer. + Improve handling of AlternateContent. This fixes handling of one representation of emojis in Word (#11113). * LaTeX writer: + Control figure placement with attribute (#10369, Sean Soon). If a `latex-placement` attribute is present on a figure, it will be used as the optional positioning hint in LaTeX (e.g. `ht`). With implicit figures, `latex-placement` will be added to the figure (and removed from the image) if it is present on the image. + Include cancel package only if there is math that contains `\cancel`, `\bcancel`, or `\xcancel`. + Add braces around comments in `title-meta` (#10501). This is needed to prevent PDFs from interpreting this as a sequence of titles. + Set `pdf-trailer-id` if `SOURCE_DATE_EPOCH` envvar is set (#6539, Albert Krewinkel). The `SOURCE_DATE_EPOCH` environment variable is used to trigger reproducible PDF compilation, i.e., PDFs that are identical down to the byte level for repeated runs. + Be more conservative about using `\url` (#8802). We only use it when the URL is all ASCII, since the `\url` macro causes problems when used with some non-ASCII characters. + Support soft hyphens (Albert Krewinkel). + Change handling of math environments (#9711, #9296). When certain math environments (e.g. `align`) are found in Math elements, we emit them "raw" instead of putting them in `$..$`. * Typst writer: + Check `XID_Continue` in identifiers (Tuong Nguyen Manh). + Add escapes to prevent inadvertent lists due to automatic wrapping (#10047). Also simplify existing code that was meant to do this. + Add parentheses around typst-native year-only citations (#11044). + Add native Typst support for `nocite` (#10680, Albert Krewinkel). The `nocite` metadata field can now be used to supply additional citations that don't appear in the text, just as with citeproc and LaTeX's bibtex and natbib. + Set `lang` attribute in Divs (#10965). + Rename `numbering` variable to `section-numbering` (Albert Krewinkel). This is the name expected by the default template. + Add support for custom and/or translated "Abstract" titles (Albert Krewinkel, #9724). * Org writer: + Don't wrap link descriptions (#9000). Org doesn't reliable display these as links if they have hard breaks. + Disable smart quotes by default (Albert Krewinkel). * Markdown writer: + Better handling of pandoc-generated code blocks (#10926). Omit the wrapper sourceCode divs added by pandoc around code blocks. More intelligently identify which class to use for the one class allowed in GFM code blocks. If there is a class of form `language-X`, use `X`; otherwise use the first class other than `sourceCode`. + Use fenced divs even with empty attributes (#10955, Carlos Scheidegger). Previously fenced divs were not used in this case, causing the writer to fall back to raw HTML. + Match indents in definition items (#10890, Albert Krewinkel). Previously, the first line of a definition details item always used a colon and three spaces instead of respecting the tab-stop setting, which could lead to round-tripping issues. Likewise, the indentation of continuation paragraphs in definition lists now matches the two-characters leader of the first line for Markua output. * DocBook writer: + Use `startingnumber` instead of `override` for start numbers on ordered lists (#10912). * ANSI writer: + Make `--wrap=none` work properly (#10898). * Djot writer: + Fix duplicate attributes before section headings (#10984). * Docx writer: + Ensure that documents don't start with a section separator (#10578, Albert Krewinkel). Any leading section separator is removed from the result. * HTML writer: + Unwrap "wrapper" divs (#11014). Some of the readers (e.g. djot) add "wrapper" divs to hold attributes for elements that have no slot for attributes in the pandoc AST. The HTML reader now "unwraps" these wrappers so that the attributes go on the intended elements. * Asciidoc writer: + Handle lists with sublists following continuations (#11006). These require an additional blank line in some cases. * HTML styles template: prefix default styles with informative CSS comment (Albert Krewinkel, #8819). * Org template: add `#+options` lines if necessary (Albert Krewinkel). The default template now adds `#+options` lines if non-default settings are used for the `smart_quotes` and `special_strings` extensions. * LaTeX template: + Don't emit empty `linkcolor=` in hypersetup (#11098). + Add RTL support for LuaTeX engine (Reuben Thomas). * Typst template: + Add several new variables (Christopher T. Kenny, #9956): `thanks`, `abstract-title`, `linestretch`, `mathfont`, `codefont`, `linkcolor`, `filecolor`, `citecolor`. * `reference.docx`: + Don't left-align table header row (R. N. West, #11019). + Update East Asia font theme in `styles.xml` to `minorEastAsia` (TomBen). + Update language settings in `styles.xml` for East Asia to Simplified Chinese (TomBen). * Text.Pandoc.PDF: + `makePDF`: automatically embed resources from media bag in HTML before trying to convert it with weasyprint, etc. (#11099). This will give better results when converting from formats like docx. + Use `utf8ToText` for LaTeX log messages. + Make images from MediaBag available in tmp dir for every PDF engine, not just LaTeX/ConTeXt (#10911). + Improve error readability when pdf-engine is not supported (Albert Krewinkel). Each supported engine is now printed on a line of its own. + Allow `pdflatex-dev` and `lualatex-dev` as PDF engines (#10991, Albert Krewinkel). These are the development versions of the LaTeX binaries; installable, e.g., with `tlmgr install latex-base-dev`. + Clean up `makePDF` (Albert Krewinkel). + Avoid encoding errors when reading LaTeX logs (#10954). * Text.Pandoc.Readers: + Raise unknown reader error for `ods`, `odp`, `odf`, `xls`, `xslx`, `zip` extensions. * Text.Pandoc.App: + Recognize binary signatures and fail early (Repetitive). Fail early when receiving binary input with recognized signature: zip[-based], including OpenDocument and Microsoft formats, PDF, CFBF-based (old Microsoft formats including .doc and .xls), DjVu. + Remove code duplication around version info. Text.Pandoc.App.CommandLineOptions and `pandoc-cli/src/pandoc.hs` had similar code for generating version information. To avoid duplication, we now export `versionInfo` from Text.Pandoc.App [API change]. This function has three parameters that can be filled in when it is called by `pandoc-cli`. * Text.Pandoc.Parsing: + `tableWith` and `tableWith'` now return a list of lists of Blocks, rather than a list of Blocks, for the header rows, allowing for multiple header rows [API change] (#10338, TuongNM). * Text.Pandoc.Citeproc: + Don't move footnotes around em-dashes (#11046). + Allow `--citeproc` to put the bibliography in a Div with id `refs` even when `--file-scope` is used (#11072). When `--file-scope` is used, a prefix will be added based on the filename, so the Div will end up having an identifier like `myfile.md__refs`. Previously, this prevented the bibliography from being added to the marked Div. Now pandoc will add the bibliography to any Div with the id `refs` or any id ending in `__refs`. * Text.Pandoc.Citeproc.BibTeX: Protect case in periodical titles (#11048). Thus, for example, `{npj} Quantum Information` should translate as `[npj]{.nocase} Quantum Information`. * Text.Pandoc.ImageSize: + Detect more JPEG file signatures (R. N. West and John MacFarlane, #11049). + Unpack compressed object streams in PDFs and look inside for MediaBox information (#10902). + Add Point and Pica as constructors of ImageSize [API change] (#8957). This will prevent unnecessary conversion of units. + Add Avif constructor on ImageType [API change] and support avif images (#10979). * Text.Pandoc.Writers.Shared: + Amend docs of `lookupMeta...` functions (#10634, Albert Krewinkel). * Text.Pandoc.Options: + Add and export `defaultWebTeXURL` WebTeX URL [API change] (#11029, Sean Soon). This fixes the `webtex` option when used without parameter in a defaults file. + Add type `HighlightMethod` and patterns [API Change] (Albert Krewinkel). + The `writerListings` and `writerHighlightStyle` fields of the `WriterOptions` type are replaced with `writerHighlightMethod` [API change] (Albert Krewinkel, #10525). * Text.Pandoc.Extensions: + Remove `Ext_compact_definition_lists` constructor for `Extension` [API change]. + Add `Ext_smart_quotes` and `Ext_special_strings` constructors. [API change]. * Text.Pandoc.SelfContained: + Try fetching relative resources without query or fragment if the original fetch fails. This provides a fix for #1477 in a way that doesn't raise the problems mentioned in #11021. * Text.Pandoc.Highlighting: + Export `defaultStyle` [API Change] (Albert Krewinkel). This allows to be more explicit about using a default style, and providing a single point of truth for its value. The variable is an alias for `pygments`. * Text.Pandoc.Class: + `downloadOrRead`: do not drop fragment/hash for local file paths (#11021). With the previous behavior it was impossible to have an image file containing `#` or `?`. + Export function `runSilently` [API Change] (Albert Krewinkel). The function runs an action in the PandocMonad, but returns all log messages reported by that action instead of adding them to the main log. + Make CommonState opaque. Text.Pandoc.Class now exports CommonState as an opaque object, without its fields. [API change] The internal module Text.Pandoc.Class.CommonState still exports the fields. + Text.Pandoc.Class now exports the following new functions: `getRequestHeaders`, `setRequestHeaders`, `getSourceURL`, `getTrace`. [API change] + CommonState now has a `stManager` field. This allows us to cache the HTTP client manager and reuse it for many requests, instead of creating it again (an expensive operation) for each request. This fixes a memory leak and performance issue in files with a large number of remote images (#10997). * Lua subsystem (Albert Krewinkel): + Add function `pandoc.structure.unique_identifier`. + Add functions `pandoc.text.superscript` and `subscript`. + Use proper interface functions to access the CommonState. The `PANDOC_STATE` is no longer a userdata object, but a table that behaves like the old object. Log messages in `PANDOC_STATE.log` are now in temporal order. + Add function `pandoc.path.exists`. + Add `normalize` function to *Pandoc* objects (#10356). This function performs a normalization of Pandoc documents. E.g., multiple successive spaces are collapsed, and tables are normalized such that all rows and columns contain the same number of cells. + Add more UTF-8-aware file operations to `pandoc.system`. Functions that expect UTF-8-encoded filenames should make it easier to write platform-independent scripts, as the encoding of the actual filename depends on the system. In addition, there is a new generalized method to run commands, and functions to retrieve XDG directory names. The new functions are `command`, `copy`, `read_file`, `remove`, `rename`, `times`, `write_file`, `xdg`. + Allow hslua-2.4. + Require lua-module-system 1.2.3. This provides List methods to the value returned by `pandoc.system.list_directory` (#11032). * MANUAL.txt: + Fix broken ConTeXt links (R. N. West, #11055). + Add `xml` as input/output format. + Fix minor capitalization typo (#11052, Albert Krewinkel). * `doc/lua-filters`: + Fix docs for `pandoc.Cite` (Albert Krewinkel). + Don't encourage returning tables of filters from Lua filters (R. N. West, #10995). Use the `Pandoc:walk` method instead. * doc/extras.md: Fix link to pandoc-mode (Erik Post). * doc/lua-filters.md: Add example on using pandoc.Table constructor (#10956, Sean Soon). * Update `default.csl` from new chicago-author-date.csl, which is now for the 18th edition. * Use latest releases of citeproc, typst-hs, texmath, doclayout, skylighting-core, skylighting. ## pandoc 3.7.0.2 (2025-05-28) * RST writer: + Don't emit alignment markers in grid tables (#10857). * Asciidoc writer: + Add support for sidebars (GHyman83). * LaTeX writer: + Include alt option in `\includegraphics` (#6095). * Markdown writer: + Preserve figure attributes (Nikolay Yakimov, #10867). Fixes a regression introduced by 0d2114e, which caused the Markdown writer to ignore attributes on the figure if it has class or key-value attributes set. * HTML writer: + Use the ID prefix in the ID for the footnotes section (Benjamin Esham). * Text.Pandoc.Writers.Shared: + `gridTable`: fix (3.7) regression with missing cell alignments (#10853). + `gridTable`: fix headings with colspans (#10855). If the heading contains a colspan, we still need to include information in the header line about the colspecs. + `gridTable`: fix headerless tables. The top line should encode colspan information. * Text.Pandoc.SelfContained: + Fix handling of empty script element (#10862). Previously in this case the closing tag was dropped. + Do not drop `data-` attributes in script tags (#10861). * Lua subsystem (Albert Krewinkel): + Add function `pandoc.mediabag.make_data_uri` (#10876). The function takes a MIME type and raw data from which it creates an RFC 2397 data URI. * `tools/update-lua-module-docs`: fix handling of wikilinks (Albert Krewinkel). * `doc/lua-filters.md`: add missing docs for `pandoc.Caption` (Albert Krewinkel). * Require texmath 0.12.10.3, typst 0.8.0.1 ## pandoc 3.7.0.1 (2025-05-17) * Text.Pandoc.Shared.Writer: Fix numerous problems with `gridTable` and add tests (#10848). These fixes affect the Markdown, RST, and Muse writers. * Fix context writer/template to produce tagged PDFs (#10846). As before, the `tagging` extension must be enabled. We now add the command that tells ConTeXt to start tagging. ## pandoc 3.7 (2025-05-14) * Add new command-line option `--variable-json` (#10341). This allows non-string values (booleans, lists, maps) to be given to template variables on the command line. * The `--pdf-engine` option can now take `groff` as a value. * Markdown writer: + Avoid spaces after/before open/close delimiters (#10696). E.g. instead of rendering `x space y` as `x* space *y` we render it as `x *space* y`. + Handle row/colspans in grid tables, and expand cells when it isn't possible to lay them out without breaking string of non-whitespace. + Render a figure with Para caption as implicit figure (#10755). + When falling back to a Div with class `figure` for a figure that can't be represented any other way, include a Div with class `caption` containing the caption. + Improve use of implicit figures when possible (#10758). When the alt differs from the caption, but only as regards formatting, we still use an implicit figure. + Omit initial newlines in gfm `math` blocks to avoid an ugly blank line. * Support the `four_space_rule` extension for `plain` output (#10813, Manolis Stamatogiannakis). * RST writer: + Handle row/colspans in grid tables, and expand cells when it isn't possible to lay them out without breaking string of non-whitespace. * Muse writer: + Handle row/colspans in grid tables, and expand cells when it isn't possible to lay them out without breaking string of non-whitespace. * JATS writer: + Fix escaping for writing-review-editing role (#10744). * HTML writer: + Remove trailing slash from default revealjs URL (#8749). This avoids a double slash in the URL's path component. * LaTeX writer: + Make alignment work within `multirow` in tables (#10772). * Typst writer: + Support `mark` class on spans (#10747). + Add equation label if math contains `\label{..}` (#10805). * Roff format writers (man, ms): + Use the most compatible form for roff escapes (#10716). For example, `\(xy` instead of `\[xy]`. This was the original AT&T troff form and is the most widely supported. The bracketed form causes problem for some tools, e.g. `makewhatis` on macOS. And emit `e` followed by an escape for a unicode combining accent rather than the form `\[e aa]`, which works for groff but not e.g. on macOS's man. This change affects Text.Pandoc.RoffChar, Text.Pandoc.Writers.Roff, and the Man and Ms writers. * Docx writer: + Ensure that figures and tables with custom styles are not dropped (#10705). + Preserve Relationships for images from reference docx (#10759). This should allow one to include an image in a reference.docx and reference it in an openxml template. + Don't renumber rels (#10769). We used to renumber the Relationships so they didn't conflict with the set of fixed Relationships we imposed. We are now preserving the ids from the reference doc's document.xml.refs, so we shouldn't renumber them or references introduced by the user (e.g. in a template) will fail. * Ms writer: + Improve PDF TOC labels. We now use the plain writer to render these, so that Greek characters etc. will show up properly. + When no `pdf-engine` variable is specified, do not use the `.pdfhref` macros at all (#10738). This gives better results for links in formats other than PDF, since the link text would simply disappear if it exists only in a `.pdfhref` macro. When a PDF engine is specified, escape the argument of `.pdfhref O` in a way that is appropriate. * OpenDocument writer: + Fix character styles in footnotes (#10791). Character styles governing the position of the footnote reference should not be imposed on the footnote text. * Powerpoint writer: + Use reference-doc font for captions (#9896, R. N. West). * DocBook writer: + Use literallayout element for LineBlock (#10825). * MediaWiki reader/writer: + Allow definition on same line as term (#10708). * LaTeX reader: + Skip at most one argument to LaTeX tabular newline (#7512, Evan Silberman). + Disable ligatures inside `\texttt` (#10781). + Support more symbol commands (#10782). * Commonmark Reader: + Handle GFM math irregularity with braces (#10631). In GFM, you need to use `\\{` rather than `\{` for a literal brace. * DocBook reader: + Improve handling of literallayout (#10825). This is now only made a CodeBlock when there is a `monospaced` class. Otherwise it is made a LineBlock. * Org reader: + Add AVIF to Org Reader image extensions (#10736, Christian Christiansen). + Don't include newlines in inine code/verbatim (#10730). Convert newlines to spaces as we do in other formats. + Change handling of inline TeX (#10836). Previously inline TeX was handled in a way that was different from org's own export, and that could lead to information loss. This was particularly noticeable for inline math environments such as `equation`. Previously, an `equation` environment starting at the beginning of a line would create a raw block, splitting up the paragraph containing it (see #10836). On the other hand, an `equation` environment not at the beginning of a line would be turned into regular inline elements representing the math. (This would cause the equation number to go missing and in some cases degrade the math formatting.) Now, we parse all of these as raw "latex" inlines, which will be omitted when converting to formats other than LaTeX (and other formats like pandoc's Markdown that allow raw LaTex). * Beamer template: fix regression in 3.6.4, reverting the omission of `\date` when the document does not have a date. By default, beamer will display a date when no `\date` is present in the title block, so this was an unintended behavior change. The reverted change was motivated by the desire to include a custom `\date` in the frontmatter via header-includes. This can be achieved more simply by simply setting the `date` variable. In markdown you can even use `date` in metadata and put some raw LaTeX there. * Ms template: + Use T rather than P as default font family (#10738). + Put PDF-specific things under a conditional. Don't include them if `pdf-engine` isn't set. * Upgrade reveal.js URL to v5 (#10740, Kolen Cheung). v4 is no longer available on unpkg.com. * Text.Pandoc.PDF: Allow `groff` to be used as `--pdf-engine` with `ms` (#10738). When `groff` is used as a PDF engine, the `groff` extension to `ms` is automatically enabled. Limitations: - `groff` currently produces larger PDFs than `pdfroff`. - With `groff`, a table of contents produced with `--table-of-contents/--toc` will always be placed at the end of the document. - Certain characters (e.g. Greek characters) may be dropped in the PDF outline. * Text.Pandoc.Writers.Shared: + Export `delimited` [API change]. + New version of `gridTable` (#6344) [API change]. This handles row and colspans. It also ensures that cells won't wrap text in places where it wouldn't normally wrap, even if this means making the cells wider than requested by the colspec (#9001, #7641). Because the parameters are different, this is a breaking API change. * Text.Pandoc.App: set `pdf-engine` variable. If `--pdf-engine` is specified or if a PDF is being produced, we set the `pdf-engine` variable. This allows writers and templates to behave differently depending on the PDF engine. * Text.Pandoc.Class and Text.Pandoc.URI: + Fix parsing of base64 data URIs to allow URI escapes and whitespace (which will be ignored) (#10704). + Handle percent encoding in `pBase64URI` instead of unescaping later, for efficiency (#10704). * Text.Pandoc.Citeproc.BibTeX: + Recognize `en` as a `langid` in biblatex bibliographies (#10764). * Text.Pandoc.MIME: + Add mime type and extension for `avif` (#10704). + Handle `apng`, `avif`, `jxl` (#10704). * Text.Pandoc.Readers.LaTeX.Math: export `inlineEnvironmentNames`. Internal module, not a change to the public API. * `reference.docx` (Andrew Dunning): + Remove extra spaces around text placeholders. + Add footnote block text sample. * Text.Pandoc.Class.Sandbox: + Add `sandboxWithFileTree` function [API change] (Albert Krewinkel). * Lua subsystem (Albert Krewinkel): + pandoc-lua-engine: add all test files to the cabal file. + Allow `pandoc.read` to be called in "sandbox" mode for added security (#10831). Readers running in a sandbox will not be able to access the network or file system. The sandbox is enabled if the fourth parameter is a list of files or filename/content pairs. The files are read and then made available in the sandbox via en ersatz file system. * Makefile: + Add target `release-checkist`. + Install @daisy/ace from npm if not present. + Use pandoc lua instead of lua. + Fix typo in `latex-package-dependencies` target. + Use `jq` instead of `json_reformat` in `validate-docx-golden-tests2`. * NiX infrastructure: new working `flake.nix` and simpler `shell.nix`. Removed old `default.nix`. * Require random >= 1.3 and use `splitGen`. `split` has been deprecated. * Use citeproc-0.9. Bump citeproc bounds for pandoc, pandoc-lua-engine. * Use texmath-0.12.10.1. * Use released typst 0.8 (partially supporting typst 0.13). * Use citeproc 0.9.0.1. * MANUAL.txt: + Fix default URL for revealjs. + Add note that `alerts` extension only works with commonmark (#9716). + Remove "Body Text Char" from list of Word styles that can be customized using a reference.docx (#10646). This doesn't seem to be present in pandoc-generated docx files, nor is it a Word default. + For pandoc lua, add note about the environment. + Improve documentation of `--variable` option. * `doc/typst-property-output.md`: Mention that `typst:no-figure` is a class, not an attribute (#10826, Niklas Eicker). * Change RELEASE-CHECKLIST to RELEASE-CHECKLIST-TEMPLATE.org. Use org-babel to automate many of the steps of the release. * INSTALL.md: update MacPorts information (#10719, Mohamed Akram). * COPYRIGHT: fix link to source code. * CONTRIBUTING.md: Fix link to discussion forum. (#10834, R. N. West). ## pandoc 3.6.4 (2025-03-16) * Disable `citations` extension in writers if `--citeproc` is used (#10662). Otherwise we get undesirable results, as the format's native citation mechanism is used instead of (or in addition to) the citeproc-generated citations. * Markdown reader: + Allow line break between URL and title of link (#10621). + Give better position information when YAML metadata parsing fails with a YAML exception (#10231). + Fixed `escapedChar'` parser (#10672). It should not accept escaped newlines. + Remove some misguided list fanciness (#9865, #7778, cf. #5628). Previously we tried to handle things like commented out list items: ``` - one - three ``` and also things like: ``` - one `and - two` and ``` But the code we added to handle these cases caused problems with other, more straightforward things, like: ```` - one - ``` code ``` - three ```` So we are rolling back all the fanciness, so that the markdown parser now behaves more like the commonmark parser, in which indicators of block-level structure always take priority over indicators of inline structure. * HTML reader: + Skip MathJaX-introduced cruft (#10673). + Ignore style tags in the body (#10643). * LaTeX reader: + Better handle comments/whitespace in option lists and includes (#10659). + Support `\newline`, `\linebreak`. * Docx reader/writer: + Revert commit adding row heads (cbe67b9602a736976ef6921aefbbc60d51c6755a) (#10627). Word sets `w:firstColumn="1"` by default for tables. You have to find the Table Design tab and explicitly uncheck "First Column" to make this go away. In most cases, I don't think writers intend to designate the first column as a row head, so this commit is going to produce unexpected results. In addition, because of the table normalization done by pandoc-type's `tableWith`, any table containing a colspanned cell in the left-hand column will get broken if the first column is designated a row head. For these reasons it seems best to revert this change, which was made in response to #9495. * LaTeX writer and template: + Remove `selnolig-langs` (#9863). We now specify the language as a global option again, so we no longer need to specify it when invoking selnolig. + Use babel options `shorthands=off` (#6817). + Use `*` for multirow width when no colwidth specified (#10685). Otherwise the multirow will be excessively wide. + Protect `\phantomsection` (#10688, etclub). * Markdown writer: + Omit extra space after bullets (#7172). Those who want the old behavior can obtain it by using `-t markdown+four_space_rule`. + Treat `Emph [Emph ils]]` as `ils` (#10642). Otherwise we get `**content**` which means strong emphasis. * EPUB writer: + Use a nonbreaking space after section number in nav.xhtml. This seems to be required for iOS books app to display the space. * Typst writer: + Better heuristics for escaping potential list markers (#10650). + Ensure that `citation-style` works as well as `csl` (#10661). * Powerpoint writer: + Avoid extra blank lines before author when there is no subtitle (#10619). * JATS template: + Fix typo in author prefix in article.jats_publishing template (#10622, Tiago-Manzato). * Text.Pandoc.Parsing: + Smart quote parsing: ignore curly quotes (#10610). Previously we tried to match curly quotes as well as straight quotes, producing Quoted inlines. But it seems better just to assume that those who use curly quotes want them passed through verbatim. This also fixes an (unintended) bug whereby curly single left quotes would sometimes be changed to single right quotes. * Text.Pandoc.Shared: + `makeSections`: put some attributes on section element only. Certain `role` and `epub:type` attributes should only be on the section (and indeed, many `role`s give a validation error if left on the heading element). * Text.Pandoc.Logging: + Change NoTitleElement from WARNING to INFO (#10671). Users commonly complain about the warning when producing HTML documents without an explicit title. It seems that an info message is more appropriate, since pandoc's default here (using the input's base name) ensures compliance with the standard and many users are happy with that default. Those who want to make sure the message is seen can use `--verbose`. * Beamer template: only emit `\date` if set (#10687, josch). * Fix invalid OOXML in definition_list.docx test (#10394). * MANUAL.txt: + Correct typo: 'date' for doubled 'title' (#10654, Olivier Dossmann). + Add note about `template` variable for typst. + Change maxwidth default in MANUAL.txt (#10683). + Improve EPUB metadata documentation. + In Security section, alert readers to a threat relating to iframe in HTML, and add LaTeX, Typst to the list of formats that have an `include` (#10682). * `doc/lua-filters.md`: Add missing html_math_method 'katex' (R. N. West). * Use texmath 0.12.9. * Use typst 0.7. Fixes an issue with package loading, a regression in pandoc 3.6.3. ## pandoc 3.6.3 (2025-02-09) * Track wikilinks with a class instead of a title (Evan Silberman). Previously wikilinks were distinguished by giving them the `title` `wikilink`. Now that we have link attributes, it makes more sense to give them the `class` `wikilink`. This change affects all readers and writers that support wikilinks. * DocBook reader: + Handle title inside `orderedlist` (#10594). Also some other elements that allow title: `blockquote`, `calloutlist`, etc. + Better handle `informalequation` (#10592, tombolano). Include `id` attribute. + Better handle `formalpara`, `example`, and `sidebar` (#8666, tombolano). Include identifiers and titles in each case. * Markdown reader: + Simplify and fix normal citation parsing (#10584). This fixes a bug that causes some normal citations to be parsed as bracketed regular citations. * ODT reader: + Create Figure elements for images that are figures (#10567). + Avoid producing spurious blockquotes in list items (#9505). + Fix unwanted block quotes (#10575). Previously the reader created block quotes whenever a paragraph was marked indented (even though this just affects the first line). With this change we still generate block quotes for content that has an altered left margin, but not for indented paragraphs. * Docx reader: + Do not issue warning for comments with `+styles` (#10571, Stephen Reindl). * LaTeX reader: + Test \{,re}newcommand arguments (#4470, Evan Silberman). * Pod reader: + Consume blanks after =encoding in pod reader (#10537, Evan Silberman). * JATS writer: + Add CRediT roles to JATS (Charles Tapley Hoyt and Jez Cope, #10152). Enable annotating author roles using the Contribution Role Taxonomy (CRediT) and export this information in conformant JATS. * LaTeX writer/templates: + Improve babel support (#8283). Previously we used the `.ini` files for every language, but for European languages these tend to provide inferior results to the `.ldf` files used by classic Babel. Currently Babel documentation recommends using the classic system for European languages written in Latin and Cyrillic scripts and Vietnamese. So the LaTeX writer and template now follow this guidance. Main languages in the list of languages with good "classic" support are added to global documentclass options and will be automatically handled by Babel using the `.ldf` files. If the main language is not in this list, the `babeloptions` variable will be set to `provide=*`, which will cause support to be loaded from the `.ini` file rather than an `.ldf`. So, for example, setting `-V babeloptions=''` with a polytonic Greek document will cause the `.ldf` support to be used instead of the `.ini`. The default setting of this variable can be overwritten, but in most cases the default should give good results. + Allow `csquotesoptions` to be specified. + Fix indentation bugs in `font-settings.latex`. * Docx writer: + Repeat reference doc's `sectPr` for each new section (#10577). Previously we were only carrying over the reference doc's `sectPr` at the end of the document, so it wouldn't affect the intermediate sections that are now added if `--top-level-division` is `chapter` or `part`. This could lead to bad results (e.g. page numbering starting only on the last chapter). + Create section divisions with `--top-level-division=part` (#10576). + Improve title style in reference.docx; base Author and Date on Title; remove condensed spacing (Andrew Dunning, #10581). * Typst writer: + Brace tables with `typst:no-figure` and `typst:text` attributes (#10563, Gordon Woodhull). * Ms writer: + Fix escaping of `-` (#10536). `-` should now be escaped in man output but not in ms output (where `\-` is a unicode minus sign). * HTML styles: fix style of `hr` so it works when printed (#10535, Hendrik Erz). Previously `background-color` was used to style the hr, but this gets ignored when printing. This commit uses `border-top` instead. * Text.Pandoc.Shared: + Handle `` as a span-like inline in `htmlSpanLikeElements` (#5793, Evan Silberman). * Text.Pandoc.MediaBag: + Prefer MIME type when determining extensions for MediaBag items (#10557, Max Heller). This should give different results for remote images that are served at URLs that do not contain misleading extensions (e.g. `shields.io`). * Text.Pandoc.Citeproc: + Fix moving punctuation before citation notes. This previously worked with regular citations, but not author-in-text citations. Now it works with both. * `doc/lua-filters.md`: + Correct luacheck URL (#10589, R. N. West). + Add static analysis paragraph to debugging section (#10568, R. N. West). + Add note about extensions handling in `read` and `write` (Albert Krewinkel). * `doc/extras.md`: + Add entry for pandoc-subfigs (R. N. West). + Update diagram Lua filter URL and description (R. N. West). * MANUAL.txt: + Add note on using typst to produce pdf/a-2b. + Document top-level-division functionality with Docx (#10579, Andrew Dunning). * Raise xml-conduit upper bound. * Depend on latest commonmark-pandoc, commonmark-extensions, citeproc, typst. * Makefile: make `make binpath` quiet. ## pandoc 3.6.2 (2025-01-12) * New input format: `pod` (Evan Silberman). Pod ("Plain old documentation") is a markup languaged used principally to document Perl modules and programs. * New reader module Text.Pandoc.Readers.Pod, exporting `readPod` [API change]. * Docx reader: + Support row heads in tables (#9495). Reader: When `w:tblLook` has `w:firstColumn` set (or an equivalent bit mask), we set row heads = 1 in the AST. + Read table styles as custom styles when `styles` extension is enabled (#9603). * HTML reader: + Add size information for font awesome SVG icons (#10134). If the icon has class `fa-fw` or `fa-w16` or `fa-w14`, we add a width attribute to prevent the icon from appearing full-width in PDF or docx output. * Djot reader: + Use a Span with class "mark" rather than "highlighted" for highlighted text, for consistency with the other pandoc readers and writers. * mandoc reader: + Add mdoc St for C23 (Evan Silberman). * RST reader: + Fix handling of underscores (#10497). Fixes a a regression introduced in 3.6. * Docx writer: + Support row heads in tables (#9495). Writer: set `w:firstColumn` in `w:tblLook` when there are row heads. (Word only allows one, so this is triggered by any number of row heads > 0.) * Djot writer: + Render a Span with sole class "mark" as highlighted text. * Asciidoc writer: + Don't emit the class in a span if it's just "mark" (#10511). The "mark" class is used for highlighting, and Asciidoc treats bare `#...#` with no attributes as highlighted text. + Improve escaping (#10385, #2337, #6424). * EPUB v2 writer: + Fix cover image (#10505). This is a regression introduced in 3.6. * Typst writer: + Fix handling of pixel image dimensions (#9945). These are now converted to inches as in the LaTeX writer. * Improve error message given when users specify `asciidoc` as input format (#8416, Santiago Zarate). * Allow random 1.3. * Use texmath 0.12.8.13 (typst improvements). * `lua-filters.md`: document `system.os` return values (#10523). * `MANUAL.txt`: + Improve manual's coverage of custom styles. + Replace LibreOffice PDF documentation link to latest so it links to the latest major release rather than a specific major release (which there are two of every year) (Stéphane Guillou). + Improve links and descriptions for `odt`, `opendocument` (#10518). ## pandoc 3.6.1 (2024-12-23) * Allow YAML bibliographies to be arrays of references (#10452). Previously, they had to be YAML objects with a `references` key. * Change `--template` to allow use of extensionless templates (#5270). The intent is to allow bash process substitution: e.g., `--template <(echo "foo")`. Previously pandoc *always* added an extension based on the output format, which caused problems with the absolute filenames used by bash process substitution (e.g. `/dev/fd/11`). Now, if the template has no extension, pandoc will first try to find it without the extension, and then add the extension if it can't be found. So, in general, extensionless templates can now be used. But this has been implemented in a way that should not cause problems for existing uses, unless you are using a template `NAME.FORMAT` but happen to have an extensionless file `NAME` in the template search path. * Allow `--shift-heading-level-by=-1` to work in djot in the same way it works for other formats (with the top-level heading being promoted to metadata title) (#10459). This needed special treatment because of the way djot surrounds sections with Divs. * RST reader: + Handle explicit reference links (#10484, Evan Silberman). This case was missed when changing the reference link strategy for RST to allow a single pass. (It is a regression in pandoc 3.6.) * Markdown reader: + Use T.P.URI's `pBase64DataURI` in parsing data URIs (#10075, Evan Silberman and John MacFarlane). + More efficient base64 data URI parsing (#10075, Evan Silberman and John MacFarlane). This should yield dramatic performance improvements for markdown documents containing large data URIs in images. * HTML reader: + Don't canonicalize data: URIs (#10075). It can be very expensive to call network-uri's URI parser on these. * LaTeX reader: + Handle `figure*` environment as a figure (#10472). * MediaWiki reader: + Allow empty quoted attributes (#10490). + Allow cells starting with `+` (#10491). * Textile reader: + Improve parsing of spans (#9878). The span needs to be separated from its surroundings by spaces. Also, a span can have attributes, which we now attach. + Inline constructors shouldn't trigger if closer is preceded by whitespace (#10414). * Docx writer: + Put chapters in separate sections, and restart footnotes by section by default (#2773). The main effect of this change is that when `--top-level-division=chapter` is used, chapters will start on a new page and footnote numbering will restart for each chapter. Both of these defaults can be overridden in the reference.docx. + Use styleIds not styleNames for Title, Subtitle, etc. (#10282). This fixes a regression introduced in pandoc 3.5. This change affects the default openxml template as well as the OpenXML writer. * Markdown writer: + Avoid collapsing of initial/final newline in markdown raw blocks. This makes it easy to write a filter that adds extra blank lines before certain elements (#10477). * Mediawiki writer: + Escape line-initial characters that would otherwise be interpreted as list starts (#9700). * LaTeX writer: + Properly handle boolean value for `csquotes` variable (#10403). + Use displayquote for block quotes with `csquotes` (#10456). * HTML writer: + Avoid calling parseURIString for data URIs (#10075). This was done to determine the "media category," but we can get that directly from the mime component of data: URIs. * Typst writer: + Properly handle data URIs in images (#10460). * LaTeX/Beamer templates: + Fix default.beamer `nocite` location (Thomas Hodgson). It must be inside a frame or it is ignored (#10465). + Move nocites from LaTeX preamble to body (#10461, Thomas Hodgson). Putting `\nocite` in the preamble works only with biblatex. * Text.Pandoc.Parsing: + Correct example in comment on `charsInBalanced` (Evan Silberman). * Text.Pandoc.Error: + Mention typst in rendering `PandocUnknownWriterError` for `pdf` (Evan Silberman). * Text.Pandoc.MediaBag: + `insertMedia`: fast path for data URIs. Avoid the slow URI parser from network-uri on large data URIs (#10075). * Text.Pandoc.Class: + Add shortcut for base64 data URIs in `downloadOrRead` (#10075). This avoids calling the slow URI parser from network-uri on data URIs, instead calling our own parser. * Text.Pandoc.MIME: + Fix `extensionFromMimeType`. We had a few special cases encoded, but as previously written they wouldn't work properly with modifiers like `;charset=utf-8`. * Text.Pandoc.URI: + Export `pBase64DataURI`. Modify `isURI` to use this and avoid calling network-uri's inefficient `parseURI` for data URIs. * Text.Pandoc.PDF: + Fix temp file extension in `toPdfViaTempFile` (#10468). This fixes a regression in pandoc 3.6, which changed the extension from `html` to `source`. Apparently `wkhtmltopdf` needs it to be `.html`. So now we have added a parameter to `toPdfViaTempFile` that allows the extension to be specified in a way that is appropriate to the PDF engine used. * Lua (Albert Krewinkel): + Support more elements as input to `pandoc.utils.stringify` (#10450). Elements of type Caption, Cell, TableHead, and TableFoot can now be stringified. + Add `Caption` constructor to `pandoc` module. * Miscellaneous code quality improvements (Joseph C. Sible). * Depend on citeproc 0.8.1.2, skylighting and skylighting-core 0.14.5. * `doc/lua-filters.md: Fix links to constructors (Albert Krewinkel). ## pandoc 3.6 (2024-12-07) * Add `mdoc` as input format (Evan Silberman). This change introduces a reader for mdoc, a roff-derived semantic markup language for manual pages. This reader has been developed almost exclusively against mandoc's documentation and implementation of mdoc as a reference, and the real-world manual pages tested against are those from the OpenBSD base system. Of ~3500 manuals in mdoc format shipped with a fresh OpenBSD install, 17 cause the mdoc reader to exit with a parse error. Any further chasing of edge cases is deferred to future work. * New module: Text.Pandoc.Readers.Mdoc, exporting `readMdoc` [API change]. * Issue warnings for duplicate YAML metadata keys (#10312). * Ensure that `--sandbox` affects `--embed-resources`. Previously it did not (contrary to what was implied by the manual), which means that an image with URL `/etc/passwd` would leak an encoded version of that file to HTML output with `--self-contained` or `--embed-resources`, even if `--sandbox` was used. Thanks to Samuel Mortenson for pointing out the issue. * Text.Pandoc.App.OutputSettings: add `sandbox'` function. This computes the sandboxed files from Opt and avoids code repetition. * Docx reader: + Parse index references as empty spans with attributes (#10171). Attributes included are `entry`, and optionally `bold`, `italic`, `yomi`, `see`. + Don't create multiple paragraphs for title or subtitle (#10359). If there are multiple paragraphs with Title or Subtitle style, use only the first for metadata. + Handle case where Zotero `itemData` has different id from the `citationItem` id. In this case we use the `citationItemId` in the bibliography as well, overriding the `referenceId` in the itemData (#10366). * LaTeX reader: + Put parsed minipage in specially marked Div (#10266). * HTML reader: + Parse footnotes defined by dpub-aria roles (#5294). * MediaWiki reader: + Fix indented tables with caption (#10390). + Fix parsing of col/rowspan (#6992). * Typst reader: + Avoid generating empty paragraphs. + Support `underparen`, `overparen`. + Fix `#quote` attribution. If attribution is not present, don't print the `--` (#10320). + Fix typo in unicode code point for em dash (see #10320). * Commonmark reader: + `implicit_figures` should check for empty caption and not produce an implicit figure in this case (#10429). * RST reader: + Use a new one-pass parsing strategy. Instead of having an initial pass where we collect reference definitions, we create links with target `##SUBST##something` or `##REF##something` or `##NOTE##something`, and resolve these in a pass over the parsed AST. This allows us to handle link references that are not at the top level (#10281). + Ignore newlines in URL in explicit link (#10279). + Handle block level substitutions. + Support `:file:` on raw directive (#8584). + Implement option lists (#10318). + Avoid putting metadata in Para (#7766). Create MetaInlines when possible, just as with markdown input. MetaBlocks is still used when there are multiple paragraphs or non-paragraph content. This change also affects field lists. + Fix linked substitutions (#6588). E.g. `|Python|_`. + Support inline anchors (#9196). + Explicit links define references (#5081). For example, ``Go to `g`_ `g `_.`` should produce two links to www.example.com. * EPUB writer: + Use standardized filename for cover image instead of the original name (#10404). This avoids problems with e.g. filenames containing spaces. * Markdown writer: + Issue INFO warning when not rendering table, e.g., when `raw_html` is disabled and the table can't be fit into a supported markdown table format (#10407). + Respect empty LineBlock lines in `plain` output (Evan Silberman). The plain writer behaved as a markdown variant with `Ext_line_blocks` turned off, and so empty lines in a line block would get eliminated. * LaTeX writer: Ensure that beamer footnotes go on frame, not column (#5769). * HTML writer: + Unwrap empty incremental divs (#10328, Albert Krewinkel). Divs are unwrapped if the only purpose of the div seems to be to control whether lists are presented incrementally on slides. * Typst writer: + Make template sensitive to a `page-numbering` variable (#10370). This can be set to an empty string (or, in metadata, to false) for no page numbers. + Make `smart` extension work (#10271). If `smart` is not enabled, a command in the default template will disable smartquote substitutions. When `smart` is enabled, render curly apostrophes as straight and escape straight apostrophes. When `smart` is disabled, render curly apostrophes as curly and don't escape straight apostrophes. Similarly for quotes, em and en dashes. This should give more idiomatic typst output, with fewer unnecessary escapes. * ANSI writer: + Respect empty LineBlock lines (Evan Silberman). * JATS writer: + Correct spelling of suppress attribute (#10350, Andreas Deininger). * Typst template: + Remove `definitions.typst` partial. + Remove unnecessary definition of `endnote`. + Incorporate the one remaining definition into `default.typst`. + Use typst 0.12 code for two column layout (#10294, Luis Rivera). + Note: the new templates presuppose typst 0.12; if you try to use an earlier version of typst, an error will be raised. * LaTeX/Beamer template: + Split `fonts.latex` partial into two parts: `fonts.latex` and `font-settings.latex`. + In beamer template, load beamer theme between `fonts.latex` and `font-settings.latex`. This allows a theme (such as metropolis) to set its own default font, while still allowing the user to override it. This fixes a regression in pandoc 3.5 (#10297). + Note: Users who have custom templates based on pandoc 3.5 templates will need to add `font-settings.latex()` after `fonts.latex()` in the latex template. In a beamer template, the beamer theme-setting code needs to be moved between these two partials. * ConTeXt template: Ensure that font names don't wrap (#10305). * `epub.css`: remove background-color (#10264, Suraj Patil). With this greyish background color, epubs look bad on a Kindle (#10263). * Text.Pandoc.ImageSize: add WebP support (Evan Silberman, #10397). Add `Webp` constructor on ImageType [API change]. * Text.Pandoc.Readers.Roff and a new unexported module Text.Pandoc.Readers.Roff.Escape: parameterize Roff escaping (Evan Silberman) [API change]. This allows code to be reused between the mdoc and man readers, despite the differing Token types. * Text.Pandoc.PDF: + PDF via LaTeX: always do max runs if `toc` is present (#10308). The old method (checking to see if toc hash had changed) is not completely reliable. + Use `.source` extension, not `.html`, in `toPdfViaTempFile` (#10314). * Text.Pandoc.Logging: add `YamlWarning` constructor to `LogMessage` [API change] (#10312). * Text.Pandoc.Format: remove duplicate typst entry (#10388, Caleb Mclennan). * Fix a typo in the `ua.yaml` localization for 'See' (Jens). * Lua subsystem (Albert Krewinkel): + Remove prefixes from Lua type names (#8574). Lua type names were inconsistent with regard to the use of prefixes; all prefixes are removed now, and Lua types now have the same name as the Haskell types. The use of app-specific prefixes is suggested by the Lua manual to avoid collisions. However, this shouldn't be a problem with pandoc, as it cannot be used as a Lua package. * doc/libraries.md: Add newly developed Haskell packages. Sort list alphabetically (Albert Krewinkel). * doc/lua-filters.md: document `pandoc.List:iter` method (Albert Krewinkel). List objects have a new function `iter` that returns an iterator function that returns the next list item on each call. * MANUAL.txt: + Clarify what the example of YAML EPUB metadata shows (#10405). + Fix typo in template syntax (#10265, Pascal Wagler). + Update manual with information on openxml template (#10273). + Clarify that `--variable` can only assign string values (#10298). * Fix comments in TEI writer referring to DocBook (#10430, Evan Silberman). * Fix several typos in documentation (#10349, Andreas Deininger). * Allow Diff 1.0. * Add font-settings.latex partial to pandoc.cabal (#10379). * Bump upper bound for data-default. * Use latest typst, texmath, pandoc-lua-marshal, commonmark-pandoc, commonmark-extensions, skylighting, skylighting-format-blaze-html. ## pandoc 3.5 (2024-10-04) * Add command-line options `--list-of-figures/--lof` and `--list-of-tables/--lot` (#10029, Akash Patel). Only docx, latex, and context are affected by these options currently. Setting the `lof` and `lot` variables will also work for the formats that are currently supported. * Defaults files: interpolation of environment variables now works for `to` and `from` fields (#8024). This is needed because these files can contain paths of custom readers/writers. * Docx reader: + Reset lists after headers in same list `numId` (#10258). To accomplish this, we add a Heading constructor to BodyPart and include on it all the information list items have. * DocBook reader: + Parse id, class, and tabstyle on tables (#10181, Erik Rask). Add parsing of id (xml:id), class, and tabstyle XML attributes for table and informaltable in the DocBook reader. The tabstyle value is put in the 'custom-style' attribute. * Dokuwiki reader: + Be more forgiving about misaligned lists, like dokuwiki itself (#8863). + Improve blockquote parsing in dokuwiki. Allow for quoted code blocks. + Enable smart extension. + Properly parse `--` and `---` as dashes. + Fix block quote behavior (#6461). Blockquotes are not really block containers in DokuWiki; the lines are interpreted literally (so, e.g., you can't start a list), and line breaks are added at the ends. * EPUB reader: + Fix links to other files in the EPUB, making them internal links to a fragment derived from the filename (#10207). There was already code to handle links like `#foo`, but not to handle links like `ch0001.html#foo`. * LaTeX reader: + Add em, ex, px, mu to list of units for dimension args (#10212). * ANSI writer: + Fix subscripts (Evan Silberman). * DokuWiki writer: + Don't emit `` tags (#7413). The use of these tags is now strongly discouraged for security reasons, and will be removed. We previously used them as a fallback for lists that could not be represented using DokuWiki syntax, e.g. ordered lists with fancy numbers or lists with multiple blocks in their items. We also used them for block quotes with multiple blocks as their contents. We now use the `` syntax (from the optional WRAP plugin) to handle lists with multiple blocks as their contents. A new method of handling block quotes with complex contents has the side benefit of also handling nested block quotes, which weren't supported before. `` and `` tags are only for raw HTML blocks and inlines, and only if the `raw_html` extension is enabled. (It is now a valid extension for `dokuwiki`, though off by default.) * Docx writer: + Support `--list-of-figures` and `--list-of-tables` (or `lof` and `lot` variables) (Akash Patel). * HTML writer: + Don't emit missing title/lang warnings if templates does not contain the `pagetitle` or `lang` variables respectively (#9370). * LaTeX writer: + Better fix for lists in definition lists (#10241). In commit a26ec96d89ccf532f7bca7591c96ba30d8544e4a we added an empty `\item[]` to the beginning of a list that occurs first in a definition list, to avoid having one item on the line with the label. This gave bad results in some cases (#10241) and there is a more idiomatic solution anyway: using `\hfill`. + Avoid error on `refs` div with empty citations (#10185). If there are no citations, don't emit an empty CSLReferences environment. * RST writer: + Change bullet list hang from 3 to 2. This accords with the style in the RST reference docs. + Handle cases where indented context starts with block quote (#10236). In these cases we emit an empty comment to fix the point from which indentation is measured; otherwise the block quote is not parsed as a block quote. This affects list items and admonitions. + Don't enclose the list table in a `.. table::`; this leads to doubled captions (#10226). + Fix alignment of list table items corresponding to cells (#10227). * JATS template: + Support `floats-group` (Albert Krewinkel, see #10196). The content of the `floats-group` variable is now rendered in a `` element when using the *publishing* or *archiving* tag sets. * LaTeX and Beamer templates: + Split old default.latex into two templates, `default.latex` and `default.beamer`, factoring common parts into partials: `fonts.latex`, `common.latex`, `passoptions.latex`, `hypersetup.latex`, `after-header-includes.latex`. + Make `default.beamer` the default template for beamer. + Add `shorttitle`, `shortsubtitle`, `shortauthor`, `shortinstitute`, `shortdate` variables to beamer template (#10248, Thomas Hodgson). + Make `--number-sections` work with beamer (#12045, Thomas Hodgson). + Support a list of images for `titlegraphic` in beamer template (#10246, Thomas Hodgson). Title graphic options will be applied to each title graphic. Images will be separated by `\enspace`. + Beamer theme options (#10243) + Add theme options to beamer template: `colorthemeoptions`, `fontthemeoptions`, `innerthemeoptions`, `outerthemeoptions` (#10243, Thomas Hodgson). + Don't load amsmath, amssym in beamer template. These are loaded by beamer automatically. * Text.Pandoc.SelfContained: + Improve handling of links to remote CSS (#10261). * Text.Pandoc.Class: + Allow extracting `data:` URIs even in PandocPure (`--sandbox`) (#10249). + Export `extractURIData` [API change]. * Text.Pandoc.PDF: + Read `.toc` and `.log` files from output directory (#10186). When this is different from the input directory, this is where `.toc` and `.log` files are written. * Text.Pandoc.Shared: + Modify `addPandocAttributes` for changes in commonmark-pandoc. The new commonmark-pandoc version automatically adds the attribute `wrapper="1"` on all Divs and Spans that are introduced just as containers for attributes that belong properly to their contents. So we don't need to add the attribute here. This gives much better results in some cases. Previously the wrapper attribute was being added even for explicit Divs and Spans in djot, but it is not needed in these cases. * Text.Pandoc.Options: + Add `writerListOfFigures` and `writerListOfTables` fields to `WriterOptions` (#8245, Akash Patel). [API change] * Text.Pandoc.App: + Add `optListOfFigures` and `optListOfTables` to `Opt` (#8245) [API change]. * Lua subsystem (Albert Krewinkel): + Update List module (#9835). The module now comes with a method `:at(index[, def])` that allows to access indices, accepts negative indices to count from the end, and will return the `def` value as a default if the list has no item at the given position. Furthermore, the list constructor `pandoc.List` now accepts iterators. E.g., `pandoc.List(text:gmatch '%S+')` returns the list of words in `text`. + Support character styling via `pandoc.layout`. The `Doc` values produced and handled by the `pandoc.layout` module can now be styled using `bold`, `italic`, `underlined`, or `strikeout`. The style is ignored in normal rendering, but becomes visible when rendering to ANSI output. The `pandoc.layout.render` function now takes a third parameter that defines the output style, either *plain* or *ansi*. + It is now possible to return a single filter from a filter file, e.g. ``` lua -- Switch single- and double quotes return { Quoted = function (q) elem.quotetype = elem.quotetype == 'SingleQuote' and 'DoubleQuote' or 'SingleQuote' return elem end } ``` The filter must not contain numerical indexes, or it might be treated as a list of filters. + Add `list_of_figures` and `list_of_tables` to writer options (Akash Patel). * Use latest releases of commonmark, commonmark-pandoc, texmath, djot. * Stop depending on package SHA (Albert Krewinkel). Use `crypton` instead. * `linux/make_artifacts.sh`: add riscv64 support (Olivier Benz). * Fix invalid XML in `test/docx/normalize.docx` (#10242). * `doc/lua-filters.md`: list functions in `pandoc.utils` alphabetically (Albert Krewinkel). * MANUAL.txt: + Clarify use of `beamerarticle` variable (#10250). + Add clarification to address user issues like #6704 (Yehuda Katz). ## pandoc 3.4 (2024-09-09) * New output format: `ansi` (for formatted console output) (Evan Silberman). Most Pandoc elements are supported and printed in a reasonable way, if not always ideally. This version does no detection of terminal capabilities, nor does it fall back to different output styles for less-capable terminals. * Add command line options `--table-caption-position` and `--figure-caption-position`. These allow the user to specify whether to put captions above or below tables and figures, respectively. The following output formats are supported: HTML (and related such as EPUB), LaTeX (and Beamer), Docx, ODT/OpenDocument, Typst. * Change default `--pdf-engine` via HTML to WeasyPrint (#10142). `wkhtmltopdf` is deprecated. `weasyprint` is the easiest-to-install, maintained alternative. For better results, one might prefer `pagedjs-cli`. * Org reader: + Fix parsing of src blocks with an `-i` flag (#10071, Albert Krewinkel). Tabs are now preserved in the contents of *src* blocks if the the block has the `-i` flag. * RTF reader: + Handle images inside `shp` contexts (#10145). * RST reader: + Improve simple table support (#10093). Multiline rows occur only when the *first* cell is empty; we were previously treating lines with *any* empty cell as row continuations. In addition, we no longer wrap multiline cells in Para if they can be represented as Plain. This is consistent with docutils behavior. * LaTeX reader: + Math environments don't have bracketed options (#10160). + Parse nested tabular environments (#4746). * Typst reader: + Change how "block" elements are handled. Previously they were always parsed as divs. But actually they can occur in some "inline" contexts. Now we first try to parse them as inlines, and only as blocks if that fails. A surrounding Div or Span element is added only if there is an identifier. * HTML reader: + Only parse main element's contents (if present) (#10140). If main has an id or class, we include a div with that id or class; otherwise just the contents. + Read TeX annotation in MathML content if present (#9971). + Better handle KaTeX-generated math (#9971). KaTeX emits the mathml followed by a span with an HTML fallback. Previously pandoc was converting both. We now ignore the HTML fallback span, marked with class `katex-html`. * Docx reader: + Add "SuppressAuthor" and "AuthorOnly" to citationMode when `+citations` is used (thomjur). * New module: Text.Pandoc.Writers.ANSI [API change] (Evan Silberman). * Docx writer: + Support `custom-style` attribute for docx table (Sebbones). + Support `--number-offsets`. + Make table/figure rendering sensitive to caption position settings. * OpenDocument writer: + Make table/figure rendering sensitive to caption position settings. * Typst writer/template: + Implement figure caption positions by triggering a show rule in the default template, which determines caption positions for figures and tables globally. + Don't include trailing semicolon after `@` style citations with suffixes (#10148). + Template: move header-includes before show doc (#9996, Gordon Woodhull). * LaTeX writer: + Make table/figure rendering sensitive to caption position settings (#5116). + Preserve locator labels with `--natbib` (#10057). * HTML writer/template: + Make `

` placement sensitive to caption position settings. For tables, `` must be the first element, and positioning is determined by CSS, for here we set a variable which the default template is sensitive to. + Use `makeSectionsWithOffsets` for `writerNumberOffsets`, instead of the old, inefficient code. + Don't add doc-biblioref role to every link in a citation; only to links to the bibliography (#10156). + Add `data-` when rendering `label` attribute (#10048). * Markdown writer: + Avoid emitting markdown caption if table has fallen back to raw HTML, which will then contain a `` tag (#10094). + Make math sensitive to `tex_math_gfm` extension (#9121). This means that in GFM output, the "new style" math will be used by default, e.g. $`x=y`$ ```math x = y ``` To defeat this and get the older behavior, namely $x=y$ $$x=y$$ one could use `-t gfm-tex_math_gfm`. * AsciiDoc writer: + Add `link:` prefix when needed (#10105). AsciiDoc requires it except for `http`, `https`, `irc`, `mailto`, `ftp` schemes (#10105). + Preserve original base level (#10062). We used to normalize so that the base level is always 1, but asciidoc no longer seems to care about that, and the behavior creates difficulties when we are converting fragments. + Don't emit empty figure caption (#10047). * ODT writer: + Add TableCaption to styles.xml (#10058, Ian Max Andolina). * LaTeX template: + Fix wrong beamer color in (sub)section page (Jonathan). * Text.Pandoc.Options: + Add `CaptionPosition` and new `WriterOptions` fields `writerFigureCaptionPosition` and `writerTableCaptionPosition` [API change]. * Text.Pandoc.Opt: + Change default for optNumberOffset to `[]`. This behaves the same as `[0,0,0,0,0]`. + Add `Opt` fields `optFigureCaptionPosition` and `optTableCaptionPosition` [API change]. * Text.Pandoc.Format: change `formatFromFilePaths` so that it is smarter about URLs. URLs are parsed, and we take the format from the path component, if present (#10141). This means that `https://emacs.org/` will be treated as HTML, while `https://emacs.org/sample.org` will be treated as Org. * Text.Pandoc.URI: + Add unofficial `gemini:` to list of URI schemes (Pau RE). * Text.Pandoc.Shared: + Add `makeSectionsWithOffsets` [API change]. + Remove `stripEmptyParagraphs [API change] (Albert Krewinkel). This function is no longer used. * Text.Pandoc.Highlighting: Expose `formatANSI` [API change] (Evan Silberman). * Text.Pandoc.Writers.Shared: export `to{Sub,Super}scriptInline` [API change] (Evan Silberman). * Remove use of partial functions (e.g. `head`) in code. * Use latest skylighting-core, skylighting, doclayout, texmath, typst. * pandoc-lua-engine: Add accessors for several writer options, including some that were added in previous releases. * pandoc-server: Initialize some missing fields in WriterOptions: `writerEpubTitlePage`, `writerChunkTemplate`, `writerListTables`, `writerFigureCaptionPosition`, `writerTableCaptionPosition`. * CONTRIBUTING.md: Summarize steps for adding a new cli option. * MANUAL.txt: + Clarify that the `--number-offset` option should only directly affect numbering of the first section heading in a document; subsequent headings will increment normally. + Fix asciidoc link (#10039). + Fix CSL Docs broken link (#10100, Tristano Ajmone). + Document the use of `luatexja` when CJKmainfont is used with lualatex (#3873, Kolen Cheung). + Add a `citations` (typst) section to the manual (#9127). + Clarify that `citations` affects both input and output for `org`. + Add note on `--citeproc` that you may need to disable `citations` extension on the output format (e.g., `-t markdown-citations`) to see the rendered citation (#9127, #10012). * INSTALL.md — reorganise info on static binaries and add conda-forge install options (#10098, #10069, Ian Max Andolina). ## pandoc 3.3 (2024-07-28) * New cli option: `--link-images`. This causes images to be linked rather than embedded in ODT. * Allow `--number-sections` to take an optional `true|false` argument. * RTF reader: + Handle `\*\shppict` without dropping image (#10025). * TWiki Reader: + Recognize WikiWords as internal links (#9941). + Avoid partial function. * Typst reader: + Ignore 'pad' and just parse its body (#9958). + Use typst 0.5.0.5. Fixes parsing of equations like `$1.$`. * Docx writer: + Fix regression with nested lists (#9994). The bug affects e.g. ordered lists with bullet sublists; after the sublist the top-level list reverts to bullets instead of being properly numbered. This is a regression introduced in version 3.2.1. * BibTeX writer: + Ensure that "literal" names are enclosed in braces (#9987). * Man writer: + Use default middle header when metadata does not include `header` (#9943). This change causes pandoc to omit the middle header parameter when `header` is not set, rather than emitting `""`. The parameter is optional and man will use a default based on the section if it is not specified. * HTML templates: don't load polyfill (#9918). This was added in a period when MathJaX required polyfill. MathJaX no longer recommends this and polyfill should no longer be necessary on any reasonably modern browser. * Translations: + Add `ua.yaml` (Jens Oehlschlägel). + Add a script (`tools/update-translations.py`) and Makefile target (`update-translations`) to update translation data automatically from babel and polyglossia upstream (Stephen Huan). + Use this script to update language data, increasing the number of languages we cover (Stephen Huan). Fix a few small bugs in existing translations. * Fix some mistakes with Japanese language code (#9938). In several places we were mistakenly assuming that the BCP 47 code for Japanese language was `jp`. It is `ja`. * Text.Pandoc.Options: + New field in WriterOptions: `writerLinkImages` [API change] (#9815). * Text.Pandoc.App.Opt: + New field in Opt: `optLinkImages` [API change] (#9815). * Lua subsystem: + Keep `lpeg` and `re` as "loaded" modules (Albert Krewinkel). The modules `lpeg` and `re` are now treated as if they had been loaded with `require`. Previously the modules were only assigned to global values, but could be loaded again via `require`, thereby allowing to use a system-wide installation. However, this proved to be confusing. The old behavior can be restored by adding the following lines to the top of Lua scripts, or to the `init.lua` in the data dir. debug.registry()['_LOADED'].lpeg = nil debug.registry()['_LOADED'].re = nil * `pandoc-cli`: Include pandoc copyright in Lua version info (Albert Krewinkel). * `pandoc-cli`: Refer printing of version info to the Lua interpreter (Albert Krewinkel). The Lua interpreter no longer terminates when called with `-v` or `--version` arguments, thus improving compatibility with the default `lua` interpreter program. * Avoid partial functions in JATS reader, DocBook writer, Haddock reader. * Allow tls 2.1.x. * MANUAL.txt: + Make documentation of extensions clearer (#9060). + Fix section level for two Extensions entries. * lua-filters.md: Partially autogenerate docs for module `pandoc` (Albert Krewinkel). The documentation system isn't powerful enough to generate the full documentation automatically. ## pandoc 3.2.1 (2024-06-24) * Fix `gfm_auto_identifiers` to replace emojis with their aliases, as documented (#9876). * CSV reader: + Turn line breaks into LineBreaks not SoftBreaks (#9797). * Docx reader: + Support task lists (#8211). + Fix a small bug in parsing delimiters in numbered lists, which led to the default delimiter being used wrongly in some cases. + Improve handling of captions. - Turn captioned images into Figure elements. Closes #9391. - Improve the logic for associating elements with captions (#9358). - Ensure that captions that can't be associated with an element aren't just silently dropped (#9610). + Support HorizontalRule. We support both pandoc-style and the style described on a Microsoft support page, an empty paragraph with a bottom border (#6285). + React to `"left"` value on `jc` attribute. + Handle column and cell alignments (#8551). We take the column alignments from the first body row. + Fix a bug that caused comments inside insertions or deletions to be ignored (#9833). * HTML reader: + Better handle non-`li` elements in `ul` and `ol` (#9809). For example, a `p` after a closed `li` will be incorporated into the previous `li`. This mirrors what browsers do with this invalid HTML. * LaTeX reader: + Fix parsing of dimensions beginning with `.`, e.g. `\kern.1pt` (#9902). * Markdown reader: + Allow author-only textual citations (#7219). E.g. `-@reese2002` outside of brackets. * RST reader: + Tighten up rules for when emphasis can start (#9805). + Support `:cite:` role with citeproc (#9904). A subset of the functionality of the sphinxcontrib-bibtex extension to Sphinx is supported. * Textile reader: + Don't let spans begin right after a symbol (#9878). * Typst reader: + Fix an incomplete pattern match (#9807). + Handle inline bodies ending in a parbreak. E.g. ``` `#strong[ test ] ``` * ConTeXt template: remove `\setupbackend[export=yes]` (#9820). * Docx writer: + Allow OpenXML templates to be used with `docx` (#8338, #9069, #7256, #2928). The `--reference-doc` option allows customization of styles in docx output, but it does not allow one to adjust the content of the output (e.g., changing the order in which metadata, the table of contents, and the body of the document are displayed), or adding boilerplate text before or after the document body. For these changes, one can now use `--template` with an OpenXML template. (See the default `openxml` template for a sample.) `--include-before-body` and `--include-after-body` can also now be used with `docx` output. The included files must be OpenXML fragments suitable for inclusion in the document body. + New unexported module Text.Pandoc.Writers.Docx.OpenXML. + Omit `jc` attribute on table cells with AlignDefault (#5662). + Better formatting for task lists. Task lists are now properly formatted, with no bullet (#5198). + Replace an expensive generic traverse to remove Space elements, for better performance. + Wrap figures with `id` in a bookmark (#8662). + Add eastAsia font hints to `w:r` (#9817). We do this when the text in the run contains any CJK characters. This ensures that ambiguous code points (e.g. quotation marks) will be represented as "wide" characters when together with CJK characters. + Clean up Abstract Title and Subtitle in default reference docx. Center Subtitle, remove color. * HTML writer: + Ensure URI escaping needed for `html4` (#9905). Unicode characters need not be escaped for html5, and still won't be. + Don't emit unnecessary classes in HTML tables (#9325, Thomas Soeiro). Pandoc used to emit a `header` class on the `tr` element that forms the table header. This is no longer needed, because `head > tr` will do the same thing. Similarly, pandoc used to emit `even` and `odd` classes on `tr`s, allowing striped styling. This is no longer needed, because one can use e.g. `tbody tr:nth-child(2n)`. Compatibility warning: users who relied on these classes to style tables may need to adjust their CSS. * JATS writer: + Support `supplementary-material` in metadata for `jats_articlepublishing` (#9818). * LaTeX writer: + New method for ensuring images don't overflow (#9660). Previously we relied on graphicx internals and made global changes to Gin to force images to be resized if they exceed textwidth. This approach is brittle and caused problems with `\includesvg` (see #9660). The new approach uses a new macro `\pandocbounded` that is now defined in the LaTeX template. (Thanks here to Falk Hanisch in https://github.com/mrpiggi/svg/issues/60.) The LaTeX writer has been changed to enclose `\includegraphics` and `\includesvg` commands in this macro when they don't explicitly specify a width or height. In addition, the writer now adds `keepaspectratio` to the `\includegraphics` or `\includesvg` options if `height` is specified without width, or vice versa. Previously, this was set in the preamble as a global option. Users should attend to the following compatibility issues: - If custom templates are used with the new LaTeX writer, they will have to be updated to include the new `\pandocbounded` macro, or an error will be raised because of the undefined macro. - Documents that specify explicit dimensions for an image may render differently, if the dimensions are greater than the line width or page height. Previously pandoc would shrink these images to fit, but the new behavior takes the specified dimensions literally. In addition, pandoc previously always enforced `keepaspectratio`, even when width and height were both specified, so images with width and height specified that do not conform to their intrinsic aspect ratio will appear differently. + Task lists must be unordered (#9185). + Specify language option for `selnolig` and only include it if `english` or `german` is used (#9863). (This includes changes to the LaTeX template.) This should restore proper ligature suppression when lualatex is used. + Fix `--toc-depth` with beamer output (#9861). Previously only top-level sections were ever included in the TOC, regardless of the setting of `--toc-depth`. + Use `\linewidth` instead of `\columnwidth` or `\textwidth` for resizing figures, table cells, etc. in LaTeX (#9775). `\linewidth`, unlike the others, is sensitive to indented environments like lists. * LaTeX template: put `babel-lang` in options to beamer (#9868). This is required to make beamer use proper localized terms for things like "Section." * Markdown writer: + Don't print extra caption when using `implicit_figures`. + Ensure blank line after HTML blocks in commonmark-based formats (#9792). + Fix bug rendering block quotes in lists (#9908). * Texinfo writer: + Ensure proper escaping in all node/link contexts. + Target node rather than anchor when possible in internal links. + Remove illegal characters from internal link anchors (#6177). + Use two commas not one in `@ref`. + Don't add anchors to headings. We don't need them, now that we make internal links use the node. + Avoid duplicate node names. + Improve menus. Properly handle the case where the node name is different from the descriptive title. * Texinfo template: add variables for filename and version. * Typst writer: + Support '.typst:no-figure' and 'typst:figure:kind=kind' attributes (#9778, Carlos Scheidegger). This extends support for fine-grained properties in Typst. If the `typst:no-figure` class is present on a Table, the table will not be placed in a figure. If the `typst:figure:kind` attribute is present, its value will be used for the figure's `kind` (#9777). These features are documented in `doc/typst-property-output.md`. * Typst template: + Add subtitle (#9747, Mickaël Canouil). + Use content rather than string for title, author, date, email (#9823). This allows formatting in title, author, date, and email fields. Since the PDF metadata requires a string, and typst only converts the title to a string (not the authors), we use * Textile writer: + Get rid of header, odd, even classes on `tr` (#9376). * Text.Pandoc.Class: + `fillMediaBag`: Convert IOErrors to warnings when fetching absolute paths (#9859, Albert Krewinkel). This will allow many conversions that would have failed with an error to succeed (albeit without images or other needed resources). * Text.Pandoc.ImageSize: + Don't prefer exif width/height when they conflict with image width/height (#9871). That was a mistaken call in #6936. Usually when these values disagree, it is because the image has been resized by a tool that leaves the original exif values the same, so the width/height metadata are more likely to be correct that exif width/height. * Text.Pandoc.SelfContained: + Strip CRs from XML before base64 encoding for data URI (so tests can work on Windows). + Only create `` elements for SVG images when the image has the class `inline-svg`. Otherwise just use a `data` URI as we do with other images (#9787). * Lua subsystem (Albert Krewinkel): + Split Init module into more modules. The module has grown unwieldy and is therefore split into three internal Haskell modules, `Init`, `Module`, and `Run`. + Add function `pandoc.utils.run_lua_filter` (#9803). + Add function `pandoc.template.get` (#9854, co-authored by Carsten Gips). The function allows to specify a template with the same argument value that would be used with the `--template` command line parameter. + Keep CommonState object in the registry. The state is an internal value and should be treated as such. The `PANDOC_STATE` global is merely a copy; unsetting the global no longer breaks the Lua engine. + Allow passing an environment to `run_lua_filter`. The default is now to use a *copy* of the global environment when running a filter; this ensures better separation when `run_lua_filter` is used multiple times. A custom environment can be specified via the optional third parameter. + Set `pandoc.List` as default metatable for JSON lists (#9834). Lists created by `pandoc.json.decode` now behave like lists generated via `pandoc.List`. This also ensures that `pandoc.List` tables are encoded as JSON arrays when passed to `pandoc.json.encode`. * Text.Pandoc.Writers.Shared: export `toTaskListItem` [API change]. * Add unexported module Text.Pandoc.Char. This exports `isCJK`. Use this instead of locally defined `isCJK` in T.P.Readers.MediaWiki. * MANUAL.txt: + Remove false claim that Lua mode does not support `-i` (#9757, Ian Max Andolina). + Use level-3 headings for extensions (to avoid gaps). + Add anchor for tagging extension. + Remove explicit referencess to generate anchors. These will be linkified automatically. + Fixed links to `option--reference-doc`. + Add a note that column widths aren't supported in pptx for Divs with class `columns` (#9890). + Fix alerts example (#9826, Ian Max Andolina). + Fix markup of `babelfonts` example code (Albert Krewinkel). * `doc/custom-writers.md`: + Fix usage of Template in example (Albert Krewinkel). + Document the separator arg of `Writer.Blocks` (Albert Krewinkel). * `doc/lua-filters.md` (Albert Krewinkel): + Fix outdated documentation for math and quoting functions and fields. + Autogenerate docs for module `pandoc.template` and `pandoc.layout`. + Document operators of the "Doc" type. * pandoc-lua-engine: depend on pandoc >= 3.2 (see #9755). * Allow crypton-connection 0.4, time 1.14. * Allow tasty-quickcheck 0.11. * Use latest emojis, skylighting, skylighting-core, citeproc, djot, commonmark-extensions, typst-hs ## pandoc 3.2 (2024-05-11) * Change to `--file-scope` behavior (#8741): previously a Div with an identifier derived from the filename would be added around the contents of each file. This caused problems for "chunking" files into chapters, e.g. in EPUB. We no longer add the surrounding Div. This cooperates better with chunking. Note, however, that if you have relied on the old behavior to link to the beginning of the contents of a file using its filename as identifier, that will no longer work. * Markdown reader: + Allow repeated labels in numbered example lists. Previously if you tried to use the same label as an earlier example list item, you'd get a new number, not the old one, and references to the label would go to the second occurrence. Now an existing label will be reused, and no new number will be generated. Caveat: this only works reliably when the re-used example list item occurs by itself in a list, or occurs in a list of previously used example list items that occur in exactly the same order as previously. + Fix `normalCite` so it doesn't consume past a closing `]` boundary (#9710). This was causing an exponential performance bug on long lists of links containing potential emphasis characters. + Generalize `inlinesInBalancedBrackets` to `inBalancedBrackets`, with a parameter for the inner parser. + Auto-close unclosed divs (#9635). This applies to both fenced and HTML-ish varieties. Otherwise we face an exponential performance problem with backtracking. A warning is issued when a div is implicitly closed. * RST reader: + Fix `figclass` and `align` annotations for figures (#7473, Gokul Rajiv). * LaTeX writer: + Use `polytonicgreek` instead of `polutonikogreek` with babel (#9698). `polutonikogreek` is outdated. Also recognize both in the LaTeX reader. + Improve treatment of math inside soul commands (#1294, #5529). soul commands (`ul`, `hl`, `st`) are very fragile and the math must be handled specially. * LaTeX reader: + Fix over-eager macro expansion in conditionals (#9676). + Parse `flalign`, `flalign*` math environments (#9679). We parse these as Math elements with an `aligned` environment. Semantically it's not exactly the same, but better than falling back to raw LaTeX. * LaTeX template: add `titlegraphicoptions` variable (#9207, Guilhem Saurel). * Docx reader: + Issue warning rather than error when we can't parse EndNote citations (see #8433). + Fix anchor in header after anchor (#9626, mbracke). * RTF reader: + Don't try to handle non-default code pages (#9683). Emit a warning instead. * OpenDocument writer: + Implement custom-style for spans (#9657). * Typst writer: + Add blank line in definition lists with multiple definitions (see #9704). + Property output (#9648, Gordon Woodhull). The Typst writer will pass on specially marked attributes as raw Typst parameters on selected elements. This allows extensive customization using filters. A separate document (`doc/typst-property-output.md`) has been added that provides extensive documentation and examples of the use of this feature. * Markdown writer: + Don't try to align columns in pipe tables with lines greater than COLUMNS. The alignment just reduces readibility when the lines soft wrap. + Don't use `raw_attribute` syntax for raw blocks, unless there is no other option (see #9677). Macros in a `raw_attribute` block don't get interpreted when it is read again by pandoc's markdown reader. * ConTeXt writer: + Replace depreciated `\sc` with `\setsmallcaps` (#9518, James P. Ascher). * Docx writer: + Use conventional styles/indents for Word bullet lists (#7280). * `reference.docx`: + Use current standard Word theme (#7280). This includes using the sans-serif font Aptos instead of the serif font Cambria, and default colors for headings. + Remove duplicate `DefaultParagraphFont` in `styles.xml`. * New module Text.Pandoc.Transforms [API change] (Albert Krewinkel). This module exports the following functions which were formerly exported from Text.Pandoc.Shared: `headerShift`, `filterIpynbOutput`, `eastAsianLineBreakFilter`, as well as some functions that were previously not exported. * Text.Pandoc.Shared: + `headerShift`, `filterIpynbOutput`, and `eastAsianLineBreakFilter` are no longer exported from this module; they are now exported from Text.Pandoc.Transforms (Albert Krewinkel). * Text.Pandoc.Error: + Improve reporting of unsupported extensions errors (#9247, Albert Krewinkel). * Text.Pandoc.App: + Move "transforms" after filters (#9664). This will mean that `--shift-heading-level-by` affects a heading added by `reference-section-title`. * Text.Pandoc.App.CommandLineOptions: + Simplify output for `OptVersion`. Omit the information about versions of dependencies. We no longer emit version info at this level anyway; `pandoc-cli` intercepts and handles `--version`. This code would only be called if someone used the pandoc library function `handleWithOptInfo` in their own program. * Text.Pandoc.ImageSize: + Export `ImageSize` datatype. * Text.Pandoc.SelfContained: + Merge class attribute when both img and svg specify it (#9652, Carlos Scheidegger). * Text.Pandoc.Logging: + Add `ScriptingInfo` constructor for `LogMessage` [API change] (Albert Krewinkel). + Make `DocxParserWarning` a WARNING, not INFO. [API change]. + Add `UnsupportedCodePage` constructor to `LogMessage` [API change]. + Add `UnclosedDiv` constructor for `LogMessage` [API change]. * Lua subsystem (Albert Krewinkel: + Add a `pandoc.log` module. + Uupdate to pandoc-lua-marshal version 0.2.7 (#8916). This fixes counterintuitive behavior of the `content` property on BulletList and OrderedList items. Unmarshalling of that field now matches the behavior of the constructor. + Use newest zip module. This adds a `symlink` function to Entry objects, allowing to check if an entry represents a symbolic link. + Improve `pandoc.json.decode` docs. + Update and fix docs for `pandoc.types.Version` and `pandoc.utils.type`. + Add new module `pandoc.image` The module provides basic querying functions for image properties. + Bump pandoc-lua-engine to 0.2.1.4. * Use latest KaTeX CDN asset (#9707, Salim B). * `pandoc-cli`: ensure UTF8 when emitting version info. * tools/update-lua-module-docs.lua: improve script-internal docs, cleanup (Albert Krewinkel). * Allow network 3.2. * Use latest versions of texmath, djot, skylighting-core, skylighting. * Fix command test for #9652. * Fix some typos in code comments (#9638, guqicun). * Command tests: include regular PATH after directory with the test executable (ensures that DLLs will be found on Windows). * MANUAL.txt: + Document `handout` variable for beamer (#9742). + Document formats affected by `--slide-level` (#9745). + Update the list of required LaTeX packages (#9728, Albert Krewinkel). + Use more descriptive link text for ODT (#9673). + Add clarification about `toc-title` in `docx`, `pptx` (#9645). + Better document truthiness for conditionals (#9661). + Mention that `custom-style` works with ODT (Ian Max Andolina). + Harmonize typographic dashes (#9688, Salim B). Standardize on `---` with no space. * INSTALL.md: Minor tweaks (#9705, Leo Heitmann Ruiz). ## pandoc 3.1.13 (2024-04-07) * Org reader: + Fix treatment of `id` property under heading (#9639). * DocBook reader: + Add empty title to admonition div if not present (#9569). This allows admonition elements (e.g. ``) to work with `gfm` admonitions even if the `` is not present. * DokuWiki reader: + Link text cannot contain formatting (e.g., `//` is not italics) (#9630). + An explicitly empty link text (`[[url|]]`) works the same as an omitted link text (#9632). * Typst reader: + Support Typst 0.11 table features: col/rowspans, table head and foot (#9588). + Parse cell col/rowspans. * CSLJson writer: + Put `$` or `$$` around math in `csljson` output (#9616). * ConTeXt writer: + Fix options order with `\externalfigure`. The dimensions should come after the class if both are present. * Typst writer: + Put label after Span, not before. Labels get applied to preceding markup item. + Support Typst 0.11 table features (#9588): colspans, rowspans, cell alignment overrides, relative column widths, header and footer, multiple table bodies with intermediate headers. Row heads are not yet supported. + The default typst template has been modified so that tables don't have lines by default. As is standard with pandoc, we only add a line under a header or over a footer. However, a different default stroke pattern can easily be added in a template. + More reliable escaping in inline `[..]` contexts (#9586). For example, we need to escape `[\1. April]` or it will be treated as an ordered list. + Handle `unnumbered` on headings (#9585). * LaTeX writer: + Fix math inside strikeout (#9597). * Text.Pandoc.Writers.Shared: + Export `isOrderedListMarker` [API change]. * Change lhs tests so they don't use `--standalone`. This will avoid test failures due to minor changes in skylighting versions, e.g. #9589. * Use latest texmath, typst. * Require pandoc-lua-marshal 0.2.6 (#9613, Albert Krewinkel). Fixes an issue arising when the value of `content` properties on *BlockQuote*, *Figure*, and *Div* elements was an empty list. * Update lua-filters.md (#9611, Carlos Scheidegger). ## pandoc 3.1.12.3 (2024-03-17) * Markdown reader: Fix bug with footnotes at end of fenced div (#9576). * LaTeX reader: + Improve tokenization of `@` (#9555). Make tokenization sensitive to `\makeatletter`/`\makeatother`. Previously we just always treated `@` as a letter. This led to bad results, e.g. with the sequence `\@`. E.g., `a\@ b` would parse as "ab" and `a\@b` as "a". + Make `withRaw` work inside `parseFromToks` (#9517). This is needed for raw environments to work inside table cells. + Better handling of table colwidths (#9579). Previously the parser just failed if the column width specified in `p{}` wasn't a multiple of `\linewidth`. This led to cases where content was skipped. * Typst writer: + Add 'kind' parameter to figures with tables (#9574). + Avoid unnecessary box around image in figure (#9236). + Omit width/height in images unless explicitly specified (#9236). Previously we computed width/heigth for images that didn't have size information, because otherwise typst would expand the image to fit page width. This typst behavior has changed in 0.11. This change fixes a bug in which images would sometimes overflow page margins, depending on their intrinsic size. + Don't add hard-coded `inset` to tables (#9580). Instead, set this globally in the default template, allowing it to be customized. * LaTeX template: Fix block headings support for unnumbered paragraphs (#9542, #6018, Oliver Fabel). * HTML templates: Replace polyfill provider (#9537, @SukkaW). Replace polyfill.io with cdnjs.cloudflare.com/polyfill. polyfill.io has been acquired by Funnull, and the service has become unstable. * Korean translations: delete colon in translation for 'to'. This was invalid YAML, and not desired anyway, since a colon is added. * Use latest commonmark, commonmark-extensions. This fixes a 3.12 regression in parsing of commonmark/gfm autolinks (jgm/commonmark-hs#151). * Depend on djot 0.1.1.3, which fixes a serious parsing bug affecting regular paragraphs after lists. * Depend on latest skylighting, skylighting-core, typst-hs, texmath. * MANUAL.txt: Change broken link to IDML cookbook (#9563). ## pandoc 3.1.12.2 (2024-02-29) * Docx reader: + Ensure that table captions are counted (#9518). + Detect caption by style name not id (#9518). The styleId can change depending on the localization. + Avoid emitting empty paragraph where caption was. * Markdown reader: fix regression in link parsing with wikilinks extensions (#9481). This fixes a regression introduced in 3.1.12. * Org reader/writer: support admonitions (#9475). * Org writer: omit extra blank line at end of quote block. * Typst writer: ensure that `-`, `+`, etc. are escaped at beginning of block (#9478). Our recent relaxing of escaping (#9386) caused problems for things like emphasized `-` characters that were rendered using `#strong[-]#`. This now gets rendered as `#strong[\-]`. * LaTeX writer: fix bug when a language is specified in two different ways (#9472). If you used `lang: de-DE` but then had a span or div with `lang=de`, the preamble would try to load `ngerman` twice, leading to an error. This fix ensures that a language is only loaded once. * Docx writer: Don't copy over `footnotePr` in `settings.xml` from reference.docx (#9522). * EPUB writer: omit EPUB2-specific meta tag on EPUB3 (#9493). This caused a validation failure in epubs with cover images. * Lua: avoid crashing when an error message is not valid UTF-8 (Albert Krewinkel). * Text.Pandoc.SelfContained: + Add `role="img"` to svgs. + Add `aria-label` to svg elements with `alt` text if present. Screen readers ignore `alt` attributes on svg elements but do pay attention to `aria-label` (#9525). * Text.Pandoc.Shared: Fix regression in section numbering in `makeSections` (#9516). Starting with pandoc 3.1.12, unnumbered sections incremented the section number. * Text.Pandoc.Class: fix `openUrl` TLS negotiation (#9483). With the release of TLS 2.0.0, the TLS library started requiring Extended Main Secret for the TLS handshake. This caused problems connecting to zotero's server and others that do not support TLS 1.3. This commit relaxes this requirement. * Depend on djot 0.1.1.0 (fixes rendering on multiline block attributes). * Use new releases of skylighting-format-blaze-html (#9520). Fixes auto-wrapping of long source lines in HTML print media. * Use new commonmark-extensions (fixes issue with the `rebase_relative_paths` extension when used with commonmark/gfm. * Makefile: improve epub-validation target (#9493). Use `--epub-cover-image` to catch issues that only arise with that. ## pandoc 3.1.12.1 (2024-02-17) * EPUB writer: omit EPUBv3-specific accessibility features on epub2 (#9469). Fixes a regression in 3.1.12. * More fixes for SVG ids with `--self-contained` (#9467). This generalizes the fix to #9420 so it applies to things like `style="fill(url(#..."` and should fix problems with SVGs including gradients. * Powerpoint writer: properly handle math in headings and tables (#9465). This ensures that paragraphs containing math are wrapped in a `mc:AlternateContent` node as required. * Makefile: make validate-epub check v2 output too. ## pandoc 3.1.12 (2024-02-14) * Add `djot` as input and output format. Djot is a light markup syntax (https://djot.net). + New module Text.Pandoc.Readers.Djot [API change]. The function `readDjot` is also exported by Text.Pandoc.Readers. + New module Text.Pandoc.Writers.Djot [API change]. The function `writeDjot` is also exported by Text.Pandoc.Writers. * `--number-sections` now uses the first digit for the number of the top-level section, no matter what its level. So if the top-level section is level-2, numbers will be `1`, `2`, etc. rather than `0.1`, `0.2`, as in the past (#5071). For some backwards compatibility, we revert to the old behavior when the `--number-offset` option is used. * DocBook reader: + Better handling of `` and `` (#9341): `` now gets parsed as an ordered list, and `` as a sublist. * Man reader: + Move spaces outside of emph/strong (#9445). * MediaWiki reader: + Don't make leading blanks underscores in image links (#9425). + Allow lowercase `image:` (#9424). * BibTeX reader: + Support `pagetotal` in converting BibLaTeX. * Markdown reader: + Fix wikilinks extensions to allow newlines in titles (#9454). * EPUB reader: + Don't put `#` characters in identifiers. * LaTeX reader: + Improve treatment of `\cref`, `\Cref` (#7463). Use the reference-type `ref+label` and `ref+Label`. Also, associate with `\vref` `ref` instead of `ref+page`. + Limited support for `\Cref` (#7463). + Generate relative widths for `\linewidth`, `\textheight` (#9388). * Typst reader: + Fix handling of `\overline` (#9294). Due to a typo, it was being incorrectly rendered as an `\underset`. + Improve handling of inline `#quote` (#9413). + Fix handling of `dot()`, `tilde()`, `ddot()` (jgm/typst-hs#38). + Fix character used for `norm` (jgm/typst-hs#38). * Typst writer: + Use reference form (e.g. `@jones2000[p. 30]`) for citations when possible. + Use `#ref` or `@` for links with `reference-type="ref"` (#7463). This attribute is added to LaTeX `\cref`, for example. + Improve citation support (#9452). Emit `form: "prose"` or `form: "year"` qualifiers if the citation is author-in-text or suppress-author. Strip initial comma from suffix, since typst will add an extra one. + Unescape URI escapes in image paths (#9389). + Handle labels and citaiton ids with spaces and other special characters (#9387). In these cases, we produce an explicit `label()` rather than using `<>` or `@`. + Avoid producing illegal labels (#9387). + Avoid unnecessary escapes (#9386). * LaTeX writer: + Make writer sensitive to `empty_paragraphs` extension (#9443). + Fix beamer highlighting (mh4ckt3mh4ckt1c4s). + Create valid table even when table is empty (#9350). + Set font fallback for babel main font (Max Heller). + Add some kerns where needed between quotes (#9371). * HTML writer: + Add suffix to multiple footnote section ids, so they are unique (Sam May). This is necessary when `--reference-location` is `block` or `section`. * EPUB writer: + Add ARA roles for accessibility (#9378, Iacobus1983). Footnote references are given role "doc-noteref", footnote text gets "doc-footnote", and nav gets "doc-toc". + Ensure that an alt attribute is always added (#9354). This seems to be required by iBooks; even an empty alt attribute will satisfy it. + Add `xml:lang` to package element (#9372). + Add accessibility metadata to EPUB metadata (#9372, #9400, Iacobus1983 and John MacFarlane). Reasonable default values are used to ensure that pandoc's EPUBs conform to the EU Accessibilty Act requirements, but values can be overridden using metadata. * Docx writer: + Restore ability to center-justify table (#9393). The fix to #5947 caused all tables to be left indented. This was necessary to avoid extra indentation in table cells when a table appeared in a list item. This change makes the changes conditional, so that they only affect tables in list items. * Man writer: + Fix bug with long URLs (#9458). URLs with more than 68 characters didn't display properly because of wrapping. + Support (limited) syntax highlighting in code blocks (#9446). Currently only boldface and italics are supported. The `monochrome` style might be of use for those generating man pages. * Org writer: + Escape special lines in code blocks (#9218, Albert Krewinkel). * Markdown writer: + Use different width fences for nested divs (#9450). Outer divs have longer fences. This aids clarity for the reader, making it easier to see where the div ends. It also makes the output compatible with some other implementations, e.g. micromark, which require different-width fences for nesting. + Fix output for pipe tables with a huge number of columns (#9346). Previously we got invalid pipe tables when the number of table columns exceeded the setting of `--columns`. * Powerpoint writer: + Fix regression in layout for slides with figures (#9442). + Use internal column widths in pptx writer tables (#5706, Tomas Dahlqvist). The table writer used to only divide all available width evenly for all columns. In this update the code uses the incoming widths if they are available. If they are not set the earlier even distribution is used. Some of the golden templates are adjusted slightly because of different rounding when using the new calculation model. * Custom writers: + Fix handling of common state (#9229, Albert Krewinkel). The CommonState (`PANDOC_STATE` in Lua) may change between the time that a custom writer script is first loaded and when the writer is run. However, the writer was always using the initial state, which led to problems, e.g. when the mediabag was updated in a filter, as those updates where not visible to the writer. The state is now updated right before the writer function runs. * Text.Pandoc.SelfContained: + Fix id replacements in SVGs with clipping paths (#9420). This fixes `--embed-resources` when SVGs have `clip-path` attributes. + Fix size of duplicated SVGs with `--embed-resources` (#9439). * ConTeXt template: support font fallback (#9361, Lawrence Chonavel). * Text.Pandoc.Shared: + `addPandocAttributes`: use `wrapper` attribute, not `wrap`, for Divs and Spans added as wrappers to hold attributes on elements that do not accept them. + `makeSections` behavior changes: - When the optional base level parameter is provided, we no longer ensure that the sequence of heading levels is gapless (#9398). Instead, we set the lowest heading level to the specified base level, and adjust the others accordingly. If an author wants to skip a level, e.g. from level 1 to level 3, they can do that. In general, the heading levels specified in the source document are preserved; `makeSections` only puts them into a hierarchical structure. - Section numbers are now assigned differently, as described above under `--number-sections` changes (#5071). + Improve `makeSections` code for section number calculation. * Text.Pandoc.Chunks: + Autogenerate unique ids for sections missing them (#9383). This is needed for TOC generation to work properly. We can't create TOC links if there are no ids. This fixes some EPUB validation issues we've been getting since switching over to Chunks for chunking. + Improve `fixTOCTreePaths`. We weren't adding ids for section headings that don't head a chunk, but these headings are needed for a TOC. * Lua: catch encoding error in `pandoc.read` (#9385, Albert Krewinkel). Fixed a bug that could lead to an un-catchable error and program termination when `pandoc.read` was called with invalid UTF-8 input. * LaTeX template: support font fallback (lawcho). This support is LuaLaTeX-specific. See MANUAL.txt for documentation. * Text.Pandoc.Readers: Add `readMan` to exports [API change] (George Stagg). * Text.Pandoc.PDF: + Reliably detect when TOC has changed (#9295). Sometimes the TOC changes but there are no warnings: this happens when no labels are present. In this case we must rerun LaTeX. So we now take the SHA1 hash of the TOC file and rerun LaTeX if it changes between runs. + Increase maximum number of LaTeX runs to 4 (#9299). On some documents, 4 runs are needed (e.g. when a LastPage reference is used). + Avoid `readFileLazy`, which caused improperly cleaned-up temp directories on Windows (#9460). * MANUAL.txt: + Harmonize spelling of Markdown and MultiMarkdown (#9402, Salim B). + Add `
` to list of exceptions for `markdown_in_html_blocks`
      extension (#9305).
    + Add clarification to docs for `--resource-path` (#9417).

  * Makefile: Validate generated EPUB as part of prerelease checks.

  * Add validation for docx golden files to CI (Edwin Török).

## pandoc 3.1.11.1 (2024-01-05)

  * Docx reader:

    + Fix HYPERLINK with only switch and no argument (#9246).

  * Org reader:

    + Parse caption and label for grid tables (#9279).

  * MediaWiki reader:

    + Handle multiline math in list items (#9293).

  * OPML writer:

    + Respect `--wrap` options & `--columns` in contents of notes (#9297).

  * ODT/OpenDocument writers:

    + Properly handle highlighting styles (#9287). These styles were
      going into an `office:styles` element in `content.xml`, but this
      is invalid. Instead they must go in `styles.xml`. The variable
      `highlighting-styles` no longer has any effect on the default
      opendocument template, and highlighting styles are not included
      in `opendocument` output.

  * Markdown writer:

    + Add table identifier at end of caption if present (#9279).

  * Text.Pandoc.PDF:

    + Expand list of environment variables to display in verbose output
      (#9303).
    + Ensure that we find all the LaTeX warnings requiring a rerun (#9284).
      This should fix a regression from 3.1.9 that led to incorrect
      alignments in tables (and possibly other issues).

  * Docx writer:

    + Ensure that pandoc's output validates (Edwin Török, #9273, #9269,
      John MacFarlane, #9265, #9266, #9264).
    + Don't emit empty table rows, which seem to cause problems for
      Word (#9224).

  * LaTeX writer:

    + Omit superfluous page locator label when used with `--natbib` or
      `--biblatex` (#9275). These will treat a bare number as a
      page locator, and they will be able to localize it. Note that the
      recognition of the locator label is locale-sensitive; if `lang` is
      `de`, then `S. 33` is a page reference, and `p. 33` is not!

  * Text.Pandoc.Chunks: Fine tune `makeChunks` (#9281).

    + Ensure that chunks not based on sections (those with the
      "preamble" class) get unique identifiers, by appending chunk number.
    + This will also ensure that they get unique path names when
      the path is generated from the identifier.

  * Default HTML5 template: remove html5shiv (and support for IE < 9).

  * Makefile:

    + Fix `make quick-stack`: `j` was expecting a number (Edwin Török).
    + Run built pandoc (instead of pandoc in path).
    + Add `validate-epub` target, using `epubcheck` to test the golden files.
    + Add `validate-docx-golden-tests` target.

## pandoc 3.1.11 (2023-12-15)

  * Typst writer:

    + Emit `;` after typst code, unless followed by space (#9252).
      Otherwise there's the potential that the typst code will swallow
      up a following character.

  * Text.Pandoc.Logging:

    + Add `MakePDFWarning` constructor to LogMessage [API change].
    + Add `MakePDFInfo` constructor to LogMessage [API change].

  * Text.Pandoc.PDF:

    + LaTeX warnings are passed on to the user as warnings.
    + Use `report` with `MakePDFWarning` and `MakePDFInfo` to relay
      verbose information and warnings, instead of writing directly
      to stderr.
    + Parse logs to determine whether additional runs needed, instead of
      running a fixed number of times (#9255). (The number of times
      that was appropriate given pandoc's default templates didn't
      always work for custom templates, and thus pandoc 3.1.10's
      change in the number of runs led to some regressions in PDF
      production.)

  * Makefile: in `make prelease`, add checks that pandoc-cli and
    pandoc have the same version, that pandoc-cli depends on this
    exact version of pandoc, that there is an entry for this version
    in the changelog, and that the version numbers in the
    generated man pages are correct.

  * Regenerate man pages with pandoc 3.1.10. This properly escapes hyphens
    and fixes version numbers in man pages for `pandoc-server` and
    `pandoc-lua`.

  * Depend on texmath 0.12.8.6. This omits unneeded `lr`s in typst
    math output.

  * Depend on typst 0.5. This allows the typst reader to support
    multiline strings, the version type, and the `as`
    keyword with `import`.

## pandoc 3.1.10 (2023-12-12)

  * Link pandoc-cli version to pandoc version. Henceforth pandoc-cli's
    version will be synchronized with pandoc's, and pandoc-cli will
    depend on an exact pandoc version. This will avoid confusion by
    ensuring that `cabal install pandoc-cli-X.Y.Z` installs pandoc
    version X.Y.Z. It will make things more straightforward for
    upstream packagers (see #9232). This scheme does not follow the
    Haskell PVP, but that should cause no harm, because this package
    does not expose a library.

  * Add `alerts` markdown extension. This enables GitHub style markdown
    alerts as a commonmark extension. This extension is now default for
    `gfm`. It can't be used with `markdown`, only with `commonmark` and
    variants.

  * Markdown reader:

    + Preserve newlines in math instead of changing to spaces.
      Otherwise we can get unwanted results if there's
      a `%` comment (#9193).
    + Make attributes work with reference links (#9171).

  * HTML reader:

    + Improve handling of invalidly nested sublists (#9187, cf. #8150).

  * MediaWiki reader:

    + Allow attribute keys with hyphens (#9178).

  * ODT reader:

    + Support attr `text:continue-numbering` (#8979, Stephan Meijer).

  * Typst reader:

    + Allow references (e.g. `@foo`) to become citations
      if there is no corresponding label in the document.
    + Collapse adjacent `cite` elements.
    + Handle supplements in `cite`.
    + Change `cite` (only one key allowed, a label) (typst 0.9 breaking change).
    + Support `quote` element (typst 0.9).

  * LaTeX reader:

    + Handle otherlanguage environment and language-name environments like
    `\begin{french}...\end{french}` (#9202).
    + Fix theorem label parsing (#8872, Hikaru Ibayashi).

  * Docx reader:

    + Unwrap content of shaped textboxes (Stephan Meijer, #9214).
    + Improve handling of `w:sym` (#9220). We now look up symbols in symbol
      fonts using the table defined at Text.Pandoc.Readers.Docx.Symbols.
    + Add unexported module Text.Pandoc.Readers.Docx.Symbols. This gives us a
      table to use to resolve characters included in docx via `w:sym` element.

  * Man reader:

    + Properly handle `.sp` macro inside lists and block quotes (#9201).

  * LaTeX writer:

    + Fix bug with big footnotes inside emphasis (#8982, Hikaru Ibayashi).
    + Handle identifiers inside heading contents. `\phantomsection` can't
      be used in this case, so we need `\hypertarget` (#9209).

  * LaTeX template:

    + Include `bookmark` package unconditionally. This package
      produces better PDF bookmarks than `hyperref` and does it on the
      first pass.

  * Typst writer:

    + Use `quote` for block quotes.
    + Support `--toc-depth` as in other writers (#9242).
    + Put inline image dimensions on enclosing box, not image (#9104).
    + Better handling of tables with captions (#9194).
      We now put these in a figure with a caption argument.
    + Update typst writer to typst 0.9 citation format (#9188).

  * Typst template:

    + Remove custom definition of `blockquote` in default template.
      (We now use built-in `quote`.)
    + Support table of contents.
    + Support csl (#9186, Ian Max Andolina). Typst now supports CSL for its
      native citation engine, so pandoc should use a specified `csl`
      style in the template, falling back to `bibliographystyle` if
      `csl` is not specified.

  * Docx writer:

    + Use different style for block quotes in notes (#9243).
      Using "Footnote Block Text" for the style name, so it can be
      given a different font size if footnotes are.
    + Allow embedded fonts to be used in reference.docx (#6728).

  * HTML5 writer:

    + To conform to validator's expectations, `doc-footnote` role is used
      with `aside` and `doc-endnotes` with `section`.
    + `aside` is used only for notes at ends of sections or blocks;
      if all the notes come at the end of the document, `section` is
      used so we can have the `doc-endnotes` role.

  * JATS writer:

    + Handle case where there is material after refs div (#9166). Previously in
      such cases the references were not being moved to back matter.

  * Ms writer:

    + Don't do normal escapes in filename arguments for PSPIC etc.

  * T.P.RoffChar: escape `-` as `\-`. The `groff_man (7)` man page indicates
    that `-` characters will be treated as typographic hyphens and are not
    appropriate for cases where the output should be copy-pasteable as an
    ASCII hyphen-minus character.  (E.g. in command line options.)
    However, until a recent update groff man did not actually do this;
    it treated `-` and `\-` the same.  With the new update (1.23.0)
    the two are distinguished (see https://lwn.net/Articles/947941/
    for background), so now it is important that pandoc escape `-`.

  * Text.Pandoc.Extension: add `Ext_alerts` constructor [API change].

  * Text.Pandoc.PDF: We now default to running LaTeX only
    once in producing a PDF (instead of twice). This is made possible by the
    shift to the `bookmark` package, which does not require a second pass for
    PDF bookmarks. If a table of contents is present, we still have to run
    three times to get the page numbers, and if beamer is used we still do a
    minimum of two runs.

  * Text.Pandoc.Shared:

    + `renderTags'`: use minimized tag for `rect`.
    + Allow svg `path` element to be minimized.
    + Export `combineAttr` [API change].
    + Improve `isTightList` so that it recognizes an item containing only a list
      which is itself tight as potentially an item in a tight list (#9161).

  * Text.Pandoc.MIME: Ensure we use `.svg` not `.svgz` as extension
    for `image/svg+xml` mime type. This fixes issues with embedded
    SVG images in docx output, among other things (#9195).

  * Text.Pandoc.Class: `openURL` improvements for data uris.
    Only treat data URI as `base64` if ';base64' is specified.
    Otherwise treat as UTF-8 (not 100% reliable but should cover most
    other cases). Strip off `;base64` (or `;charset=...` or whatever)
    from mime type (#9195).

  * Text.Pandoc.SelfContained: Improve treatment of embedded SVGs
    (#9206, #8948).

    + Ensure unique ids for elements by prefixing SVG id.
    + Ensure SVG `id` attribute except when `use` element is used.
    + Remove `width`, `height` attributes from svg element when `use`
      element is used. Instead, add `width` and `height` 100% to the
      `use` element. This seems to get the sizing right.

  * Text.Pandoc.Citeproc: Don't link citations if
    `suppress-bibliography` specified, for there will be nothing to
    link to (#9163).

  * epub.css: add styling for sup and sub (#9160).

  * Switch from `base64` to `base64-bytestring` (#9233).

  * Use newest versions of commonmark, commonmark-extensions,
    commonmark-pandoc, texmath, typst, skylighting, skylighting-core.

  * Benchmark: use standalone documents for reader tests.
    Otherwise typst reader benchmark fails. Note: this means that we are now
    parsing longer documents, so bench results on readers won't be comparable
    to before.

  * MANUAL.txt: update defaults file docs for bibliography fields (#9173).
    Recommend using top-level `bibliography` `csl`, etc. instead
    of a nested `metadata` field. Reason: `${USERDATA}` and `${HOME}`
    are only expanded in these contexts, not in `metadata`.

  * Move man pages to pandoc-cli package (#9245).

## pandoc 3.1.9 (2023-10-27)

  * Make `reference-section-title` work with `jats+element_citations`
    (#9021).

  * Add `bits` as synonym of `jats` as input format.

  * JATS reader:

    + Modify JATS reader to handle BITS too (#9138, Julia Diaz).
      Add provision for title-group, book, book-part-wrapper, book-meta,
      book-part-meta, book-title, book-title-group, index, toc, legend,
      title, collection-meta
    + Fix handling of alt-text (#9130, Julia Diaz). Previously we were
      looking for an attribute that doesn't exist in JATS; alt-text is
      provided by a child element.

  * CommonMark reader:

    + Handle `Ext_tex_math_gfm` (#9121). Parse GFM-specific math
      constructions when `tex_math_gfm` enabled.

  * DokuWiki reader:

    + Allow autolinks to be avoided using e.g. `https:%%//%%...` (#9153).
    + Parse `` and `` as block-level code (#9154).
      Previously we treated them as inline code in some contexts,
      but that is not how DokuWiki works.

  * LaTeX reader:

    + Better handle spacing commands `\hfill`, `\vfill`, `\hskip`,
      `\vskip`, etc. (#9150).
    + Fix incorrect abbreviation for astronomical unit (#9125,
      Michael McClurg).

  * Markdown reader:

    + Fix blindspot with superscript in links (#8981).
      Previously `[^super^](#ref)` wasn't parsed as a link, due to
      code that was meant to prevent footnote markers from being
      recognized as reference links.  This commit tightens up that
      code to avoid this bad effect. We have also added a new
      restriction on footnote labels: they cannot contain the characters
      `^`, `[`, or `]`. Though this is technically a breaking change, we
      suspect that the impact will be minimal, as it's very unlikely
      people would be using these characters in their note labels.
    + Don't apply `--default-image-extension` to data URIs (#9118).
    + More accurate check that a normalCite is not a link,
      bracketed span, or reference (#9080).

  * HTML reader:

    + Allow th to close td and vice versa (#9090).
    + Parse task lists using input elements (#9047, Seth Speaks).

  * Creole reader:

    + Handle empty cells correctly (#9141, Sascha Wilde).

  * Org writer:

    + Escape literal `*`, `|`, `#` at beginning of line with ZWS (#9159).

  * ICML writer:

    + Prevent doubled attributes (#9158).

  * Powerpoint writer:

    + Fix a corruption error caused when the document used both a
      regular png and a png in a data URI (#9113). (Similarly for any
      other image format.) The problem was that duplicate entries in
      `[Content Types].xml` were being created, one for the mime type
      `image/png`, one for `image/png;base64`.

  * LaTeX writer:

    + Fix rowspans in tables so they use the width of
      the column (`=` as the width parameter) (#9140).
    + Don't treat table as "simple" if they have col widths.
      This should help fix a problem wherein some grid tables with
      colspans were overly wide (#9140).
    + Fix uneven indents in line block output (#9088).

  * JATS writer: fix 3.1.4 regression in handling block-level metadata
    (#9092).

  * Ms writer: improvements in image handling (#4475).

    + PDFPIC is now used for PDF images in figures.
    + Inline images that are postscript or PDF are rendered using
      PSPIC or PDFPIC. This isn't ideal, because they will still be
      rendered as if in a separate paragraph, but it's probably
      better than just printing the image name.
    + Units are included in height.

  * HTML writer:

    + If raw format is an HTML side deck format, emit it (James J Balamuta).

  * Typst writer:

    + Add `#box` around image to make it inline. (#9104)
      An `#image` by itself in typst is a block-level element.
      To force images to be inline (as they are in pandoc), we need
      to add a box with an explicit width. When a width is not given
      in image attributes, we compute one from the image itself, when
      possible.
    + Don't allow long heading to wrap (#9132).
    + Escape `(` (#9137). If unescaped `(` occurs in
      certain contexts, it can be parsed as function application.

  * Man writer:

    + Fix some spacing issues around links (#9120).
      We need to use `\c` before a `.UR` or `.MT`, to avoid
      an extra space, and also after.  To ensure that a space
      at the beginning of the following line doesn't get swallowed
      up, we escape it with `\`.
    + Use UR, MT macros for URLs, emails (#9120).

  * Text.Pandoc.Extensions:

    + Add `Ext_tex_math_gfm` constructor to Extension (#9121).
      [API change]. This handles two GitHub-specific syntaxes for math.
      This is now default for `gfm`, in addition to `tex_math_dollars`.
    + Remove duplicates for `Ext_raw_html` and `Ext_pipe_tables`
      in some of the lists (Tim Stewart).

  * Text.Pandoc.Metadata: Add helpful message on some metadata
    YAML errors (#9155).

  * Text.Pandoc.Shared:

    + `splitSentences`: don't split after initials.
      This improves the man and ms writer output, preventing
      sentence breaks after initials.
    + Add `addPandocAttributes` function [API change].
      This is meant to simplify addition of attributes to Pandoc
      elements: for elements that don't have a slot for attributes, an
      enclosing Div or Span is added to hold the attributes.

  * MANUAL.txt:

    + Clarify that formatting can't cross line boundaries
      in line blocks (#9119).
    + Fix legacy option for citation (#8737, 3w36zj6)

  * Update `et` translations (priiduonu).

  * Updated `no` translations (Stephan Daus).
    Renamed no.yaml (macrolanguage Norwegian) to nb.yaml (Norwegian Bokmål).
    Created soft symbolic link from no.yaml pointing to nb.yaml.

  * Lua subsystem: Use the newest LPeg version (lpeg-1.1.*) (#9107,
    Albert Krewinkel).

  * Default `epub.css`: Apply style to h6, format styles, and
    combine identical styles under shared selectors (samuel-weinhardt).

  * Update nix flake with dependencies (piq9117).

  * LaTeX template: fix `\CSLBlock` vertical space (John Purnell).

  * Allow tasty 1.5 and Diff 0.5.

  * Require commonmark-extensions 0.2.4, commonmark 0.2.4.

  * Require texmath 0.12.8.4. This should improve math in
    powerpoint, fixing empty boxes around roots in some cases.

  * Require typst 0.3.2.1

## pandoc 3.1.8 (2023-09-08)

  * JATS reader:

    + Ignore `` element (#9057, Julia Diaz).
    + Fix conversion of date to ISO 8601 format (#8865).

  * LaTeX template:

    + Add code allow `\cite` to break across lines (#9050).
    + Fix regression with CSL `display="block"` (#7363).
      This restores the line break before the block.
    + Rewrite `CSLReferences` environment to avoid depending on
      `enumitem`, which plays badly with beamer.  Instead we use
      a regular list environment. Thanks to @jpcirrus for the
      concept (#9053).
    + Restore the pre-3.1.7 format of the `CSLReferences`
      environment, which again has two parameters. The first
      determines whether a hanging indent is used (1 = yes, 0 = no),
      and the second is the entry line spacing (0 = none).
    + Add a strut to avoid inconsistencies in spacing (#9058).
    - Remove a break at the end of `CSLRightInline` to avoid
      inconsistencies in spacing. It shouldn't be necessary
      because the paragraph should extend to the right margin (#9058).

  * LaTeX writer:

    + Fix regression with figure labels (#9045). In 3.1.7, pandoc
      added two labels to LaTeX figure environments, one with a
      phantomsection.
    + Fix default citeproc entry-spacing. According to the CSL manual,
      the default entry spacing is 1. We were treating it as 0 (#9058).

  * HTML writer:

    + Use the ID prefix in the ID for the footnotes section (#9044,
      Benjamin Esham).
    + Fix CSL entry-spacing default (#9058).

  * Text.Pandoc.Citeproc:  always include an `entry-spacing` attribute
    in the Div if the bibliography element contains an entry-spacing
    attribute (previously we omitted it when it was 0) (#9058).

  * Clean up pandoc's own man pages by regenerating with pandoc 3.1.7.

  * pandoc-lua-engine: bump lower bound for pandoc (#9046).

  * Depend on texmath 0.12.8.2, fixing binom in typst writer (#9063).

## pandoc 3.1.7 (2023-08-31)

  * Org reader:

    + Don't parse alphabetical lists unless the `fancy_lists` extension is
      enabled (#9042).
    + Allow escaping commas in macro arguments (Amneesh Singh).

  * JATS reader:

    + Support for `` metadata (#9037, Julia Diaz).
      metadata objects with multiple fields are created, matching the
      structure in JATS.
    + Correct name of JATS element `attrib`.

  * Markdown reader:

    + Support images with wikilink syntax, e.g. `![[foo|bar]]`, when
      one of the `wikilinks` extension is enabled (#8853).
    + Allow a citation or reference link to be parsed after a `!` (#8254).
    + Fix dropped `!` before nonexistent reference (#9038).

  * LaTeX writer:

    + Fix regression in escaping URLs (#9043).
    + Use `\cite` and `\bibitem` to link up citations, even with citeproc.
      (#9031). This will give us better accessibility; when tagging is
      enabled, the citation can be linked to the bibliography entry.
      This changes some of the details of the layout and the default
      template. We now make `CSLReferences` a special enumitem list
      that will contain `\bibitem`s. Internal links inside citations to
      ids beginning in `ref-` are creating using `\cite` instead of
      `\hyperref`.
    + Use `\phantomsection` and `\label` instead of `\hypertarget` (#9022).
    + Use `\hyperref` for LaTeX internal links, `\hyperlink` for
      beamer (since `\hyperref` doesn't seem to work) (#9022).
    + Backslash-escape `%` and `#` in URLs (#9014).

  * JATS writer:

    + Fix placement of ref-list when no title is specified for the
      reference section (#9017). (In this case we place it in `back`
      with an empty title.)

  * Man writer:

    + Avoid a `.PP` right after a section heading (#9020).
      This is at best a no-op (in groff man and mandoc) and at worst
      (in some formatters) may create extra whitespace.
    + We revert the fanciness introduced in #7506, which employs a
      custom font name `V` and a macro that makes this act like boldface
      in a terminal and monospace in other formats.  Unfortunately,
      this code uses a mechanism that is not portable (and does not
      work in mandoc) (#9020).
    + Instead of using `V` for inline code, we simply use `CR`.
      Note that `\f[CR]` is emitted instead of plain `\f[C]`,
      because there is no `C` font in man.  (This produces warnings
      in recent versions of groff, #9020.)
    + For code blocks, we now use the `.EX` and `.EE` macros,
      together with `.IP` for spacing and indentation.  This gives
      more standard code that can be better interpreted e.g. by mandoc
      (#9020).

  * Man template: don't emit `.hy`, regardless of setting of
    `hyphenate` variable (#9020).

  * LaTeX template: special redefinition of `\st` for CJK (#9019).
    soul's version raises on error on CJK text.

  * Use latest skylighting-format-blaze-html (#7248).
    This works around a longstanding iOS Safari bug that caused long
    lines to be displayed in a different font size in highlighted code.

  * Allow skylighting 0.14 (and require it in pandoc core).

  * Allow text 2.1.

## pandoc 3.1.6.2 (2023-08-22)

  * Org reader: allow example lines to end immediately after the colon
    (Brian Leung).

  * Docx reader:

    + Omit "Table NN" from caption (#9002).
    + Avoid spurious block quotes in list items (#8836).

  * JATS reader: Fix display of block elements (#8889, Julia Diaz).
    A number of block elements, like disp-quote, list, and disp-formula, were
    always treated as inlines if appearing inside paragraphs, even if their
    usage granted a separate block.

  * HTML reader: avoid duplicate id on header and div (#8991).

  * Typst writer:

    + Use `~` for nonbreaking space, and escape literal `~` (#9010).
    + Put the label in right place for Div, use `#block` (#8991).
      Previously we were putting the label at the beginning of
      the Div's contents, but according to the documentation such a
      label gets attached to the *preceding* element.  We now use an
      explicit `#block` and add the label at the end.

  * LaTeX writer:

    + Improve escaping of URIs in href, url (#8992).
    + Improve internal links and targets (#8744). We no longer
      wrap section headings in a `\hypertarget`. This is unnecessary
      (hyperref creates an anchor based on the label) and it interferes with
      tagging. In addition, we now use `\hyperref` rather than `\hyperlink`
      for internal links. Currently `\hypertarget` is still being used for
      link anchors not on headings. Thanks to @u-fischer.

  * HTML format templates (style.html): Fix typo in clause for svg
    (Jackson Schuster).

  * Use lastest texmath, typst-symbols, typst. Targets typst 0.7.


## pandoc 3.1.6.1 (2023-08-11)

 * HTML reader: properly calculate RowHeadColumns (#8984). This fixes a
   bug in the calculation of the number of header columns in table row.
   It also changes the algorithm for determining the table body's
   RowHeadColumns based on the numbers of head columns in each row.
   Previously we used the max, and #8634 switched to the min, which
   led to bad results. Now we only set RowHeadColumns to a non-zero value
   if *all* rows have the same number of head columns.

  * OpenDocument writer:

    + Implement syntax highlighting for inline and block code (#6710).
    + Support highlighted text in ODT/OpenDocument writers for Span
      with class `mark` (#8960). The color can be adjusted by
      modifying the Highlighted style.

  * Typst writer: escape `//` so it doesn't get interpreted as a comment
    (#8966).

  * ChunkedHTML writer: Fix regression including MathJax script (#8967).
    The fix for #8620 caused the script to be included when the table of
    contents but not the body text of a page contains math.  But it broke the
    case where the table of contents doesn't contain math but the page does.
    This patch fixes the issue.

  * Text.Pandoc.SelfContained:

    + Retain attributes in SVG tag when referring to another
      SVG's content using `` (#8969).
    + Allow units in width and height for SVG. Units are optional but allowed.
    + Don't coerce calculated SVG dimensions to Int.
    + fix calculation of SVG width and height. We were computing width and
      height from viewBox incorrectly (#8969).
    + Add clause for SVG to default CSS for HTML (#8969).
    + Ensure that width and height attributes don't get specified
      twice is both the img tag and the svg include them (#8965).
    + Omit unnecessary attributes xmlns, xmlns:xlink, and version on
      SVG element (#8965).
    + Use 20 character rather than 40 character hashes for generated IDs
      (#8965).

  * Use pandoc-types 1.23.1. This fixes a regression with toJSONFilter (#8976),
    which in 1.23.0.1 no longer worked on pure values of type `a -> [a]`.

  * Use ghc 9.6 for release builds (#8947).

  * Fix some links in FAQs (Diogo Almiro).


## pandoc 3.1.6 (2023-07-20)

  * Fix CVE-2023-38745, a variant of the vulnerability in CVE-2023-35936.
    Guilhem Moulin noticed that the fix to CVE-2023-35936 was incomplete.
    An attacker could get around it by double-encoding the malicious
    extension to create or override arbitrary files.

  * `--embed-resources`: Use inline SVG instead of data uris for SVG
    images in HTML5 (#8948). Note that SelfContained does not have
    access to the writer name, so we check for HTML5 by determining
    whether the document starts with ``. This means
    that inline SVG won't be used when generating document fragments.

  * Fix regression on short boolean arguments (#8956).
    In 3.1.5 boolean arguments were allowed an optional argument
    (`true|false`).  This created a regression for uses of fused
    short arguments, e.g. `-somyfile.html`, which was equivalent
    to `-s -omyfile.html`, but now raised an error because
    pandoc attempted to parse `o` as a boolean `true` or `false`.
    This change allows the fused short arguments to be used again.
    Note that `-strue` will be interpreted as `-s` with an
    argument `true`, not as `-s -t -rue`.  It is best to
    use long option names with the optional boolean values,
    to avoid confusion.

  * Make `--epub-title-page`'s argument optional. It takes a boolean
    argument, and now that all of our boolean flags take such an
    argument, we can make this one optional for consistency.

  * Improve errors for illegal output formats. Previously if you did
    `pandoc -s -t bbb`, it would give you an error about the missing
    `bbb` template instead of saying that `bbb` is not a
    supported output format.

  * Improve errors for incorrect command-line option values (#8879).
    Always give the name of the relevant argument.

  * Fix typo on error message for incorrect `--preserve-tabs` argument.
    Thanks @fsoedjede

  * Docx reader: use SVG version of image if present (#7244).
    Previously the backup PNG was exported even if an SVG was
    present, but the SVG should be preferred.

  * Typst reader: fix regression in recognition of display math (#8949).
    The last release caused all math to be parsed as inline math.

  * JATS writer: don't use `` for inline code (#8889).
    It is intended for block-level code.

  * HTML writer: don't make line blocks sensitive to `--wrap` (#8952).

  * RST writer: fix figure handling (#8930, #8871).
    This fixes a number of regressions from pandoc 2.x.
    Properly handle caption, alt attribute in figures.
    No longer treat a paragraph with a single image in it as a figure
    (we have a dedicated Figure element now).

  * Docx writer: Copy "mirror margins" property from reference.docx (#8946).

  * Text.Pandoc.UTF8: Deprecate `decodeArg` which is now a no-op.
    This was needed for old base versions which we no longer support.

  * Use released skylighting, typst.

  * Allow latest commonmark-extensions. This allows entities in wikilinks.

  * Switch back to using ghc 9.2 for linux and Windows binary releases
    (#8947, #8955). With ghc 9.4+, we were getting AVX instructions
    in the amd64 binary, which aren't supported on older hardware.
    For maximum compatibility we switch back to ghc 9.2, which doesn't
    cause the problem. (As documented, ghc should not be emitting these
    instructions, so we aren't clear on the diagnosis, but the cure
    has been tested.)

  * Change Windows release build to use cabal instead of stack.

## pandoc 3.1.5 (2023-07-07)

  * Allow all boolean flags to take an optional `true` or `false` value
    (#8788, Sam S. Almahri). The default is true if no value is specified,
    so this is fully backwards-compatible.

  * Support `--id-prefix` for markdown output (#8878)

  * Markdown reader:

    + Add strictness annotations to fix a memory leak (#8762).

  * Typst reader:

    + Use typst-hs 0.3.0.0, which is more robust, fixes many bugs, and
      targets typst 0.6.
    + Package loading is now supported, as long as the package has been
      cached or is local.
    + Rewrite Typst reader in a way that makes it easier to extend.
    + Filter out CR in raw.
    + Handle block content for link element.
    + Handle block-level content in text element.
    + Handle style, align, place in inline contexts too.
    + Improve info message for skipped elements.

  * Add typst reader tests (#8942).

  * MediaWiki reader:

    + Revise treatment of "link trail." Previously we only included ASCII
      letters. That is correct for English but not for, e.g., Spanish (see
      comment in #8525). A safer approach is to include all letters except
      those in the CJK unified ideograph ranges.

  * AsciiDoc writer:

    + Make modern AsciiDoc the target for `asciidoc` (#8936).
      The AsciiDoc community now regards the dialect parsed by `asciidoctor`
      as the official AsciiDoc syntax, so it should be the target of our
      `asciidoc` format. The `asciidoc` output format now behaves like
      `asciidoctor` used to. `asciidoctor` is a deprecated synonym. For
      the old `asciidoc` behavior (targeting the Python script),
      use `asciidoc_legacy`. The templates have been consolidated. Instead of
      separate `default.asciidoctor` and `default.asciidoc` templates, there
      is just `default.asciidoc`.
    + Text.Pandoc.Writers.AsciiDoc API changes:
      - `writeAsciiDoc` now behaves like `writeAsciiDoctor` used to.
      - `writeAsciiDoctor` is now a deprecated synonym for `writeAsciiDoc`.
      - New exported function `writeAsciiDocLegacy` behaves like
        `writeAsciDoc` used to.
    + Update line-through for asciidoc writer to custom inline style (#8933,
      Kevin Broch).

  * Typst writer:

    + Support `unlisted` class in headings (#8941).
    + Consolidate bibliography files into one `#bibliography` command (#8937).
    + Improve handling of autolinks (#8931).

  * Docx writer:

    + Make relative widths work in tables. This didn't work before because we
      were missing an attribute that tells Word to used fixed widths rather
      than computing optimal ones.

  * DokuWiki writer: fix lists with Div elements (#8920).
    The DokuWiki writer doesn't render Divs specially, so their presence in
    a list (e.g. because of custom-styles) need not prevent a regular
    DokuWiki list from being used. (Falling back to raw HTML in this case is
    pointless because no new information is given.)

  * LaTeX writer:

    + Fix babel name for `fa` (should be `persian`).
    + Prevent babel language from being imported twice (#8925).

  * Text.Pandoc.Class:

    + Add `toTextM` [API change]. This is like `Text.Pandoc.UTF8.toText`,
      except:

      - it takes a file path as first argument, in addition to
        bytestring contents
      - it raises an informative error with source position if
        the contents are not UTF8-encoded

    This replaces `utf8ToText` whenever we have the filename and are
    in a PandocMonad instance. This will lead to more informative error
    messages for UTF8-encoding, indicating the file path and byte offset
    where the error occurs (#8884).

  * Remove invalid term "Subject" from Turkish translations (#8921).

  * stack.yaml: add pkg-config to nix packages (#8927, pacien).

  * Allow aeson 2.2.

  * MANUAL: Add clarification on --section-divs. Closes #8882.


## pandoc 3.1.4 (2023-06-24)

  * Fix a security vulnerability in MediaBag and T.P.Class.IO.writeMedia.
    This vulnerability, discovered by Entroy C, allows users to write
    arbitrary files to any location by feeding pandoc a specially crafted
    URL in an image element.  The vulnerability is serious for anyone
    using pandoc to process untrusted input.  The vulnerability does
    not affect pandoc when run with the `--sandbox` flag. [CVE-2023-35936]

  * Allow `epub-title-page` to be used in defaults files (#8908).

  * Issue `Extracting` info message (in `--verbose` mode) when using
    `--extract-media` or extracting media temporarily in PDF production.

  * HTML reader: Update TableBody RowHeadColumns caculation (#8634,
    Ruqi). This change sets RowHeadColumns to the minimum value of each row,
    which gives better results in cases where rows have different numbers
    of leading th tags.

  * Dokuwiki reader: retain image query parameters as attributes (#8887, echo0).

  * Textile reader: Add support for link references (#8706, Stephen Altamirano).
    Textile supports what it calls "link alias", which are analogous to
    Markdown's reference-style links.

  * LaTeX reader: support alt text on images (#8743, Albert Krewinkel).

  * Commonmark reader: Make `implicit_figures` work again.
    Support for this (introduced in #6350) disappeared when we made an
    architectural change.

  * JATS reader:

    + Add footer and multiple body parsing to table reader (#8765, Noah Malmed).
    + Parse references title from ref-list (#8365).

  * JATS writer:

     + Make `--number-sections` work.
     + Include title in ref-list (#8364). Previously the reference title ended
       up in a separate section at the back of the body instead of in the ref-list
       in the back matter.

  * Mediawiki writer: allow highlighting to work for F# language
    (Adelar da Silva Queiróz).

  * LaTeX writer: Fix escaping of `&` in `\href` and `\url` (#8903).

  * Docx writer:

    + Fix localization of "Abstract" title (#8702).
    + Allow `abstract-title` to be specified in docx metadata (#8794).

  * ChunkedHTML writer: Make math work in top-level page (#8915).

  * Text.Pandoc.Logging: add new log message type `ScriptingWarning`
    [API change] (Albert Krewinkel).

  * Lua: report warnings from Lua scripts (Albert Krewinkel).
    Lua's warning system is plugged into pandoc's reporting architecture.
    Warnings that are raised with the Lua `warn` function are now reported
    together with other messages.

  * Use crypton-connection instead of connection (#8896, Felix Yan).
    Follows the change introduced in tls 1.7.0.

  * Bump versions for skylighting-core, skylighting.

  * Include lua/module/sample.svg in cabal extra-source-files (Felix Yan).

  * Add Nynorsk (New Norwegian) translations (Per Christian Gaustad).

  * Add tests for `fillMediaBag`/`extractMedia`.

  * INSTALL.md:

    + Mention alternatives to LaTeX to generate PDF (Norwid Behrnd).
    + Update Linux install links (harabat).

  * pandoc-extras.md: add to "Academic publishing workflows" (#8696,
    Vladimir Alexiev).

## pandoc 3.1.3 (2023-06-07)

  * New output format: `typst`.

  * New module: Text.Pandoc.Readers.Typst [API change].

  * DocBook reader:

    + Support more emphasis roles (Albert Krewinkel).
      The role "bf" is taken to indicate "bold face", i.e.,
      "strongly emphasized" text, while "underline" leads to
      underlined text.

  * JATS reader:

    + Improve title and label parsing in the JATS reader (#8718,
      Noah Malmed.)
    + Add rowspan, colspan and alignment to cells in jats table
      reader (#8408, Noah Malmed)

  * Org reader (Albert Krewinkel):

    + Require abstract environment to use lowercase.
    + Treat `#+NAME` as synonym for `#+LABEL` (#8578).

  * ODT reader:

    + Allow lists in table cells (#8892).
    + Allow frames inside spans (#8886).

  * RST reader:

    + Fix sorting on anonymous keys (#8877). This fixes a link
      resolution bug bug affecting RST documents with anonymous links.

  * HTML reader:

    + Fix iframe with data URI of an image (#8856).
      In this case we don't want to try to parse the data at the URL.
      Instead, create an image inside a div.

  * RTF reader:

    + Fix bug in table parsing (#8767). In certain cases, text before a
      table was being incorporated into the table itself.

  * Docx reader:

    + Introduce support for Intense Quote (Stephan Meijer).

  * Markdown reader:

    + Disallow escaping of `~` and `"` in `markdown_strict` (#8777,
      Albert Krewinkel). This matches the behavior of the legacy
      `Markdown.pl` as well as what is described in the manual.

  * LaTeX reader: ignore args to column type in `\multicolumn` (#8789).

  * HTML writer:

    + Use first paragraph in task item as checkbox label (#8729, Albert
      Krewinkel).

  * Ms writer:

    + Coerce titles to inlines (#8835). Block-level formatting is not
      allowed inside `.TL`.

  * LaTeX writer:

    + Fix width for multicolumn simple table (#8831).

  * Jira writer:

    + Use first code block class as highlighting language (#8814, Albert
      Krewinkel). The writer no longer searches the list of
      classes for a known programming language but always uses
      the first class in that list as the language identifier.

  * OpenDocument writer:

    + Handle row header column cells as header cells (#8764, Michael Stahl).
    + Fix invalid `text:p` inside `text:p` from meta (#8256).

  * ODT writer:

    + Don't add settings.xml (Michael Stahl). This will cause defaults
      to be used, which is what we want.
    + Don't add unnecessary Configurations2 directory (Michael Stahl).
    + Don't add thumbnail (Michael Stahl).
    + Put `manifest.version` on directory file-entry (Michael Stahl).
      See ODF 1.3 part 2, 4.16.14.1.
    + Stop validator complaints by producing ODF 1.3 (Michael Stahl).

  * MediaWiki writer:

    + Remove links from inside links in mediawiki writer (#8739,
      Wout Gevaert).

  * Typst writer:

     + Omit bibliography if `citations` not enabled (#8763).
      With this change, the typst writer will omit the `#bibliography`
      command when `citations` is not enabled.  (If you want to use
      pandoc's own `--citeproc`, you should combine it with
      `-t typst-citations` to disable native typst citations.
    + Use `<..>` for labels, create internal links.
    + Use `#footnote` for notes (#8893).
    + Fix alignment issue in lists.  It's an aesthetic issue
      only; the first line had an extra space indent after the
      list marker.

  * Commonmark writer:

    + Use shortcut reference links: commonmark supports these.

  * EPUB template: add `lang` attribute to `` (Gabriel Lewertoski).

  * Template styles.html: fix task-list styling in reveal.js
    (#8731, Albert Krewinkel).

  * LaTeX template: Fix `\babelfont` (#8728).

  * Text.Pandoc.Parsing:

    + Remove unnecessary 'spaces' in `parseFromString`.

  * Text.Pandoc.ImageSize: Drop BOM at start of SVG if present.
    Otherwise our code can fail to determine image size.

  * Lua subsystem:

    + Fix value of PANDOC_SCRIPT_FILE for custom readers & writers
      (#8781, Albert Krewinkel). The value did not hold the actual
      file path for scripts in the *custom* folder of the datadir.

  * Fix YAML in translation files for `cs` and `pl` (#8787).

  * Fix pdf output via typst (#8754).  One must now use `typst
    compile` rather than `typst`.

  * MANUAL.txt:

    + Added note that the user will need to create the user data
      dir (#8727).
    + Add `wikilinks` to non-default extensions (Ilona).
    + Update link to custom djot writer (Albert Krewinkel).
    + Better link to citation syntax.
    + Fix typo (sdhoward).
    + Note that `#` fancy list markers don't work with commonmark (#8772,
      William Lupton).
    + Add commonmark `fenced_div` note (#8773, William Lupton).
    + Move highlighting documentation, with minor adjustments
      (William Lupton).
    + Fix inaccurate statement about spaces and tabs in template
      syntax
      (Frank Seifferth).

  * Update documentation for org-mode (Christian Christiansen, #8716).

  * doc/lua-filter.md:

    + Fix typos (#8734, perro tuerto).
    + Fix anchor (Toni Dietze).
    + Use full field name in example (#8857, Matt Dodson).
    + Fix copy-paste error (#8798, thron7).

  * CONTRIBUTING.md: update info on ghc versions.

  * INSTALL.md:

    + Fix cabal install instructions (Albert Krewinkel).
    + Use more relevant link to NetBSD/pkgsrc entry (Charlotte Koch).
    + Fix Windows install instructions for winget (#8799).

  * Tests: Rename test/docx/block_quotes_parse_indent.native for
    consistency (Stephan Meijer).

  * Add `tls` constraint on cabal.project. This is needed to
    avoid problems caused by the transition to `crypton`.

  * Require texmath 0.12.8.


## pandoc 3.1.2 (2023-03-26)

  * Add a Lua REPL (Albert Krewinkel). This can be started
    with `pandoc lua -i`.  It is also possible to instruct a filter to
    open the REPL at a certain point, for debugging (see `pandoc.cli.repl`).

  * Support `typst` as a `--pdf-engine`.

  * Add typst writer (#8713).  New module Text.Pandoc.Writers.Typst,
    exporting `writeTypst` [API change].

  * Org reader:

    + Allow zero width space as an escape character (#8716,
      Christian Christiansen). Allow the character U+200B to be used as
      an escape character as described in the Org-mode documentation
      ().

  * DocBook reader:

    + Handle "book" for xref references (#8712, Andres Freund)
      This also adds a test xref to book and part.
    + Handle `` (#8712).

  * HTML reader:

    + Fix behavior with `-native_spans-raw_html` (#8711). Previously with
      this configuration, ``s were not treated as inline elements at all.

  * HTML writer:

    + Avoid duplicate classes (#8705).
    + Use img element instead of embed for `.svg.gz` and `.png.gz` etc. (#8699).
    + HTML writer footnotes changes (#8695): when `--reference-location=section`
      or `=block`, use an `aside` element for the notes rather than a `section`.
      When `--reference-location=section`, include the `aside` element inside
      the section element, rather than outside. (In slide shows, this option
      causes footnotes on a slide to be displayed at the bottom of the slide.)

  * EPUB writer:

    + Use different structure for epub footnotes (#8676, see #8672, #5583).
      Many EPUB readers are thrown off by pandoc's current footnote
      output.  Both the ol and the fact that the footnote backlink is
      at the end of the note seem to pose problems.
      With this commit, we now create a list of aside (or div) elements,
      instead of an ordered list. Each element begins with a note number
      that is linked back to the note reference.  (So, the backlink occurs
      at the beginning rather than the end.) Thanks to @Porges and @lewer.

  * Docx writer:

    + Include abstract title (#8702). Uses localized term for abstract.

  * Markdown writer:

    + Use implicit figures if there's a caption but no alt (#8689,
      Albert Krewinkel).

  * Jira reader (Albert Krewinkel):

    + Add panel title as nested div (#8681).
    + Require jira-wiki-markup 1.5.1 (#8680). This fixes a bug in the parser
      that caused text between two exclamation marks to be parsed as an
      image. The first `!` of image markup must now be followed by a
      non-space character; otherwise, the enclosed text is parsed as
      normal content.

  * Ms writer:

    + Fix handling of Figure (#8660).

  * ICML writer:

    + Fix images with data (#8675). The Contents element
      should be inside Properties.

  * LaTeX writer:

    + Add Chinese to Babel languages.
    + Fix background image in Beamer when there are figure environments (#8671,
      Martín Pozo).

  * LaTeX template:

    + Add `babelfonts` variable to default LaTeX template.
      This allows specifying certain fonts to be used with
      certain babel languages. Thanks to Frederik Elwert.
    + Fix highlight/underline with lualatex (#8707). We need the lua-ul package
      instead of soul, which doesn't work with lualatex.

  * Lua (Albert Krewinkel):

    + Add `pandoc.cli.repl` function
    + Fix `json.encode` for nested AST elements. Ensures that objects with
      nested AST elements can be encoded as JSON.
    + Auto-generate docs for pandoc modules.
    + Load text module as `pandoc.text`. This only affects the name in the
      Lua-internal documentation. It is still possible to load the modules
      via `require 'text'`, although this is deprecated.
    + Move docs from module `text` to `pandoc.text`
      The latter is easier to use and more consistent with the other modules.
    + Keep the Lua stack clean A metatable used during initialization was
      not properly removed from the stack. Likewise, accessing the
      CommonState from Lua previously led to the pollution of the
      Lua stack with a left-over value.
    * Add function `pandoc.format.from_path`.
    + Allow to get the JSON encoding of log messages.

  * Text.Pandoc.Format: Add new function `formatFromFilePaths` [API change]
    (#8710, Albert Krewinkel).

  * The old Text.Pandoc.App.FormatHeuristics module has been removed.

  * In `--version`, use Windows `%APPDATA%` variable to describe
    user data dir (#8686, Pablo Rodríguez).

  * Text.Pandoc.App.CommandLineOptions: don't lowercase arg to `--from`/`--read`
    (Albert Krewinkel). This prevented users to use custom writers with
    uppercase characters in their filenames. Format-normalization,
    including lower-casing of format identifiers, happens during
    format parsing.

  * Documentation:

    + Add `doc/nix.md`.
    + Add `doc/extras.md`. This was formally in the website repo.
    + `doc/lua-filters.md`: improve docs for `pandoc.zip`.

  * Factor out `make_macos_release.sh` from the release candidate workflow.
    Use cabal instead of stack to build the macos binary.

  * Modify linux/make_artifacts.sh so it will work on cirrus.

  * Switch to hslua-2.3

  * Depend on latest releases of texmath, doclayout.

## pandoc 3.1.1 (2023-03-05)

  * EPUB reader: Give additional information in error if the epub
    zip container can't be unpacked.

  * TSV reader: don't gobble tabs as whitespace (#8661).

  * Org reader: accept empty tables (#8659).

  * LaTeX reader: fix multiplication syntax for tabular (#8658).
    We recognized `*{6}{...}` but not `*6{...}` or `*6c`.

  * Docx reader: parse image alt texts in LibreOffice generated files.
    LibreOffice tags images slightly differently than Word; this change lets
    the parses take that difference into account when looking for an image
    description (alt text).

  * DocBook reader:

    + Fix `` references to tables in DocBook files
      (#8626, Pavol Otto).
    + Parse `figure` as a Figure element in the AST (#8668).

  * JATS reader: avoid generating duplicate figure captions (#8669).

  * RST reader: align with spec in syntax for role names (#8653).
    In particular, we now allow colons in row names.

  * Add note on converting from .doc format to FAQs (#8654).

  * Trap error in getAppUserDataDirectory (#8648).
    This can raise an error if pandoc is run in a non-user environment.

  * LaTeX writer: do not use longtable foot with Beamer (#8638, Albert
    Krewinkel). The table foot is made part of the table body, as
    otherwise it won't show up in the output. The root cause for
    this is that longtable cannot detect page breaks in Beamer.

  * LaTeX template: Add CJKsansfont and CJKmonofont for XeLaTeX
    (#8656, Yudong Jin). `CJKsansfont` and `CJKmonofont` will be
    set for xelatex only if `CJKmainfont` is also provided.

  * URL style in ConTeXt (#8612, Thomas Hodgson). Previously, a
    URL like this would be in monospace text:
    `\useURL[url1][https://example.com]`.  Now, it will match the
    main text unless the `linkstyle` variable is set, which
    controls the styling of all links.  Closes #8602.

  * Asciidoc writer: Properly escape `|` in table cells (#8665).

  * asciidoc{,tor} template: fix revision date when author is unset
    (#8637, arcnmx). Revision line syntax is only valid in
    combination with an author line, so the date attribute must be
    set explicitly when the author is missing

  * HTML writer: allow "track" element to be treated as block-level HTML
    (#8629).

  * Include needed polyfill when MathJaX is used (#8625).

  * JATS writer: include alt-text in ``,
    `` elements (#8631, Albert Krewinkel).

  * Chunked HTML writer:  Retain metadata in processing sections
    for chunked HTML (#8620).  Previously we suppressed metadata
    in all but the top page, in order to prevent the title block
    from being printed on every page. This prevented use of
    custom variables set by metadata fields.  This commit moves
    to a better solution: a conditional in the default template
    restricts the title block to the top page.

  * Lua API:

    + Add new function `pandoc.system.cputime` (Albert
      Krewinkel).  The function returns the CPU time consumed by
      pandoc and can be used to benchmark Lua computations.
    + Add module `pandoc.json` to handle JSON encoding (#8605,
      Albert Krewinkel).

  * Use pandoc-lua-marshal 0.2.1 (Albert Krewinkel).
    All major AST elements now have `__tojson` metamethods that return the
    JSON representation of an element. This allows to JSON-encode these
    elements with libraries that respect the `__tojson` metamethod,
    including dkjson.

  * Use latest zip-archive.  This allows pandoc to open certain
    epubs that it could not open before.

  * Use commonmark-extensions 0.2.3.4. This fixes some bugs involving
    definition lists and inline formatting.

  * Use latest skylighting-format-context

  * MANUAL.txt:

    + Document chunk-template in defaults file.
    + Remove obsolete "raw content in a style" section.
    + Revise documentation for `--mathml` to reflect support in all major
      browsers (#8667).

  * docs/custom-readers.md: Update JSON parsing example. The example now
    uses the built-in `pandoc.json` library to parse the API output.

  * doc/press.md: Add article on CiTO in J Cheminform by @egonw.

  * doc/lua-filters.md: fix typo in `run_json_filter` (Morgan Willcock).


## pandoc 3.1 (2023-02-09)

  * Fix regression with `--print-highlight-style` option (#8586).

  * Add new `--chunk-template` option (#8581), allowing more control
    over the filenames in chunked HTML output.

  * Text.Pandoc.App: Add `optChunkTemplate` constructor to Opt [API change].

  * Text.Pandoc.Options: add `writerChunkTemplate` constructor to
    `WriterOptions` [API change].

  * Text.Pandoc.Chunks: add Data, Typeable, Generic, ToJSON, FromJSON
    instances for `PathTemplate` [API change].

  * Text.Pandoc.Citeproc: Fix bug in `metaValueToReference` (#8611).
    This bug caused us to get some repeated content when converting
    MetaBlock to Inlines.

  * Textile reader:

    + Support footnote backlinks (#8585, Stephen Altamirano).
    + Don't allow brackets in URLs (#8582).

  * ODT reader: fix blockquote indent detection (#3437, Daniel Kessler).

  * LaTeX writer: include short figure/table caption if one is given
    (Albert Krewinkel). Short captions are used by LaTeX when generating
    the list of figures or list of tables. Adding a short caption will
    now overwrite the full caption in these lists.

  * Powerpoint writer: fix handling of simple figures (#8565,
    Albert Krewinkel). This ensures that simple figures are displayed
    in the same way as before the introduction of a dedicated `Figure`
    constructor in the AST.

  * Improve handling of `%` in bib(la)tex parsing (#8597, #8595).

  * Use released skylighting 0.13.2.1

  * INSTALL.md: direct people to cabal install pandoc-cli.

  * doc/lua-filters.md: document 'Figure' type and constructor (Albert
    Krewinkel). Fix typos (Martin Joerg).

  * Fix link in manual (#8583, Salim B).

## pandoc 3.0.1 (2023-01-25)

  * Fix use of extensions with custom readers (#8571).

  * Text.Pandoc.Writers.Shared: export `setupTranslations` [API change].
    Use this in HTML and OpenDocument writers, to ensure that
    translations are set up properly even when we don't go through
    `convertWithOpts`.

  * LaTeX reader: fix regression in macro resolution for environments (#8573).

  * Chunked HTML writer: Fix handling of images with absolute URLs (#8567).

  * HTML writer:

    + Don't omit newlines in task lists.
    + Don't disable checkboxes in task lists (#8562).

  * Ensure that automatically set variables `pandoc-version`, `outputfile`,
    `title-prefix`, `epub-cover-image`, `curdir`, `dzslides-core` can be
    overridden by `--variable` on the command line. Previously they would
    create lists in the template Context, which is not desirable.

  * Fix man page copying in `linux/make_artifacts.sh` (#8566).
    Previously we were copying the pandoc-server.1 pandoc page to pandoc-lua.1.

  * pandoc.cabal: remove pandoc.cabal, stack.cabal from extra-source-files
    (#8560). The problem is that if these are in extra-source-files, then they
    get put in the tarball, and then anyone trying to build the source
    from an unpacked tarball will run into the problem that cabal.project
    and stack.yaml refer to pandoc-server, pandoc-lua-engine, and
    pandoc-cli, which aren't in the tarball.

  * Require texmath 0.12.6 for better MathML output.

  * Fix typo in Lua filter documentation (Carlos Scheidegger).

  * Fix formatting of link in pandoc-server.md (James Scott-Brown).

  * Minor changelog fixups.

## pandoc 3.0 (2023-01-18)

  * Split pandoc-server, pandoc-cli, and pandoc-lua-engine
    into separate packages (#8309). Note that installing
    the `pandoc` package from Hackage will no longer give you the
    `pandoc` executable; for that you need to install `pandoc-cli`.

  * Pandoc now behaves like a Lua interpreter when called as
    `pandoc-lua` or when `pandoc lua` is used (#8311, Albert Krewinkel).
    The Lua API that is available in filters is automatically
    available to the interpreter. (See the `pandoc-lua` man page.)

  * Pandoc behaves like a server when called as `pandoc-server`
    or when `pandoc server` is used. (See the `pandoc-server` man page.)

  * A new command-line option `--list-tables`, causes tables to be
    formatted as list tables in RST (#4564, with Francesco Occhipinti).

  * New command line option: `--epub-title-page=true|false` allows
    the EPUB title page to be omitted (#6097).

  * `--reference-doc` can now accept a URL argument (#8535) and
    load a remote reference doc.

  * `--version` output no longer contains version info for dependent
    packages. Instead, it contains a "Features" line that indicates
    whether the binary was compiled with support for acting as a server,
    and for using Lua filters and Custom writers.

  * A new option `--split-level` replaces `--epub-chapter-level`
    and affects both EPUB and chunked HTML output.  `--epub-chapter-level`
    will still work but is deprecated.

  * Multiple input files with `--file-scope`: fix case where the links
    are URL-encoded, e.g. with `%20` (#8467).

  * Produce error if `--csl` is used more than once (#8195, Prat).

  * Remove deprecated `--atx-headers` option.

  * Remove deprecated option `--strip-empty-paragraphs`.

  * In `--verbose` mode add message when running citeproc (as with
    other filters).

  * Add new `mark` extension for highlighted text in Markdown,
    using `==` delimiters (#7743).

  * Add new extensions `wikilinks_title_after_pipe` and
    `wikilinks_title_before_pipe` for `commonmark` and `markdown`.
    (#2923, Albert Krewinkel). The former enables links of style
    `[[Name of page|Title]]` and the latter `[[Title|Name of
    page]]`. Titles are optional in both variants, so this works
    for both: `[[https://example.org]]`, `[[Name of page]]`. The
    writer is modified to render links with title `wikilink` as
    a wikilink if a respective extension is enabled. Pandoc will
    use `wikilinks_title_after_pipe` if both extensions are
    enabled.

  * Add prefixes to identifiers with `--file-scope` (#6384).
    This change only affects the case where `--file-scope` is used
    and more than one file is specified on the command line.
    In this case, identifiers will be prefixed with a string
    derived from the file path, to disambiguate them. For example,
    an identifier `foo` in `contents/file1.txt` will become
    `contents__file1.txt__foo`.  Links will be adjusted accordingly:
    if `file2.txt` links to `file1.txt#foo`, then the link will
    be changed to point to `#file1.txt__foo`.  Similarly, a link
    to `file1.txt` will point to `#file1.txt`.  A Div with an
    identifier derived from the file path will be added around
    each file's content, so that links to files will still work.

  * New output format: `chunkedhtml`. This creates a zip file
    containing multiple HTML files, one for each section,
    linked with "next," "previous," "up," and "top" links.
    (If `-o` is used with an argument without an extension,
    it is treated as a directory and the zip file is automatically
    extracted there, unless it already exists.) The top page will
    contain a table of contents if `--toc` is used.  A
    `sitemap.json` file is also included. The option
    `--split-level` determines the level at which sections are
    to be split.

  * Support complex figures (Albert Krewinkel, Aner Lucero).
    There is now a dedicate Figure block constructor for
    figures.  The old hack of representing a figure as
    `Para [Image attr [..alt..] (source, "fig:title")]`
    has been dropped.  Here is a summary of figure support
    in different formats:

    + Markdown reader: paragraphs containing just an image are treated as
      figures if the `implicit_figures` extension is enabled. The identifier
      is used as the figure's identifier and the image description is also
      used as figure caption; all other attributes are treated as belonging
      to the image.
    + Markdown writer: figures are output as implicit figures if possible,
      via HTML if the `raw_html` extension is enabled, and as Div elements
      otherwise.
    + HTML reader: `
` elements are parsed as figures, with the caption taken from the respective `
` elements. + HTML writer: the alt text is no longer constructed from the caption, as was the case with implicit figures. This reduces duplication, but comes at the risk of images that are missing alt texts. Authors should take care to provide alt texts for all images. Some readers, most notably the Markdown reader with the `implicit_figures` extension, add a caption that's identical to the image description. The writer checks for this and adds an `aria-hidden` attribute to the `
` element in that case. + JATS reader: The `` and `` elements are parsed into figure elements, even if the contents is more complex. + JATS writer: The `` and `` elements are used write figures. + LaTeX reader: support for figures with non-image contents and for subfigures. + LaTeX writer: complex figures, e.g. with non-image contents and subfigures, are supported. The `subfigure` template variable is set if the document contains subfigures, triggering the conditional loading of the *subcaption* package. Contants of figures that contain tables are become unwrapped, as longtable environments are not allowed within figures. + DokuWiki, Haddock, Jira, Man, MediaWiki, Ms, Muse, PPTX, RTF, TEI, ZimWiki writers: Figures are rendered like Div elements. + Asciidoc writer: The figure contents is unwrapped; each image in the the figure becomes a separate figure. + Classic custom writers: Figures are passed to the global function `Figure(caption, contents, attr)`, where `caption` and `contents` are strings and `attr` is a table of key-value pairs. + ConTeXt writer: Figures are wrapped in a "placefigure" environment with `\startplacefigure`/`\endplacefigure`, adding the features caption and listing title as properties. Subfigures are place in a single row with the `\startfloatcombination` environment. + DocBook writer: Uses `mediaobject` elements, unless the figure contains subfigures or tables, in which case the figure content is unwrapped. - Docx writer: figures with multiple content blocks are rendered as tables with style `FigureTable`; like before, single-image figures are still output as paragraphs with style `Figure` or `Captioned Figure`, depending on whether a caption is attached. + DokuWiki writer: Caption and "alt-text" are no longer combined. The alt text of a figure will now be lost in the conversion. + FB2 writer: The figure caption is added as alt text to the images in the figure; pre-existing alt texts are kept. + ICML writer: Only single-image figures are supported. The contents of figures with additional elements gets unwrapped. + OpenDocument writer: A separate paragraph is generated for each block element in a figure, each with style `FigureWithCaption`. Behavior for single-image figures therefore remains unchanged. + Org writer: Only the first element in a figure is given a caption; additional block elements in the figure are appended without any caption being added. + RST writer: Single-image figures are supported as before; the contents of more complex images become nested in a container of type `float`. + Texinfo writer: Figures are rendered as float with type `figure`. + Textile writer: Figures are rendered with the help of HTML elements. + XWiki: Figures are placed in a group. * Changes in custom readers/writers: + It is now possible to have a custom reader and a custom writer for a format together in the same file. The file may also define a custom template for the writer. + Pandoc now checks the folder `custom` in the user's data directory for a matching script if it can't find one in the local directory. Previously, the `readers` and `writers` data directories were searched for custom readers and writers, respectively. Scripts in those directories must be moved to the `custom` folder. + Custom readers used to implement a fallback behavior that allowed to consume just a string value as input to the `Reader` function. This has been removed, the first argument is now always a list of sources. Use `tostring` on that argument to get a string. * New module Text.Pandoc.Writers.ChunkedHTML, exporting `writeChunkedHtml` [API change]. * We now set the `pandoc-version` variable centrally rather than in the writers. One effect is the man writer now emits a comment with the pandoc version. * pandoc-server: + Add simple CORS support to pandoc-server (#8427). + Print message to stderr when starting the server. * Docx reader: + Mark unnumbered headings with class `unnumbered` (#8148, Albert Krewinkel). This change ensures good conversion results when converting with `--number-sections`. + Support parsing of highlighted text. + Fix handling of `oMathPara` in `w:p` with other content (#8483). * ODT reader: + Fix relative links. ODT adds a `../` to relative links (see #3524); this needs to be removed when converting from ODT. + Handle "section" elements (#8409). + Rename Text.Pandoc.Readers.Odt -> Text.Pandoc.Readers.ODT, for consistency with Writers.ODT. Rename `readOdt` -> `readODT`. [API change] * DocBook reader: + Support href on link even in a fragment (#8437). (We now just look for an `href` attribute without worrying about the namespace.) + Parse title from imageobject/objectinfo (#8437). * JATS reader: + Handle uri element in references (#8270). * Ipynb reader: + Add cell id to attachment filename when storing in MediaBag (#8415). Otherwise attachments with the same name can overwrite each other. * LaTeX reader: + Skip parenthenized args of toprule, midrule, etc (#8242). + Handle `##` macro arguments properly (#8243). + Remove unused function `toksToString` in Parsing module. + Support more `soul` commands, including `\hl`. + Add `unnumbered` class for `\part*` (#8447) + Fix `TEXINPUTS` handling (#8392). If `TEXINPUTS` ends with `:`, then the system default `TEXINPUTS` is added. We handle this by just adding the working directory in this case. + Parse short table caption (see jgm/pandoc-types#103). This is not too useful yet, because writers don't do anything with the short caption. * MediaWiki reader: + Parse table cell with attributess, to support rowspan, colspan (#8231, Ruqi). + Refine "blending" rules for MediaWiki links (#8525, Ruqi). The rules for "blending" characters outside a link into the link are described here: https://en.wikipedia.org/wiki/Help:Wikitext#Blend_link These pose a problem for CJK languages, which generally don't have spaces after links. However, it turns out that the blending behavior, as implemented on Wikipedia, is (contrary to the documentation) only for ASCII letters. This commit implements that restriction, which fixes the problem for CJK. * HTML reader: + Fix regression for `` (#8330). It was no longer being parsed as Code (Justin Wood). * RST reader: + Support `mark` role for round-trip. * Textile reader: + Support linked images (#8541). + Fix strong emph ending with link (#8540). + Adding a Parser to look for ordered list start attribute numbers if any (#2465, vkraven). + Handle empty paragraphs (#8487). Also, if attributes are added explicitly to a paragraph, put it in a Div with the attributes. * Markdown reader: + Allow fenced code block "bare" language to be combined with attributes (#8174, Siphalor), e.g. ```` ```haskell {.class #id} ``` ```` + Allow table caption labels to start with lowercase `t` (#8259). + Grid tables: allow specifying a table foot by enclosing it with part separator lines, i.e., row separator lines consisting only of `+` and `=` characters (#8257, Albert Krewinkel). E.g.: ``` +------+-------+ | Item | Price | +======+=======+ | Eggs | 5£ | +------+-------+ | Spam | 3£ | +======+=======+ | Sum | 8£ | +======+=======+ ``` + Fix `implicit_header_references` with duplicate headings (#8300). Documentation says that when more than one heading has the same text, an implicit reference `[Heading text][]` refers to the first one. Previously pandoc linked to the last one instead. This patch makes pandoc conform to the documented behavior. + Parse highlighted text inside `==..==` if `mark` extension enabled. * Org reader: + Allow org-ref v2 citations with `&` prefix (#8302). + Make `#+pandoc-emphasis-pre` work as expected (#8360, Amir Dekel). * BibTeX reader: + Fix handling of `%` in `url` field (#7678). `%` does not function as a comment character inside `url` (where URL-encoding is common). + Allow `url` field in `bibtex` as well as `biblatex` (#8287). This field is not officially supported for BibTeX, but many styles can handle it (), and others will ignore it. + Support `software` type in biblatex <-> CSL conversions (#8504). + Make sure `version` field comes through in biblatex (#8504). * BibTeX writer: + Pass through `url` even for `bibtex` (#8287). * Org writer: + Pass through unknown languages in code blocks (#8278), instead of producing `begin_example`. + Use span attributes `tag-name` in headers as tags (#8513, Albert Krewinkel). This enables round-tripping of tags in Org headings. * EndNote reader: + Better error when parsing EndNote references fails. * DocBook writer: + Rename Text.Pandoc.Writers.Docbook -> Text.Pandoc.Writers.DocBook. Rename `writeDocbook` -> `writeDocBook`, for consistency with the DocBook reader's naming. [API change] + Fix position of textobject (#8437). It is a child of `inlinemediaobject`, not `imageobject`. + Add regression tests for #8437. + Render image alt text using textobject element (#8437). + Don't indent contents of title element. + Store "unnumbered" class in DocBook role attribute (#1402, lifeunleaded). * ConTeXt writer (Albert Krewinkel): + Support syntax highlighting for code. + Always use `\type` for inline code. Inline codes that contained curly braces where previously rendered with `\mono`; this led to unexpected results when the presentation of `\type` was customized, as those changes would not have been applied to code rendered with `\mono`. + Add support for unlisted, unnumbered headings (#8486). + Support `tagging` extension (Albert Krewinkel). Paragraphs are enclosed by `\bpar` and `\epar` commands, and `highlight` commands are used for emphasis. This results in much better tagging in PDF output. * LaTeX writer: + Do not repeat caption on headless tables (Albert Krewinkel). The caption of headless tables was repeated on each page that contained part of the table. It is now made part of the "first head", i.e. the table head that is printed only once. + Add separator line between table's body and its foot (Albert Krewinkel). + Ignore languages with no babel equivalent, instead of generating an invalid command in the preamble (#8325). + Use `\includesvg` for SVGs and include the `svg` package (#8334). + Use `soul` instead of `ulem` for strikeout, underline (#8411). This handles things like hyphenation, line breaks, and nonbreaking spaces better. + Use `\toprule\noalign{}` instead of `\toprule()` in tables, and similarly for `\midrule` and `\bottomrule` (#8223). This facilitates redefining `\toprule`, `\midrule`, and `\bottomrule` without needing to gobble the ()s. (Those who redefine these macros on the assumption that they will be followed by `()` may need to change their definitions.) + Support highlighted text for Span with class `mark`. * JATS writer: + Use `` for LineBreak in the limited contexts that accept it (#8344). + Officially deprecate `writeJATS` in favor of `writeJatsArchiving`. * RTF writer: + Add space after unicode escape commands (#8264). This fixes a bug that caused characters to disappear after unicode escapes. * RST writer: + Render tables as list tables when the `--list-tables` option is specified (`writerListTables`) (#4564, Francesco Occhipinti). + Improve inline escaping rules (#8380). + Use special `mark` role for Span with class `mark`. * Commonmark writer: + Ensure that we don't have blank lines in raw HTML (#8307). * HTML writer: + Only add role attribute in HTML5 (#8241). It is not valid in HTML4. + Avoid aria-hidden in code blocks for HTML4 (#8241). + Only treat `. . .` as a slide pause in slides, and not in regular HTML output (#8281). + Properly merge classes for headings of level > 6 (#8363). + Prevent `` inside `` (#7585). If a link text contains a link, we replace it with a span. + Replace deprecated aria roles for bibliography entries (#8354). `doc-biblioentry` -> `listitem`, `doc-bibliography` -> `list`. + Remove obsolete stuff about mathml-script. This was a shim we used to include for mathml support. We don't do anything with this any more, so this is dead code. + Include math links if there are raw commands or environments that can be interpreted as math e.g. by MathJax (#8469). + Add prooftree to list of math environments (#8462). This will cause raw LaTeX prooftree environments to be rendered appropriately when `--mathjax` is used. * HTML, Markdown writers: filter out empty class attributes (#8251). These should not be generated by any pandoc readers, but they might be produced programmatically. * Markdown writer: + Avoid HTML fallbacks in the generated TOC (Albert Krewinkel, #8131). The generated table of contents usually has IDs for each TOC link, allowing to link back to specific parts of the TOC. However, this leads to unidiomatic markup in formats like gfm, which do not support attributes on links and hence fall back to HTML. The IDs on TOC items are now removed in that case, leading to more aesthetic TOCs. + Escape `!` before `[` (#8254). + Support `mark` extension. * AsciiDoc writer: + In link text, only replace commas with entities when they're in Str elements. If a link contains an image, it may have attributes, and the commas there should not be converted (see #8437, #8070). * ODT writer: + Fix relative links (#3524). * Docx writer: + Better handling of tables in lists (#5947). Previously the content of each list cell was indented when the table belonged to a list item. + Indent tables in list items (#5947). + Adjust correct attribute on `lang` element (#7022). For East Asian languages, we need to adjust `w:eastAsia` rather than `w:val`. This allows normal fonts to be used for any Latin-font text. Similarly, for bidi languages, we need to adjust `w:bidi` rather than `w:val`. We treat `he` and `ar` as bidi languages, `zh`, `ja`, `ko` as East Asian languages. + Support relative image widths (Albert Krewinkel). Image widths given in percent are interpreted to be relative to the text width. Previously, percent widths were taken relative to the image's native size, inconsistently with other writers. + Avoid using 'error' for unassigned table cells (#8468). Instead, throw a regular pandoc error. + Render a Span with class `mark` as highlighted. Currently yellow is hardcoded. * MediaWiki writer: + Use the 'new' table structure, so that colspan and rowspan are supported (Wout Gevaert). * Man writer: + Use UTF-8 by default for non-ascii characters (#8507). Only use groff escapes if `--ascii` has been specified on the command line (`writerPreferAscii`). * ICML writer: + Use Contents element for images with raw data instead of a link with a data: uri (#8398). * EPUB writer: + Refactor to use Text.Pandoc.Chunks. + Refactored and simplified code. + Make title page optional (#6097). * Ms writer: + Properly format display equations (#8308). + Remove -C option on PSPIC. Some old versions don't support this option, and since it's the default it shouldn't be necessary. * XWiki writer: + Use template if it is specified (#8296). Previously templates were ignored. * LaTeX template: + Set fonts after Beamer theme (Jeremie Knuesel). Beamer themes such as metropolis and saintpetersburg change the default fonts. This change gives precedence to the user font settings by moving them after the loading of the Beamer theme. + Set `\babelfont` when `mainlang` and `lang` are specified and `pdflatex` is not being used (#8538). This is needed for good results in Arabic. + Add variable `urlstyle` (#8429, Amar Al-Zubaidi). This is set to `same` by default, so users should not experience any change. * HTML template: + Remove default font size, line height and font family in default inline css (#8423). `mainfont`, `fontsize`, and `linestretch` can still be used as before; the only difference is that we no longer provide opinionated defaults. This commit also adds a `maxwidth` variable that sets `max-width`; if not set, 36em is used as a default. + Add `code { hyphens: manual; }`. + Use `styles.citations.html` partial in `styles.html`. + Fix class name `hanging` -> `hanging-indent` in `styles.citations.html`. + Put Consolas before Lucida Console for code font (#8543). This is to prevent Lucida Console from being used on Windows, where it causes spacing issues in some applications, with boldface glyphs wider than regular ones. * EPUB CSS changes: Reduce the amount of inline CSS used for EPUBs (#8379). Almost everything is now in the default EPUB CSS (`data/epub.css`), which can be overridden either by putting `epub.css` in the user data directory or by using `--css` on the command line. Inline styles are only used for syntax highlighting (which depends on the style specified, and is only included on pages with highlighted code) and for bibliography formatting (which can depend on the CSL style, and is only used in the page containing the bibliography). Note that, for compatibility with older readers, we don't use flexbox to style `column/columns` divs by default, as we do in HTML. Instead, we use an older method which only works when there are two `column` divs inside a `columns` div. If you need more than two columns and aren't worried about support for older EPUB readers, you can modify the default CSS (there is a comment in the CSS telling you what to do). * Reveal.js template: prevent line-wrapping of parallax options (#8503, Albert Krewinkel). * reference.pptx: Remove unsupported element (#8342, #6338, Link Swanson). The default template contained text above the header, which can mislead users into thinking there is a way to put text there using pandoc. * Text.Pandoc.Readers.Metadata: + Fix metadata parsing corner case (#8465). + Don't fail on inline metadata beginning with newline (#8358). * Text.Pandoc.App: + Move initial input-to-Pandoc code to internal submodule (Albert Krewinkel). + Change `parseOptionsFromArgs` and `parseOptions` (#8406) They now return `Either OptInfo Opt`. [API change] + Add `OptInfo` type [API change]. + Add `handleOptInfo` function. This performs the IO actions for things like `--version` that were previously done in `parseOptionsFromArgs` [API change]. + `convertWithOpts`: add argument for a `ScriptingEngine` [API change]. + Unify check for standalone output (Albert Krewinkel). + New `optEpubTitlePage` field on `Opt` [API change] (#6097). + Remove `optEpubChapterLevel`, add `optSplitLevel` [API change]. + Export `IpynbOutput(..)` [API change]. * Text.Pandoc.App.OutputSettings: + Remove unused field `outputWriterName` in `OutputSettings`. * Text.Pandoc.Citeproc: + Check both extension and mime type to determine bibliography type when the bibliography is fetched remotely (#7151). + CslJson: allow an object with `items` property in addition to an array of references. This is what is returned by e.g. `https://api.zotero.org/groups/904125/items?v=...&format=csljson` + Require a digit for an implicit "page" locator inside explicit locator syntax `{...}` (#8288). Previously a locator specified as `{}` would be rendered as `p.` with nothing after it. + Update `sub verbo` to `sub-verbo` (#8315). This is a change in the term's canonical name in citeproc. As a result of this change, `sub verbo` locators have not worked in pandoc since citeproc 0.7. + Text.Pandoc.Citeproc.MetaValue: remove unused function `metaValueToPath`. + Add internal module Text.Pandoc.Citeproc.Name (#8345). This exports `toName`, which previously had been part of T.P.Citeproc.BibTeX, and allows for cleaner module dependencies. * Export module `Text.Pandoc.Slides` [API Change] (Albert Krewinkel). * Add new module Text.Pandoc.Format [API change] (Albert Krewinkel). The module provides functions and types for format spec parsing and processing. The function `parseFormatSpec` was moved from Text.Pandoc.Extensions to the new module and renamed to `parseFlavoredFormat`. It now operates in a PandocMonad and is based on the updated types. * Text.Pandoc.Sources: + Add UpdateSourcePos instances for String and strict and lazy ByteString [API change]. * Text.Pandoc.Extensions: + Fix JSON decoding of Extensions (#8352, Albert Krewinkel). + Add new exported function `readExtension` [API change]. + Remove `parseFormatSpec` [API change]. This has been moved to Text.Pandoc.Format and renamed as `parseFlavoredFormat` (Albert Krewinkel). + Simpler implementation of Extensions based on Set (benchmarks show no performance penalty). + Add `CustomExtension` constructor to `Extension` [API change]. + Remove `Bounded`, `Enum` instances for `Extension`. + Add `extensionsToList` function. + Revise `readExtension` so it can handle `CustomExtension`, and so that it returns a Text rather than `Maybe Text`. + Add `showExtension` [API change]. + Add `Ext_mark` extension [API change]. + Add `Ext_tagging` constructor [API change] (Albert Krewinkel). + Add `Ext_wikilinks_title_after_pipe`, `Ext_wikilinks_title_before_pipe` [API change] (Albert Krewinkel). * Text.Pandoc.PDF: + Fix `papersize` on PDF generation via ms (#8403). We need to set an option in pdfroff in addition to including a macro in the ms file. With this fix, `-Vpapersize=a4` should be sufficient to produce A4 PDF via ms. + Change default background color of PDFs generated via HTML (#8422, Marcin Serwin). * Text.Pandoc.MIME: + Base module on package `mime-types`, which is already a transitive dependency (#8277, Albert Krewinkel). + Remove deprecated overrides (#8292). * Text.Pandoc.XML: + Re-export `lookupEntity` from commonmark-hs [API change]. * Text.Pandoc.Parsing: + Remove gratuitious renaming of Parsec types. We were exporting Parser, ParserT as synonyms of Parsec, ParsecT. There is no good reason for this and it can cause confusion. Also, when possible, we replace imports of Text.Parsec with Text.Pandoc.Parsing. The idea is to make it easier, at some point, to switch to megaparsec or another parsing engine if we want to. New (re-)exports: `Stream(..)`, `updatePosString`, `SourceName`, `Parsec`, `ParsecT`. Removed exports: `Parser`, `ParserT` [API change]. + Export `errorMessages`, `messageString` [API change]. + Export `fromParsecError`, which can be used to turn a parsec ParseError into a regular PandocParseError (#8382) [API change]. + Remove `nested` [API change]. It was not being used, and in fact it was a bad idea from the beginning, as it had no hope of solving the problem it was introduced to solve. + Change `characterReference`, `charsInBalanced`. `characterReference` so they now return a Text (some named references don't correspond to a single Char). Use the the `lookupEntity` function from commonmark-hs instead of the slow one from tagsoup [API change]. + `charsInBalanced` now takes a Text parser rather than a Char parser as argument [API change]. * Text.Pandoc.Shared: + Export `textToIdentifier` [API change]. + Remove deprecated `crFilter`. [API change] + Remove deprecated `deLink`. [API change] + Deprecate `notElemText`. + Deprecate `makeMeta`. + Remove `pandocVersion` (now available in Text.Pandoc.Version as `pandocVersionText`). + Remove `findM` [API change]. This was only used in one place, and can be replaced with simpler code. + Remove deprecated `makeMeta` [API change]. + Remove `ordNub` [API change]. This is just `nubOrd` from Data.Containers.ListUtils. + Remove `mapLeft` [API change]. This is just a synonym for Bifunctor.first. + Remove `elemText`, `notElemText` [API change]. + Drop export of `pandocVersion` and `pandocVersionText`, which are now exported by Text.Pandoc.Version. + Remove `escapeURI`, `isURI`. These are now exported by Text.Pandoc.URI, and removing them from Shared helps make the module structure more straightforward. + Use LineBreak as default block sep in `blocksToInlines`. (#8499, Albert Krewinkel). This change also affects the `pandoc.utils.blocks_to_inlines` Lua function. + `defaultUserDataDir` is no longer exported (it has been moved to T.P.Data) [API change]. + New function `figureDiv`, offering offers a standardized way to convert a figure into a Div element (Albert Krewinkel) [API change]. * Text.Pandoc.Writers.Shared: + Export `htmlAddStyle`, `htmlAlignmentToString` and `htmlAttrs` [API change] (Wout Gevaert). + Use 'literal tag' instead of 'text (T.unpack tag)' in `tagWithAttrs` (Wout Gevaert). + `toTableOfContents`: handle nested Divs better (#8402). * Rename Text.Pandoc.Network.HTTP -> Text.Pandoc.URI. This is still an unexported internal module. Export `urlEncode`, `escapeURI`, `isURI`, `schemes`, `uriPathToPath`. Drop exports of `schemes` and `uriPathToPath`. * Text.Pandoc.URI `isURI`: don't require non-ASCII characters to be escaped (#8508). * Rename Text.Pandoc.Readers.LaTeX.Types -> Text.Pandoc.TeX (internal module). * Text.Pandoc.Options: + WriterOptions now has a field `writerListTables`, specifying that list tables be used in RST output [API change]. + New `writerEpubTitlePage` field on `WriterOptions` (#6097) [API change]. + Remove `writerEpubChapterLevel`, add `writerSplitLevel` [API change]. * Text.Pandoc.Filter: + Export `applyFilters` [API change]. + Export `applyJSONFilter` [API Change] (Albert Krewinkel). + Parameterize `applyFilters` over scripting engine [API change] (Albert Krewinkel). * New exported module Text.Pandoc.Chunks [API change]. This module provides functions to split Pandoc documents into chunks to be rendered in separate files, e.g. one per section. Internal identifiers are rewritten appropriately to point to the new locations (#6122). * Text.Pandoc.Readers: + Change argument type of `getReader`, so it takes a `FlavoredFormat` instead of a `Text` [API change] (Albert Krewinkel). * Text.Pandoc.Writers: + Change argument type of `getWriter`, so it takes a `FlavoredFormat` instead of a `Text` [API change] (Albert Krewinkel). * Text.Pandoc.Templates: + Do not try to normalize input to `getDefaultTemplate` (Albert Krewinkel). The function `getDefaultTemplate` no longer splits off extension modifers from the given format, as that conflicts with using custom writers as formats. Haskell library users should use `getDefaultTemplate <=< (fmap formatName . parseFlavoredFormat)` if the input format can still contain extensions. The same is true for `compileDefaultTemplate`, which calls `getDefaultTemplate` internally + Add Wrapper type documentation (#8490, William Rusnack). * New exported module Text.Pandoc.Scripting (Albert Krewinkel). The module contains the central data structure for scripting engines (e.g., Lua) [API change]. * Text.Pandoc.Error: + Add new PandocError constructor `PandocNoScriptingEngine` [API change] (Albert Krewinkel). + Add new PandocError constructor `PandocFormatError` [API change] (Albert Krewinkel). The new error is used to report problems with input or output format specifications. + Add new PandocError constructor `PandocNoTemplateError` (Albert Krewinkel). + Remove `PandocParsecError` constructor from `PandocError` (#8385). Henceforth we just use `PandocParseError`. * New module Text.Pandoc.Version, exporting `pandocVersionText` and `pandocVersion` [API change]. `pandocVersion` returns a `Version` instead of a `Text`, which is consistent with `pandocTypesVersion`. * Text.Pandoc.Class: + Make `getPOSIXTime`, `getZonedTime` sensitive to `SOURCE_DATE_EPOCH` environment variable if set (#7093). (`getTimestamp` was already sensitive.) This ensures that EPUB builds are reproducible. + Text.Pandoc.Class no longer exports `readDataFile`, `readDefaultDataFile`, `setTranslations`, and `translateTerm` [API change]. + Text.Pandoc.Class now exports `checkUserDataDir` [API change]. * T.P.Class.IO: export function `writeMedia` [API change] (Albert Krewinkel). This is useful for the `pandoc.mediabag` module. * Separate out Text.Pandoc.Data and Text.Pandoc.Translations from Text.Pandoc.Class (#8348). This makes Text.Pandoc.Class more self-contained. + Text.Pandoc.Data is now an exported module, providing `readDataFile` and `readDefaultDataFile` (both formerly provided by Text.Pandoc.Class), and also `getDataFileNames` (formerly unexported in Text.Pandoc.App.CommandLineOptions) and `defaultUSerDataDir` (formerly provided by Text.Pandoc.Shared). [API change] + Text.Pandoc.Translations is now an exported module (along with Text.Pandoc.Translations.Types), providing `readTranslations`, `getTranslations`, `setTranslations`, `translateTerm`, `lookupTerm`, `readTranslations`, `Term(..)`, and `Translations` [API change]. * Text.Pandoc now exports Text.Pandoc.Data and `setTranslations` and `translateTerm` {API change]. * Export module Text.Pandoc.Class.IO [API change]. The module is useful when defining instances of class PandocMonad for types that are also instances of MonadIO. * Remove modules Text.Pandoc.Writers.Custom and Text.Pandoc.Readers.Custom [API Change] (Albert Krewinkel). The functions `writeCustom` and `readCustom` are available from module Text.Pandoc.Lua. * Text.Pandoc.Server: + Split this module into a separate package, `pandoc-server`, allowing the `pandoc` library to be compiled without server support. + Return object if JSON is accepted. Previously we just returned a JSON-encoded string. Now we return something like: ``` { "output": "

hello

" "base64": false, "messages": [ { "message": "Not rendering RawInline (Format \"tex\") \"\\\\noe\"", "verbosity": "INFO" } ], } ``` This is a change in the pandoc-server JSON API. + Set translations in the writer based on `lang` metadata. + Return error in JSON object if response is JSON. + Remove `parseServerOpts`. [API change] * Text.Pandoc.Lua: + This module has been moved to a separate package, `pandoc-lua-engine`. + Export `applyFilter`, `readCustom`, and `writeCustom`. No longer export the lower-level function `runFilterFile` [API change]. + Change type of `applyFilter` [API Change] (Albert Krewinkel). The module Text.Pandoc.Filter.Lua has been merged into Text.Pandoc.Lua. The function `applyFilter` now has type ``` haskell applyFilter :: (PandocMonad m, MonadIO m) => Environment-> [String]-> FilePath-> Pandoc-> m Pandoc ``` where `Environment` is defined in Text.Pandoc.Filter.Environment. + Export new function `getEngine` [API Change]. The function returns the Lua scripting engine. + Add unexported modules T.P.Lua.Reader, T.P.Lua.Writer. These contain the definitions of `readCustom` and `writeCustom` that were previously in T.P.Readers.Custom and T.P.Writers.Custom. + Cleanup module dependencies, for a cleaner module dependency graph. + The `writeCustom` function has changed to return a Writer and an ExtensionsConfig [API change]. This allows ByteString writers to be defined. + The `readCustom` function has changed to return a Reader and an ExtensionsConfig [API change]. * Lua subsystem (Albert Krewinkel): + The whole Lua subsystem has been moved to a separate package, `pandoc-lua-engine`. `pandoc` does not depend on it. `convertWithOpts` has a new parameter that can be used to pass in the scripting engine defined in `pandoc-lua-engine` (or a different one, in theory). + Fix the behavior of Lua "Version" objects under equality comparisons (#8267). + Support running Lua with a GC-collected Lua state. + Ensure that extensions marshaling is consistent. + Produce more informative error messages for pandoc errors. Errors are reported in Lua in the same words in which they would be reported in the terminal. + Add new module `pandoc.format`. The module provides functions to query the set of extensions supported by formats and the set of extension enabled per default. + Add function `pandoc.template.apply`. + Add function `pandoc.template.meta_to_context`. The functions converts Meta values to template contexts; the intended use is in combination with `pandoc.template.apply`. + Allow Doc values in `WriterOptions.variables`. The specialized peeker and pusher function for `Context Text` values does not go via JSON, and thus keeps Doc values unchanged during round-tripping. + Fix rendering of Lua errors in Lua, so that the `Error running Lua` message is not prepended multiple times. + Add new module `pandoc.zip`. + Allow strings in place of compiled templates (#8321). This allows to use a string as parameter to `pandoc.template.apply` and in the WriterOptions `template` field. + Rename `reader_extensions`/`writer_extensions` globals as `Extensions` (#8390). + Add `pandoc.scaffolding.Writer` (#8377). This can be used to reduce boilerplate in custom writers. + Fix peeker for PandocError (Albert Krewinkel). String error messages were incorrectly popped of the stack when retrieving a PandocError. + Add functions `pandoc.text.toencoding`, `pandoc.text.fromencoding` (#8512, Albert Krewinkel). + Add `pandoc.cli` module. Allow processing of CLI options in Lua. + Support `-D` CLI option for custom writers. A new error `PandocNoTemplateError` (code 87) is thrown if a template is required but cannot be found. + Allow table structure as format spec. This allows to pass structured values as format specifiers to `pandoc.write` and `pandoc.read`. + Add function `pandoc.mediabag.write` (Albert Krewinkel). + Add module `pandoc.structure` (Albert Krewinkel). The function `make_sections` has been given a friendlier interface and moved to the new module; the old `pandoc.utils.make_sections` has been deprecated. * Custom writers: + The global variables `PANDOC_DOCUMENT` and `PANDOC_WRITER_OPTIONS` are no longer set when the writer script is loaded. Both variables are still set in classic writers before the conversion is started, so they can be used when they are wrapped in functions. + Deprecate classic custom writers. + Add function `pandoc.write_classic`. The function can be used to convert a classic writer into a new-style writer by setting it as the value of `Writer`: ``` lua Writer = pandoc.write_classic ``` or to fully restore the old behavior: ``` lua function Writer (doc, opts) PANDOC_DOCUMENT = doc PANDOC_WRITER_OPTIONS = opts load(PANDOC_SCRIPT_FILE)() return pandoc.write_classic(doc, opts) end ``` + Support extensions in custom writers. Custom writers can define the extensions that they support via the global `writer_extensions`. The variable's value must be a table with all supported extensions as keys, and their default status as values. For example, the below specifies that the writer supports the extensions `smart` and `sourcepos`, but only the `smart` extension is enabled by default: ``` lua writer_extensions = { smart = true, sourcepos = false, } ``` + Custom writers can define a default template via a global `Template` function; the data directory is no longer searched for a default template. Writer authors can restore the old lookup behavior with ``` lua Template = function () local template return template.compile(template.default(PANDOC_SCRIPT_FILE)) end ``` * Custom readers: + Support extensions in custom readers. Custom readers, like writers, can define the set of supported extensions by setting a global. E.g.: ``` lua reader_extensions = { smart = true, citations = false, } ``` * Use latest versions of `commonmark-extensions`, `texmath`, `citeproc`, `gridtables`, and `skylighting`. * Use pandoc-types 1.23. This adds the `Figure` Block constructor and removes the `Null` Block constructor. * Require aeson >= 2.0. * Use jira-wiki-markup 1.5.0 (#8511, Albert Krewinkel). Fixes issues with icon-like sequences at the beginning of words. * Use doctemplates 0.11, avoiding a transitive dependency on HsYAML. * Use skylighting 0.13.1.2. * Allow mtl 2.3.1 (Alexander Batischev). * Use latest skylighting-format-context. * Allow building with mtl 2.3. * Remove `lua53` flag. We now only support Lua 5.4. * Add hie.yaml for haskell language server. * Add tools/latex-package-dependencies.lua. * Update default CSL with latest `chicago-author-date.csl`. * make_artifacts.sh: various small improvements. * Remove sample.lua from data files (#8356). * Documentation: + Deprecate `PANDOC_WRITER_OPTIONS` in custom writers (Albert Krewinkel). + Document `pandoc.write_classic` (Albert Krewinkel). + Document new table features (Albert Krewinkel). + Clarify what background-image does in reveal.js (#6450). + Documentation improvements for `blank_before_blockquote` (#8324, Pranesh Prakash). + Update grid table documentation (#8346). + Add note about MathJax fonts to `--embed-resources`. + Use cabal's --package-env more (#8317, Artem Pelenitsyn). + Modify Zerobrane instructions to use Lua 5.4 (#8353, Ian Max Andolina). + Fix documentation for highlight-style in `pandoc-server.md`. + Fix link to fedora package site (#8246, Akos Marton). + Rephrase paragraph on format extensions (#8375, Ilona Silverwood). + Update README.template (#8496, Sven Wick). + Fix a tiny typo in lua-filters.md (TomBen). + Clarify that `--css` should be used with `-s`. + Clarify font selection for pdf -t ms (#8421, nbehrnd). + Clarify docs for `--metadata-file` (#8459). + Fix typo in epub.md (Vladimir Alexiev). + Add missing backtick in filters.md (R. N. West). + `doc/lua-filters.md`: add documentation for `pandoc.format` (Albert Krewinkel). + Fix epub-embed-font documentation (#8455, Terence Eden). + Removed obsolete Templates section in CONTRIBUTING.md. + Add manual section on accessible PDFs, archiving standards (#8312, Albert Krewinkel). * Tests.Command: remove unused `runTest`. * Add pandoc-lua.1 man page. * Improve `shell.nix`. * Add `tools/moduledeps.lua` for inspecting the internal module dependency tree. * Fix macOS zip so pandoc-server is a symlink. This cuts its size by 2x. * CI: Improve CI speed by caching more, eliminating macos builds, and splitting benchmarks into a separate action, run by manual dispatch. (We still test that benchmarks build in the regular CI.) The cache can be expired manually by modifying the secret `CACHE_VERSION`. * Remove the unnecessary Setup.hs from pandoc. Cabal does not need this with build-type 'simple'. * Add pandoc-lua and pandoc-server (symlinks) and their man pages to releases. * Use hslua-cli package for pandoc-lua interface (Albert Krewinkel). * Add `server` flag to pandoc-cli, allowing it to be compiled without server support. * pandoc-cli: Allow building a binary without Lua support (Albert Krewinkel). Disabling the `lua` cabal flag will result in a binary without Lua. * Move `--version` handling to pandoc-cli. We need it here in order to print information about whether server and Lua support have been compiled in. * Move `nightly` flag from pandoc to pandoc-cli (#8339). * Makefile changes: - `make help` will now print all the targets and what they do. - Add targets: `coverage`, `weeder`, `moduledeps`, `prerelease`, `ghcid`, `repl`, `linecounts`, `hie.yaml`, `binpath`. - Note that you can `` alias pandoc=`make binpath` `` for convenient local testing of a build. - Rename `quick-cabal` -> `build`, `quick-test` -> `test`. - Exclude tests from `SOURCEFILES`. * Factor out xml-light into an internal library. * Add CITATION.cff (#8434). * Move trypandoc to a separate repository, jgm/trypandoc. ## pandoc 2.19.2 (2022-08-22) * Fix regression with data uris in 2.19.1 (#8239). In 2.19.1 we used the base64URL encoding rather than base64. * pandoc-server: handle `citeproc` parameter as documented (#8235). * Org reader: treat *emacs-jupyter* src blocks as code cells (#8236, Albert Krewinkel). This improves support for notebook-like org files that are intended to be used with emacs-jupyter package. * HTML writer and templates: revert to using `width` property for column widths (Albert Krewinkel). The default `flex` and `overflow-x` properties of a column are set to `auto`. In combination, these changes allow to get good results when using columns with or without explicit widths. * Org writer (Albert Krewinkel): + Add support for jupyter nodebook cells (#6367). + Prefix code language of ipynb code blocks with `jupyter-`. This is the convention used by the *emacs-jupyter* package. + Keep code block attributes as header args. This allows to keep more information in the resulting `src` blocks, making it easier to roundtrip from or through Org. Org babel ignores unknown header arguments. + Add code block identifier as `#+name` to src blocks. * Fix some typos in the codebase (luz paz). * Require hslua-module-path 1.0.3 (#8228, Albert Krewinkel). ## pandoc 2.19.1 (2022-08-18) * Add server capabilities. + New exported module Text.Pandoc.Server [API change]. + The pandoc executable now starts up a web server when renamed or symlinked as `pandoc-server`, and functions as a CGI program when renamed or symlinked as `pandoc-server.cgi`. See the man page for `pandoc-server` for full documentation. * Text.Pandoc.App.Opts: Redo `FromJSON` for `Opt` so that optional values can be omitted (in which case the values from `defaultOptions` are used). * Org reader: treat "abstract" block as metadata (Albert Krewinkel, #8204). A block of type "abstract" is assumed to define the document's abstract. It is transferred from the main text to the metadata. * Org template: add abstract from metadata as block of type "abstract" (#8204). * HTML writer: use `flex` property for column widths (Albert Krewinkel, #8232). * LaTeX writer: + Add label to tables that have an identifier (Albert Krewinkel, #8219). Tables with an identifier are marked with a `\label`. A caption is always included in this case, even if the caption is empty. + Use `\textquotesingle` for straight quotes in text. + Fix widths of multicolumn cells (#8218). * LaTeX template: fix behavior of `colorlinks` variable (Albert Krewinkel, #8226). Fixes a regression in 2.19 that required the `boxlinks` variable to be set in addition to the usual link coloring variables. Otherwise links were never colored in LaTeX PDF output. * Text.Pandoc.Highlighting: Export `lookupHighlightingStyle` [API change]. Previously this lived in an unexported module Text.Pandoc.App.CommandLineOptions, under the name `lookupHighlightStyle`. * Text.Pandoc.App: + Remove unneeded MonadIO constraints in readSources. + Factor out `convertWithOpts'` from `convertWithOpts`. This runs in any PandocMonad, MonadIO, MonadMask instance. So far it is not exported, but it might find a use later. * Support `--strip-comments` in commonmark/gfm (#8222). This change makes the commonmark reader sensitive to `readerStripComments`. * Lua: add function `pandoc.utils.citeproc` (Albert Krewinkel). The function runs the *citeproc* processor on a Pandoc document. Exposing this functionality to Lua allows to make citation processing part of a filter or writer, simplifies the creation of multiple bibliographies, and enables the use of varying citation styles in different parts of a document. * Refactor `linux/make_artifacts.sh`. * Update INSTALL.md installation from source instructions. * Use base64 package instead of base64-bytestring. It is supposed to be faster and more standards-compliant. * trypandoc improvements: + Add dropdown with canned examples. + Add citeproc support. + Support csv, bibliographic and binary formats. + Add load from file. + Add permalink. Don't always reload page. + Use vanilla JS and CSS + the new `pandoc-server.cgi`. * Allow haddock-library-1.11.0. * Convert `tool/extract-changes.hs` to a Lua filter. ## pandoc 2.19 (2022-08-03) * Add `--embed-resources` flag (Elliot Bobrow, #7331). This can be used to embed resources without implying `--standalone`. Deprecate `--self-contained` in favor of `--embed-resources --standalone`. * Allow environment variable interpolation in `highlight-style` and `pdf-engine` fields in defaults files (#8061; Jaehwang Jung, #8073). * Allow placing custom readers and writers in user data directory (Albert Krewinkel, #8112) (`readers` and `writers` subdirectories). * Add `tsv` (tab separated values) as an input format (#7974). [API change]: Text.Pandoc.Readers.CSV now exports `readTSV`. Internal change: In Text.Pandoc.CSV, `CSVOptions` has changed so that `csvQuote` takes a Maybe value. * Add `tex_math_dollars` to `gfm` default extensions (reflecting gfm's new support for math). * RST, Org, Markdown readers: support rowspans and colspans in grid tables (#8202, Albert Krewinkel). Note: the writers does not yet support these more complex grid table features, so these complex grid tables will not round-trip. * HTML, LaTeX, and MediaWiki readers: use `formatCode` (#8162, #8129, Elliot Bobrow). This moves formatting from inside inline code elements to the outside, since pandoc's Code element only takes string content. * Markdown reader: + Don't parse inline notes with blank lines inside (#8028). + Allow attributes in special spans (e.g. `smallcaps`, `underline`) (Albert krewinkel, #4102). These spans are parsed as SmallCaps or Underline elements, but any attributes are included in a wrapping Span. * HTML reader: + Allow sublists that are not marked as items (Albert Krewinkel, #8150). This is technically invalid HTML, but it can be found in the wild and browsers handle it. * Org reader (Albert Krewinkel): + Recognize absolute paths on Windows (Albert Krewinkel, #8201). + Recognize {webp,jxl} files as images (YI). + Allow attrs for Org tables (Albert Krewinkel, #8049). Tables with attributes are no longer wrapped in Div elements; attributes are added directly to the table element. + Support line selection in INCLUDE directives (Brian Leung, #8060). + Fix Post / Pre mixup when setting emphasis chars (Amir Dekel, #8134). * LaTeX reader: + Support `\includesvg` (#8027). + Unescape characters in `\lstinline` inside `\passthrough` (#8179). + Improve `mathEnvWith` (#8122). When converting e.g. an align environment to an aligned environment inside a Math element, we need to include a newline before the `\end{aligned}`, since the previous line might end in a comment. + Fix treatment of extensions for `\input` in LaTeX reader (#8092). Previously we required a `.tex` extension, but TeX allows any extension for `\input` (as opposed to `\include`). * RTF reader: + support `\nosupersub` (#8170). * TikiWiki reader: + Support underlined text * DocBook reader: + Improved reading `` elements (Frerich Raabe, #8065). * JATS reader: + Strip `ref-` prefix from ref id in xref (#8007). + Support edition in references (#8087). * RIS reader: + Make parser more forgiving (#8034). Allow blank lines after entries. Allow entries with no space after the `-`, provided they just have a newline, e.g. `DB -\n`. + Get right order of names (#8055). * MediaWiki reader: + Allow HTML comment after row start (#8110). * DokuWiki reader: + The `tex_math_dollars` extension is now supported for `dokuwiki` (but off by default) (#8178). + Content inside `...` is parsed as raw LaTeX inline, and inside `..` as raw LaTeX block (#8178). + The behavior of `...` is changed, so that instead of producing a code block, it produces raw HTML with ``. * LaTeX writer: + Improve grouping with autocites (#8088). + Extend list of book documentclasses (Wentau Han, #8053). + Fix width of multicolumn cells (Albert Krewinkel, #8090). Cells spanning multiple columns must be given an explicit width, calculated from the table properties. + Beamer: allow containsverbatim as alternative to fragile (#8080). * HTML writer: + Add 'footnotes' identifier to footnotes section (#8043). + Fix bug with `--number-offset`. This formerly caused section divs to be produced, even when `--section-divs` was not specified (#8097). + Use CSS flexboxes for columns (Albert Krewinkel). This allows an arbitrary number of columns, while the previous approach assumed exactly two columns. + Allow "spanlike" classes to be combined (see #8194). Previously classes like "underline" and "marked" had to be the first class in a span in order for the span to be interpreted as a "ul" or "mark" element. This commit allows these special classes to be "stacked," e.g. `[test]{.mark .underline}`; in addition, the special classes are no longer required to come first in the list of classes. + Avoid doubled style attribute when height and width are added to style because of an image, but the image already has a style attribute (#8047). + Do not include the deprecated doc-endnote role (#8030). doc-endnote was deprecated in DPUB-ARIA 1.1. + Remove extra soft break for tasklist (black-desk, #8142). Browser will display the extra newline character between checkbox and text as a space, which make tasklist items cannot be aligned. * EPUB writer: + Allow choice of math method for v3 (#8164). Previously we always used MathML for math in EPUB3, because the spec includes MathML. But this is not widely supported by readers, so it seems better to allow users to choose their math method as they can with EPUB2 or HTML. **NOTE:** Existing workflows that produce EPUBv3 documents including math will be affected by this change. You must add `--mathml` to your command line if you want to continue producing MathML. * RST writer: + Fix missing spaces with nested inlines (#8182). + Always escape literal backslash (#8178). * Ms writer: + Add comment in preamble stating generator. + Fix roff ms syntax highlighting definitions (#8175, thanks to Branden Robinson). * ConTeXt writer: + Support complex table structures (Albert Krewinkel, #8116). The following table feature are now supported in ConTeXt: - colspans, - rowspans, - multiple bodies, - row headers, and - multi-row table head and foot. The wrapping `placetable` environment is also given a `reference` option with the table identifier, enabling referencing of the table from within the document. + Unify link handling (Albert Krewinkel, #8096). Autolinks, i.e. links with content that's the same as the linked URL, are now marked with the `\url` command. All other links, both internal and external, are created with the `\goto` command, leading to shorter, slightly more idiomatic code. As before, autolinks can still be styled via `\setupurl`, other links via `\setupinteraction`. + Use "sectionlevel" environment for headings (Albert Krewinkel, #5539). The document hierarchy is now conveyed using the `\startsectionlevel`/`\stopsectionlevel` by default. This makes it easy to include pandoc-generated snippets in documents at arbitrary levels. The more semantic environments "chapter", "section", "subsection", etc. are used if the `--top-level-division` command line parameter is set to a non-default value. * Docx writer: + Add `w:lang` to `rPr` for Span and Div with lang attribute, so that Word can know that "Apfel" is not a spelling error (#8026). + Prevent crashing when handling invalid tables (Albert Krewinkel, #8102). Tables with different numbers of cells per row would sometimes crash pandoc. This fix prevents this by cutting off overlong rows. * ICML writer: + Support custom-style attribute on Table (#8079). * AsciiDoc writer: + Fix commas in link text (#8070). Commas in link text trigger interpretation of attributes. To block this, we replace them with numeric entities. + Fix underline. We were rendering it as `+++text+++`; this is now changed to `[.underline]#text#`. See comment at . * FB2 writer: + Fix handling of non-section Divs (#8123). * Markdown writer: + Disable soft wrapping when `hard_line_breaks` enabled (#8035). We were already doing this for `markdown`; this commit does the same thing for `markua` and `commonmark` and `gfm`. + Avoid excessive indentation on bullet lists for `commonmark`, `markua`, `gfm`. They are now nested by 2 spaces instead of 4 (#8011). * Text.Pandoc.Class: + Add new function `findFileWithDataFallback` [API Change] (Albert Krewinkel). + `fillMediaBag`: Keep attributes of original image on Span (Albert Krewinkel, #8099). Images that cannot be fetched are replaced with a Span that contains the image's description. The span now also retains all original image attributes and inherits all attributes of the image. Furthermore, the classes `image` and `placeholder` are added, and path and title are store in attributes `original-image-src` and `original-image-title`, respectively. * Text.Pandoc.Shared: + `makeSections`: don't make a section for a div with class "fragments" (#8098). + Ensure that Nulls are ignored by `makeSection` and in segmenting slides (#8155). + Add `formatCode` function to Text.Pandoc.Shared [API change] (Elliot Bobrow, #8129). + `taskListItemToAscii`: handle asciidoctor's characters (#8011). Asciidoctor uses different unicode characters for task lists; we should recognize them too and be able to convert them to ascii task lists in formats like gfm. + Deprecate `deLink` and mark for later removal. * Text.Pandoc.Writers.Shared: + `toTableOfContents`: Don't replace links with empty spans in TOC (#8020). * Text.Pandoc.Readers.Metadata: + Ensure that metadata values w/o trailing newlines are parsed as inlines, as the manual states. Previously, they were parsed as inlines if they would otherwise have been a single Plain or Para, but otherwise left unchanged. This led to some quirky results (e.g. #8143). We now use the general function `blocksToInlines` from T.P.Shared. * Text.Pandoc.Parsing: + Simplify `gridTableWith'`, `gridTableWith` [API Change] (Albert Krewinkel). The functions `gridTableWith` and `gridTableWith'` no longer takes a boolean argument that toggles whether a table head should be parsed: both, tables with heads and without heads, are always accepted now. * Lua subsystem (Albert Krewinkel): + Extend `pandoc.system` module (Albert Krewinkel, #8184). The module now has the additional functions `list_directory`, `make_directory`, and `remove_directory`. This makes it easier to write cross-platform scripts that need to inspect or modify the file system. + Require pandoc-lua-marshal 0.1.7. Adds a `clone` methods to Pandoc objects and allows to pass Blocks in instead of full Caption elements. + Add fields `pandoc.readers` and `pandoc.writers` (#8177). The set of supported input and output formats is made available to Lua users. + Ensure that tables marshaled via JSON arrays behave like Lists. This allows to invoke methods like `map` and `includes` on lists like `PANDOC_WRITER_OPTIONS.extensions`. + Require hslua-2.2.1, unless lua53 flag is set, and do not reset foreign encoding before running Lua. This fixes a problem where the encoding used for Lua filenames would sometimes mismatch the encoding used by the OS. + Simplify module loading code. Modules are now loaded directly; the special pandoc Lua package searcher is no longer necessary and has been removed. + Add function pandoc.mediabag.fill (#8104). The function allows to fill the mediabag with all images in a given document. Images that cannot be fetched are replaced with a Span containing the image description. * Populate mediabag after filters have run (Albert Krewinkel, #8099). The mediabag is filled with document resources after the filters have run. This allows, for example, filter authors to modify image paths before pandoc tries to fetch the images. Lua filters that rely on a filled mediabag can use the new `pandoc.mediabag.fill` function to perform that action in the filter. * Ms template: redefine rather than removing .CH macro (#8175). * JATS template (Albert Krewinkel, except as noted): + Include particles, prefix, suffix in names. + Mark authors with cor-id as corresponding authors. Corresponding authors are marked by setting the attribute `corresp="yes"` in their respective `` element. + Unconditionally include permissions element (#8040). Fixes a bug that caused license information to be omitted when no copyright information was provided. + Follow JATS4R recommendation and PudMed Central for license URI (Castedo Ellerman, #8041). * LaTeX template: + Rename `\textormath` to `\TextOrMath` (Hos Es, #8036). + Fix links-as-notes (Albert Krewinkel, #8077). * HTML template styles: + Remove `span.underline` rule. This is superfluous now that we render Underline as ``. + Improve CSS for task lists (#8151). * LaTeX template: Add `boxlinks` variable for LaTeX/PDF output (#8198). If `boxlinks` is set but `colorlinks` is not, then boxes will be printed around links (`hidelinks` will not be set in `hypersetup`). * `--self-contained`: Handle `url()` in ` ================================================ FILE: data/docbook-entities.txt ================================================ aacgr 03AC Aacgr 0386 aacute 00E1 Aacute 00C1 abreve 0103 Abreve 0102 ac 223E acd 223F acE 223E 0333 acirc 00E2 Acirc 00C2 acute 00B4 acy 0430 Acy 0410 aelig 00E6 AElig 00C6 af 2061 afr 1D51E Afr 1D504 agr 03B1 Agr 0391 agrave 00E0 Agrave 00C0 alefsym 2135 aleph 2135 alpha 03B1 Alpha 0391 amacr 0101 Amacr 0100 amalg 2A3F amp 0026 AMP 0026 and 2227 And 2A53 andand 2A55 andd 2A5C andslope 2A58 andv 2A5A ang 2220 ange 29A4 angle 2220 angmsd 2221 angmsdaa 29A8 angmsdab 29A9 angmsdac 29AA angmsdad 29AB angmsdae 29AC angmsdaf 29AD angmsdag 29AE angmsdah 29AF angrt 221F angrtvb 22BE angrtvbd 299D angsph 2222 angst 00C5 angzarr 237C aogon 0105 Aogon 0104 aopf 1D552 Aopf 1D538 ap 2248 apacir 2A6F ape 224A apE 2A70 apid 224B apos 0027 ApplyFunction 2061 approx 2248 approxeq 224A aring 00E5 Aring 00C5 ascr 1D4B6 Ascr 1D49C Assign 2254 ast 002A asymp 2248 asympeq 224D atilde 00E3 Atilde 00C3 auml 00E4 Auml 00C4 awconint 2233 awint 2A11 b.alpha 1D6C2 b.beta 1D6C3 b.chi 1D6D8 b.delta 1D6C5 b.Delta 1D6AB b.epsi 1D6C6 b.epsiv 1D6DC b.eta 1D6C8 b.gamma 1D6C4 b.Gamma 1D6AA b.gammad 1D7CB b.Gammad 1D7CA b.iota 1D6CA b.kappa 1D6CB b.kappav 1D6DE b.lambda 1D6CC b.Lambda 1D6B2 b.mu 1D6CD b.nu 1D6CE b.omega 1D6DA b.Omega 1D6C0 b.phi 1D6D7 b.Phi 1D6BD b.phiv 1D6DF b.pi 1D6D1 b.Pi 1D6B7 b.piv 1D6E1 b.psi 1D6D9 b.Psi 1D6BF b.rho 1D6D2 b.rhov 1D6E0 b.sigma 1D6D4 b.Sigma 1D6BA b.sigmav 1D6D3 b.tau 1D6D5 b.Theta 1D6AF b.thetas 1D6C9 b.thetav 1D6DD b.upsi 1D6D6 b.UpsiUpsilon b.xi 1D6CF b.Xi 1D6B5 b.zeta 1D6C7 backcong 224C backepsilon 03F6 backprime 2035 backsim 223D backsimeq 22CD Backslash 2216 Barv 2AE7 barvee 22BD barwed 2305 Barwed 2306 barwedge 2305 bbrk 23B5 bbrktbrk 23B6 bcong 224C bcy 0431 Bcy 0411 bdquo 201E becaus 2235 because 2235 Because 2235 bemptyv 29B0 bepsi 03F6 bernou 212C Bernoullis 212C beta 03B2 Beta 0392 beth 2136 between 226C bfr 1D51F Bfr 1D505 bgr 03B2 Bgr 0392 bigcap 22C2 bigcirc 25EF bigcup 22C3 bigodot 2A00 bigoplus 2A01 bigotimes 2A02 bigsqcup 2A06 bigstarUB starf bigtriangledown 25BD bigtriangleup 25B3 biguplus 2A04 bigvee 22C1 bigwedge 22C0 bkarow 290D blacklozengeUB lozf blacksquare 25AA blacktriangleUB utrif blacktriangledownUB dtrif blacktriangleleftUB ltrif blacktrianglerightUB rtrif blank 2423 blk12 2592 blk14 2591 blk34 2593 block 2588 bne 003D 20E5 bnequiv 2261 20E5 bnot 2310 bNot 2AED bopf 1D553 Bopf 1D539 bot 22A5 bottom 22A5 bowtie 22C8 boxbox 29C9 boxdl 2510 boxdL 2555 boxDl 2556 boxDL 2557 boxdr 250C boxdR 2552 boxDr 2553 boxDR 2554 boxh 2500 boxH 2550 boxhd 252C boxhD 2565 boxHd 2564 boxHD 2566 boxhu 2534 boxhU 2568 boxHu 2567 boxHU 2569 boxminus 229F boxplus 229E boxtimes 22A0 boxul 2518 boxuL 255B boxUl 255C boxUL 255D boxur 2514 boxuR 2558 boxUr 2559 boxUR 255A boxv 2502 boxV 2551 boxvh 253C boxvH 256A boxVh 256B boxVH 256C boxvl 2524 boxvL 2561 boxVl 2562 boxVL 2563 boxvr 251C boxvR 255E boxVr 255F boxVR 2560 bprime 2035 breve 02D8 Breve 02D8 brvbar 00A6 bscr 1D4B7 Bscr 212C bsemi 204F bsim 223D bsime 22CD bsol 005C bsolb 29C5 bsolhsub 27C8 bull 2022 bulletUB bull bump 224E bumpe 224F bumpE 2AAE bumpeq 224F Bumpeq 224E cacute 0107 Cacute 0106 cap 2229 Cap 22D2 capand 2A44 capbrcup 2A49 capcap 2A4B capcup 2A47 capdot 2A40 CapitalDifferentialD 2145 caps 2229 FE00 caret 2041 caron 02C7 Cayleys 212D ccaps 2A4D ccaron 010D Ccaron 010C ccedil 00E7 Ccedil 00C7 ccirc 0109 Ccirc 0108 Cconint 2230 ccups 2A4C ccupssm 2A50 cdot 010B Cdot 010A cedil 00B8 Cedilla 00B8 cemptyv 29B2 cent 00A2 centerdotUM middot CenterDotUM middot cfr 1D520 Cfr 212D chcy 0447 CHcy 0427 check 2713 checkmarkUB check chi 03C7 Chi 03A7 cir 25CB circ 02C6 circeq 2257 circlearrowleft 21BA circlearrowright 21BB circledast 229B circledcirc 229A circleddash 229D CircleDot 2299 circledRUM reg circledS 24C8 CircleMinus 2296 CirclePlus 2295 CircleTimes 2297 cire 2257 cirE 29C3 cirfnint 2A10 cirmid 2AEF cirscir 29C2 ClockwiseContourIntegral 2232 CloseCurlyDoubleQuoteUM rdquo CloseCurlyQuoteUM rsquo clubs 2663 clubsuitUB clubs colon 003A Colon 2237 colone 2254 Colone 2A74 coloneq 2254 comma 002C commat 0040 comp 2201 compfn 2218 complement 2201 complexes 2102 cong 2245 congdot 2A6D Congruent 2261 conint 222E Conint 222F ContourIntegral 222E copf 1D554 Copf 2102 coprod 2210 Coproduct 2210 copy 00A9 COPY 00A9 copysr 2117 CounterClockwiseContourIntegral 2233 crarr 21B5 cross 2717 Cross 2A2F cscr 1D4B8 Cscr 1D49E csub 2ACF csube 2AD1 csup 2AD0 csupe 2AD2 ctdot 22EF cudarrl 2938 cudarrr 2935 cuepr 22DE cuesc 22DF cularr 21B6 cularrp 293D cup 222A Cup 22D3 cupbrcap 2A48 cupcap 2A46 CupCap 224D cupcup 2A4A cupdot 228D cupor 2A45 cups 222A FE00 curarr 21B7 curarrm 293C curlyeqprec 22DE curlyeqsucc 22DF curlyvee 22CE curlywedge 22CF curren 00A4 curvearrowleft 21B6 curvearrowright 21B7 cuvee 22CE cuwed 22CF cwconint 2232 cwint 2231 cylcty 232D dagger 2020 Dagger 2021 daleth 2138 darr 2193 dArr 21D3 Darr 21A1 dash 2010 dashv 22A3 Dashv 2AE4 dbkarow 290F dblac 02DD dcaron 010F Dcaron 010E dcy 0434 Dcy 0414 dd 2146 DD 2145 ddaggerUB Dagger ddarr 21CA DDotrahd 2911 ddotseq 2A77 deg 00B0 Del 2207 delta 03B4 Delta 0394 demptyv 29B1 dfisht 297F dfr 1D521 Dfr 1D507 dgr 03B4 Dgr 0394 dHar 2965 dharl 21C3 dharr 21C2 DiacriticalAcute 00B4 DiacriticalDot 02D9 DiacriticalDoubleAcute 02DD DiacriticalGrave 0060 DiacriticalTilde 02DC diam 22C4 diamond 22C4 Diamond 22C4 diamondsuitUB diams diams 2666 die 00A8 DifferentialD 2146 digamma 03DD disin 22F2 divUM divide divide 00F7 divideontimes 22C7 divonx 22C7 djcy 0452 DJcy 0402 dlcorn 231E dlcrop 230D dollar 0024 dopf 1D555 Dopf 1D53B dot 02D9 Dot 00A8 DotDot 20DC doteq 2250 doteqdot 2251 DotEqual 2250 dotminus 2238 dotplus 2214 dotsquare 22A1 doublebarwedge 2306 DoubleContourIntegral 222F DoubleDot 00A8 DoubleDownArrow 21D3 DoubleLeftArrow 21D0 DoubleLeftRightArrow 21D4 DoubleLeftTee 2AE4 DoubleLongLeftArrow 27F8 DoubleLongLeftRightArrow 27FA DoubleLongRightArrow 27F9 DoubleRightArrow 21D2 DoubleRightTee 22A8 DoubleUpArrow 21D1 DoubleUpDownArrow 21D5 DoubleVerticalBar 2225 downarrowUM darr Downarrow 21D3 DownArrowUM darr DownArrowBar 2913 DownArrowUpArrow 21F5 DownBreve 0311 downdownarrows 21CA downharpoonleft 21C3 downharpoonright 21C2 DownLeftRightVector 2950 DownLeftTeeVector 295E DownLeftVector 21BD DownLeftVectorBar 2956 DownRightTeeVector 295F DownRightVector 21C1 DownRightVectorBar 2957 DownTee 22A4 DownTeeArrow 21A7 drbkarow 2910 drcorn 231F drcrop 230C dscr 1D4B9 Dscr 1D49F dscy 0455 DScy 0405 dsol 29F6 dstrok 0111 Dstrok 0110 dtdot 22F1 dtri 25BF dtrif 25BE duarr 21F5 duhar 296F dwangle 29A6 dzcy 045F DZcy 040F dzigrarr 27FF eacgr 03AD Eacgr 0388 eacute 00E9 Eacute 00C9 easter 2A6E ecaron 011B Ecaron 011A ecir 2256 ecirc 00EA Ecirc 00CA ecolon 2255 ecy 044D Ecy 042D eDDot 2A77 edot 0117 eDot 2251 Edot 0116 ee 2147 eeacgr 03AE EEacgr 0389 eegr 03B7 EEgr 0397 efDot 2252 efr 1D522 Efr 1D508 eg 2A9A egr 03B5 Egr 0395 egrave 00E8 Egrave 00C8 egs 2A96 egsdot 2A98 el 2A99 Element 2208 elinters 23E7 ell 2113 els 2A95 elsdot 2A97 emacr 0113 Emacr 0112 empty 2205 emptyset 2205 EmptySmallSquare 25FB emptyv 2205 EmptyVerySmallSquare 25AB emsp 2003 emsp13 2004 emsp14 2005 eng 014B ENG 014A ensp 2002 eogon 0119 Eogon 0118 eopf 1D556 Eopf 1D53C epar 22D5 eparsl 29E3 eplus 2A71 epsi 03B5 epsilon 03B5 Epsilon 0395 epsiv 03F5 eqcirc 2256 eqcolon 2255 eqsim 2242 eqslantgtr 2A96 eqslantless 2A95 Equal 2A75 equals 003D EqualTilde 2242 equest 225F Equilibrium 21CC equiv 2261 equivDD 2A78 eqvparsl 29E5 erarr 2971 erDot 2253 escr 212F Escr 2130 esdot 2250 esim 2242 Esim 2A73 eta 03B7 Eta 0397 eth 00F0 ETH 00D0 euml 00EB Euml 00CB euro 20AC excl 0021 exist 2203 Exists 2203 expectation 2130 exponentiale 2147 ExponentialE 2147 fallingdotseq 2252 fcy 0444 Fcy 0424 female 2640 ffilig FB03 fflig FB00 ffllig FB04 ffr 1D523 Ffr 1D509 filig FB01 FilledSmallSquare 25FC FilledVerySmallSquare 25AA fjlig 0066 006A flat 266D fllig FB02 fltns 25B1 fnof 0192 fopf 1D557 Fopf 1D53D forall 2200 ForAll 2200 fork 22D4 forkv 2AD9 Fouriertrf 2131 fpartint 2A0D frac12 00BD frac13 2153 frac14 00BC frac15 2155 frac16 2159 frac18 215B frac23 2154 frac25 2156 frac34 00BE frac35 2157 frac38 215C frac45 2158 frac56 215A frac58 215D frac78 215E frasl 2044 frown 2322 fscr 1D4BB Fscr 2131 gacute 01F5 gamma 03B3 Gamma 0393 gammad 03DD Gammad 03DC gap 2A86 gbreve 011F Gbreve 011E Gcedil 0122 gcirc 011D Gcirc 011C gcy 0433 Gcy 0413 gdot 0121 Gdot 0120 ge 2265 gE 2267 gel 22DB gEl 2A8C geq 2265 geqq 2267 geqslant 2A7E ges 2A7E gescc 2AA9 gesdot 2A80 gesdoto 2A82 gesdotol 2A84 gesl 22DB FE00 gesles 2A94 gfr 1D524 Gfr 1D50A gg 226B Gg 22D9 ggg 22D9 ggr 03B3 Ggr 0393 gimel 2137 gjcy 0453 GJcy 0403 gl 2277 gla 2AA5 glE 2A92 glj 2AA4 gnap 2A8A gnapprox 2A8A gne 2A88 gnE 2269 gneq 2A88 gneqq 2269 gnsim 22E7 gopf 1D558 Gopf 1D53E grave 0060 GreaterEqual 2265 GreaterEqualLess 22DB GreaterFullEqual 2267 GreaterGreater 2AA2 GreaterLess 2277 GreaterSlantEqual 2A7E GreaterTilde 2273 gscr 210A Gscr 1D4A2 gsim 2273 gsime 2A8E gsiml 2A90 gt 003E Gt 226B GT 003E gtcc 2AA7 gtcir 2A7A gtdot 22D7 gtlPar 2995 gtquest 2A7C gtrapprox 2A86 gtrarr 2978 gtrdot 22D7 gtreqless 22DB gtreqqless 2A8C gtrless 2277 gtrsim 2273 gvertneqq 2269 FE00 gvnE 2269 FE00 Hacek 02C7 hairsp 200A half 00BD hamilt 210B hardcy 044A HARDcy 042A harr 2194 hArr 21D4 harrcir 2948 harrw 21AD Hat 005E hbar 210F hcirc 0125 Hcirc 0124 hearts 2665 heartsuitUB hearts hellip 2026 hercon 22B9 hfr 1D525 Hfr 210C HilbertSpace 210B hksearow 2925 hkswarow 2926 hoarr 21FF homtht 223B hookleftarrow 21A9 hookrightarrow 21AA hopf 1D559 Hopf 210D horbar 2015 HorizontalLine 2500 hscr 1D4BD Hscr 210B hslash 210F hstrok 0127 Hstrok 0126 HumpDownHump 224E HumpEqual 224F hybull 2043 hyphen 2010 iacgr 03AF Iacgr 038A iacute 00ED Iacute 00CD ic 2063 icirc 00EE Icirc 00CE icy 0438 Icy 0418 idiagr 0390 idigr 03CA Idigr 03AA Idot 0130 iecy 0435 IEcy 0415 iexcl 00A1 iff 21D4 ifr 1D526 Ifr 2111 igr 03B9 Igr 0399 igrave 00EC Igrave 00CC ii 2148 iiiint 2A0C iiint 222D iinfin 29DC iiota 2129 ijlig 0133 IJlig 0132 Im 2111 imacr 012B Imacr 012A image 2111 ImaginaryI 2148 imagline 2110 imagpart 2111 imath 0131 imof 22B7 imped 01B5 Implies 21D2 in 2208 incare 2105 infin 221E infintie 29DD inodot 0131 int 222B Int 222C intcal 22BA integers 2124 Integral 222B intercal 22BA Intersection 22C2 intlarhk 2A17 intprod 2A3C InvisibleComma 2063 InvisibleTimes 2062 iocy 0451 IOcy 0401 iogon 012F Iogon 012E iopf 1D55A Iopf 1D540 iota 03B9 Iota 0399 iprod 2A3C iquest 00BF iscr 1D4BE Iscr 2110 isin 2208 isindot 22F5 isinE 22F9 isins 22F4 isinsv 22F3 isinv 2208 it 2062 itilde 0129 Itilde 0128 iukcyUkrainian IukcyUkrainian iuml 00EF Iuml 00CF jcirc 0135 Jcirc 0134 jcy 0439 Jcy 0419 jfr 1D527 Jfr 1D50D jmath 0237 jopf 1D55B Jopf 1D541 jscr 1D4BF Jscr 1D4A5 jsercy 0458 Jsercy 0408 jukcyUkrainian JukcyUkrainian kappa 03BA Kappa 039A kappav 03F0 kcedil 0137 Kcedil 0136 kcy 043A Kcy 041A kfr 1D528 Kfr 1D50E kgr 03BA Kgr 039A kgreen 0138 khcy 0445 KHcy 0425 khgr 03C7 KHgr 03A7 kjcy 045C KJcy 040C kopf 1D55C Kopf 1D542 kscr 1D4C0 Kscr 1D4A6 lAarr 21DA lacute 013A Lacute 0139 laemptyv 29B4 lagran 2112 lambda 03BB Lambda 039B lang 27E8 Lang 27EA langd 2991 langle 27E8 lap 2A85 Laplacetrf 2112 laquo 00AB larr 2190 lArr 21D0 Larr 219E larrb 21E4 larrbfs 291F larrfs 291D larrhk 21A9 larrlp 21AB larrpl 2939 larrsim 2973 larrtl 21A2 lat 2AAB latail 2919 lAtail 291B late 2AAD lates 2AAD FE00 lbarr 290C lBarr 290E lbbrk 2772 lbraceUM lcub lbrackUM lsqb lbrke 298B lbrksld 298F lbrkslu 298D lcaron 013E Lcaron 013D lcedil 013C Lcedil 013B lceil 2308 lcub 007B lcy 043B Lcy 041B ldca 2936 ldquo 201C ldquor 201E ldrdhar 2967 ldrushar 294B ldsh 21B2 le 2264 lE 2266 LeftAngleBracket 27E8 leftarrowUM larr Leftarrow 21D0 LeftArrowUM larr LeftArrowBar 21E4 LeftArrowRightArrow 21C6 leftarrowtail 21A2 LeftCeiling 2308 LeftDoubleBracket 27E6 LeftDownTeeVector 2961 LeftDownVector 21C3 LeftDownVectorBar 2959 LeftFloor 230A leftharpoondown 21BD leftharpoonup 21BC leftleftarrows 21C7 leftrightarrow 2194 Leftrightarrow 21D4 LeftRightArrow 2194 leftrightarrows 21C6 leftrightharpoons 21CB leftrightsquigarrow 21AD LeftRightVector 294E LeftTee 22A3 LeftTeeArrow 21A4 LeftTeeVector 295A leftthreetimes 22CB LeftTriangle 22B2 LeftTriangleBar 29CF LeftTriangleEqual 22B4 LeftUpDownVector 2951 LeftUpTeeVector 2960 LeftUpVector 21BF LeftUpVectorBar 2958 LeftVector 21BC LeftVectorBar 2952 leg 22DA lEg 2A8B leq 2264 leqq 2266 leqslant 2A7D les 2A7D lescc 2AA8 lesdot 2A7F lesdoto 2A81 lesdotor 2A83 lesg 22DA FE00 lesges 2A93 lessapprox 2A85 lessdot 22D6 lesseqgtr 22DA lesseqqgtr 2A8B LessEqualGreater 22DA LessFullEqual 2266 LessGreater 2276 lessgtr 2276 LessLess 2AA1 lesssim 2272 LessSlantEqual 2A7D LessTilde 2272 lfisht 297C lfloor 230A lfr 1D529 Lfr 1D50F lg 2276 lgE 2A91 lgr 03BB Lgr 039B lHar 2962 lhard 21BD lharu 21BC lharul 296A lhblk 2584 ljcy 0459 LJcy 0409 ll 226A Ll 22D8 llarr 21C7 llcorner 231E Lleftarrow 21DA llhard 296B lltri 25FA lmidot 0140 Lmidot 013F lmoust 23B0 lmoustache 23B0 lnap 2A89 lnapprox 2A89 lne 2A87 lnE 2268 lneq 2A87 lneqq 2268 lnsim 22E6 loang 27EC loarr 21FD lobrk 27E6 longleftarrow 27F5 Longleftarrow 27F8 LongLeftArrow 27F5 longleftrightarrow 27F7 Longleftrightarrow 27FA LongLeftRightArrow 27F7 longmapsto 27FC longrightarrow 27F6 Longrightarrow 27F9 LongRightArrow 27F6 looparrowleft 21AB looparrowright 21AC lopar 2985 lopf 1D55D Lopf 1D543 loplus 2A2D lotimes 2A34 lowast 2217 lowbar 005F LowerLeftArrow 2199 LowerRightArrow 2198 loz 25CA lozengeUB loz lozf 29EB lpar 0028 lparlt 2993 lrarr 21C6 lrcorner 231F lrhar 21CB lrhard 296D lrm 200E lrtri 22BF lsaquo 2039 lscr 1D4C1 Lscr 2112 lsh 21B0 Lsh 21B0 lsim 2272 lsime 2A8D lsimg 2A8F lsqb 005B lsquo 2018 lsquor 201A lstrok 0142 Lstrok 0141 lt 003C Lt 226A LT 003C ltcc 2AA6 ltcir 2A79 ltdot 22D6 lthree 22CB ltimes 22C9 ltlarr 2976 ltquest 2A7B ltri 25C3 ltrie 22B4 ltrif 25C2 ltrPar 2996 lurdshar 294A luruhar 2966 lvertneqq 2268 FE00 lvnE 2268 FE00 macr 00AF male 2642 malt 2720 malteseUB malt map 21A6 Map 2905 mapsto 21A6 mapstodown 21A7 mapstoleft 21A4 mapstoup 21A5 marker 25AE mcomma 2A29 mcy 043C Mcy 041C mdash 2014 mDDot 223A measuredangle 2221 MediumSpace 205F Mellintrf 2133 mfr 1D52A Mfr 1D510 mgr 03BC Mgr 039C mho 2127 micro 00B5 mid 2223 midast 002A midcir 2AF0 middot 00B7 minus 2212 minusb 229F minusd 2238 minusdu 2A2A MinusPlus 2213 mlcp 2ADB mldr 2026 mnplus 2213 models 22A7 mopf 1D55E Mopf 1D544 mp 2213 mscr 1D4C2 Mscr 2133 mstpos 223E mu 03BC Mu 039C multimap 22B8 mumap 22B8 nabla 2207 nacute 0144 Nacute 0143 nang 2220 20D2 nap 2249 napE 2A70 0338 napid 224B 0338 napos 0149 napprox 2249 natur 266E naturalUB natur naturals 2115 nbsp 00A0 nbump 224E 0338 nbumpe 224F 0338 ncap 2A43 ncaron 0148 Ncaron 0147 ncedil 0146 Ncedil 0145 ncong 2247 ncongdot 2A6D 0338 ncup 2A42 ncy 043D Ncy 041D ndash 2013 ne 2260 nearhk 2924 nearr 2197 neArr 21D7 nearrow 2197 nedot 2250 0338 NegativeMediumSpace 200B NegativeThickSpace 200B NegativeThinSpace 200B NegativeVeryThinSpace 200B nequiv 2262 nesear 2928 nesim 2242 0338 NestedGreaterGreater 226B NestedLessLess 226A NewLine 000A nexist 2204 nexists 2204 nfr 1D52B Nfr 1D511 nge 2271 ngE 2267 0338 ngeq 2271 ngeqq 2267 0338 ngeqslant 2A7E 0338 nges 2A7E 0338 nGg 22D9 0338 ngr 03BD Ngr 039D ngsim 2275 ngt 226F nGt 226B 20D2 ngtr 226F nGtv 226B 0338 nharr 21AE nhArr 21CE nhpar 2AF2 ni 220B nis 22FC nisd 22FA niv 220B njcy 045A NJcy 040A nlarr 219A nlArr 21CD nldr 2025 nle 2270 nlE 2266 0338 nleftarrow 219A nLeftarrow 21CD nleftrightarrow 21AE nLeftrightarrow 21CE nleq 2270 nleqq 2266 0338 nleqslant 2A7D 0338 nles 2A7D 0338 nless 226E nLl 22D8 0338 nlsim 2274 nlt 226E nLt 226A 20D2 nltri 22EA nltrie 22EC nLtv 226A 0338 nmid 2224 NoBreak 2060 NonBreakingSpaceUM nbsp nopf 1D55F Nopf 2115 not 00AC Not 2AEC NotCongruent 2262 NotCupCap 226D NotDoubleVerticalBar 2226 NotElement 2209 NotEqual 2260 NotEqualTilde 2242 0338 NotExists 2204 NotGreater 226F NotGreaterEqual 2271 NotGreaterFullEqual 2267 0338 NotGreaterGreater 226B 0338 NotGreaterLess 2279 NotGreaterSlantEqual 2A7E 0338 NotGreaterTilde 2275 NotHumpDownHump 224E 0338 NotHumpEqual 224F 0338 notin 2209 notindot 22F5 0338 notinE 22F9 0338 notinva 2209 notinvb 22F7 notinvc 22F6 NotLeftTriangle 22EA NotLeftTriangleBar 29CF 0338 NotLeftTriangleEqual 22EC NotLess 226E NotLessEqual 2270 NotLessGreater 2278 NotLessLess 226A 0338 NotLessSlantEqual 2A7D 0338 NotLessTilde 2274 NotNestedGreaterGreater 2AA2 0338 NotNestedLessLess 2AA1 0338 notni 220C notniva 220C notnivb 22FE notnivc 22FD NotPrecedes 2280 NotPrecedesEqual 2AAF 0338 NotPrecedesSlantEqual 22E0 NotReverseElement 220C NotRightTriangle 22EB NotRightTriangleBar 29D0 0338 NotRightTriangleEqual 22ED NotSquareSubset 228F 0338 NotSquareSubsetEqual 22E2 NotSquareSuperset 2290 0338 NotSquareSupersetEqual 22E3 NotSubset 2282 20D2 NotSubsetEqual 2288 NotSucceeds 2281 NotSucceedsEqual 2AB0 0338 NotSucceedsSlantEqual 22E1 NotSucceedsTilde 227F 0338 NotSuperset 2283 20D2 NotSupersetEqual 2289 NotTilde 2241 NotTildeEqual 2244 NotTildeFullEqual 2247 NotTildeTilde 2249 NotVerticalBar 2224 npar 2226 nparallel 2226 nparsl 2AFD 20E5 npart 2202 0338 npolint 2A14 npr 2280 nprcue 22E0 npre 2AAF 0338 nprec 2280 npreceq 2AAF 0338 nrarr 219B nrArr 21CF nrarrc 2933 0338 nrarrw 219D 0338 nrightarrow 219B nRightarrow 21CF nrtri 22EB nrtrie 22ED nsc 2281 nsccue 22E1 nsce 2AB0 0338 nscr 1D4C3 Nscr 1D4A9 nshortmid 2224 nshortparallel 2226 nsim 2241 nsime 2244 nsimeq 2244 nsmid 2224 nspar 2226 nsqsube 22E2 nsqsupe 22E3 nsub 2284 nsube 2288 nsubE 2AC5 0338 nsubset 2282 20D2 nsubseteq 2288 nsubseteqq 2AC5 0338 nsucc 2281 nsucceq 2AB0 0338 nsup 2285 nsupe 2289 nsupE 2AC6 0338 nsupset 2283 20D2 nsupseteq 2289 nsupseteqq 2AC6 0338 ntgl 2279 ntilde 00F1 Ntilde 00D1 ntlg 2278 ntriangleleft 22EA ntrianglelefteq 22EC ntriangleright 22EB ntrianglerighteq 22ED nu 03BD Nu 039D num 0023 numero 2116 numsp 2007 nvap 224D 20D2 nvdash 22AC nvDash 22AD nVdash 22AE nVDash 22AF nvge 2265 20D2 nvgt 003E 20D2 nvHarr 2904 nvinfin 29DE nvlArr 2902 nvle 2264 20D2 nvlt 003C 20D2 nvltrie 22B4 20D2 nvrArr 2903 nvrtrie 22B5 20D2 nvsim 223C 20D2 nwarhk 2923 nwarr 2196 nwArr 21D6 nwarrow 2196 nwnear 2927 oacgr 03CC Oacgr 038C oacute 00F3 Oacute 00D3 oast 229B ocir 229A ocirc 00F4 Ocirc 00D4 ocy 043E Ocy 041E odash 229D odblac 0151 Odblac 0150 odiv 2A38 odot 2299 odsold 29BC oelig 0153 OElig 0152 ofcir 29BF ofr 1D52C Ofr 1D512 ogon 02DB ogr 03BF Ogr 039F ograve 00F2 Ograve 00D2 ogt 29C1 ohacgr 03CE OHacgr 038F ohbar 29B5 ohgr 03C9 OHgr 03A9 ohm 03A9 oint 222E olarr 21BA olcir 29BE olcross 29BB oline 203E olt 29C0 omacr 014D Omacr 014C omega 03C9 Omega 03A9 omicron 03BF Omicron 039F omid 29B6 ominus 2296 oopf 1D560 Oopf 1D546 opar 29B7 OpenCurlyDoubleQuoteUM ldquo OpenCurlyQuoteUM lsquo operp 29B9 oplus 2295 or 2228 Or 2A54 orarr 21BB ord 2A5D order 2134 orderof 2134 ordf 00AA ordm 00BA origof 22B6 oror 2A56 orslope 2A57 orv 2A5B oS 24C8 oscr 2134 Oscr 1D4AA oslash 00F8 Oslash 00D8 osol 2298 otilde 00F5 Otilde 00D5 otimes 2297 Otimes 2A37 otimesas 2A36 ouml 00F6 Ouml 00D6 ovbar 233D OverBar 203E OverBrace 23DE OverBracket 23B4 OverParenthesis 23DC par 2225 para 00B6 parallel 2225 parsim 2AF3 parsl 2AFD part 2202 PartialD 2202 pcy 043F Pcy 041F percnt 0025 period 002E permil 2030 perp 22A5 pertenk 2031 pfr 1D52D Pfr 1D513 pgr 03C0 Pgr 03A0 phgr 03C6 PHgr 03A6 phi 03C6 Phi 03A6 phiv 03D5 phmmat 2133 phone 260E pi 03C0 Pi 03A0 pitchfork 22D4 piv 03D6 planck 210F planckh 210E plankv 210F plus 002B plusacir 2A23 plusb 229E pluscir 2A22 plusdo 2214 plusdu 2A25 pluse 2A72 PlusMinusUM plusmn plusmn 00B1 plussim 2A26 plustwo 2A27 pmUM plusmn Poincareplane 210C pointint 2A15 popf 1D561 Popf 2119 pound 00A3 pr 227A Pr 2ABB prap 2AB7 prcue 227C pre 2AAF prE 2AB3 prec 227A precapprox 2AB7 preccurlyeq 227C Precedes 227A PrecedesEqual 2AAF PrecedesSlantEqual 227C PrecedesTilde 227E preceq 2AAF precnapprox 2AB9 precneqq 2AB5 precnsim 22E8 precsim 227E prime 2032 Prime 2033 primes 2119 prnap 2AB9 prnE 2AB5 prnsim 22E8 prod 220F Product 220F profalar 232E profline 2312 profsurf 2313 prop 221D Proportion 2237 Proportional 221D propto 221D prsim 227E prurel 22B0 pscr 1D4C5 Pscr 1D4AB psgr 03C8 PSgr 03A8 psi 03C8 Psi 03A8 puncsp 2008 qfr 1D52E Qfr 1D514 qint 2A0C qopf 1D562 Qopf 211A qprime 2057 qscr 1D4C6 Qscr 1D4AC quaternions 210D quatint 2A16 quest 003F questeq 225F quot 0022 QUOT 0022 rAarr 21DB race 223D 0331 racute 0155 Racute 0154 radic 221A raemptyv 29B3 rang 27E9 Rang 27EB rangd 2992 range 29A5 rangle 27E9 raquo 00BB rarr 2192 rArr 21D2 Rarr 21A0 rarrap 2975 rarrb 21E5 rarrbfs 2920 rarrc 2933 rarrfs 291E rarrhk 21AA rarrlp 21AC rarrpl 2945 rarrsim 2974 rarrtl 21A3 Rarrtl 2916 rarrw 219D ratail 291A rAtail 291C ratio 2236 rationals 211A rbarr 290D rBarr 290F RBarr 2910 rbbrk 2773 rbraceUM rcub rbrackUM rsqb rbrke 298C rbrksld 298E rbrkslu 2990 rcaron 0159 Rcaron 0158 rcedil 0157 Rcedil 0156 rceil 2309 rcub 007D rcy 0440 Rcy 0420 rdca 2937 rdldhar 2969 rdquo 201D rdquor 201D rdsh 21B3 Re 211C real 211C realine 211B realpart 211C reals 211D rect 25AD reg 00AE REG 00AE ReverseElement 220B ReverseEquilibrium 21CB ReverseUpEquilibrium 296F rfisht 297D rfloor 230B rfr 1D52F Rfr 211C rgr 03C1 Rgr 03A1 rHar 2964 rhard 21C1 rharu 21C0 rharul 296C rho 03C1 Rho 03A1 rhov 03F1 RightAngleBracket 27E9 rightarrowUM rarr Rightarrow 21D2 RightArrowUM rarr RightArrowBar 21E5 RightArrowLeftArrow 21C4 rightarrowtail 21A3 RightCeiling 2309 RightDoubleBracket 27E7 RightDownTeeVector 295D RightDownVector 21C2 RightDownVectorBar 2955 RightFloor 230B rightharpoondown 21C1 rightharpoonup 21C0 rightleftarrows 21C4 rightleftharpoons 21CC rightrightarrows 21C9 rightsquigarrow 219D RightTee 22A2 RightTeeArrow 21A6 RightTeeVector 295B rightthreetimes 22CC RightTriangle 22B3 RightTriangleBar 29D0 RightTriangleEqual 22B5 RightUpDownVector 294F RightUpTeeVector 295C RightUpVector 21BE RightUpVectorBar 2954 RightVector 21C0 RightVectorBar 2953 ring 02DA risingdotseq 2253 rlarr 21C4 rlhar 21CC rlm 200F rmoust 23B1 rmoustache 23B1 rnmid 2AEE roang 27ED roarr 21FE robrk 27E7 ropar 2986 ropf 1D563 Ropf 211D roplus 2A2E rotimes 2A35 RoundImplies 2970 rpar 0029 rpargt 2994 rppolint 2A12 rrarr 21C9 Rrightarrow 21DB rsaquo 203A rscr 1D4C7 Rscr 211B rsh 21B1 Rsh 21B1 rsqb 005D rsquo 2019 rsquor 2019 rthree 22CC rtimes 22CA rtri 25B9 rtrie 22B5 rtrif 25B8 rtriltri 29CE RuleDelayed 29F4 ruluhar 2968 rx 211E sacute 015B Sacute 015A sbquo 201A sc 227B Sc 2ABC scap 2AB8 scaron 0161 Scaron 0160 sccue 227D sce 2AB0 scE 2AB4 scedil 015F Scedil 015E scirc 015D Scirc 015C scnap 2ABA scnE 2AB6 scnsim 22E9 scpolint 2A13 scsim 227F scy 0441 Scy 0421 sdot 22C5 sdotb 22A1 sdote 2A66 searhk 2925 searr 2198 seArr 21D8 searrow 2198 sect 00A7 semi 003B seswar 2929 setminus 2216 setmn 2216 sext 2736 sfgr 03C2 sfr 1D530 Sfr 1D516 sfrown 2322 sgr 03C3 Sgr 03A3 sharp 266F shchcy 0449 SHCHcy 0429 shcy 0448 SHcy 0428 ShortDownArrow 2193 ShortLeftArrow 2190 shortmid 2223 shortparallel 2225 ShortRightArrow 2192 ShortUpArrow 2191 shy 00AD sigma 03C3 Sigma 03A3 sigmaf 03C2 sigmav 03C2 sim 223C simdot 2A6A sime 2243 simeq 2243 simg 2A9E simgE 2AA0 siml 2A9D simlE 2A9F simne 2246 simplus 2A24 simrarr 2972 slarr 2190 SmallCircle 2218 smallsetminus 2216 smashp 2A33 smeparsl 29E4 smid 2223 smile 2323 smt 2AAA smte 2AAC smtes 2AAC FE00 softcy 044C SOFTcy 042C sol 002F solb 29C4 solbar 233F sopf 1D564 Sopf 1D54A spades 2660 spadesuitUB spades spar 2225 sqcap 2293 sqcaps 2293 FE00 sqcup 2294 sqcups 2294 FE00 Sqrt 221A sqsub 228F sqsube 2291 sqsubset 228F sqsubseteq 2291 sqsup 2290 sqsupe 2292 sqsupset 2290 sqsupseteq 2292 squ 25A1 square 25A1 Square 25A1 SquareIntersection 2293 SquareSubset 228F SquareSubsetEqual 2291 SquareSuperset 2290 SquareSupersetEqual 2292 SquareUnion 2294 squarf 25AA squf 25AA srarr 2192 sscr 1D4C8 Sscr 1D4AE ssetmn 2216 ssmile 2323 sstarf 22C6 star 2606 Star 22C6 starf 2605 straightepsilon 03F5 straightphi 03D5 strns 00AF sub 2282 Sub 22D0 subdot 2ABD sube 2286 subE 2AC5 subedot 2AC3 submult 2AC1 subne 228A subnE 2ACB subplus 2ABF subrarr 2979 subset 2282 Subset 22D0 subseteq 2286 subseteqq 2AC5 SubsetEqual 2286 subsetneq 228A subsetneqq 2ACB subsim 2AC7 subsub 2AD5 subsup 2AD3 succ 227B succapprox 2AB8 succcurlyeq 227D Succeeds 227B SucceedsEqual 2AB0 SucceedsSlantEqual 227D SucceedsTilde 227F succeq 2AB0 succnapprox 2ABA succneqq 2AB6 succnsim 22E9 succsim 227F SuchThat 220B sum 2211 Sum 2211 sung 266A sup 2283 Sup 22D1 sup1 00B9 sup2 00B2 sup3 00B3 supdot 2ABE supdsub 2AD8 supe 2287 supE 2AC6 supedot 2AC4 Superset 2283 SupersetEqual 2287 suphsol 27C9 suphsub 2AD7 suplarr 297B supmult 2AC2 supne 228B supnE 2ACC supplus 2AC0 supset 2283 Supset 22D1 supseteq 2287 supseteqq 2AC6 supsetneq 228B supsetneqq 2ACC supsim 2AC8 supsub 2AD4 supsup 2AD6 swarhk 2926 swarr 2199 swArr 21D9 swarrow 2199 swnwar 292A szlig 00DF Tab 0009 target 2316 tau 03C4 Tau 03A4 tbrk 23B4 tcaron 0165 Tcaron 0164 tcedil 0163 Tcedil 0162 tcy 0442 Tcy 0422 tdot 20DB telrec 2315 tfr 1D531 Tfr 1D517 tgr 03C4 Tgr 03A4 there4 2234 therefore 2234 Therefore 2234 theta 03B8 Theta 0398 thetasym 03D1 thetav 03D1 thgr 03B8 THgr 0398 thickapprox 2248 thicksim 223C ThickSpace 205F 200A thinsp 2009 ThinSpaceUB thinsp thkap 2248 thksim 223C thorn 00FE THORN 00DE tilde 02DC Tilde 223C TildeEqual 2243 TildeFullEqual 2245 TildeTilde 2248 times 00D7 timesb 22A0 timesbar 2A31 timesd 2A30 tint 222D toea 2928 top 22A4 topbot 2336 topcir 2AF1 topf 1D565 Topf 1D54B topfork 2ADA tosa 2929 tprime 2034 trade 2122 TRADE 2122 triangleUB utri triangledownUB dtri triangleleftUB ltri trianglelefteq 22B4 triangleq 225C trianglerightUB rtri trianglerighteq 22B5 tridot 25EC trie 225C triminus 2A3A TripleDot 20DB triplus 2A39 trisb 29CD tritime 2A3B trpezium 23E2 tscr 1D4C9 Tscr 1D4AF tscy 0446 TScy 0426 tshcy 045B TSHcy 040B tstrok 0167 Tstrok 0166 twixt 226C twoheadleftarrow 219E twoheadrightarrow 21A0 uacgr 03CD UacgrUpsilon uacute 00FA UacuteU with acute uarr 2191 uArr 21D1 Uarr 219F Uarrocir 2949 ubrcy 045E UbrcyU ubreve 016D UbreveU ucirc 00FB UcircU with circumflex ucy 0443 UcyU udarr 21C5 udblac 0171 UdblacU udhar 296E udiagr 03B0 udigr 03CB UdigrUpsilon ufisht 297E ufr 1D532 UfrU ugr 03C5 UgrUpsilon ugrave 00F9 UgraveU with grave uHar 2963 uharl 21BF uharr 21BE uhblk 2580 ulcorn 231C ulcorner 231C ulcrop 230F ultri 25F8 umacr 016B UmacrU uml 00A8 UnderBar 005F UnderBrace 23DF UnderBracket 23B5 UnderParenthesis 23DD Union 22C3 UnionPlus 228E uogon 0173 UogonU uopf 1D566 UopfU uparrowUM uarr Uparrow 21D1 UpArrowUM uarr UpArrowBar 2912 UpArrowDownArrow 21C5 updownarrow 2195 Updownarrow 21D5 UpDownArrow 2195 UpEquilibrium 296E upharpoonleft 21BF upharpoonright 21BE uplus 228E UpperLeftArrow 2196 UpperRightArrow 2197 upsi 03C5 UpsiUpsilon capital Upsilon upsih 03D2 upsilon 03C5 UpsilonUgr UpTee 22A5 UpTeeArrow 21A5 upuparrows 21C8 urcorn 231D urcorner 231D urcrop 230E uring 016F UringU urtri 25F9 uscr 1D4CA UscrU utdot 22F0 utilde 0169 UtildeU utri 25B5 utrif 25B4 uuarr 21C8 uuml 00FC UumlU with diaeresis uwangle 29A7 vangrt 299C varepsilon 03F5 varkappa 03F0 varnothing 2205 varphi 03D5 varpi 03D6 varpropto 221D varr 2195 vArrUpdownarrow A: up&down dbl arrow varrho 03F1 varsigma 03C2 varsubsetneq 228A FE00 varsubsetneqq 2ACB FE00 varsupsetneq 228B FE00 varsupsetneqq 2ACC FE00 vartheta 03D1 vartriangleleft 22B2 vartriangleright 22B3 vBar 2AE8 Vbar 2AEB vBarv 2AE9 vcy 0432 Vcy 0412 vdash 22A2 vDash 22A8 Vdash 22A9 VDash 22AB Vdashl 2AE6 vee 2228 Vee 22C1 veebar 22BB veeeq 225A vellip 22EE verbar 007C Verbar 2016 vertUM verbar Vert 2016 VerticalBar 2223 VerticalLineUM verbar VerticalSeparator 2758 VerticalTilde 2240 VeryThinSpaceUB hairsp vfr 1D533 Vfr 1D519 vltri 22B2 vnsub 2282 20D2 vnsup 2283 20D2 vopf 1D567 Vopf 1D54D vprop 221D vrtri 22B3 vscr 1D4CB Vscr 1D4B1 vsubne 228A FE00 vsubnE 2ACB FE00 vsupne 228B FE00 vsupnE 2ACC FE00 Vvdash 22AA vzigzag 299A wcirc 0175 Wcirc 0174 wedbar 2A5F wedge 2227 Wedge 22C0 wedgeq 2259 weierp 2118 wfr 1D534 Wfr 1D51A wopf 1D568 Wopf 1D54E wp 2118 wr 2240 wreath 2240 wscr 1D4CC Wscr 1D4B2 xcap 22C2 xcirc 25EF xcup 22C3 xdtri 25BD xfr 1D535 Xfr 1D51B xgr 03BE Xgr 039E xharr 27F7 xhArr 27FA xi 03BE Xi 039E xlarr 27F5 xlArr 27F8 xmap 27FC xnis 22FB xodot 2A00 xopf 1D569 Xopf 1D54F xoplus 2A01 xotime 2A02 xrarr 27F6 xrArr 27F9 xscr 1D4CD Xscr 1D4B3 xsqcup 2A06 xuplus 2A04 xutri 25B3 xvee 22C1 xwedge 22C0 yacute 00FD Yacute 00DD yacy 044F YAcy 042F ycirc 0177 Ycirc 0176 ycy 044B YcyU yen 00A5 yfr 1D536 Yfr 1D51C yicyUkrainian YIcyUkrainian yopf 1D56A Yopf 1D550 yscr 1D4CE Yscr 1D4B4 yucy 044E YUcyU yuml 00FF Yuml 0178 zacute 017A Zacute 0179 zcaron 017E Zcaron 017D zcy 0437 Zcy 0417 zdot 017C Zdot 017B zeetrf 2128 ZeroWidthSpace 200B zeta 03B6 Zeta 0396 zfr 1D537 Zfr 2128 zgr 03B6 Zgr 0396 zhcy 0436 ZHcy 0416 zigrarr 21DD zopf 1D56B Zopf 2124 zscr 1D4CF Zscr 1D4B5 zwj 200D zwnj 200C ================================================ FILE: data/docx/[Content_Types].xml ================================================ ================================================ FILE: data/docx/_rels/.rels ================================================ ================================================ FILE: data/docx/docProps/app.xml ================================================ 83 false false 12 12.0000 false Microsoft Word 12.0.0 583 0 6 false 475 8 1 ================================================ FILE: data/docx/docProps/core.xml ================================================ TitleAuthor2017-12-27T05:22:50Z2017-12-27T05:22:50Z ================================================ FILE: data/docx/docProps/custom.xml ================================================ ================================================ FILE: data/docx/word/_rels/document.xml.rels ================================================ ================================================ FILE: data/docx/word/_rels/footnotes.xml.rels ================================================ ================================================ FILE: data/docx/word/comments.xml ================================================ ================================================ FILE: data/docx/word/document.xml ================================================ Title Subtitle Author Date Abstract Heading 1 Heading 2 Heading 3 Heading 4 Heading 5 Heading 6 Heading 7 Heading 8 Heading 9 First Paragraph. Body Text. Body Text Char. Verbatim Char . Hyperlink . Footnote. Block Text. Table caption. Table Table 1 2 Image Caption DefinitionTerm Definition DefinitionTerm Definition ================================================ FILE: data/docx/word/fontTable.xml ================================================ ================================================ FILE: data/docx/word/footnotes.xml ================================================ Footnote Text. Footnote Block Text ================================================ FILE: data/docx/word/numbering.xml ================================================ ================================================ FILE: data/docx/word/settings.xml ================================================ ================================================ FILE: data/docx/word/styles.xml ================================================ ================================================ FILE: data/docx/word/theme/theme1.xml ================================================ ================================================ FILE: data/docx/word/webSettings.xml ================================================ ================================================ FILE: data/dzslides/template.html ================================================ The Title Of Your Presentation

My Presentation

by John Doe

Some random text: But I've never been to the moon! You can see how I lived before I met you. Also Zoidberg. I could if you hadn't turned on the light and shut off my stereo.

An incremental list

  • Item 1
  • Item 2
  • Item 3
    • Item 3.1
    • Item 3.2
Some notes. They are only visible using onstage shell.
Who's brave enough to fly into something we all keep calling a death sphere?

In the onstage shell, notes scroll rather than overflow:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla ac dui eu est feugiat lacinia sit amet nec leo. Mauris eu ipsum leo. Nulla mi odio, cursus sed sollicitudin non, fringilla id magna. Suspendisse sit amet posuere elit. Maecenas iaculis, turpis a placerat imperdiet, libero lorem feugiat nisi, nec tincidunt diam nibh sit amet massa. Vestibulum quis adipiscing tellus. Maecenas sollicitudin sodales pulvinar. Donec dui ipsum, bibendum facilisis consequat interdum, tempus ut mauris. Aliquam ut dolor nec odio scelerisque bibendum quis in neque. Aliquam dui dui, pulvinar quis fermentum quis, gravida eu augue. Nunc tristique dolor a urna pulvinar bibendum. Curabitur mollis cursus neque, in scelerisque metus porta non. Donec tempor enim in nibh vestibulum et convallis nisi malesuada. Duis ut lectus sed metus venenatis porttitor id pharetra quam. Suspendisse sapien turpis, ornare in molestie et, gravida eget turpis.

Part two

An image
Kittens are so cute!
A video

End!

================================================ FILE: data/epub.css ================================================ /* This defines styles and classes used in the book */ @page { margin: 10px; } html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video, ol, ul, li, dl, dt, dd { margin: 0; padding: 0; border: 0; font-size: 100%; vertical-align: baseline; } html { line-height: 1.2; font-family: Georgia, serif; color: #1a1a1a; } p { text-indent: 0; margin: 1em 0; widows: 2; orphans: 2; } img { max-width: 100%; } sup { vertical-align: super; font-size: smaller; } sub { vertical-align: sub; font-size: smaller; } h1 { margin: 3em 0 0 0; font-size: 2em; page-break-before: always; line-height: 150%; } h2 { margin: 1.5em 0 0 0; font-size: 1.5em; line-height: 135%; } h3 { margin: 1.3em 0 0 0; font-size: 1.3em; } h4 { margin: 1.2em 0 0 0; font-size: 1.2em; } h5 { margin: 1.1em 0 0 0; font-size: 1.1em; } h6 { font-size: 1em; } h1, h2, h3, h4, h5, h6 { text-indent: 0; text-align: left; font-weight: bold; page-break-after: avoid; page-break-inside: avoid; } ol, ul { margin: 1em 0 0 1.7em; } li > ol, li > ul { margin-top: 0; } blockquote { margin: 1em 0 1em 1.7em; } code { font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace; font-size: 85%; margin: 0; hyphens: manual; } pre { margin: 1em 0; overflow: auto; } pre code { padding: 0; overflow: visible; overflow-wrap: normal; } .sourceCode { background-color: transparent; overflow: visible; } hr { background-color: #1a1a1a; border: none; height: 1px; margin: 1em 0; } table { margin: 1em 0; border-collapse: collapse; width: 100%; overflow-x: auto; display: block; } table caption { margin-bottom: 0.75em; } tbody { margin-top: 0.5em; border-top: 1px solid #1a1a1a; border-bottom: 1px solid #1a1a1a; } th, td { padding: 0.25em 0.5em 0.25em 0.5em; } th { border-top: 1px solid #1a1a1a; } header { margin-bottom: 4em; text-align: center; } #TOC li { list-style: none; } #TOC ul { padding-left: 1.3em; } #TOC > ul { padding-left: 0; } #TOC a:not(:hover) { text-decoration: none; } code { white-space: pre-wrap; } span.smallcaps { font-variant: small-caps; } /* This is the most compatible CSS, but it only allows two columns: */ div.column { display: inline-block; vertical-align: top; width: 50%; } /* If you can rely on CSS3 support, use this instead: */ /* div.columns { display: flex; gap: min(4vw, 1.5em); } div.column { flex: auto; overflow-x: auto; } */ div.hanging-indent { margin-left: 1.5em; text-indent: -1.5em; } ul.task-list { list-style: none; } ul.task-list li input[type="checkbox"] { width: 0.8em; margin: 0 0.8em 0.2em -1.6em; vertical-align: middle; } .display.math { display: block; text-align: center; margin: 0.5rem auto; } /* For title, author, and date on the cover page */ h1.title { } p.author { } p.date { } nav#toc ol, nav#landmarks ol { padding: 0; margin-left: 1em; } nav#toc ol li, nav#landmarks ol li { list-style-type: none; margin: 0; padding: 0; } a.footnote-ref { vertical-align: super; } em, em em em, em em em em em { font-style: italic; } em em, em em em em { font-style: normal; } q { quotes: "“" "”" "‘" "’"; } @media screen { /* Workaround for iBooks issue; see #6242 */ .sourceCode { overflow: visible !important; white-space: pre-wrap !important; } } ================================================ FILE: data/init.lua ================================================ -- This Lua script is run every time the Lua interpreter is started when running -- a Lua filter. It can be customized to load additional modules or to alter the -- default modules. ================================================ FILE: data/odt/META-INF/manifest.xml ================================================ ================================================ FILE: data/odt/content.xml ================================================ Hello World! ================================================ FILE: data/odt/manifest.rdf ================================================ ================================================ FILE: data/odt/meta.xml ================================================ Pandoc ================================================ FILE: data/odt/mimetype ================================================ application/vnd.oasis.opendocument.text ================================================ FILE: data/odt/styles.xml ================================================ 1 ================================================ FILE: data/pptx/[Content_Types].xml ================================================ ================================================ FILE: data/pptx/_rels/.rels ================================================ ================================================ FILE: data/pptx/docProps/app.xml ================================================ 249Microsoft Macintosh PowerPointOn-screen Show (16:9)154200falseFonts Used2Theme1Slide Titles4ArialCalibriOffice ThemePresentation TitleSlide TitleSection headerSlide Title for Two-Contentfalsefalsefalse16.0000 ================================================ FILE: data/pptx/docProps/core.xml ================================================ TitleJesse RosenthalJesse Rosenthal52017-06-05T14:10:58Z2022-01-02T22:49:03Z ================================================ FILE: data/pptx/ppt/_rels/presentation.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/notesMasters/notesMaster1.xml ================================================ 1/2/22Click to edit Master text stylesSecond levelThird levelFourth levelFifth level‹#› ================================================ FILE: data/pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/notesSlides/notesSlide1.xml ================================================ Here is a noteWith another paragraph.1 ================================================ FILE: data/pptx/ppt/notesSlides/notesSlide2.xml ================================================ A speaker note on this slide too.2 ================================================ FILE: data/pptx/ppt/presProps.xml ================================================ ================================================ FILE: data/pptx/ppt/presentation.xml ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout1.xml ================================================ Click to edit Master title styleClick to edit Master subtitle style1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout10.xml ================================================ Click to edit Master title styleClick to edit Master text stylesSecond levelThird levelFourth levelFifth level1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout11.xml ================================================ Click to edit Master title styleClick to edit Master text stylesSecond levelThird levelFourth levelFifth level1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout2.xml ================================================ Click to edit Master title styleClick to edit Master text stylesSecond levelThird levelFourth levelFifth level1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout3.xml ================================================ Click to edit Master title styleClick to edit Master text styles1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout4.xml ================================================ Click to edit Master title styleClick to edit Master text stylesSecond levelThird levelFourth levelFifth levelClick to edit Master text stylesSecond levelThird levelFourth levelFifth level1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout5.xml ================================================ Click to edit Master title styleClick to edit Master text stylesClick to edit Master text stylesSecond levelThird levelFourth levelFifth levelClick to edit Master text stylesClick to edit Master text stylesSecond levelThird levelFourth levelFifth level1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout6.xml ================================================ Click to edit Master title style1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout7.xml ================================================ 1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout8.xml ================================================ Click to edit Master title styleClick to edit Master text stylesSecond levelThird levelFourth levelFifth levelClick to edit Master text styles1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideLayouts/slideLayout9.xml ================================================ Click to edit Master title styleClick to edit Master text styles1/2/22‹#› ================================================ FILE: data/pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slideMasters/slideMaster1.xml ================================================ Click to edit Master title styleClick to edit Master text stylesSecond levelThird levelFourth levelFifth level1/2/22‹#› ================================================ FILE: data/pptx/ppt/slides/_rels/slide1.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slides/_rels/slide2.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slides/_rels/slide3.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slides/_rels/slide4.xml.rels ================================================ ================================================ FILE: data/pptx/ppt/slides/slide1.xml ================================================ Presentation TitlePresentation Subtitle ================================================ FILE: data/pptx/ppt/slides/slide2.xml ================================================ Slide TitleHello, world. ================================================ FILE: data/pptx/ppt/slides/slide3.xml ================================================ Section header ================================================ FILE: data/pptx/ppt/slides/slide4.xml ================================================ Slide Title for Two-ContentSome content on the left.Some content on the right. ================================================ FILE: data/pptx/ppt/tableStyles.xml ================================================ ================================================ FILE: data/pptx/ppt/theme/theme1.xml ================================================ ================================================ FILE: data/pptx/ppt/theme/theme2.xml ================================================ ================================================ FILE: data/pptx/ppt/viewProps.xml ================================================ ================================================ FILE: data/templates/affiliations.jats ================================================ $-- $-- Affiliations $-- $-- wrap affiliation if it has a known institution identifier $if(it.group)$ ${it.group} $endif$ $if(it.department)$ ${it.department} $endif$ $if(it.organization)$ ${it.organization} $else$ ${it.name} $endif$ $if(it.isni)$ ${it.isni} $endif$ $if(it.ringgold)$ ${it.ringgold} $endif$ $if(it.ror)$ ${it.ror} $endif$ $for(it.pid)$ ${it.id} $endfor$ $if(it.street-address)$, $for(it.street-address)$ ${it}$sep$, $endfor$ $else$$if(it.city)$, $it.city$$endif$$endif$$if(it.country)$, $it.country$$endif$ ================================================ FILE: data/templates/after-header-includes.latex ================================================ \usepackage{bookmark} \IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available \urlstyle{$if(urlstyle)$$urlstyle$$else$same$endif$} $if(links-as-notes)$ % Make links footnotes instead of hotlinks: \DeclareRobustCommand{\href}[2]{#2\footnote{\url{#1}}} $endif$ $if(verbatim-in-note)$ \VerbatimFootnotes % allow verbatim text in footnotes $endif$ ================================================ FILE: data/templates/article.jats_publishing ================================================ $if(article.type)$
$else$
$endif$ $if(journal.publisher-id)$ $journal.publisher-id$ $endif$ $if(journal.nlm-ta)$ $journal.nlm-ta$ $endif$ $if(journal.pmc)$ $journal.pmc$ $endif$ $-- Fallback: an empty journal-id in case none is available. $if(journal.publisher-id)$ $elseif(journal.nlm-ta)$ $elseif(journal.pmc)$ $else$ $endif$ $if(journal.title)$ $journal.title$ $endif$ $if(journal.abbrev-title)$ $journal.abbrev-title$ $endif$ $if(journal.pissn)$ $journal.pissn$ $endif$ $if(journal.eissn)$ $journal.eissn$ $endif$ $-- At least one issn element is required; use empty issn as fallback $if(journal.pissn)$ $elseif(journal.eissn)$ $else$ $endif$ $journal.publisher-name$ $if(journal.publisher-loc)$ $journal.publisher-loc$ $endif$ $if(article.publisher-id)$ $article.publisher-id$ $endif$ $if(article.doi)$ $article.doi$ $endif$ $if(article.pmid)$ $article.pmid$ $endif$ $if(article.pmcid)$ $article.pmcid$ $endif$ $if(article.art-access-id)$ $article.art-access-id$ $endif$ $if(article.heading)$ $article.heading$ $if(article.categories)$ $for(article.categories)$ $article.categories$ $endfor$ $endif$ $endif$ $if(title)$ $title$ $if(subtitle)$ ${subtitle} $endif$ $endif$ $if(author)$ $for(author)$ $if(author.orcid)$ $author.orcid$ $endif$ $if(author.surname)$ $if(author.non-dropping-particle)$${author.non-dropping-particle} $endif$$author.surname$ $author.given-names$$if(author.dropping-particle)$ ${author.dropping-particle}$endif$ $if(author.prefix)$ ${author.prefix} $endif$ $if(author.suffix)$ ${author.suffix} $endif$ $elseif(author.name)$ $author.name$ $else$ $author$ $endif$ $for(author.roles)$ $if(it.credit)$ $if(it.name)$$it.name$$else$$it.credit-name$$endif$ $elseif(it.name)$ $it.name$ $endif$ $endfor$ $if(author.email)$ $author.email$ $endif$ $-- if affiliations are listed separately, then create links. Otherwise $-- include them here. $if(affiliation)$ $for(author.affiliation)$ $endfor$ $else$ $for(author.affiliation)$ ${ it:affiliations.jats() } $endfor$ $endif$ $if(author.cor-id)$ * $endif$ $endfor$ $for(affiliation)$ ${ it:affiliations.jats() } $endfor$ $endif$ $if(article.author-notes)$ $if(article.author-notes.corresp)$ $for(article.author-notes.corresp)$ * E-mail: $article.author-notes.corresp.email$ $endfor$ $endif$ $if(article.author-notes.conflict)$

$article.author-notes.conflict$

$endif$ $if(article.author-notes.con)$

$article.author-notes.con$

$endif$
$endif$ $if(date)$ $if(date.day)$ $date.day$ $endif$ $if(date.month)$ $date.month$ $endif$ $date.year$ $endif$ $if(article.volume)$ $article.volume$ $endif$ $if(article.issue)$ $article.issue$ $endif$ $if(article.fpage)$ $article.fpage$ $endif$ $if(article.lpage)$ $article.lpage$ $endif$ $if(article.elocation-id)$ $article.elocation-id$ $endif$ $if(history)$ $endif$ $for(copyright.statement)$ $copyright.statement$ $endfor$ $for(copyright.year)$ $copyright.year$ $endfor$ $for(copyright.holder)$ $copyright.holder$ $endfor$ $if(copyright.text)$ $copyright.text$ $endif$ $for(license)$ $if(it.link)$ ${it.link} $endif$ $if(it.text)$${it.text}$else$${it}$endif$ $endfor$ $if(abstract)$ $abstract$ $endif$ $if(tags)$ $for(tags)$ $tags$ $endfor$ $endif$ $if(article.funding-statement)$ $article.funding-statement$ $endif$
$if(notes)$ $notes$ $endif$
$body$ $if(back)$ $back$ $endif$ $if(floats-group)$ $floats-group$ $endif$
================================================ FILE: data/templates/common.latex ================================================ $if(linestretch)$ \usepackage{setspace} $endif$ $-- $-- paragraph formatting $-- $if(indent)$ $else$ \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 $endif$ $if(beamer)$ $else$ $if(block-headings)$ % Make \paragraph and \subparagraph free-standing \makeatletter \ifx\paragraph\undefined\else \let\oldparagraph\paragraph \renewcommand{\paragraph}{ \@ifstar \xxxParagraphStar \xxxParagraphNoStar } \newcommand{\xxxParagraphStar}[1]{\oldparagraph*{#1}\mbox{}} \newcommand{\xxxParagraphNoStar}[1]{\oldparagraph{#1}\mbox{}} \fi \ifx\subparagraph\undefined\else \let\oldsubparagraph\subparagraph \renewcommand{\subparagraph}{ \@ifstar \xxxSubParagraphStar \xxxSubParagraphNoStar } \newcommand{\xxxSubParagraphStar}[1]{\oldsubparagraph*{#1}\mbox{}} \newcommand{\xxxSubParagraphNoStar}[1]{\oldsubparagraph{#1}\mbox{}} \fi \makeatother $endif$ $endif$ $-- $-- verbatim in notes $-- $if(verbatim-in-note)$ \usepackage{fancyvrb} $endif$ $-- highlighting $if(listings)$ \usepackage{listings} \newcommand{\passthrough}[1]{#1} \lstset{defaultdialect=[5.3]Lua} \lstset{defaultdialect=[x86masm]Assembler} $endif$ $if(lhs)$ \lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{} $endif$ $if(highlighting-macros)$ $highlighting-macros$ $endif$ $-- $-- tables $-- $if(tables)$ \usepackage{longtable,booktabs,array} \newcounter{none} % for unnumbered tables $if(multirow)$ \usepackage{multirow} $endif$ \usepackage{calc} % for calculating minipage widths $if(beamer)$ \usepackage{caption} % Make caption package work with longtable \makeatletter \def\fnum@table{\tablename~\thetable} \makeatother $else$ % Correct order of tables after \paragraph or \subparagraph \usepackage{etoolbox} \makeatletter \patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{} \makeatother % Allow footnotes in longtable head/foot \IfFileExists{footnotehyper.sty}{\usepackage{footnotehyper}}{\usepackage{footnote}} \makesavenoteenv{longtable} $endif$ $endif$ $-- $-- graphics $-- $if(graphics)$ \usepackage{graphicx} \makeatletter \newsavebox\pandoc@box \newcommand*\pandocbounded[1]{% scales image to fit in text height/width \sbox\pandoc@box{#1}% \Gscale@div\@tempa{\textheight}{\dimexpr\ht\pandoc@box+\dp\pandoc@box\relax}% \Gscale@div\@tempb{\linewidth}{\wd\pandoc@box}% \ifdim\@tempb\p@<\@tempa\p@\let\@tempa\@tempb\fi% select the smaller of both \ifdim\@tempa\p@<\p@\scalebox{\@tempa}{\usebox\pandoc@box}% \else\usebox{\pandoc@box}% \fi% } % Set default figure placement to htbp \def\fps@figure{htbp} \makeatother $endif$ $if(svg)$ \usepackage{svg} $endif$ $-- $-- strikeout/underline $-- $if(strikeout)$ \ifLuaTeX \usepackage{luacolor} \usepackage[soul]{lua-ul} \else \usepackage{soul} $if(beamer)$ \makeatletter \let\HL\hl \renewcommand\hl{% fix for beamer highlighting \let\set@color\beamerorig@set@color \let\reset@color\beamerorig@reset@color \HL} \makeatother $endif$ $if(CJKmainfont)$ \ifXeTeX % soul's \st doesn't work for CJK: \usepackage{xeCJKfntef} \renewcommand{\st}[1]{\sout{#1}} \fi $endif$ \fi $endif$ $-- $-- CSL citations $-- $if(csl-refs)$ % definitions for citeproc citations \NewDocumentCommand\citeproctext{}{} \NewDocumentCommand\citeproc{mm}{% \begingroup\def\citeproctext{#2}\cite{#1}\endgroup} \makeatletter % allow citations to break across lines \let\@cite@ofmt\@firstofone % avoid brackets around text for \cite: \def\@biblabel#1{} \def\@cite#1#2{{#1\if@tempswa , #2\fi}} \makeatother \newlength{\cslhangindent} \setlength{\cslhangindent}{1.5em} \newlength{\csllabelwidth} \setlength{\csllabelwidth}{3em} \newenvironment{CSLReferences}[2] % #1 hanging-indent, #2 entry-spacing {\begin{list}{}{% \setlength{\itemindent}{0pt} \setlength{\leftmargin}{0pt} \setlength{\parsep}{0pt} % turn on hanging indent if param 1 is 1 \ifodd #1 \setlength{\leftmargin}{\cslhangindent} \setlength{\itemindent}{-1\cslhangindent} \fi % set entry spacing \setlength{\itemsep}{#2\baselineskip}}} {\end{list}} \usepackage{calc} \newcommand{\CSLBlock}[1]{\hfill\break\parbox[t]{\linewidth}{\strut\ignorespaces#1\strut}} \newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{\strut#1\strut}} \newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{\strut#1\strut}} \newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1} $endif$ $-- $-- Babel language support $-- $if(lang)$ \ifLuaTeX \usepackage[bidi=basic$if(shorthands)$$else$,shorthands=off$endif$$for(babeloptions)$,$babeloptions$$endfor$]{babel} \else \usepackage[bidi=default$if(shorthands)$$else$,shorthands=off$endif$$for(babeloptions)$,$babeloptions$$endfor$]{babel} \fi $if(babel-lang)$ $if(mainfont)$ \ifPDFTeX \else \babelfont{rm}[$for(mainfontoptions)$$mainfontoptions$$sep$,$endfor$$if(mainfontfallback)$,RawFeature={fallback=mainfontfallback}$endif$]{$mainfont$} \fi $endif$ $endif$ $for(babelfonts/pairs)$ \babelfont[$babelfonts.key$]{rm}{$babelfonts.value$} $endfor$ \ifLuaTeX \usepackage{selnolig} % disable illegal ligatures \fi $endif$ $-- $-- pagestyle $-- $if(pagestyle)$ \pagestyle{$pagestyle$} $endif$ $-- $-- prevent overfull lines $-- \setlength{\emergencystretch}{3em} % prevent overfull lines $-- $-- tight lists $-- \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} $-- $-- subfigure support $-- $if(subfigure)$ \usepackage{subcaption} $endif$ $-- $-- text direction support for pdftex $-- $if(dir)$ \ifPDFTeX \TeXXeTstate=1 \newcommand{\RL}[1]{\beginR #1\endR} \newcommand{\LR}[1]{\beginL #1\endL} \newenvironment{RTL}{\beginR}{\endR} \newenvironment{LTR}{\beginL}{\endL} \fi \ifluatex \newcommand{\RL}[1]{\bgroup\textdir TRT#1\egroup} \newcommand{\LR}[1]{\bgroup\textdir TLT#1\egroup} \newenvironment{RTL}{\textdir TRT\pardir TRT\bodydir TRT}{} \newenvironment{LTR}{\textdir TLT\pardir TLT\bodydir TLT}{} \fi $endif$ $-- $-- bibliography support support for natbib and biblatex $-- $if(natbib)$ \usepackage[$natbiboptions$]{natbib} \bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$} $endif$ $if(biblatex)$ \usepackage[$if(biblio-style)$style=$biblio-style$,$endif$$for(biblatexoptions)$$biblatexoptions$$sep$,$endfor$]{biblatex} $for(bibliography)$ \addbibresource{$bibliography$} $endfor$ $endif$ $-- $-- csquotes $-- $if(csquotes)$ \usepackage[$for(csquotesoptions)$$csquotesoptions$$sep$,$endfor$]{csquotes} $endif$ ================================================ FILE: data/templates/default.ansi ================================================ $if(titleblock)$ $titleblock$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $table-of-contents$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.asciidoc ================================================ $if(titleblock)$ = $title$ $if(author)$ $for(author)$$author$$sep$; $endfor$ $if(date)$ $date$ $endif$ $elseif(date)$ :revdate: $date$ $endif$ $if(keywords)$ :keywords: $for(keywords)$$keywords$$sep$, $endfor$ $endif$ $if(lang)$ :lang: $lang$ $endif$ $if(toc)$ :toc: $endif$ $if(math)$ :stem: latexmath $endif$ $endif$ $if(abstract)$ [abstract] == Abstract $abstract$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.bbcode ================================================ $body$ ================================================ FILE: data/templates/default.beamer ================================================ $passoptions.latex()$ \documentclass[ $if(fontsize)$ $fontsize$, $endif$ ignorenonframetext, $if(handout)$ handout, $endif$ $if(aspectratio)$ aspectratio=$aspectratio$, $endif$ $if(babel-lang)$ $babel-lang$, $endif$ $for(classoption)$ $classoption$$sep$, $endfor$ ]{$documentclass$} $if(geometry)$ \geometry{$for(geometry)$$geometry$$sep$,$endfor$} $endif$ \newif\ifbibliography $if(background-image)$ \usebackgroundtemplate{% \includegraphics[width=\paperwidth]{$background-image$}% } % In beamer background-image does not work well when other images are used, so this is the workaround \pgfdeclareimage[width=\paperwidth,height=\paperheight]{background}{$background-image$} \usebackgroundtemplate{\pgfuseimage{background}} $endif$ \usepackage{pgfpages} \setbeamertemplate{caption}[numbered] \setbeamertemplate{caption label separator}{: } \setbeamercolor{caption name}{fg=normal text.fg} \beamertemplatenavigationsymbols$if(navigation)$$navigation$$else$empty$endif$ $-- $-- section numbering $-- $if(numbersections)$ $else$ % remove section numbering \setbeamertemplate{part page}{ \centering \begin{beamercolorbox}[sep=16pt,center]{part title} \usebeamerfont{part title}\insertpart\par \end{beamercolorbox} } \setbeamertemplate{section page}{ \centering \begin{beamercolorbox}[sep=12pt,center]{section title} \usebeamerfont{section title}\insertsection\par \end{beamercolorbox} } \setbeamertemplate{subsection page}{ \centering \begin{beamercolorbox}[sep=8pt,center]{subsection title} \usebeamerfont{subsection title}\insertsubsection\par \end{beamercolorbox} } $endif$ $for(beameroption)$ \setbeameroption{$beameroption$} $endfor$ % Prevent slide breaks in the middle of a paragraph \widowpenalties 1 10000 \raggedbottom $if(section-titles)$ \AtBeginPart{ \frame{\partpage} } \AtBeginSection{ \ifbibliography \else \frame{\sectionpage} \fi } \AtBeginSubsection{ \frame{\subsectionpage} } $endif$ $fonts.latex()$ $-- Set Beamer theme before user font settings so they can override theme $if(theme)$ \usetheme[$for(themeoptions)$$themeoptions$$sep$,$endfor$]{$theme$} $endif$ $if(colortheme)$ \usecolortheme[$for(colorthemeoptions)$$colorthemeoptions$$sep$,$endfor$]{$colortheme$} $endif$ $if(fonttheme)$ \usefonttheme[$for(fontthemeoptions)$$fontthemeoptions$$sep$,$endfor$]{$fonttheme$} $endif$ $if(mainfont)$ \usefonttheme{serif} % use mainfont rather than sansfont for slide text $endif$ $if(innertheme)$ \useinnertheme[$for(innerthemeoptions)$$innerthemeoptions$$sep$,$endfor$]{$innertheme$} $endif$ $if(outertheme)$ \useoutertheme[$for(outerthemeoptions)$$outerthemeoptions$$sep$,$endfor$]{$outertheme$} $endif$ $font-settings.latex()$ $common.latex()$ $for(header-includes)$ $header-includes$ $endfor$ $after-header-includes.latex()$ $hypersetup.latex()$ $if(title)$ \title$if(shorttitle)$[$shorttitle$]$endif${\texorpdfstring{$title$}{$title-meta$}$if(thanks)$\thanks{$thanks$}$endif$} $endif$ $if(subtitle)$ \subtitle$if(shortsubtitle)$[$shortsubtitle$]$endif${\texorpdfstring{$subtitle$}{$subtitle-meta$}} $endif$ \author$if(shortauthor)$[$shortauthor$]$endif${\texorpdfstring{$for(author)$$author$$sep$ \and $endfor$}{$author-meta$}} \date$if(shortdate)$[$shortdate$]$endif${$date$} $if(institute)$ \institute$if(shortinstitute)$[$shortinstitute$]$endif${$for(institute)$$institute$$sep$ \and $endfor$} $endif$ $if(titlegraphic)$ \titlegraphic{ $for(titlegraphic)$ \includegraphics$if(titlegraphicoptions)$[$for(titlegraphicoptions)$$titlegraphicoptions$$sep$, $endfor$]$endif${$titlegraphic$}$sep$\enspace $endfor$} $endif$ $if(logo)$ \logo{\includegraphics$if(logooptions)$[$for(logooptions)$$logooptions$$sep$, $endfor$]$endif${$logo$}} $endif$ \begin{document} $if(title)$ \frame{\titlepage} $if(abstract)$ \begin{abstract} $abstract$ \end{abstract} $endif$ $endif$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $if(toc-title)$ \renewcommand*\contentsname{$toc-title$} $endif$ \begin{frame}[allowframebreaks] $if(toc-title)$ \frametitle{$toc-title$} $endif$ \setcounter{tocdepth}{$toc-depth$} \tableofcontents \end{frame} \setcounter{tocdepth}{$toc-depth$} \tableofcontents } $endif$ $if(lof)$ \listoffigures $endif$ $if(lot)$ \listoftables $endif$ $if(linestretch)$ \setstretch{$linestretch$} $endif$ $body$ $if(natbib)$ $if(bibliography)$ $if(biblio-title)$ $if(has-chapters)$ \renewcommand\bibname{$biblio-title$} $else$ \renewcommand\refname{$biblio-title$} $endif$ $endif$ \begin{frame}[allowframebreaks]{$biblio-title$} $if(nocite-ids)$ \nocite{$for(nocite-ids)$$it$$sep$, $endfor$} $endif$ \bibliographytrue \bibliography{$for(bibliography)$$bibliography$$sep$,$endfor$} \end{frame} $endif$ $endif$ $if(biblatex)$ \begin{frame}[allowframebreaks]{$biblio-title$} $if(nocite-ids)$ \nocite{$for(nocite-ids)$$it$$sep$, $endfor$} $endif$ \bibliographytrue \printbibliography[heading=none] \end{frame} $endif$ $for(include-after)$ $include-after$ $endfor$ \end{document} ================================================ FILE: data/templates/default.biblatex ================================================ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.bibtex ================================================ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.chunkedhtml ================================================ $for(author-meta)$ $endfor$ $if(date-meta)$ $endif$ $if(keywords)$ $endif$ $if(description-meta)$ $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ $for(css)$ $endfor$ $for(header-includes)$ $header-includes$ $endfor$ $if(math)$ $math$ $endif$ $for(include-before)$ $include-before$ $endfor$
$if(top)$ $-- only print title block if this is NOT the top page $else$ $if(title)$

$title$

$if(subtitle)$

$subtitle$

$endif$ $for(author)$

$author$

$endfor$ $if(date)$

$date$

$endif$ $if(abstract)$
$abstract-title$
$abstract$
$endif$ $endif$
$endif$ $if(toc)$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.commonmark ================================================ $if(titleblock)$ $titleblock$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $table-of-contents$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.context ================================================ $if(tagging)$ \setupbackend[format=pdf/ua-2] \enabledirectives[backend.usetags=mkiv] \setuptagging[state=start] $endif$ $if(context-lang)$ \mainlanguage[$context-lang$] $endif$ $if(context-dir)$ \setupalign[$context-dir$] \setupdirections[bidi=on,method=two] $endif$ % Enable hyperlinks \setupinteraction [state=start, $if(title)$ title={$title$}, $endif$ $if(subtitle)$ subtitle={$subtitle$}, $endif$ $if(author)$ author={$for(author)$$author$$sep$; $endfor$}, $endif$ $if(keywords)$ keyword={$for(keywords)$$keywords$$sep$; $endfor$}, $endif$ style=$linkstyle$, color=$linkcolor$, contrastcolor=$linkcontrastcolor$] \setupurl[style=$urlstyle$] % make chapter, section bookmarks visible when opening document \placebookmarks[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][chapter, section] \setupinteractionscreen[option={bookmark,title}] $if(papersize)$ \setuppapersize[$for(papersize)$$papersize$$sep$,$endfor$] $endif$ $if(layout)$ \setuplayout[$for(layout)$$layout$$sep$,$endfor$] $endif$ $if(pagenumbering)$ \setuppagenumbering[$for(pagenumbering)$$pagenumbering$$sep$,$endfor$] $else$ \setuppagenumbering[location={footer,middle}] $endif$ $if(pdfa)$ % attempt to generate PDF/A \setupbackend [format=PDF/A-$pdfa$, profile={$if(pdfaiccprofile)$$for(pdfaiccprofile)$$pdfaiccprofile$$sep$,$endfor$$else$sRGB.icc$endif$}, intent=$if(pdfaintent)$$pdfaintent$$else$sRGB IEC61966-2.1$endif$] $endif$ \setupstructure[state=start,method=auto] % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] \definefontfeature[default:tnum][default][tnum=yes, pnum=no] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] \setupbodyfontenvironment[default][em=italic] % use italic as em, not slanted $-- set up font fallbacks $for(mainfontfallback)$ \definefallbackfamily[mainface][rm][$mainfontfallback/nowrap$][range=0x0000-0xFFFF, check=yes, force=no] $endfor$ $for(sansfontfallback)$ \definefallbackfamily[mainface][ss][$sansfontfallback/nowrap$][range=0x0000-0xFFFF, check=yes, force=no] $endfor$ $for(monofontfallback)$ \definefallbackfamily[mainface][tt][$monofontfallback/nowrap$][range=0x0000-0xFFFF, check=yes, force=no] $endfor$ \definefallbackfamily[mainface][rm][CMU Serif][preset=range:greek, force=yes] \definefontfamily[mainface][rm][$if(mainfont)$$mainfont/nowrap$$else$Latin Modern Roman$endif$] \definefontfamily[mainface][mm][$if(mathfont)$$mathfont/nowrap$$else$Latin Modern Math$endif$] \definefontfamily[mainface][ss][$if(sansfont)$$sansfont/nowrap$$else$Latin Modern Sans$endif$] \definefontfamily[mainface][tt][$if(monofont)$$monofont/nowrap$$else$Latin Modern Typewriter$endif$][features=none] \setupbodyfont[mainface$if(fontsize)$,$fontsize$$endif$] \setupwhitespace[$if(whitespace)$$whitespace$$else$medium$endif$] $if(indenting)$ \setupindenting[$for(indenting)$$indenting$$sep$,$endfor$] $endif$ $if(interlinespace)$ \setupinterlinespace[$for(interlinespace)$$interlinespace$$sep$,$endfor$] $endif$ \setuphead[chapter] [style=\tfd\setupinterlinespace,header=empty] \setuphead[section] [style=\tfc\setupinterlinespace] \setuphead[subsection] [style=\tfb\setupinterlinespace] \setuphead[subsubsection] [style=\bf] \setuphead[subsubsubsection] [style=\sc] \setuphead[subsubsubsubsection][style=\it] \definesectionlevels [default] [section, subsection, subsubsection, subsubsubsection, subsubsubsubsection] $if(headertext)$ \setupheadertexts$for(headertext)$[$headertext$]$endfor$ $endif$ $if(footertext)$ \setupfootertexts$for(footertext)$[$footertext$]$endfor$ $endif$ $if(number-sections)$ $else$ \setuphead[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][number=no] $endif$ \definedescription [description] [headstyle=bold, style=normal, location=hanging, width=broad, margin=1cm, alternative=hanging] \setupitemize[autointro] % prevent orphan list intro \setupitemize[indentnext=no] \defineitemgroup[enumerate] \setupenumerate[each][fit][itemalign=left,distance=.5em,style={\feature[+][default:tnum]}] \setupfloat[figure][default={here,nonumber}] \setupfloat[table][default={here,nonumber}] \setupxtable[frame=off] \setupxtable[head][topframe=on] \setupxtable[body][] \setupxtable[foot][] \setupxtable[lastrow][bottomframe=on] $if(emphasis-commands)$ $emphasis-commands$ $endif$ $if(highlighting-commands)$ $highlighting-commands$ $endif$ $if(csl-refs)$ \definemeasure[cslhangindent][1.5em] \definenarrower[hangingreferences][left=\measure{cslhangindent}] \definestartstop [cslreferences] [ $if(csl-hanging-indent)$ before={% \starthangingreferences[left] \setupindenting[-\leftskip,yes,first] \doindentation }, after=\stophangingreferences, $endif$ ] $endif$ $if(includesource)$ $for(sourcefile)$ \attachment[file=$curdir$/$sourcefile$,method=hidden] $endfor$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ \starttext $if(title)$ \startalignment[middle] {\tfd\setupinterlinespace $title$} $if(subtitle)$ \smallskip {\tfa\setupinterlinespace $subtitle$} $endif$ $if(author)$ \smallskip {\tfa\setupinterlinespace $for(author)$$author$$sep$\crlf $endfor$} $endif$ $if(date)$ \smallskip {\tfa\setupinterlinespace $date$} $endif$ \bigskip \stopalignment $endif$ $if(abstract)$ \midaligned{\it Abstract} \startnarrower[2*middle] $abstract$ \stopnarrower \blank[big] $endif$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ \completecontent $endif$ $if(lof)$ \completelistoffigures $endif$ $if(lot)$ \completelistoftables $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ \stoptext ================================================ FILE: data/templates/default.djot ================================================ $if(title)$ # $title$ $endif$ $if(author)$ $for(author)$ $author$ $endfor$ $endif$ $if(date)$ $date$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.docbook4 ================================================ $if(mathml)$ $else$ $endif$
$title$ $if(author)$ $for(author)$ $author$ $endfor$ $endif$ $if(date)$ $date$ $endif$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$
================================================ FILE: data/templates/default.docbook5 ================================================
$title$ $if(subtitle)$ $subtitle$ $endif$ $if(author)$ $for(author)$ $author$ $endfor$ $endif$ $if(date)$ $date$ $endif$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$
================================================ FILE: data/templates/default.dokuwiki ================================================ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ __TOC__ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.dzslides ================================================ $for(author-meta)$ $endfor$ $if(date-meta)$ $endif$ $if(keywords)$ $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ $if(css)$ $for(css)$ $endfor$ $else$ $endif$ $if(math)$ $math$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $if(title)$

$title$

$if(subtitle)$

$subtitle$

$endif$
$if(author)$$for(author)$$author$$sep$, $endfor$ · $endif$$if(institute)$$for(institute)$$institute$$sep$, $endfor$ · $endif$$if(date)$$date$$endif$
$endif$ $if(toc)$
$table-of-contents$
$endif$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ $dzslides-core$ ================================================ FILE: data/templates/default.epub2 ================================================ $pagetitle$ $for(css)$ $endfor$ $for(header-includes)$ $header-includes$ $endfor$ $if(titlepage)$ $for(title)$ $if(title.text)$

$title.text$

$else$

$title$

$endif$ $endfor$ $if(subtitle)$

$subtitle$

$endif$ $for(author)$

$author$

$endfor$ $for(creator)$

$creator.text$

$endfor$ $if(publisher)$

$publisher$

$endif$ $if(date)$

$date$

$endif$ $if(rights)$
$rights$
$endif$ $if(abstract)$
$abstract-title$
$abstract$
$endif$ $else$ $if(coverpage)$
$else$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ $endif$ $endif$ ================================================ FILE: data/templates/default.epub3 ================================================ $pagetitle$ $for(css)$ $endfor$ $for(header-includes)$ $header-includes$ $endfor$ $if(titlepage)$
$for(title)$ $if(title.type)$

$title.text$

$else$

$title$

$endif$ $endfor$ $if(subtitle)$

$subtitle$

$endif$ $for(author)$

$author$

$endfor$ $for(creator)$

$creator.text$

$endfor$ $if(publisher)$

$publisher$

$endif$ $if(date)$

$date$

$endif$ $if(rights)$
$rights$
$endif$ $if(abstract)$
$abstract-title$
$abstract$
$endif$
$else$ $if(coverpage)$
$else$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ $endif$ $endif$ ================================================ FILE: data/templates/default.haddock ================================================ $body$ ================================================ FILE: data/templates/default.html4 ================================================ $for(author-meta)$ $endfor$ $if(date-meta)$ $endif$ $if(keywords)$ $endif$ $if(description-meta)$ $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ $for(css)$ $endfor$ $for(header-includes)$ $header-includes$ $endfor$ $if(math)$ $math$ $endif$ $for(include-before)$ $include-before$ $endfor$ $if(title)$

$title$

$if(subtitle)$

$subtitle$

$endif$ $for(author)$

$author$

$endfor$ $if(date)$

$date$

$endif$ $if(abstract)$
$abstract-title$
$abstract$
$endif$
$endif$ $if(toc)$
$if(toc-title)$

$toc-title$

$endif$ $table-of-contents$
$endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.html5 ================================================ $for(author-meta)$ $endfor$ $if(date-meta)$ $endif$ $if(keywords)$ $endif$ $if(description-meta)$ $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ $for(css)$ $endfor$ $for(header-includes)$ $header-includes$ $endfor$ $if(math)$ $math$ $endif$ $for(include-before)$ $include-before$ $endfor$ $if(title)$

$title$

$if(subtitle)$

$subtitle$

$endif$ $for(author)$

$author$

$endfor$ $if(date)$

$date$

$endif$ $if(abstract)$
$abstract-title$
$abstract$
$endif$
$endif$ $if(toc)$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.icml ================================================ $charStyles$ LeftAlign . 10 $parStyles$ $if(objectStyles)$ $objectStyles$ $endif$ $body$ $hyperlinks$ ================================================ FILE: data/templates/default.jats_archiving ================================================ $if(xml-stylesheet)$ $endif$ ${ article.jats_publishing() } ================================================ FILE: data/templates/default.jats_articleauthoring ================================================ $if(xml-stylesheet)$ $endif$ $if(article.type)$
$else$
$endif$ $if(title)$ $title$ $if(subtitle)$ ${subtitle} $endif$ $endif$ $if(author)$ $for(author)$ $if(author.orcid)$ $author.orcid$ $endif$ $if(author.surname)$ $if(author.non-dropping-particle)$${author.non-dropping-particle} $endif$${author.surname} ${author.given-names}$if(author.dropping-particle)$ ${author.dropping-particle}$endif$ $if(author.prefix)$ ${author.prefix} $endif$ $if(author.suffix)$ ${author.suffix} $endif$ $elseif(author.name)$ $author.name$ $else$ $author$ $endif$ $for(author.affiliation)$ ${ it:affiliations.jats() } $endfor$ $if(author.email)$ $author.email$ $endif$ $if(author.cor-id)$ * $endif$ $endfor$ $endif$ $for(copyright.statement)$ $copyright.statement$ $endfor$ $for(copyright.year)$ $copyright.year$ $endfor$ $for(copyright.holder)$ $copyright.holder$ $endfor$ $if(copyright.text)$ $copyright.text$ $endif$ $for(license)$ $if(it.text)$${it.text}$else$${it}$endif$ $endfor$ $abstract$ $if(tags)$ $for(tags)$ $tags$ $endfor$ $endif$ $if(article.funding-statement)$ $article.funding-statement$ $endif$ $if(supplementary-material)$ $supplementary-material$ $endif$ $body$ $if(back)$ $back$ $endif$
================================================ FILE: data/templates/default.jats_publishing ================================================ $if(xml-stylesheet)$ $endif$ ${ article.jats_publishing() } ================================================ FILE: data/templates/default.jira ================================================ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.latex ================================================ $document-metadata.latex()$ $passoptions.latex()$ \documentclass[ $for(babel-otherlangs)$ $babel-otherlangs$, $endfor$ $if(babel-lang)$ $babel-lang$, $endif$ $if(fontsize)$ $fontsize$, $endif$ $if(papersize)$ $papersize$paper, $endif$ $for(classoption)$ $classoption$$sep$, $endfor$ ]{$documentclass$} $if(beamerarticle)$ \usepackage{beamerarticle} % needs to be loaded first $endif$ \usepackage{xcolor} $if(geometry)$ \usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry} $endif$ \usepackage{amsmath,amssymb} $if(cancel)$ \usepackage{cancel} $endif$ $-- $-- section numbering $-- $if(numbersections)$ \setcounter{secnumdepth}{$if(secnumdepth)$$secnumdepth$$else$5$endif$} $else$ \setcounter{secnumdepth}{-\maxdimen} % remove section numbering $endif$ $fonts.latex()$ $font-settings.latex()$ $common.latex()$ $for(header-includes)$ $header-includes$ $endfor$ $after-header-includes.latex()$ $hypersetup.latex()$ $if(pdf-trailer-id)$ \ifXeTeX \special{pdf:trailerid [ $pdf-trailer-id$ ]} \fi \ifPDFTeX \pdftrailerid{} \pdftrailer{/ID [ $pdf-trailer-id$ ]} \fi \ifLuaTeX \pdfvariable trailerid {[ $pdf-trailer-id$ ]} \fi $endif$ $if(title)$ \title{$title$$if(thanks)$\thanks{$thanks$}$endif$} $endif$ $if(subtitle)$ \usepackage{etoolbox} \makeatletter \providecommand{\subtitle}[1]{% add subtitle to \maketitle \apptocmd{\@title}{\par {\large #1 \par}}{}{} } \makeatother \subtitle{$subtitle$} $endif$ \author{$for(author)$$author$$sep$ \and $endfor$} \date{$date$} \begin{document} $if(has-frontmatter)$ \frontmatter $endif$ $if(title)$ \maketitle $if(abstract)$ \begin{abstract} $abstract$ \end{abstract} $endif$ $endif$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $if(toc-title)$ \renewcommand*\contentsname{$toc-title$} $endif$ { $if(colorlinks)$ $if(toccolor)$ \hypersetup{linkcolor=$toccolor$} $endif$ $endif$ \setcounter{tocdepth}{$toc-depth$} \tableofcontents } $endif$ $if(lof)$ \listoffigures $endif$ $if(lot)$ \listoftables $endif$ $if(linestretch)$ \setstretch{$linestretch$} $endif$ $if(has-frontmatter)$ \mainmatter $endif$ $body$ $if(has-frontmatter)$ \backmatter $endif$ $if(nocite-ids)$ \nocite{$for(nocite-ids)$$it$$sep$, $endfor$} $endif$ $if(natbib)$ $if(bibliography)$ $if(biblio-title)$ $if(has-chapters)$ \renewcommand\bibname{$biblio-title$} $else$ \renewcommand\refname{$biblio-title$} $endif$ $endif$ \bibliography{$for(bibliography)$$bibliography$$sep$,$endfor$} $endif$ $endif$ $if(biblatex)$ \printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$ $endif$ $for(include-after)$ $include-after$ $endfor$ \end{document} ================================================ FILE: data/templates/default.man ================================================ $if(has-tables)$ '\" t $endif$ $if(pandoc-version)$ .\" Automatically generated by Pandoc $pandoc-version$ .\" $endif$ $if(adjusting)$ .ad $adjusting$ $endif$ .TH "$title/nowrap$" "$section/nowrap$" "$date/nowrap$" "$footer/nowrap$"$if(header)$ "$header/nowrap$"$endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ $if(author)$ .SH AUTHORS $for(author)$$author$$sep$; $endfor$. $endif$ ================================================ FILE: data/templates/default.markdown ================================================ $if(titleblock)$ $titleblock$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $table-of-contents$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.markua ================================================ $if(titleblock)$ $titleblock$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $table-of-contents$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.mediawiki ================================================ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ __TOC__ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.ms ================================================ $if(pandoc-version)$ .\" Automatically generated by Pandoc $pandoc-version$ .\" $endif$ .\" **** Custom macro definitions ********************************* .\" * Super/subscript .\" (https://lists.gnu.org/archive/html/groff/2012-07/msg00046.html) .ds { \v'-0.3m'\\s[\\n[.s]*9u/12u] .ds } \s0\v'0.3m' .ds < \v'0.3m'\s[\\n[.s]*9u/12u] .ds > \s0\v'-0.3m' .\" * Horizontal line .de HLINE .LP .ce \l'20' .. $if(highlighting-macros)$ .\" * Syntax highlighting macros $highlighting-macros$ $endif$ .\" **** Settings ************************************************* .\" text width .nr LL 5.5i .\" left margin .nr PO 1.25i .\" top margin .nr HM 1.25i .\" bottom margin .nr FM 1.25i .\" header/footer width .nr LT \n[LL] .\" point size .nr PS $if(pointsize)$$pointsize$$else$10p$endif$ .\" line height .nr VS $if(lineheight)$$lineheight$$else$12p$endif$ .\" font family: A, BM, H, HN, N, P, T, ZCM .fam $if(fontfamily)$$fontfamily$$else$T$endif$ .\" paragraph indent .nr PI $if(indent)$$indent$$else$0m$endif$ .\" interparagraph space .nr PD 0.4v .\" footnote width .nr FL \n[LL] .\" footnote point size .nr FPS (\n[PS] - 2000) $if(papersize)$ .\" paper size .ds paper $papersize$ $endif$ .\" color used for strikeout .defcolor strikecolor rgb 0.7 0.7 0.7 .\" point size difference between heading levels .nr PSINCR 1p .\" heading level above which point size no longer changes .nr GROWPS 2 .\" comment these out if you want a dot after section numbers: .als SN SN-NO-DOT .als SN-STYLE SN-NO-DOT .\" page numbers in footer, centered .ds CH .ds CF % $if(adjusting)$ .ad $adjusting$ $endif$ $if(hyphenate)$ .hy $else$ .nh $endif$ $if(has-inline-math)$ .EQ delim @@ .EN $endif$ $if(pdf-engine)$ .\" color for links (rgb) .ds PDFHREF.COLOUR 0.35 0.00 0.60 .\" border for links (default none) .ds PDFHREF.BORDER 0 0 0 .\" pdf outline fold level .nr PDFOUTLINE.FOLDLEVEL 3 .\" start out in outline view .pdfview /PageMode /UseOutlines .\" *************************************************************** .\" PDF metadata .pdfinfo /Title "$title-meta$" .pdfinfo /Author "$author-meta$" $endif$ $for(header-includes)$ $header-includes$ $endfor$ $if(title)$ .TL $title$ $endif$ $for(author)$ .AU $author$ $endfor$ $if(date)$ .AU .sp 0.5 .ft R $date$ $endif$ $if(abstract)$ .AB $abstract$ .AE $endif$ .\" 1 column (use .2C for two column) .1C $for(include-before)$ $include-before$ $endfor$ $body$ $if(toc)$ .TC $endif$ $for(include-after)$ $include-after$ $endfor$ .pdfsync ================================================ FILE: data/templates/default.muse ================================================ $if(author)$ #author $for(author)$$author$$sep$; $endfor$ $endif$ $if(title)$ #title $title$ $endif$ $if(lang)$ #lang $lang$ $endif$ $if(LISTtitle)$ #LISTtitle $LISTtitle$ $endif$ $if(subtitle)$ #subtitle $subtitle$ $endif$ $if(SORTauthors)$ #SORTauthors $SORTauthors$ $endif$ $if(SORTtopics)$ #SORTtopics $SORTtopics$ $endif$ $if(date)$ #date $date$ $endif$ $if(notes)$ #notes $notes$ $endif$ $if(source)$ #source $source$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.opendocument ================================================ $automatic-styles$ $for(header-includes)$ $header-includes$ $endfor$ $if(title)$ $title$ $endif$ $if(subtitle)$ $subtitle$ $endif$ $for(author)$ $author$ $endfor$ $if(date)$ $date$ $endif$ $if(abstract)$ $abstract$ $endif$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $toc-title$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.openxml ================================================ $if(title)$ $title$ $endif$ $if(subtitle)$ $subtitle$ $endif$ $for(author)$ $author$ $endfor$ $if(date)$ $date$ $endif$ $if(abstract)$ $if(abstract-title)$ $abstract-title$ $endif$ $abstract$ $endif$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $toc$ $endif$ $if(lof)$ $lof$ $endif$ $if(lot)$ $lot$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ $sectpr$ ================================================ FILE: data/templates/default.opml ================================================ $title$ $date$ $for(author)$$author$$sep$; $endfor$ $body$ ================================================ FILE: data/templates/default.org ================================================ $if(title)$ #+title: $title$ $endif$ $if(author)$ #+author: $for(author)$$author$$sep$; $endfor$ $endif$ $if(date)$ #+date: $date$ $endif$ $if(options/pairs)$ $for(options/pairs)$ #+options: ${it.key}:${it.value} $endfor$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $if(abstract)$ #+begin_abstract $abstract$ #+end_abstract $endif$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.plain ================================================ $if(titleblock)$ $titleblock$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ $table-of-contents$ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.revealjs ================================================ $for(author-meta)$ $endfor$ $if(date-meta)$ $endif$ $if(keywords)$ $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ $if(theme)$ $else$ $endif$ $if(highlight-js)$ $endif$ $for(css)$ $endfor$ $if(math)$ $math$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$
$if(title)$

$title$

$if(subtitle)$

$subtitle$

$endif$ $for(author)$

$author$

$endfor$ $for(institute)$

$institute$

$endfor$ $if(date)$

$date$

$endif$
$endif$ $if(toc)$
$endif$ $body$
$if(mathjax)$ $endif$ $if(highlight-js)$ $endif$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.rst ================================================ $if(titleblock)$ $titleblock$ $for(author)$ :Author: $^$$author$ $endfor$ $if(authors)$ :Authors: $author$ $endif$ $if(date)$ :Date: $^$$date$ $endif$ $if(address)$ :Address: $^$$address$ $endif$ $if(contact)$ :Contact: $^$$contact$ $endif$ $if(copyright)$ :Copyright: $^$$copyright$ $endif$ $if(dedication)$ :Dedication: $^$$dedication$ $endif$ $if(organization)$ :Organization: $^$$organization$ $endif$ $if(revision)$ :Revision: $^$$revision$ $endif$ $if(status)$ :Status: $^$$status$ $endif$ $if(version)$ :Version: $^$$version$ $endif$ $if(abstract)$ :Abstract: $abstract$ $endif$ $endif$ $if(rawtex)$ .. role:: raw-latex(raw) :format: latex .. $endif$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ .. contents:: :depth: $toc-depth$ .. $endif$ $if(number-sections)$ .. section-numbering:: $endif$ $for(header-includes)$ $header-includes$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.rtf ================================================ {\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 \fmodern Courier;}} {\colortbl;\red255\green0\blue0;\red0\green0\blue255;} \widowctrl\hyphauto $for(header-includes)$ $header-includes$ $endfor$ $if(title)$ {\pard \qc \f0 \sa180 \li0 \fi0 \b \fs36 $title$\par} $endif$ $for(author)$ {\pard \qc \f0 \sa180 \li0 \fi0 $author$\par} $endfor$ $if(date)$ {\pard \qc \f0 \sa180 \li0 \fi0 $date$\par} $endif$ $if(spacer)$ {\pard \ql \f0 \sa180 \li0 \fi0 \par} $endif$ $if(toc)$ $table-of-contents$ $endif$ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ } ================================================ FILE: data/templates/default.s5 ================================================ $for(author-meta)$ $endfor$ $if(date-meta)$ $endif$ $if(keywords)$ $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ $for(css)$ $endfor$ $if(math)$ $math$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $for(include-before)$ $include-before$ $endfor$
$if(title)$

$title$

$if(subtitle)$

$subtitle$

$endif$ $if(author)$

$for(author)$$author$$sep$
$endfor$

$endif$ $if(institute)$

$for(institute)$$institute$$sep$
$endfor$

$endif$ $if(date)$

$date$

$endif$
$endif$ $if(toc)$
$table-of-contents$
$endif$ $body$ $for(include-after)$ $include-after$ $endfor$
================================================ FILE: data/templates/default.slideous ================================================ $for(author-meta)$ $endfor$ $if(date-meta)$ $endif$ $if(keywords)$ $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ $for(css)$ $endfor$ $if(math)$ $math$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $if(duration)$ $endif$ $for(include-before)$ $include-before$ $endfor$
of {$$slidecount} ½ {$$title}, {$$author}
$if(title)$

$title$

$if(subtitle)$

$subtitle$

$endif$ $if(author)$

$for(author)$$author$$sep$
$endfor$

$endif$ $if(institute)$

$for(institute)$$institute$$sep$
$endfor$

$endif$ $if(date)$

$date$

$endif$
$endif$ $if(toc)$
$table-of-contents$
$endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.slidy ================================================ $for(author-meta)$ $endfor$ $if(date-meta)$ $endif$ $if(keywords)$ $endif$ $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ $for(css)$ $endfor$ $if(math)$ $math$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ $if(duration)$ $endif$ $for(include-before)$ $include-before$ $endfor$ $if(title)$

$title$

$if(subtitle)$

$subtitle$

$endif$ $if(author)$

$for(author)$$author$$sep$
$endfor$

$endif$ $if(institute)$

$for(institute)$$institute$$sep$
$endfor$

$endif$ $if(date)$

$date$

$endif$
$endif$ $if(toc)$
$table-of-contents$
$endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.tei ================================================ $title$ $for(author)$ $author$ $endfor$ $if(publicationStmt)$

$if(publicationStmt)$$publicationStmt$$endif$

$endif$ $if(license)$ $license$ $endif$ $if(publisher)$ $publisher$ $endif$ $if(pubPlace)$ $pubPlace$ $endif$ $if(address)$
$address$
$endif$ $if(date)$ $date$ $endif$
$if(sourceDesc)$ $sourceDesc$ $else$

Produced by pandoc.

$endif$
$for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$
================================================ FILE: data/templates/default.texinfo ================================================ \input texinfo @c -*-texinfo-*- $if(filename)$ @setfilename $filename$ $endif$ $if(title)$ @settitle $title$$if(version)$ $version$$endif$ $endif$ @documentencoding UTF-8 $for(header-includes)$ $header-includes$ $endfor$ $if(strikeout)$ @macro textstrikeout{text} ~~\text\~~ @end macro $endif$ @ifnottex @paragraphindent 0 @end ifnottex $if(titlepage)$ @titlepage @title $title$ $if(version)$ @subtitle $version$ $endif$ $for(author)$ @author $author$ $endfor$ $if(date)$ $date$ $endif$ @end titlepage $endif$ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ @contents $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ @bye ================================================ FILE: data/templates/default.textile ================================================ $for(include-before)$ $include-before$ $endfor$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.typst ================================================ #let horizontalrule = line(start: (25%,0%), end: (75%,0%)) #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, stroke: none ) #show figure.where( kind: table ): set figure.caption(position: $if(table-caption-position)$$table-caption-position$$else$top$endif$) #show figure.where( kind: image ): set figure.caption(position: $if(figure-caption-position)$$figure-caption-position$$else$bottom$endif$) $if(highlighting-definitions)$ // syntax highlighting functions from skylighting: $highlighting-definitions$ $endif$ $if(template)$ #import "$template$": conf $else$ $template.typst()$ $endif$ $if(smart)$ $else$ #set smartquote(enabled: false) $endif$ $for(header-includes)$ $header-includes$ $endfor$ #show: doc => conf( $if(title)$ title: [$title$], $endif$ $if(subtitle)$ subtitle: [$subtitle$], $endif$ $if(author)$ authors: ( $for(author)$ $if(author.name)$ ( name: [$author.name$], affiliation: [$author.affiliation$], email: [$author.email$] ), $else$ ( name: [$author$], affiliation: "", email: "" ), $endif$ $endfor$ ), $endif$ $if(keywords)$ keywords: ($for(keywords)$$keywords$$sep$,$endfor$), $endif$ $if(date)$ date: [$date$], $endif$ $if(lang)$ lang: "$lang$", $endif$ $if(region)$ region: "$region$", $endif$ $if(abstract-title)$ abstract-title: [$abstract-title$], $endif$ $if(abstract)$ abstract: [$abstract$], $endif$ $if(thanks)$ thanks: [$thanks$], $endif$ $if(margin)$ margin: ($for(margin/pairs)$$margin.key$: $margin.value$,$endfor$), $endif$ $if(papersize)$ paper: "$papersize$", $endif$ $if(mainfont)$ font: ("$mainfont$",), $endif$ $if(fontsize)$ fontsize: $fontsize$, $endif$ $if(mathfont)$ mathfont: ($for(mathfont)$"$mathfont$",$endfor$), $endif$ $if(codefont)$ codefont: ($for(codefont)$"$codefont$",$endfor$), $endif$ $if(linestretch)$ linestretch: $linestretch$, $endif$ $if(section-numbering)$ sectionnumbering: "$section-numbering$", $endif$ pagenumbering: $if(page-numbering)$"$page-numbering$"$else$none$endif$, $if(linkcolor)$ linkcolor: [$linkcolor$], $endif$ $if(citecolor)$ citecolor: [$citecolor$], $endif$ $if(filecolor)$ filecolor: [$filecolor$], $endif$ cols: $if(columns)$$columns$$else$1$endif$, doc, ) $for(include-before)$ $include-before$ $endfor$ $if(toc)$ #outline( title: auto, depth: $toc-depth$ ); $endif$ $body$ $if(citations)$ $for(nocite-ids)$ #cite(label("${it}"), form: none) $endfor$ $if(csl)$ #set bibliography(style: "$csl$") $elseif(bibliographystyle)$ #set bibliography(style: "$bibliographystyle$") $endif$ $if(bibliography)$ #bibliography(($for(bibliography)$"$bibliography$"$sep$,$endfor$)$if(full-bibliography)$, full: true$endif$) $endif$ $endif$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.vimdoc ================================================ $if(filename)$*${filename}* $endif$$if(abstract)$${abstract}$endif$$if(filename)$ $endif$$if(combined-title)$${combined-title} $endif$$toc-reminder$ $if(toc)$ $toc$ $endif$ $body$ $modeline$ ================================================ FILE: data/templates/default.xwiki ================================================ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ {{toc /}} $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/default.zimwiki ================================================ Content-Type: text/x-zim-wiki Wiki-Format: zim 0.4 $for(include-before)$ $include-before$ $endfor$ $if(toc)$ __TOC__ $endif$ $body$ $for(include-after)$ $include-after$ $endfor$ ================================================ FILE: data/templates/document-metadata.latex ================================================ $-- $-- 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$ ================================================ FILE: data/templates/font-settings.latex ================================================ $-- User font settings (must come after default font and Beamer theme) $if(fontfamily)$ \usepackage[$for(fontfamilyoptions)$$fontfamilyoptions$$sep$,$endfor$]{$fontfamily$} $endif$ \ifPDFTeX\else % xetex/luatex font selection $if(mainfont)$ $if(mainfontfallback)$ \ifLuaTeX \usepackage{luaotfload} \directlua{luaotfload.add_fallback("mainfontfallback",{ $for(mainfontfallback)$"$mainfontfallback$"$sep$,$endfor$ })} \fi $endif$ \setmainfont[$for(mainfontoptions)$$mainfontoptions$$sep$,$endfor$$if(mainfontfallback)$,RawFeature={fallback=mainfontfallback}$endif$]{$mainfont$} $endif$ $if(sansfont)$ $if(sansfontfallback)$ \ifLuaTeX \usepackage{luaotfload} \directlua{luaotfload.add_fallback("sansfontfallback",{ $for(sansfontfallback)$"$sansfontfallback$"$sep$,$endfor$ })} \fi $endif$ \setsansfont[$for(sansfontoptions)$$sansfontoptions$$sep$,$endfor$$if(sansfontfallback)$,RawFeature={fallback=sansfontfallback}$endif$]{$sansfont$} $endif$ $if(monofont)$ $if(monofontfallback)$ \ifLuaTeX \usepackage{luaotfload} \directlua{luaotfload.add_fallback("monofontfallback",{ $for(monofontfallback)$"$monofontfallback$"$sep$,$endfor$ })} \fi $endif$ \setmonofont[$for(monofontoptions)$$monofontoptions$$sep$,$endfor$$if(monofontfallback)$,RawFeature={fallback=monofontfallback}$endif$]{$monofont$} $endif$ $for(fontfamilies)$ \newfontfamily{$fontfamilies.name$}[$for(fontfamilies.options)$$fontfamilies.options$$sep$,$endfor$]{$fontfamilies.font$} $endfor$ $if(mathfont)$ $if(mathspec)$ \ifXeTeX \setmathfont(Digits,Latin,Greek)[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} \else \setmathfont[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} \fi $else$ \setmathfont[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$} $endif$ $endif$ $if(CJKmainfont)$ \ifXeTeX \usepackage{xeCJK} \setCJKmainfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$} $if(CJKsansfont)$ \setCJKsansfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKsansfont$} $endif$ $if(CJKmonofont)$ \setCJKmonofont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmonofont$} $endif$ \fi $endif$ $if(luatexjapresetoptions)$ \ifLuaTeX \usepackage[$for(luatexjapresetoptions)$$luatexjapresetoptions$$sep$,$endfor$]{luatexja-preset} \fi $endif$ $if(CJKmainfont)$ \ifLuaTeX \usepackage[$for(luatexjafontspecoptions)$$luatexjafontspecoptions$$sep$,$endfor$]{luatexja-fontspec} \setmainjfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$} \fi $endif$ \fi $if(zero-width-non-joiner)$ %% Support for zero-width non-joiner characters. \makeatletter \def\zerowidthnonjoiner{% % Prevent ligatures and adjust kerning, but still support hyphenating. \texorpdfstring{% \TextOrMath{\nobreak\discretionary{-}{}{\kern.03em}% \ifvmode\else\nobreak\hskip\z@skip\fi}{}% }{}% } \makeatother \ifPDFTeX \DeclareUnicodeCharacter{200C}{\zerowidthnonjoiner} \else \catcode`^^^^200c=\active \protected\def ^^^^200c{\zerowidthnonjoiner} \fi %% End of ZWNJ support $endif$ % Use upquote if available, for straight quotes in verbatim environments \IfFileExists{upquote.sty}{\usepackage{upquote}}{} \IfFileExists{microtype.sty}{% use microtype if available \usepackage[$for(microtypeoptions)$$microtypeoptions$$sep$,$endfor$]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts }{} ================================================ FILE: data/templates/fonts.latex ================================================ \usepackage{iftex} \ifPDFTeX \usepackage[$if(fontenc)$$fontenc$$else$T1$endif$]{fontenc} \usepackage[utf8]{inputenc} \usepackage{textcomp} % provide euro and other symbols \else % if luatex or xetex $if(mathspec)$ \ifXeTeX \usepackage{mathspec} % this also loads fontspec \else \usepackage{unicode-math} % this also loads fontspec \fi $else$ \usepackage{unicode-math} % this also loads fontspec $endif$ \defaultfontfeatures{Scale=MatchLowercase}$-- must come before Beamer theme \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1} \fi $if(fontfamily)$ $else$ $-- Set default font before Beamer theme so the theme can override it \usepackage{lmodern} $endif$ ================================================ FILE: data/templates/hypersetup.latex ================================================ \hypersetup{ $if(title-meta)$ pdftitle={$title-meta$}, $endif$ $if(author-meta)$ pdfauthor={$author-meta$}, $endif$ $if(lang)$ pdflang={$lang$}, $endif$ $if(subject)$ pdfsubject={$subject$}, $endif$ $if(keywords)$ pdfkeywords={$for(keywords)$\xmpquote{$keywords$}$sep$, $endfor$}, $endif$ $if(colorlinks)$ colorlinks=true, linkcolor={$if(linkcolor)$$linkcolor$$else$Maroon$endif$}, filecolor={$if(filecolor)$$filecolor$$else$Maroon$endif$}, citecolor={$if(citecolor)$$citecolor$$else$Blue$endif$}, urlcolor={$if(urlcolor)$$urlcolor$$else$Blue$endif$}, $else$ $if(boxlinks)$ $else$ hidelinks, $endif$ $endif$ pdfcreator={LaTeX via pandoc}} ================================================ FILE: data/templates/passoptions.latex ================================================ % Options for packages loaded elsewhere \PassOptionsToPackage{unicode$for(hyperrefoptions)$,$hyperrefoptions$$endfor$}{hyperref} \PassOptionsToPackage{hyphens}{url} $if(colorlinks)$ \PassOptionsToPackage{dvipsnames,svgnames,x11names}{xcolor} $endif$ $if(CJKmainfont)$ \PassOptionsToPackage{space}{xeCJK} $endif$ ================================================ FILE: data/templates/styles.citations.html ================================================ /* CSS for citations */ div.csl-bib-body { } div.csl-entry { clear: both; $if(csl-entry-spacing)$ margin-bottom: $csl-entry-spacing$; $endif$ } .hanging-indent div.csl-entry { margin-left:2em; text-indent:-2em; } div.csl-left-margin { min-width:2em; float:left; } div.csl-right-inline { margin-left:2em; padding-left:1em; } div.csl-indent { margin-left: 2em; } ================================================ FILE: data/templates/styles.html ================================================ /* Default styles provided by pandoc. ** See https://pandoc.org/MANUAL.html#variables-for-html for config info. */ $if(document-css)$ html { $if(mainfont)$ font-family: $mainfont$; $endif$ $if(fontsize)$ font-size: $fontsize$; $endif$ $if(linestretch)$ line-height: $linestretch$; $endif$ color: $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$; background-color: $if(backgroundcolor)$$backgroundcolor$$else$#fdfdfd$endif$; } body { margin: 0 auto; max-width: $if(maxwidth)$$maxwidth$$else$36em$endif$; padding-left: $if(margin-left)$$margin-left$$else$50px$endif$; padding-right: $if(margin-right)$$margin-right$$else$50px$endif$; padding-top: $if(margin-top)$$margin-top$$else$50px$endif$; padding-bottom: $if(margin-bottom)$$margin-bottom$$else$50px$endif$; hyphens: auto; overflow-wrap: break-word; text-rendering: optimizeLegibility; font-kerning: normal; } @media (max-width: 600px) { body { font-size: 0.9em; padding: 12px; } h1 { font-size: 1.8em; } } @media print { html { background-color: $if(backgroundcolor)$$backgroundcolor$$else$white$endif$; } body { background-color: transparent; color: black; font-size: 12pt; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3, h4 { page-break-after: avoid; } } p { margin: 1em 0; } a { color: $if(linkcolor)$$linkcolor$$else$#1a1a1a$endif$; } a:visited { color: $if(linkcolor)$$linkcolor$$else$#1a1a1a$endif$; } img { max-width: 100%; } svg { height: auto; max-width: 100%; } h1, h2, h3, h4, h5, h6 { margin-top: 1.4em; } h5, h6 { font-size: 1em; font-style: italic; } h6 { font-weight: normal; } ol, ul { padding-left: 1.7em; margin-top: 1em; } li > ol, li > ul { margin-top: 0; } blockquote { margin: 1em 0 1em 1.7em; padding-left: 1em; border-left: 2px solid #e6e6e6; color: #606060; } $if(abstract)$ div.abstract { margin: 2em 2em 2em 2em; text-align: left; font-size: 85%; } div.abstract-title { font-weight: bold; text-align: center; padding: 0; margin-bottom: 0.5em; } $endif$ code { white-space: pre-wrap; font-family: $if(monofont)$$monofont$$else$Menlo, Monaco, Consolas, 'Lucida Console', monospace$endif$; $if(monobackgroundcolor)$ background-color: $monobackgroundcolor$; padding: .2em .4em; $endif$ font-size: 85%; margin: 0; hyphens: manual; } pre { margin: 1em 0; $if(monobackgroundcolor)$ background-color: $monobackgroundcolor$; padding: 1em; $endif$ overflow: auto; } pre code { padding: 0; overflow: visible; overflow-wrap: normal; } .sourceCode { background-color: transparent; overflow: visible; } hr { border: none; border-top: 1px solid #1a1a1a; height: 1px; margin: 1em 0; } table { margin: 1em 0; border-collapse: collapse; width: 100%; overflow-x: auto; display: block; font-variant-numeric: lining-nums tabular-nums; } table caption { $if(table-caption-below)$ caption-side: bottom; margin-top: 0.75em; $else$ margin-bottom: 0.75em; $endif$ } tbody { margin-top: 0.5em; border-top: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$; border-bottom: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$; } th { border-top: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$; padding: 0.25em 0.5em 0.25em 0.5em; } td { padding: 0.125em 0.5em 0.25em 0.5em; } header { margin-bottom: 4em; text-align: center; } #TOC li { list-style: none; } #TOC ul { padding-left: 1.3em; } #TOC > ul { padding-left: 0; } #TOC a:not(:hover) { text-decoration: none; } $endif$ span.smallcaps{font-variant: small-caps;} div.columns{display: flex; gap: min(4vw, 1.5em);} div.column{flex: auto; overflow-x: auto;} div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} /* The extra [class] is a hack that increases specificity enough to override a similar rule in reveal.js */ ul.task-list[class]{list-style: none;} ul.task-list li input[type="checkbox"] { font-size: inherit; width: 0.8em; margin: 0 0.8em 0.2em -1.6em; vertical-align: middle; } $if(quotes)$ q { quotes: "“" "”" "‘" "’"; } $endif$ $if(displaymath-css)$ .display.math{display: block; text-align: center; margin: 0.5rem auto;} $endif$ $if(highlighting-css)$ /* CSS for syntax highlighting */ $highlighting-css$ $endif$ $if(csl-css)$ $styles.citations.html()$ $endif$ ================================================ FILE: data/templates/template.typst ================================================ #let content-to-string(content) = { if content.has("text") { content.text } else if content.has("children") { content.children.map(content-to-string).join("") } else if content.has("body") { content-to-string(content.body) } else if content == [ ] { " " } } #let conf( title: none, subtitle: none, authors: (), keywords: (), date: none, abstract-title: none, abstract: none, thanks: none, cols: 1, margin: (x: 1.25in, y: 1.25in), paper: "us-letter", lang: "en", region: "US", font: none, fontsize: 11pt, mathfont: none, codefont: none, linestretch: 1, sectionnumbering: none, linkcolor: none, citecolor: none, filecolor: none, pagenumbering: "1", doc, ) = { set document( title: title, keywords: keywords, ) set document( author: authors.map(author => content-to-string(author.name)).join(", ", last: " & "), ) if authors != none and authors != () set page( paper: paper, margin: margin, numbering: pagenumbering, columns: cols ) set par( justify: true, leading: linestretch * 0.65em ) set text(lang: lang, region: region, size: fontsize) set text(font: font) if font != none show math.equation: set text(font: mathfont) if mathfont != none show raw: set text(font: codefont) if codefont != none set heading(numbering: sectionnumbering) show link: set text(fill: rgb(content-to-string(linkcolor))) if linkcolor != none show ref: set text(fill: rgb(content-to-string(citecolor))) if citecolor != none show link: this => { if filecolor != none and type(this.dest) == label { text(this, fill: rgb(content-to-string(filecolor))) } else { text(this) } } if title != none { place(top, float: true, scope: "parent", clearance: 4mm, block(below: 1em, width: 100%)[ #if title != none { align(center, block[ #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, hyphenate: false)[#subtitle] } )]) } #if authors != none and authors != [] { let count = authors.len() let ncols = calc.min(count, 3) grid( columns: (1fr,) * ncols, row-gutter: 1.5em, ..authors.map(author => align(center)[ #author.name \ #author.affiliation \ #author.email ]) ) } #if date != none { align(center)[#block(inset: 1em)[ #date ]] } #if abstract != none { block(inset: 2em)[ #text(weight: "semibold")[#abstract-title] #h(1em) #abstract ] } ]) } doc } ================================================ FILE: data/translations/af.yaml ================================================ Abstract: Samevatting Appendix: Bylae Bibliography: Bibliografie Cc: a.a. Chapter: Hoofstuk Contents: Inhoudsopgawe Encl: Bylae(n) Figure: Figuur Index: Inhoud ListOfFigures: Lys van figure ListOfTables: Lys van tabelle Page: Bladsy Part: Deel Preface: Voorwoord Proof: Bewys References: Verwysings See: sien SeeAlso: sien ook Table: Tabel To: Aan ================================================ FILE: data/translations/alt.yaml ================================================ Chapter: Бажалык Contents: Бажалыктар Figure: Јурук Glossary: Сӧзлик Preface: Сӧс бажы ================================================ FILE: data/translations/am.yaml ================================================ Abstract: አኅጽተሮ ጽሁፍ Appendix: መድበል Bibliography: ቢዋ መጽሃፍት Cc: ግልባጭ Chapter: ክፍል Contents: ይዘት Encl: አባሪዎች Figure: ሥዕል Index: ምህጻር ቃል ListOfFigures: የሥዕችሎ ማውጫ ListOfTables: የሰንጠዥረ ማውጫ Page: ገጽ Part: ንዑስ ክፍል Preface: መቅድም Proof: ማረጋገጫ References: የነሥ ጹሁፍ ምንጭ See: ይመልከቱ SeeAlso: ይህምን ይመልከቱ Table: ሰንጠረዥ To: ለ ================================================ FILE: data/translations/ar.yaml ================================================ Abstract: ملخص Appendix: الملاحق Bibliography: المصادر Cc: نسخة ل‬ Chapter: باب Contents: المحتويات Encl: المرفقات Figure: شكل Glossary: قاموس Index: الفهرس ListOfFigures: قائمة الأشكال ListOfTables: قائمة الجداول Page: صفحة Part: القسم Preface: مدخل Proof: برهان References: المراجع See: راجع SeeAlso: راجع أيضًا Table: جدول To: إلى ================================================ FILE: data/translations/as.yaml ================================================ Abstract: মূলভাৱ Appendix: পৰিশিষ্ট Bibliography: তথ্যসূত্ৰ Cc: প্ৰতিলিপি Chapter: অধ্যায় Contents: সমল Encl: সংযোজিত Figure: চিত্ৰ Glossary: অৰ্থকোষ Index: বিষয়সূচী ListOfFigures: চিত্ৰসূচী ListOfTables: তালিকাসূচী Page: পৃষ্ঠা Part: অংশ Preface: পাতনি Proof: প্ৰমাণ References: প্ৰসংগ See: চাওক SeeAlso: ও Table: তালিকা To: মুখে ================================================ FILE: data/translations/ast.yaml ================================================ Abstract: Sumariu Appendix: Apéndiz Bibliography: Bibliografía Cc: cc Chapter: Capítulu Contents: Conteníu Encl: incl. Figure: Figura Glossary: Glosariu Index: Índiz ListOfFigures: Llista de figures ListOfTables: Llista de tables Page: Páxina Part: Parte Preface: Entamu Proof: Demostración References: Referencies See: ver SeeAlso: ver tamién Table: Tabla To: Pa ================================================ FILE: data/translations/az.yaml ================================================ Abstract: Annotasiya Appendix: Əlavə Bibliography: Ədəbiyyat Cc: cc Chapter: Fəsil Contents: Mündəricat Encl: encl Figure: Şəkil Glossary: Terminlər lüğəti Index: Mövzu göstəricisi ListOfFigures: Şəkillərin siyahısı ListOfTables: Cədvəllərin siyahısı Page: səh. Part: Hissə Preface: Ön söz Proof: İsbatı References: Ədəbiyyat See: baxın SeeAlso: həmçinin baxın Table: Cədvəl To: To ================================================ FILE: data/translations/be.yaml ================================================ Abstract: Анатацыя Appendix: Дадатак Bibliography: Літаратура Cc: зых. Chapter: Глава Contents: Зьмест Encl: укл. Figure: Рыс. Glossary: Слоўнік тэрмінаў Index: Прадметны паказальнік ListOfFigures: Сьпіс ілюстрацый ListOfTables: Сьпіс табліц Page: с. Part: Частка Preface: Прадмова Proof: Доказ References: Сьпіс літаратуры See: гл. SeeAlso: гл. таксама Table: Табліца To: вх. ================================================ FILE: data/translations/bg.yaml ================================================ Abstract: Абстракт Appendix: Приложение Bibliography: Библиография Cc: копия Chapter: Глава Contents: Съдържание Encl: Приложения Figure: Фигура Glossary: Притурка Index: Азбучен указател ListOfFigures: Списък на фигурите ListOfTables: Списък на таблиците Page: Стр. Part: Част Preface: Предговор Proof: Доказателство References: Литература See: вж. SeeAlso: вж. също и Table: Таблица To: За ================================================ FILE: data/translations/bn.yaml ================================================ Abstract: সারসংক্ষেপ Appendix: পরিশিষ্ট Bibliography: তথ্যবিবরণ Cc: অনুলিপি Chapter: অধ্যায় Contents: সূচীপত্র Encl: সংযুক্তি Figure: ছবি/নকশা Glossary: পরিভাষার শব্দসম্ভার Index: সূচক/নির্দেশক ListOfFigures: ছবি/নকশা সমূহের তালিকা ListOfTables: তালিকাসারণী Page: পৃষ্ঠা Part: খন্ড Preface: পূর্বকথা Proof: প্রমাণ References: তথ্যসুত্রসমূহ See: দেখুন SeeAlso: আরও দেখুন Table: সারনী To: প্রতি ================================================ FILE: data/translations/bo.yaml ================================================ Abstract: གནད་བསྡུས། Appendix: ཞར་བྱུང་། Bibliography: དཔེ་ཆའི་ཐོ་གཞུང་། Cc: འདྲ་བཤུས་ལེན་མཁན་ Chapter: ལེའུ་ Contents: དཀར་ཆག། Figure: པར་རིས་ Glossary: མིང་ཚིག་རེའུ་མིག། Index: གསུལ་བྱང་། Page: ཤོག་ Part: ཆ་ཤས་ Preface: དཔེ་དེབ་ཀྱི་གླེང་བརྗོད། Proof: བདེན་དཔང་། Table: རེའུ་མིག་ ================================================ FILE: data/translations/br.yaml ================================================ Abstract: Dvierrañ Appendix: Stagadenn Bibliography: Lennadurezh Cc: Eilskrid da Chapter: Pennad Contents: Taolenn Encl: Dielloù kevret Figure: Figurenn Glossary: Glossary Index: Meneger ListOfFigures: Listenn ar Figurennoù ListOfTables: Listenn an taolennoù Page: Pajenn Part: Lodenn Preface: Rakskrid Proof: Proof References: Daveennoù See: Gwelout SeeAlso: Gwelout ivez Table: Taolenn To: evit ================================================ FILE: data/translations/bs.yaml ================================================ Abstract: Sažetak Appendix: Dodatak Bibliography: Bibliografija Cc: Kopija Chapter: Poglavlje Contents: Sadržaj Encl: Prilozi Figure: Slika Glossary: Rječnik Index: Indeks ListOfFigures: Popis slika ListOfTables: Popis tabela Page: Stranica Part: Dio Preface: Predgovor Proof: Dokaz References: Literatura See: Vidjeti SeeAlso: Također vidjeti Table: Tabela To: Prima ================================================ FILE: data/translations/bua.yaml ================================================ Abstract: Тобшолол Appendix: Хабсаралта Bibliography: Ном зүй Chapter: Бүлэг Contents: Гаршаг Encl: Оруулаха Figure: Зураг Index: Бүгэд хэлхээс ListOfFigures: Зурагай жагсаалт ListOfTables: Хүснэгэтэй жагсаалт Page: Хуудаһан Part: Бүлэг Preface: Оршол References: Ашаглаһан ном See: Үз SeeAlso: Mүн үз Table: Хүсэнэгт ================================================ FILE: data/translations/ca.yaml ================================================ Abstract: Resum Appendix: Apèndix Bibliography: Bibliografia Cc: Còpies a Chapter: Capítol Contents: Índex Encl: Adjunt Figure: Figura Glossary: Glossari Index: Índex alfabètic ListOfFigures: Índex de figures ListOfTables: Índex de taules Page: Pàgina Part: Part Preface: Pròleg Proof: Demostració References: Referències See: Vegeu SeeAlso: Vegeu també Table: Taula To: A ================================================ FILE: data/translations/ckb-Arab.yaml ================================================ Abstract: پوختە Appendix: پاشکۆ Bibliography: کتێبنامە Cc: ڕوونووس Chapter: بەندی Contents: نێوەڕۆک Encl: هاوپێچ Figure: وێنەی Glossary: فەرهەنگۆک Index: پێنوێن ListOfFigures: لیستی وێنەکان ListOfTables: لیستی خشتەکان Page: لاپەڕە Part: بەشی Preface: پێشەكی Proof: سەلماندن References: سەرچاوەکان See: چاو لێکەن SeeAlso: هەروەها چاو لێکەن Table: خشتەی To: بۆ ================================================ FILE: data/translations/ckb-Latn.yaml ================================================ Abstract: Puxte Appendix: Paşko Bibliography: Kitêbname Cc: Rûnûs Chapter: Bendî Contents: Nêwerrok Encl: Hawpêç Figure: Wêney Glossary: Ferhengok Index: Pêrrist ListOfFigures: Lîstî Wênekan ListOfTables: Lîstî Xiştekan Page: Laperre Part: Beşî Preface: Pêşekî Proof: Selmandin References: Serçawekan See: Çaw lêken SeeAlso: Herweha çaw lêken Table: Xiştey To: Bo ================================================ FILE: data/translations/cs.yaml ================================================ Abstract: Abstrakt Appendix: Příloha Bibliography: Literatura Cc: Na vědomí Chapter: Kapitola Contents: Obsah Encl: Příloha Figure: Obrázek Glossary: Slovník Index: Rejstřík ListOfFigures: Seznam obrázků ListOfTables: Seznam tabulek Page: Strana Part: Část Preface: Předmluva Proof: Důkaz References: Reference See: viz SeeAlso: viz také Table: Tabulka To: Komu ================================================ FILE: data/translations/cu.yaml ================================================ Abstract: Аннотация Appendix: Приложение Bibliography: Литература Cc: исх. Chapter: Глава Contents: Содержание Encl: вкл. Figure: Рис. Glossary: Словарь терминов Index: Предметный указатель ListOfFigures: Список иллюстраций ListOfTables: Список таблиц Page: с. Part: Часть Preface: Предисловие Proof: Доказательство References: Список литературы See: см. SeeAlso: см. также Table: Таблица To: вх. ================================================ FILE: data/translations/cy.yaml ================================================ Abstract: Crynodeb Appendix: Atodiad Bibliography: Llyfryddiaeth Cc: copïau Chapter: Pennod Contents: Cynnwys Encl: amgaeëdig Figure: Darlun Glossary: Rhestr termau Index: Mynegai ListOfFigures: Rhestr Ddarluniau ListOfTables: Rhestr Dablau Page: tudalen Part: Rhan Preface: Rhagair Proof: Prawf References: Cyfeiriadau See: gweler SeeAlso: gweler hefyd Table: Taflen To: At ================================================ FILE: data/translations/cz.yaml ================================================ Abstract: Abstrakt Appendix: Dodatek Bibliography: Literatura Cc: Na vědomí Chapter: Kapitola Contents: Obsah Encl: Příloha Figure: Obrázek Glossary: Slovník Index: Index ListOfFigures: Seznam obrázků ListOfTables: Seznam tabulek Page: Strana Part: Část Preface: Předmluva Proof: Důkaz References: Reference See: viz SeeAlso: viz Table: Tabulka To: Komu ================================================ FILE: data/translations/da.yaml ================================================ Abstract: Resumé Appendix: Bilag Bibliography: Litteratur Cc: Kopi til Chapter: Kapitel Contents: Indhold Encl: Vedlagt Figure: Figur Glossary: Gloseliste Index: Indeks ListOfFigures: Figurer ListOfTables: Tabeller Page: Side Part: Del Preface: Forord Proof: Bevis References: Litteratur See: Se SeeAlso: Se også Table: Tabel To: Til ================================================ FILE: data/translations/de.yaml ================================================ Abstract: Zusammenfassung Appendix: Anhang Bibliography: Literaturverzeichnis Cc: Verteiler Chapter: Kapitel Contents: Inhaltsverzeichnis Encl: Anlage(n) Figure: Abbildung Glossary: Glossar Index: Index Listing: Auflistung ListOfFigures: Abbildungsverzeichnis ListOfTables: Tabellenverzeichnis Page: Seite Part: Teil Preface: Vorwort Proof: Beweis References: Literatur See: siehe SeeAlso: siehe auch Table: Tabelle To: An ================================================ FILE: data/translations/dsb.yaml ================================================ Abstract: Abstrakt Appendix: Dodawki Bibliography: Literatura Cc: CC Chapter: Kapitl Contents: Wopśimjeśe Encl: Pśiłoga Figure: Wobraz Glossary: Glossary Index: Indeks ListOfFigures: Zapis wobrazow ListOfTables: Zapis tabulkow Page: Strona Part: Źěl Preface: Zawod Proof: Proof References: Referency See: gl. SeeAlso: gl.~teke Table: Tabulka To: Komu ================================================ FILE: data/translations/el.yaml ================================================ Abstract: Περίληψη Appendix: Παράρτημα Bibliography: Βιβλιογραφία Cc: Κοινοποίηση Chapter: Κεφάλαιο Contents: Περιεχόμενα Encl: Συνημμένα Figure: Σχήμα Glossary: Γλωσσάρι Index: Ευρετήριο ListOfFigures: Κατάλογος Σχημάτων ListOfTables: Κατάλογος Πινάκων Page: Σελίδα Part: Μέρος Preface: Πρόλογος Proof: Απόδειξη References: Αναφορές See: βλέπε SeeAlso: βλέπε επίσης Table: Πίνακας To: Προς ================================================ FILE: data/translations/en.yaml ================================================ Abstract: Abstract Appendix: Appendix Bibliography: Bibliography Cc: cc Chapter: Chapter Contents: Contents Encl: encl Figure: Figure Glossary: Glossary Index: Index Listing: Listing ListOfFigures: List of Figures ListOfTables: List of Tables Page: page Part: Part Preface: Preface Proof: Proof References: References See: see SeeAlso: see also Table: Table To: To ================================================ FILE: data/translations/eo.yaml ================================================ Abstract: Resumo Appendix: Apendico Bibliography: Bibliografio Cc: Kopie al Chapter: Ĉapitro Contents: Enhavo Encl: Aldono(j) Figure: Figuro Glossary: Glosaro Index: Indekso ListOfFigures: Listo de figuroj ListOfTables: Listo de tabeloj Page: Paĝo Part: Parto Preface: Antaŭparolo Proof: Pruvo References: Citaĵoj See: vidu SeeAlso: vidu ankaŭ Table: Tabelo To: Al ================================================ FILE: data/translations/es-ES.yaml ================================================ Abstract: Resumen Appendix: Apéndice Bibliography: Bibliografía Cc: Copia a Chapter: Capítulo Contents: Índice general Encl: Adjunto(s) Figure: Figura Glossary: Glosario Index: Índice alfabético ListOfFigures: Índice de figuras ListOfTables: Índice de cuadros Page: Página Part: Parte Preface: Prefacio Proof: Prueba References: Referencias See: véase SeeAlso: véase también Table: Cuadro To: A ================================================ FILE: data/translations/es-MX.yaml ================================================ Abstract: Resumen Appendix: Apéndice Bibliography: Bibliografía Cc: Copia a Chapter: Capítulo Contents: Índice Encl: Anexo Figure: Figura Glossary: Glosario Index: Índice Alfabético ListOfFigures: Índice de Figuras ListOfTables: Índice de Cuadros Page: Página Part: Parte Preface: Prefacio Proof: Demostración References: Referencias See: véase SeeAlso: véase también Table: Cuadro To: A ================================================ FILE: data/translations/es.yaml ================================================ Abstract: Resumen Appendix: Apéndice Bibliography: Bibliografía Cc: Copia a Chapter: Capítulo Contents: Índice Encl: Adjunto Figure: Figura Glossary: Glosario Index: Índice alfabético ListOfFigures: Índice de figuras ListOfTables: Índice de cuadros Page: página Part: Parte Preface: Prefacio Proof: Demostración References: Referencias See: véase SeeAlso: véase también Table: Cuadro To: A ================================================ FILE: data/translations/et.yaml ================================================ Abstract: Kokkuvõte Appendix: Lisa Bibliography: Kirjandus Cc: Koopia(d) Chapter: Peatükk Contents: Sisukord Encl: Lisa(d) Figure: Joonis Glossary: Sõnastik Index: Indeks Listing: Kood ListOfFigures: Joonised ListOfTables: Tabelid Page: Lk. Part: Osa Preface: Sissejuhatus Proof: Korrektuur References: Viited See: vt. SeeAlso: vt. ka Table: Tabel To: Saaja ================================================ FILE: data/translations/eu.yaml ================================================ Abstract: Laburpena Appendix: Eranskina Bibliography: Bibliografia Cc: Kopia Chapter: Kapitulua Contents: Gaien Aurkibidea Encl: Erantsia Figure: Irudia Glossary: Glosarioa Index: Kontzeptuen Aurkibidea ListOfFigures: Irudien Zerrenda ListOfTables: Taulen Zerrenda Page: Orria Part: Atala Preface: Hitzaurrea Proof: Frogapena References: Erreferentziak See: Ikusi SeeAlso: Ikusi, halaber Table: Taula To: Nori ================================================ FILE: data/translations/fa.yaml ================================================ Abstract: چکیده Appendix: پیوست Bibliography: کتاب‌نامه Cc: رونوشت Chapter: فصل Contents: فهرست Encl: پیوست Figure: شكل Glossary: فرهنگ‌نامه Index: نمایه ListOfFigures: فهرست شکل‌ها ListOfTables: فهرست جدول‌ها Page: صفحه Part: بخش Preface: پیش‌گفتار Proof: اثبات References: مرجع‌ها See: ببینید SeeAlso: نیز ببینید Table: جدول To: به ================================================ FILE: data/translations/fi.yaml ================================================ Abstract: Tiivistelmä Appendix: Liite Bibliography: Kirjallisuutta Cc: Jakelu Chapter: Luku Contents: Sisällys Encl: Liitteet Figure: Kuva Glossary: Sanasto Index: Hakemisto ListOfFigures: Kuvat ListOfTables: Taulukot Page: Sivu Part: Osa Preface: Esipuhe Proof: Todistus References: Viitteet See: katso SeeAlso: katso myös Table: Taulukko To: Vastaanottaja ================================================ FILE: data/translations/fil.yaml ================================================ Abstract: Abstrak Appendix: Hugpong Bibliography: Talaaklatan Chapter: Kabanata Contents: Talalamanan Figure: Laraw Glossary: Glosaryo Index: Talatuntunan ListOfFigures: Talalarawan ListOfTables: Talatalayan Page: Pahina Part: Bahagi Preface: Paunang Salita Proof: Patunay References: Talasanggunian See: tignan ang SeeAlso: tignan din Table: Talay ================================================ FILE: data/translations/fr.yaml ================================================ Abstract: Résumé Appendix: Annexe Bibliography: Bibliographie Cc: Cc Chapter: Chapitre Contents: Table des matières Figure: Figure Glossary: Glossaire Index: Index ListOfFigures: Table des figures ListOfTables: Liste des tableaux Page: Page Part: Partie Preface: Préface Proof: Démonstration References: Références See: Voir SeeAlso: Voir aussi Table: Tableau To: À ================================================ FILE: data/translations/fur.yaml ================================================ Abstract: Somari Appendix: Zonte Bibliography: Bibliografie Cc: Cun copie a Chapter: Cjapitul Contents: Tabele gjenerâl Encl: Zonte(is) Figure: Figure Glossary: Glossari Index: Tabele analitiche ListOfFigures: Liste des figuris ListOfTables: Liste des tabelis Page: Pagjine Part: Part Preface: Prefazion Proof: Dimostrazion References: Riferiments See: cjale SeeAlso: cjale ancje Table: Tabele To: Par ================================================ FILE: data/translations/ga.yaml ================================================ Abstract: Achoimre Appendix: Aguisín Bibliography: Leabharliosta Cc: cc Chapter: Caibidil Contents: Clár Ábhair Encl: faoi iamh Figure: Léaráid Glossary: Glossary Index: Innéacs ListOfFigures: Léaráidí ListOfTables: Táblaí Page: Leathanach Part: Cuid Preface: Réamhrá Proof: Cruthúnas References: Tagairtí See: féach SeeAlso: féach freisin Table: Tábla To: Go ================================================ FILE: data/translations/gd.yaml ================================================ Abstract: Brìgh Appendix: Ath-sgr`ıobhadh Bibliography: Leabhraichean Cc: lethbhreac gu Chapter: Caibideil Contents: Clàr-obrach Encl: a-staigh Figure: Dealbh Glossary: Glossary Index: Clàr-innse ListOfFigures: Liosta Dhealbh ListOfTables: Liosta Chlàr Page: t.d. Part: Cuid Preface: Preface Proof: Proof References: Iomraidh See: see SeeAlso: see also Table: Clàr To: gu ================================================ FILE: data/translations/gl.yaml ================================================ Abstract: Resumo Appendix: Apéndice Bibliography: Bibliografía Cc: Copia a Chapter: Capítulo Contents: Índice Encl: Adxunto Figure: Figura Glossary: Glosario Index: Índice alfabético ListOfFigures: Índice de figuras ListOfTables: Índice de cadros Page: Páxina Part: Parte Preface: Prefacio Proof: Demostración References: Referencias See: véxase SeeAlso: véxase tamén Table: Cadro To: A ================================================ FILE: data/translations/grc.yaml ================================================ Abstract: Περίληψις Appendix: Παράρτημα Bibliography: Βιβλιογραφία Cc: Κοινοποίησις Chapter: Κεφάλαιον Contents: Περιεχόμενα Encl: Συνημμένως Figure: Σχῆμα Glossary: Γλωσσάριον Index: Εὑρετήριον ListOfFigures: Κατάλογος σχημάτων ListOfTables: Κατάλογος πινάκων Page: Σελὶς Part: Μέρος Preface: Προοίμιον Proof: Ἀπόδειξις References: Ἀναφοραὶ See: ὃρα SeeAlso: ὃρα ὡσαύτως Table: Πίναξ To: Πρὸς ================================================ FILE: data/translations/gu.yaml ================================================ Appendix: અનુસૂચિ Chapter: પ્રકરણ Index: અનુક્રમણિકા Page: પાનુ Part: ભાગ Proof: પુરાવો See: જુવો SeeAlso: ઉપરાંત Table: ટેબલ ================================================ FILE: data/translations/ha.yaml ================================================ Abstract: Taƙaitawa Appendix: Ƙarin bayani Bibliography: Fahirinsa Cc: cc Chapter: Babi Contents: Abin da ke ciki Encl: saƙe Figure: Addadi Glossary: Ƙamus Index: Fihirinsa ListOfFigures: Tsarin Addadi ListOfTables: Tsarin tabili Page: Shafi Part: Kashi Preface: Gabatarwa Proof: Shaida References: Nassoshi See: duba SeeAlso: kuma Table: tabili To: je ================================================ FILE: data/translations/he.yaml ================================================ Abstract: תקציר Appendix: נספח Bibliography: ביבליוגרפיה Cc: העתקים Chapter: פרק Contents: תוכן העניינים Encl: רצ"ב Figure: איור Glossary: מילון מונחים Index: מפתח ListOfFigures: רשימת האיורים ListOfTables: רשימת הטבלאות Page: עמוד Part: חלק Preface: מבוא Proof: הוכחה References: מקורות See: ראה SeeAlso: ראה גם Table: טבלה To: אל ================================================ FILE: data/translations/hi.yaml ================================================ Abstract: सारांश Appendix: परिशिष्ट Bibliography: संदर्भ-ग्रन्थ Chapter: अध्याय Contents: अनुक्रम Figure: चित्र Glossary: शब्दार्थ सूची Index: सूची ListOfFigures: चित्रों की सूची ListOfTables: तालिकाओं की सूची Page: पृष्ठ Part: खण्ड Preface: प्रस्तावना Proof: प्रमाण References: हवाले See: देखिए SeeAlso: और देखिए Table: तालिका ================================================ FILE: data/translations/hr.yaml ================================================ Abstract: Sažetak Appendix: Dodatak Bibliography: Bibliografija Cc: Kopija Chapter: Poglavlje Contents: Sadržaj Encl: Prilozi Figure: Slika Glossary: Pojmovnik Index: Kazalo ListOfFigures: Popis slika ListOfTables: Popis tablica Page: Stranica Part: Dio Preface: Predgovor Proof: Dokaz References: Literatura See: Vidjeti SeeAlso: Također vidjeti Table: Tablica To: Prima ================================================ FILE: data/translations/hsb.yaml ================================================ Abstract: Abstrakt Appendix: Dodawki Bibliography: Literatura Cc: CC Chapter: Kapitl Contents: Wobsah Encl: Přłoha Figure: Wobraz Glossary: Glossary Index: Indeks ListOfFigures: Zapis wobrazow ListOfTables: Zapis tabulkow Page: Strona Part: Dźěl Preface: Zawod Proof: Proof References: Referency See: hl. SeeAlso: hl.~tež Table: Tabulka To: Komu ================================================ FILE: data/translations/hu.yaml ================================================ Abstract: Kivonat Appendix: függelék Bibliography: Irodalomjegyzék Cc: Körlevél–címzettek Chapter: fejezet Contents: Tartalomjegyzék Encl: Melléklet Figure: ábra Glossary: Szójegyzék Index: Tárgymutató ListOfFigures: Ábrák jegyzéke ListOfTables: Táblázatok jegyzéke Page: oldal Part: rész Preface: Előszó Proof: Bizonyítás References: Hivatkozások See: lásd SeeAlso: lásd még Table: táblázat To: Címzett ================================================ FILE: data/translations/hy.yaml ================================================ Abstract: Սեղմագիր Appendix: Հավելված Bibliography: Գրականություն Cc: Կրկնօրինակը՝ Chapter: Գլուխ Contents: Բովանդակություն Encl: Կից՝ Figure: Նկար Glossary: Տերմինների ցանկ Index: Առարկայական ցանկ ListOfFigures: Նկարների ցանկ ListOfTables: Աղյուսակների ցանկ Page: էջ Part: Մաս Preface: Նախաբան Proof: Ապացույց References: Հղումներ See: տե՛ս SeeAlso: տե՛ս նաև Table: Աղյուսակ ================================================ FILE: data/translations/ia.yaml ================================================ Abstract: Summario Appendix: Appendice Bibliography: Bibliographia Cc: Copia Chapter: Capitulo Contents: Contento Encl: Incluso Figure: Figura Glossary: Glossario Index: Indice ListOfFigures: Lista de figuras ListOfTables: Lista de tabellas Page: Pagina Part: Parte Preface: Prefacio Proof: Prova References: Referentias See: vide SeeAlso: vide etiam Table: Tabella To: A ================================================ FILE: data/translations/id.yaml ================================================ Abstract: Ringkasan Appendix: Lampiran Bibliography: Bibliografi Cc: cc Chapter: Bab Contents: Daftar Isi Encl: Lampiran Figure: Gambar Glossary: Daftar Istilah Index: Indeks ListOfFigures: Daftar Gambar ListOfTables: Daftar Tabel Page: Halaman Part: Bagian Preface: Pendahuluan Proof: Bukti References: Pustaka See: lihat SeeAlso: lihat juga Table: Tabel To: Kepada ================================================ FILE: data/translations/is.yaml ================================================ Abstract: Útdráttur Appendix: Viðauki Bibliography: Heimildir Cc: Samrit Chapter: Kafli Contents: Efnisyfirlit Encl: Hjálagt Figure: Mynd Glossary: Orðalisti Index: Atriðisorðaskrá ListOfFigures: Myndaskrá ListOfTables: Töfluskrá Page: Blaðsíða Part: Hluti Preface: Formáli Proof: Sönnun References: Heimildir See: Sjá SeeAlso: Sjá einnig Table: Tafla To: Til ================================================ FILE: data/translations/it.yaml ================================================ Abstract: Sommario Appendix: Appendice Bibliography: Bibliografia Cc: e~p.~c. Chapter: Capitolo Contents: Indice Encl: Allegati Figure: Figura Glossary: Glossario Index: Indice analitico ListOfFigures: Elenco delle figure ListOfTables: Elenco delle tabelle Page: Pag. Part: Parte Preface: Prefazione Proof: Dimostrazione References: Riferimenti bibliografici See: vedi SeeAlso: vedi anche Table: Tabella To: Per ================================================ FILE: data/translations/ja.yaml ================================================ Abstract: 概要 Appendix: 付録 Bibliography: 文献目録 Cc: 同報 Chapter: 章 Contents: 目次 Encl: 添付 Figure: 図 Glossary: 用語集 Index: 索引 ListOfFigures: 図目次 ListOfTables: 表目次 Page: 頁 Part: 部 Preface: 端書き Proof: 証明 References: 参考文献 See: 参照 SeeAlso: 参照 Table: 表 To: 宛先 ================================================ FILE: data/translations/ka.yaml ================================================ Abstract: ანოტაცია Appendix: დანართი Bibliography: ლიტერატურა Cc: წყარ. Chapter: თავი Contents: შინაარსი Encl: ჩათვ. Figure: სურ. Glossary: ტერმინები Index: საძიებელი ListOfFigures: სურათი ListOfTables: ცხრილი Page: გვ. Part: ნაწილი Preface: წინასიტყვაობა Proof: დამტკიცება References: ლიტერატურა See: იხ. SeeAlso: იხ. ასევე Table: ცხრ. To: შ. ================================================ FILE: data/translations/km.yaml ================================================ Abstract: សង្ខេប Appendix: សេចក្ដីបន្ថែម Bibliography: គន្ថនិទ្ទេស Cc: ចម្លងជួន Chapter: ជំពូក Contents: មាតិការ Encl: ឯកសារភ្ជាប់ Figure: រូប Glossary: សទានុក្រម Index: សន្ទស្សន៍ ListOfFigures: បញ្ជីរូបភាព ListOfTables: បញ្ជីតារាង Page: ទំព័រ Part: ផ្នែក Preface: អារម្ភកថា Proof: សម្រាយ References: ឯកសារយោង See: មើល SeeAlso: មើលបន្ថែម Table: តារាង To: ផ្ញើរទៅ ================================================ FILE: data/translations/kmr-Arab.yaml ================================================ Abstract: کورتەبیر Appendix: پاشکۆ Bibliography: چاڤکانییا پرتووکان Cc: بەلاڤکەر Chapter: سەرێ Contents: ناڤێرۆک Encl: دوماهک Figure: دیمەنێ Glossary: چاڤکانییا لێکۆلینێ Index: پێرست ListOfFigures: هەژمارا دیمەنا ListOfTables: هەژمارا کەڤالێن Page: رووپەلێ Part: بەشا Preface: پێشگۆتن Proof: دەلیل References: پرتووکێن بژارتی See: بنێرا SeeAlso: لە ڤێیا ژ بنێرا Table: کەڤالا To: ژ بۆ ================================================ FILE: data/translations/kmr-Latn.yaml ================================================ Abstract: Kurtebîr Appendix: Tebînîya Bibliography: Çavkanîya Pirtukan Cc: Belavker Chapter: Serê Contents: Navêrok Encl: Dumahik Figure: Dimenê Glossary: Çavkanîya lêkolînê Index: Endeks ListOfFigures: Hejmara Dimena ListOfTables: Hejmara Kevalen Page: Rûpelê Part: Bêşa Preface: Peşgotin Proof: Delîl References: Pirtuken bijartî See: binêra SeeAlso: le vêya ji binêra Table: Kevala To: Ji bo ================================================ FILE: data/translations/kn.yaml ================================================ Abstract: ಸಾರಾಂಶ Appendix: ಅನುಬಂಧ Bibliography: ಗ್ರಂಥಸೂಚಿ Cc: cc Chapter: ಅಧ್ಯಾಯ Contents: ವಿಷಯಗಳು Encl: encl Figure: ಚಿತ್ರ Index: ಸೂಚಿ ListOfFigures: ಚಿತ್ರಗಳ ಪಟ್ಟಿ ListOfTables: ಕೋಷ್ಟಕಗಳ ಪಟ್ಟಿ Page: ಪುಟ Part: ಭಾಗ Preface: ಮುನ್ನುಡಿ Proof: ಕರಡುಪ್ರತಿ References: ಉಲ್ಲೇಖಗಳು See: ನೋಡು SeeAlso: ಇದನ್ನೂ ಸಹ ನೋಡು Table: ಕೋಷ್ಟಕ To: ಗೆ ================================================ FILE: data/translations/ko.yaml ================================================ Abstract: 요약 Appendix: 부록 Bibliography: 참고문헌 Cc: 사본 Chapter: 장 Contents: 차례 Encl: 동봉 Figure: 그림 Index: 찾아보기 ListOfFigures: 그림 차례 ListOfTables: 표 차례 Page: 페이지 Preface: 서문 Proof: 증명 References: 참고문헌 Table: 표 To: 수신 ================================================ FILE: data/translations/la.yaml ================================================ Abstract: Summarium Appendix: Additamentum Bibliography: Conspectus librorum Cc: Exemplar Chapter: Caput Contents: Index Encl: Additur Figure: Descriptio Glossary: Glossarium Index: Index rerum notabilium ListOfFigures: Conspectus descriptionum ListOfTables: Conspectus tabularum Page: charta Part: Pars Preface: Praefatio Proof: Demonstratio References: Conspectus librorum See: cfr. SeeAlso: cfr. Table: Tabula ================================================ FILE: data/translations/lb.yaml ================================================ Abstract: Resümee Appendix: Annex Bibliography: Bibliographie Cc: Distributeur Chapter: Kapitel Contents: Contenue Encl: Annex(en) Figure: Bild Glossary: Glossär Index: Index ListOfFigures: Lëscht vun de Biller ListOfTables: Lëscht vun den Tabellen Page: Säit Part: Deel Preface: Virwuert Proof: Beweis References: Literatur See: kuck SeeAlso: kuck och Table: Tabell To: Un ================================================ FILE: data/translations/lo.yaml ================================================ Abstract: ບົດຫຍໍ້ຄວາມ Appendix: ພາກຄັດຕິດ Bibliography: ເອກະສານອ້າງອີງ Cc: ສໍາເນົາເຖິງ Chapter: ບົດທີ Contents: ສາລະບານ Encl: ເອກະສານປະກອບ Figure: ຮູບທີ Glossary: ປະມວນສັບ Index: ດັດຊະນີ ListOfFigures: ສາລະບານຮູບ ListOfTables: ສາລະບານຕາຕະລາງ Page: ໜ້າ Part: ພາກ Preface: ຄໍານໍາ Proof: ຂໍ້ພິສູດ References: ໜັງສືອ້າງອີງ See: ອ່ານ SeeAlso: ອ່ານເພີ່ມ Table: ຕາຕະລາງທີ To: ຮຽນ ================================================ FILE: data/translations/lt.yaml ================================================ Abstract: Santrauka Appendix: Priedas Bibliography: Literatūra Cc: Kopijos Chapter: Skyrius Contents: Turinys Encl: Įdėta Figure: pav. Glossary: Terminų žodynas Index: Rodyklė ListOfFigures: Iliustracijų sąrašas ListOfTables: Lentelių sąrašas Page: puslapis Part: Dalis Preface: Pratarmė Proof: Įrodymas References: Literatūra See: žiūrėk SeeAlso: taip pat Table: lentelė To: Kam ================================================ FILE: data/translations/lv.yaml ================================================ Abstract: Anotācija Appendix: Pielikums Bibliography: Literatūra Cc: Kopija(s) Chapter: Nodaļa Contents: Saturs Encl: Pielikumā Figure: Att. Index: Priekšmetu rādītājs ListOfFigures: Attēlu saraksts ListOfTables: Tabulu saraksts Page: lpp. Part: Daļa Preface: Priekšvārds Proof: Pierādījums References: Literatūras saraksts See: sk. SeeAlso: sk. arī Table: Tabula To: To ================================================ FILE: data/translations/mk.yaml ================================================ Abstract: Апстракт Appendix: Прилог Bibliography: Библиографиjа Cc: копиjа Chapter: Глава Contents: Содржина Encl: Прилози Figure: Слика Glossary: Речник Index: Индекс на термини ListOfFigures: Листа на слики ListOfTables: Листа на табели Page: стр. Part: Дел Preface: Предговор Proof: доказ References: Литература See: види SeeAlso: види исто така Table: Табела To: За ================================================ FILE: data/translations/ml.yaml ================================================ Abstract: സാരാംശം Appendix: ശിഷ്ടം Chapter: അദ്ധ്യായം Contents: ഉള്ളടക്കം Figure: ചിത്രം Index: സൂചിക ListOfFigures: ചിത്രസൂചിക ListOfTables: പട്ടികകളുടെ സൂചിക Part: ഭാഗം See: കാണുക SeeAlso: ഇതും കാണുക Table: പട്ടിക ================================================ FILE: data/translations/mn.yaml ================================================ Abstract: Удиртгал Appendix: Хавсралт Bibliography: Номзүй Cc: э.с. Chapter: Бүлэг Contents: Гарчиг Encl: Ишлэл Figure: Зураг Index: Товъёг ListOfFigures: Зургийн жагсаалт ListOfTables: Хүснэгтийн жагсаалт Page: тал Part: Хэсэг Preface: Өмнөх үг Proof: Баталгаа References: Ашигласан ном See: талд үз SeeAlso: мөн талд үз Table: Хүснэгт ================================================ FILE: data/translations/mr.yaml ================================================ Abstract: सारांश Appendix: परिशिष्ट Bibliography: संदर्भसूची Cc: प्रत Chapter: प्रकरण Contents: अनुक्रमणिका Encl: समाविष्ट Figure: आकृती Glossary: संज्ञासूची Index: सूची ListOfFigures: आकृत्यांची सूची ListOfTables: कोष्टकसूची Page: पृष्ठ Part: खंड Preface: प्रस्तावना Proof: सिद्धता References: संदर्भ See: पाहा SeeAlso: हेदेखील पाहा Table: कोष्टक To: प्रति ================================================ FILE: data/translations/ms.yaml ================================================ Abstract: Abstrak Appendix: Lampiran Bibliography: Bibliografi Cc: sk Chapter: Bab Contents: Kandungan Encl: Lampiran Figure: Gambar Glossary: Istilah Index: Indeks ListOfFigures: Senarai Gambar ListOfTables: Senarai Jadual Page: Halaman Part: Bahagian Preface: Prakata Proof: Bukti References: Rujukan See: sila rujuk SeeAlso: rujuk juga Table: Jadual To: Kepada ================================================ FILE: data/translations/nb.yaml ================================================ Abstract: Sammendrag Appendix: Tillegg Bibliography: Bibliografi Cc: Kopi sendt Chapter: Kapittel Contents: Innhold Encl: Vedlegg Figure: Figur Glossary: Ordliste Index: Register ListOfFigures: Figurer ListOfTables: Tabeller Page: Side Part: Del Preface: Forord Proof: Bevis References: Referanser See: Se SeeAlso: Se også Table: Tabell To: Til ================================================ FILE: data/translations/nko.yaml ================================================ Abstract: ߓߊߕߐߡߐ߲ Appendix: ߘߋ߬ߙߋ Bibliography: ߟߍߙߊߥߙߍߟߐ߲߲ Cc: ߓߊ ߘߏ߫ ߘߌ߫ Chapter: ߛߌ߰ߘߊ Contents: ߞߣߐߘߐ Encl: ߝߍ߬ߕߊ Figure: ߢߊ Glossary: ߞߘߐߝߐߟߊ߲ Index: ߛߙߍߘߍ ListOfFigures: ߢߊ ߟߎ߬ ߛߙߍߘߍ ListOfTables: ߦߌ߬ߘߊ߬ߥߟߊ ߟߎ߬ ߛߙߍߘߍ Page: ߞߐߜߍ Part: ߛߌ߰ߘߊ߬ߙߋ߲ Preface: ߢߍߛߓߍ Proof: ߦߌ߬ߘߊ߬ߞߏ References: ߞߐߡߊߛߙߋ See: ߡߊߝߟߍ߫ SeeAlso: ߝߟߍߡߊߛߊ߬ߦߌ߬ Table: ߦߌ߬ߘߊ߬ߥߟߊ To: ߞߊߕߙߍ߬ ================================================ FILE: data/translations/nl.yaml ================================================ Abstract: Samenvatting Appendix: Bijlage Bibliography: Bibliografie Cc: cc Chapter: Hoofdstuk Contents: Inhoudsopgave Encl: Bijlage(n) Figure: Figuur Glossary: Verklarende Woordenlijst Index: Index ListOfFigures: Lijst van figuren ListOfTables: Lijst van tabellen Page: Pagina Part: Deel Preface: Voorwoord Proof: Bewijs References: Referenties See: zie SeeAlso: zie ook Table: Tabel To: Aan ================================================ FILE: data/translations/nn.yaml ================================================ Abstract: Samandrag Appendix: Tillegg Bibliography: Litteratur Cc: Kopi til Chapter: Kapittel Contents: Innhald Encl: Vedlegg Figure: Figur Glossary: Ordliste Index: Register ListOfFigures: Figurar ListOfTables: Tabellar Page: Side Part: Del Preface: Forord Proof: Bevis References: Referansar See: Sjå SeeAlso: Sjå òg Table: Tabell To: Til ================================================ FILE: data/translations/oc.yaml ================================================ Abstract: Resumit Appendix: Annèx Bibliography: Bibliografia Cc: còpia a Chapter: Capítol Contents: Ensenhador Encl: Pèça junta Figure: Figura Glossary: Glossari Index: Indèx ListOfFigures: Taula de las figuras ListOfTables: Taula dels tablèus Page: Pagina Part: Partida Preface: Prefàcia Proof: Demostracion References: Referéncias See: vejatz SeeAlso: vejatz tanben Table: Tablèu To: A ================================================ FILE: data/translations/or.yaml ================================================ Abstract: ସାରାଂଶ Appendix: ପରିଶିଷ୍ଟ Bibliography: ସୂଚନା Cc: କପି କରନ୍ତୁ Chapter: ଅଧ୍ୟାୟ Contents: ସୁଚୀ ପତ୍ର Encl: ସଂଲଗ୍ନ Figure: ପ୍ରତିଛବି Glossary: ଶବ୍ଦର ଶବ୍ଦକୋଷ Index: ସୂଚକାଙ୍କ ListOfFigures: ପ୍ରତିଛବି ତାଲିକା ListOfTables: ଟେବୁଲ ତାଲିକା Page: ପୃଷ୍ଠା Part: ବିଭାଗ Preface: ପ୍ରାରମ୍ଭ Proof: ପ୍ରମାଣ References: ସନ୍ଦର୍ଭ See: ଦେଖନ୍ତୁ SeeAlso: ଅଧିକ ଦେଖନ୍ତୁ Table: ଟେବୁଲ୍ To: ପ୍ରତି ================================================ FILE: data/translations/pa.yaml ================================================ Abstract: ਨਿਚੋੜ Appendix: ਲੜੀਵਾਰ Bibliography: ਸੰਬੰਧਤ ਹਵਾਲੇ Cc: ਕਾਪੀ Chapter: ਪਾਠ Contents: ਸਮਗਰੀ Encl: ਨੱਥੀ Figure: ਸ਼ਕਲ Glossary: ਕੁੰਜੀ Index: ਸੂਚੀ ListOfFigures: ਸ਼ਕਲਾਂ ਦੀ ਲੜੀ ListOfTables: ਲੜੀਆਂ ਦਾ ਲੇਖਾ Page: ਵਰਕਾ Part: ਹਿੱਸਾ Preface: ਮੁੱਖਬੰਦ Proof: ਪਰਮਾਣ References: ਹਵਾਲਾ See: ਵੇਖੋ SeeAlso: ਹੋਰ Table: ਲੇਖਾ ================================================ FILE: data/translations/pl.yaml ================================================ Abstract: Streszczenie Appendix: Dodatek Bibliography: Bibliografia Cc: Kopie Chapter: Rozdział Contents: Spis treści Encl: Załącznik Figure: Rysunek Glossary: Glossary Index: Indeks ListOfFigures: Spis rysunków ListOfTables: Spis tabel Page: Strona Part: Część Preface: Przedmowa Proof: Dowód References: Literatura See: Zobacz SeeAlso: Zobacz też Table: Tabela To: Do ================================================ FILE: data/translations/pms.yaml ================================================ Abstract: Somari Appendix: Gionta Bibliography: Bibliografìa Cc: Con còpia a Chapter: Capìtol Contents: Tàula Encl: Gionta/e Figure: Figura Glossary: Glossari Index: Tàula analìtica ListOfFigures: Lista dle figure ListOfTables: Lista dle tabele Page: Pàgina Part: Part Preface: Prefassion Proof: Dimostrassion References: Riferiment See: vëd SeeAlso: vëd anche Table: Tabela To: Për ================================================ FILE: data/translations/pt-BR.yaml ================================================ Abstract: Resumo Appendix: Apêndice Bibliography: Referências Bibliográficas Cc: Cópia para Chapter: Capítulo Contents: Sumário Encl: Anexo Figure: Figura Glossary: Glossário Index: Índice Remissivo ListOfFigures: Lista de Figuras ListOfTables: Lista de Tabelas Page: Página Part: Parte Preface: Prefácio Proof: Demonstração References: Referências See: veja SeeAlso: veja também Table: Tabela To: Para ================================================ FILE: data/translations/pt-PT.yaml ================================================ Abstract: Resumo Appendix: Apêndice Bibliography: Bibliografia Cc: Com cópia a Chapter: Capítulo Contents: Conteúdo Encl: Anexo Figure: Figura Glossary: Glossário Index: Índice ListOfFigures: Lista de Figuras ListOfTables: Lista de Tabelas Page: Página Part: Parte Preface: Prefácio Proof: Demonstração References: Referências See: ver SeeAlso: ver também Table: Tabela To: Para ================================================ FILE: data/translations/pt.yaml ================================================ Abstract: Resumo Appendix: Apêndice Bibliography: Bibliografia Cc: Com cópia a Chapter: Capítulo Contents: Conteúdo Encl: Anexo Figure: Figura Glossary: Glossário Index: Índice ListOfFigures: Lista de Figuras ListOfTables: Lista de Tabelas Page: Página Part: Parte Preface: Prefácio Proof: Demonstração References: Referências See: ver SeeAlso: ver também Table: Tabela To: Para ================================================ FILE: data/translations/rm.yaml ================================================ Abstract: Recapitulaziun Appendix: Appendix Bibliography: Index bibliografic Cc: Copia a Chapter: Chapitel Contents: Tavla dal cuntegn Encl: Agiunta(s) Figure: Figura Glossary: Glossari Index: Register da materias ListOfFigures: Tavla da las figuras ListOfTables: Tavla da las tabellas Page: pagina Part: Part Preface: Prefaziun Proof: Demonstraziun References: Bibliografia See: vesair SeeAlso: vesair era Table: Tabella To: A ================================================ FILE: data/translations/ro.yaml ================================================ Abstract: Rezumat Appendix: Anexa Bibliography: Bibliografie Cc: Copie Chapter: Capitolul Contents: Cuprins Encl: Anexă Figure: Figura Glossary: Glosar Index: Glosar ListOfFigures: Listă de figuri ListOfTables: Listă de tabele Page: Pagina Part: Partea Preface: Prefaţă Proof: Demonstraţie References: Bibliografie See: Vezi SeeAlso: Vezi de asemenea Table: Tabela To: Pentru ================================================ FILE: data/translations/ru.yaml ================================================ Abstract: Аннотация Appendix: Приложение Bibliography: Литература Cc: исх. Chapter: Глава Contents: Содержание Encl: вкл. Figure: Рис. Glossary: Словарь терминов Index: Предметный указатель ListOfFigures: Список иллюстраций ListOfTables: Список таблиц Page: с. Part: Часть Preface: Предисловие Proof: Доказательство References: Список литературы See: см. SeeAlso: см. также Table: Таблица To: вх. ================================================ FILE: data/translations/se.yaml ================================================ Abstract: Čoahkkáigeassu Appendix: Čuovus Bibliography: Girjjálašvuohta Cc: Kopia sáddejuvvon Chapter: Kapihttal Contents: Sisdoallu Encl: Mielddus Figure: Govus Glossary: Sátnelistu Index: Registtar ListOfFigures: Govvosat ListOfTables: Tabeallat Page: Siidu Part: Oassi Preface: Ovdasátni Proof: Duođaštus References: Čujuhusat See: geahča SeeAlso: geahča maiddái Table: Tabealla To: Vuostáiváldi ================================================ FILE: data/translations/si.yaml ================================================ Abstract: සංක්ෂෙපය Appendix: උපග්‍රන්ථය Bibliography: ග්‍රන්ථ නාමාවලිය Cc: පිටපත් Chapter: පරිච්ඡේදය Contents: පටුන Encl: ඇමුණුම Figure: සටහන Glossary: පදමාලාව Index: සූචිය ListOfFigures: සටහන් ලේඛනය ListOfTables: වගු ලේඛනය Page: පිට Part: කොටස Preface: පෙරවදන Proof: සාධනය References: යොමුව See: බලනු SeeAlso: තවද Table: වගුව To: වෙත ================================================ FILE: data/translations/sk.yaml ================================================ Abstract: Abstrakt Appendix: Dodatok Bibliography: Literatúra Cc: cc. Chapter: Kapitola Contents: Obsah Encl: Príloha Figure: Obr. Glossary: Slovník Index: Register ListOfFigures: Zoznam obrázkov ListOfTables: Zoznam tabuliek Page: Str. Part: Časť Preface: Predhovor Proof: Dôkaz References: Literatúra See: viď SeeAlso: viď tiež Table: Tabuľka To: Pre ================================================ FILE: data/translations/sl.yaml ================================================ Abstract: Povzetek Appendix: Dodatek Bibliography: Literatura Cc: Kopije Chapter: Poglavje Contents: Kazalo Encl: Priloge Figure: Slika Glossary: Slovar Index: Stvarno kazalo ListOfFigures: Slike ListOfTables: Tabele Page: Stran Part: Del Preface: Predgovor Proof: Dokaz References: Literatura See: glej SeeAlso: glej tudi Table: Tabela To: Prejme ================================================ FILE: data/translations/sq.yaml ================================================ Abstract: Përmbledhja Appendix: Shtesa Bibliography: Bibliografia Cc: Kopja Chapter: Kapitulli Contents: Përmbajtja Encl: Lidhja Figure: Figura Glossary: Përhasja e Fjalëve Index: Indeksi ListOfFigures: Figurat ListOfTables: Tabelat Page: Faqe Part: Pjesa Preface: Parathenia Proof: Vërtetim References: Referencat See: shiko SeeAlso: shiko dhe Table: Tabela To: Për ================================================ FILE: data/translations/sr-Cyrl.yaml ================================================ Abstract: Сажетак Appendix: Додатак Bibliography: Библиографиjа Cc: Копиjе Chapter: Глава Contents: Садржаj Encl: Прилози Figure: Слика Glossary: Речник Index: Индекс ListOfFigures: Списак слика ListOfTables: Списак табела Page: страна Part: Део Preface: Предговор Proof: Доказ References: Литература See: види SeeAlso: видитакође Table: Табела To: Прима ================================================ FILE: data/translations/sr-Latn.yaml ================================================ Abstract: Sažetak Appendix: Dodatak Bibliography: Bibliografija Cc: Kopije Chapter: Glava Contents: Sadržaj Encl: Prilozi Figure: Slika Glossary: Rečnik Index: Indeks ListOfFigures: Spisak slika ListOfTables: Spisak tabela Page: strana Part: Deo Preface: Predgovor Proof: Dokaz References: Literatura See: vidi SeeAlso: vidi takođe Table: Tabela To: Prima ================================================ FILE: data/translations/sr.yaml ================================================ Abstract: Сажетак Appendix: Додатак Bibliography: Библиографиjа Cc: Копиjе Chapter: Глава Contents: Садржаj Encl: Прилози Figure: Слика Glossary: Речник Index: Индекс ListOfFigures: Списак слика ListOfTables: Списак табела Page: страна Part: Део Preface: Предговор Proof: Доказ References: Литература See: види SeeAlso: видитакође Table: Табела To: Прима ================================================ FILE: data/translations/sv.yaml ================================================ Abstract: Sammanfattning Appendix: Bilaga Bibliography: Litteraturförteckning Cc: Kopia för kännedom Chapter: Kapitel Contents: Innehåll Encl: Bil. Figure: Figur Glossary: Ordlista Index: Sakregister ListOfFigures: Figurer ListOfTables: Tabeller Page: Sida Part: Del Preface: Förord Proof: Bevis References: Referenser See: se SeeAlso: se även Table: Tabell To: Till ================================================ FILE: data/translations/ta.yaml ================================================ Abstract: சாராம்சம் Appendix: பிற்சேர்க்கை Chapter: அத்தியாயம் Contents: உள்ளே Figure: படம் Index: சுட்டி ListOfFigures: படங்களின் பட்டியல் ListOfTables: அட்டவணை பட்டியல் Part: பகுதி See: பார்க்க Table: அட்டவணை ================================================ FILE: data/translations/te.yaml ================================================ Abstract: సారాంశం Appendix: అదనంగా Bibliography: గ్రంథాల జాబితా Cc: సిసి Chapter: అధ్యాయము Contents: విషయాలు Encl: ఎంక్లోజర్* Figure: ఆకృతి Glossary: నిఘంటువు Index: విషయ సూచిక ListOfFigures: ఆకృతుల జాబితా ListOfTables: పట్టికల జాబితా Page: పేజి Part: భాగం Preface: ముందుమాట Proof: రుజువు References: ఆధారాలు See: చూడండి SeeAlso: కూడా చూడండి Table: పట్టిక To: కి ================================================ FILE: data/translations/th.yaml ================================================ Abstract: บทคัดย่อ Appendix: ภาคผนวก Bibliography: บรรณานุกรม Cc: สำเนาถึง Chapter: บทที่ Contents: สารบัญ Encl: สิ่งที่แนบมาด้วย Figure: รูปที่ Index: ดรรชนี ListOfFigures: สารบัญรูป ListOfTables: สารบัญตาราง Page: หน้า Part: ภาค Preface: บทนำ Proof: พิสูจน์ References: หนังสืออ้างอิง See: ดู SeeAlso: ดูเพิ่มเติม Table: ตารางที่ To: เรียน ================================================ FILE: data/translations/tk.yaml ================================================ Abstract: Gysgaça manysy Appendix: Goşmaça Bibliography: Çeşmeler Cc: Iberilenler Chapter: Bap Contents: Mazmuny Encl: Goşmaça Figure: Surat Glossary: Sözlük Index: Indeks ListOfFigures: Suratlaryň sanawy ListOfTables: Tablisalaryň sanawy Page: Sahypa Part: Bölüm Preface: Sözbaşy Proof: Delil References: Çeşmeler See: ser. SeeAlso: şuňa-da ser. Table: Tablisa To: Kime ================================================ FILE: data/translations/tr.yaml ================================================ Abstract: Özet Appendix: Ek Bibliography: Kaynakça Cc: Diğer Alıcılar Chapter: Bölüm Contents: İçindekiler Encl: İlişik Figure: Şekil Glossary: Lügatçe Index: Dizin ListOfFigures: Şekil Listesi ListOfTables: Tablo Listesi Page: Sayfa Part: Kısım Preface: Önsöz Proof: Kanıt References: Kaynaklar See: bkz. SeeAlso: ayrıca bkz. Table: Tablo To: Alıcı ================================================ FILE: data/translations/ua.yaml ================================================ Abstract: Резюме Appendix: Додаток Bibliography: Бібліографія Cc: Розповсюджувач Chapter: Розділ Contents: Зміст Encl: Додаток(и) Figure: Ілюстрація Glossary: Глосарій Index: Індекс Listing: Список ListOfFigures: Список рисунків ListOfTables: Список таблиць Page: Сторінка Part: Частина Preface: Передмова Proof: Докази References: Література See: див. SeeAlso: див. також Table: Таблиця To: До ================================================ FILE: data/translations/ug.yaml ================================================ Abstract: ئابستراكت Appendix: قوشۇمچە Bibliography: پايدىلانما Cc: باشقا تاپشۇرۇۋالغۇچى Chapter: باب Contents: مۇندەرىجە Encl: قوشۇمچە ھۆججەت Figure: رەسىم Glossary: لۇغەت Index: ئىندېكىس ListOfFigures: رەسىملەر ListOfTables: جەدۋەللەر Page: بەت Part: قىسىم Preface: كىرىش سۆز Proof: ئىسپات References: پايدىلانما See: قاراڭ SeeAlso: ئايرىم قاراڭ Table: جەدۋەل To: تاپشۇرۇۋالغۇچى ================================================ FILE: data/translations/uk.yaml ================================================ Abstract: Анотація Appendix: Додаток Bibliography: Бібліоґрафія Cc: копія Chapter: Розділ Contents: Зміст Encl: вкладка Figure: Рис. Glossary: Словник термінів Index: Покажчик ListOfFigures: Перелік ілюстрацій ListOfTables: Перелік таблиць Page: с. Part: Частина Preface: Вступ Proof: Доведення References: Література See: див. SeeAlso: див. також Table: Табл. To: До ================================================ FILE: data/translations/ur.yaml ================================================ Abstract: ملخّص Appendix: ضمیمہ Bibliography: کتابیات Cc: نقل Chapter: باب Contents: فہرست عنوانات Encl: منسلک Figure: شكل Glossary: لغت Index: اشاریہ ListOfFigures: فہرست اشکال ListOfTables: فہرست جداول Page: صفحہ Part: حصّہ Preface: دیباچہ Proof: ثبوت References: حوالہ جات See: ملاحظہ ہو SeeAlso: ایضاً Table: جدول To: بملاحظہ ================================================ FILE: data/translations/vi.yaml ================================================ Abstract: Tóm tắt nội dung Appendix: Phụ lục Bibliography: Tài liệu tham khảo Cc: Cùng gửi Chapter: Chương Contents: Mục lục Encl: Kèm theo Figure: Hình Glossary: Từ điển chú giải Index: Chỉ mục ListOfFigures: Danh sách hình vẽ ListOfTables: Danh sách bảng Page: Trang Part: Phần Preface: Lời nói đầu Proof: Chứng minh References: Tài liệu See: Xem SeeAlso: Xem thêm Table: Bảng To: Gửi ================================================ FILE: data/translations/zh-Hans.yaml ================================================ Abstract: 摘要 Appendix: 附录 Bibliography: 文献目录 Cc: 副本 Chapter: 章 Contents: 目录 Encl: 附件 Figure: 图 Glossary: 术语 Index: 索引 Listing: 列表 ListOfFigures: 附图目录 ListOfTables: 表格索引 Page: 页 Part: 段 Preface: 序 Proof: 校对 References: 参考文献 See: 见 SeeAlso: 参见 Table: 表 To: 到 ================================================ FILE: data/translations/zh-Hant.yaml ================================================ Abstract: 摘要 Appendix: 附錄 Bibliography: 文獻目錄 Cc: 副本 Chapter: 章 Contents: 目錄 Encl: 附件 Figure: 圖 Glossary: 術語 Index: 索引 Listing: 列表 ListOfFigures: 附圖目錄 ListOfTables: 表格索引 Page: 頁 Part: 段 Preface: 序 Proof: 校對 References: 參考文獻 See: 見 SeeAlso: 參見 Table: 表 To: 到 ================================================ FILE: doc/custom-readers.md ================================================ --- author: - John MacFarlane date: 'November 18, 2021' title: Creating Custom Pandoc Readers in Lua --- # Introduction If you need to parse a format not already handled by pandoc, you can create a custom reader using the [Lua] language. Pandoc has a built-in Lua interpreter, so you needn't install any additional software to do this. [Lua]: https://www.lua.org A custom reader is a Lua file that defines a function called `Reader`, which takes two arguments: - the raw input to be parsed, as a list of sources - optionally, a table of reader options, e.g. `{ columns = 62, standalone = true }`. The `Reader` function should return a `Pandoc` AST. This can be created using functions in the [`pandoc` module], which is automatically in scope. (Indeed, all of the utility functions that are available for [Lua filters] are available in custom readers, too.) Each source item corresponds to a file or stream passed to pandoc containing its text and name. E.g., if a single file `input.txt` is passed to pandoc, then the list of sources will contain just a single element `s`, where `s.name == 'input.txt'` and `s.text` contains the file contents as a string. The sources list, as well as each of its elements, can be converted to a string via the Lua standard library function `tostring`. [Lua filters]: https://pandoc.org/lua-filters.html [`pandoc` module]: https://pandoc.org/lua-filters.html#module-pandoc A minimal example would be ```lua function Reader(input) return pandoc.Pandoc({ pandoc.CodeBlock(tostring(input)) }) end ``` This just returns a document containing a big code block with all of the input. Or, to create a separate code block for each input file, one might write ``` lua function Reader(input) return pandoc.Pandoc(input:map( function (s) return pandoc.CodeBlock(s.text) end)) end ``` In a nontrivial reader, you'll want to parse the input. You can do this using standard Lua library functions (for example, the [patterns] library), or with the powerful and fast [lpeg] parsing library, which is automatically in scope. You can also use external Lua libraries (for example, an XML parser). A previous pandoc version passed a raw string instead of a list of sources to the Reader function. Reader functions that rely on this are obsolete, but still supported: Pandoc analyzes any script error, detecting when code assumed the old behavior. The code is rerun with raw string input in this case, thereby ensuring backwards compatibility. [patterns]: http://lua-users.org/wiki/PatternsTutorial [lpeg]: http://www.inf.puc-rio.br/~roberto/lpeg/ # Bytestring readers In order to read binary formats, including docx, odt, and epub, pandoc supports the `ByteStringReader` function. A `ByteStringReader` function is similar to the `Reader` function that processes text input. Instead of a list of sources, the ByteStringReader function is passed a bytestring, i.e., a string that contains the binary input. ``` lua -- read input as epub function ByteStringReader (input) return pandoc.read(input, 'epub') end ``` # Format extensions Custom readers can be built such that their behavior is controllable through format extensions, such as `smart`, `citations`, or `hard-line-breaks`. Supported extensions are those that are present as a key in the global `Extensions` table. Fields of extensions that are enabled default have the value `true` or `enable`, while those that are supported but disabled have value `false` or `disable`. Example: A reader with the following global table supports the extensions `smart`, `citations`, and `foobar`, with `smart` enabled and the other two disabled by default: ``` lua Extensions = { smart = 'enable', citations = 'disable', foobar = true } ``` The users control extensions as usual, e.g., `pandoc -f my-reader.lua+citations`. The extensions are accessible through the reader options' `extensions` field, e.g.: ``` lua function Reader (input, opts) print( 'The citations extension is', opts.extensions:includes 'citations' and 'enabled' or 'disabled' ) -- ... end ``` Extensions that are neither enabled nor disabled in the `Extensions` field are treated as unsupported by the reader. Trying to modify such an extension via the command line will lead to an error. # Example: plain text reader This is a simple example using [lpeg] to parse the input into space-separated strings and blankline-separated paragraphs. ```lua -- A sample custom reader that just parses text into blankline-separated -- paragraphs with space-separated words. -- For better performance we put these functions in local variables: local P, S, R, Cf, Cc, Ct, V, Cs, Cg, Cb, B, C, Cmt = lpeg.P, lpeg.S, lpeg.R, lpeg.Cf, lpeg.Cc, lpeg.Ct, lpeg.V, lpeg.Cs, lpeg.Cg, lpeg.Cb, lpeg.B, lpeg.C, lpeg.Cmt local whitespacechar = S(" \t\r\n") local wordchar = (1 - whitespacechar) local spacechar = S(" \t") local newline = P"\r"^-1 * P"\n" local blanklines = newline * (spacechar^0 * newline)^1 local endline = newline - blanklines -- Grammar G = P{ "Pandoc", Pandoc = Ct(V"Block"^0) / pandoc.Pandoc; Block = blanklines^0 * V"Para" ; Para = Ct(V"Inline"^1) / pandoc.Para; Inline = V"Str" + V"Space" + V"SoftBreak" ; Str = wordchar^1 / pandoc.Str; Space = spacechar^1 / pandoc.Space; SoftBreak = endline / pandoc.SoftBreak; } function Reader(input) return lpeg.match(G, tostring(input)) end ``` Example of use: ``` % pandoc -f plain.lua -t native *Hello there*, this is plain text with no formatting except paragraph breaks. - Like this one. ^D [ Para [ Str "*Hello" , Space , Str "there*," , Space , Str "this" , Space , Str "is" , Space , Str "plain" , Space , Str "text" , Space , Str "with" , Space , Str "no" , Space , Str "formatting" , SoftBreak , Str "except" , Space , Str "paragraph" , Space , Str "breaks." ] , Para [ Str "-" , Space , Str "Like" , Space , Str "this" , Space , Str "one." ] ] ``` # Example: a wiki Creole reader This is a parser for [Creole common wiki markup]. It uses an [lpeg] grammar. Fun fact: this custom reader is faster than pandoc's built-in creole reader! This shows that high-performance readers can be designed in this way. [Creole common wiki markup]: http://www.wikicreole.org/wiki/CheatSheet ```lua -- A sample custom reader for Creole 1.0 (common wiki markup) -- http://www.wikicreole.org/wiki/CheatSheet -- For better performance we put these functions in local variables: local P, S, R, Cf, Cc, Ct, V, Cs, Cg, Cb, B, C, Cmt = lpeg.P, lpeg.S, lpeg.R, lpeg.Cf, lpeg.Cc, lpeg.Ct, lpeg.V, lpeg.Cs, lpeg.Cg, lpeg.Cb, lpeg.B, lpeg.C, lpeg.Cmt local whitespacechar = S(" \t\r\n") local specialchar = S("/*~[]\\{}|") local wordchar = (1 - (whitespacechar + specialchar)) local spacechar = S(" \t") local newline = P"\r"^-1 * P"\n" local blankline = spacechar^0 * newline local endline = newline * #-blankline local endequals = spacechar^0 * P"="^0 * spacechar^0 * newline local cellsep = spacechar^0 * P"|" local function trim(s) return (s:gsub("^%s*(.-)%s*$", "%1")) end local function ListItem(lev, ch) local start if ch == nil then start = S"*#" else start = P(ch) end local subitem = function(c) if lev < 6 then return ListItem(lev + 1, c) else return (1 - 1) -- fails end end local parser = spacechar^0 * start^lev * #(- start) * spacechar^0 * Ct((V"Inline" - (newline * spacechar^0 * S"*#"))^0) * newline * (Ct(subitem("*")^1) / pandoc.BulletList + Ct(subitem("#")^1) / pandoc.OrderedList + Cc(nil)) / function (ils, sublist) return { pandoc.Plain(ils), sublist } end return parser end -- Grammar G = P{ "Doc", Doc = Ct(V"Block"^0) / pandoc.Pandoc ; Block = blankline^0 * ( V"Header" + V"HorizontalRule" + V"CodeBlock" + V"List" + V"Table" + V"Para") ; Para = Ct(V"Inline"^1) * newline / pandoc.Para ; HorizontalRule = spacechar^0 * P"----" * spacechar^0 * newline / pandoc.HorizontalRule; Header = (P("=")^1 / string.len) * spacechar^1 * Ct((V"Inline" - endequals)^1) * endequals / pandoc.Header; CodeBlock = P"{{{" * blankline * C((1 - (newline * P"}}}"))^0) * newline * P"}}}" / pandoc.CodeBlock; Placeholder = P"<<<" * C(P(1) - P">>>")^0 * P">>>" / function() return pandoc.Div({}) end; List = V"BulletList" + V"OrderedList" ; BulletList = Ct(ListItem(1,'*')^1) / pandoc.BulletList ; OrderedList = Ct(ListItem(1,'#')^1) / pandoc.OrderedList ; Table = (V"TableHeader" + Cc{}) * Ct(V"TableRow"^1) / function(headrow, bodyrows) local numcolumns = #(bodyrows[1]) local aligns = {} local widths = {} for i = 1,numcolumns do aligns[i] = pandoc.AlignDefault widths[i] = 0 end return pandoc.utils.from_simple_table( pandoc.SimpleTable({}, aligns, widths, headrow, bodyrows)) end ; TableHeader = Ct(V"HeaderCell"^1) * cellsep^-1 * spacechar^0 * newline ; TableRow = Ct(V"BodyCell"^1) * cellsep^-1 * spacechar^0 * newline ; HeaderCell = cellsep * P"=" * spacechar^0 * Ct((V"Inline" - (newline + cellsep))^0) / function(ils) return { pandoc.Plain(ils) } end ; BodyCell = cellsep * spacechar^0 * Ct((V"Inline" - (newline + cellsep))^0) / function(ils) return { pandoc.Plain(ils) } end ; Inline = V"Emph" + V"Strong" + V"LineBreak" + V"Link" + V"URL" + V"Image" + V"Str" + V"Space" + V"SoftBreak" + V"Escaped" + V"Placeholder" + V"Code" + V"Special" ; Str = wordchar^1 / pandoc.Str; Escaped = P"~" * C(P(1)) / pandoc.Str ; Special = specialchar / pandoc.Str; Space = spacechar^1 / pandoc.Space ; SoftBreak = endline * # -(V"HorizontalRule" + V"CodeBlock") / pandoc.SoftBreak ; LineBreak = P"\\\\" / pandoc.LineBreak ; Code = P"{{{" * C((1 - P"}}}")^0) * P"}}}" / trim / pandoc.Code ; Link = P"[[" * C((1 - (P"]]" + P"|"))^0) * (P"|" * Ct((V"Inline" - P"]]")^1))^-1 * P"]]" / function(url, desc) local txt = desc or {pandoc.Str(url)} return pandoc.Link(txt, url) end ; Image = P"{{" * #-P"{" * C((1 - (S"}"))^0) * (P"|" * Ct((V"Inline" - P"}}")^1))^-1 * P"}}" / function(url, desc) local txt = desc or "" return pandoc.Image(txt, url) end ; URL = P"http" * P"s"^-1 * P":" * (1 - (whitespacechar + (S",.?!:;\"'" * #whitespacechar)))^1 / function(url) return pandoc.Link(pandoc.Str(url), url) end ; Emph = P"//" * Ct((V"Inline" - P"//")^1) * P"//" / pandoc.Emph ; Strong = P"**" * Ct((V"Inline" -P"**")^1) * P"**" / pandoc.Strong ; } function Reader(input, reader_options) return lpeg.match(G, tostring(input)) end ``` Example of use: ``` % pandoc -f creole.lua -t markdown == Wiki Creole You can make things **bold** or //italic// or **//both//** or //**both**//. Character formatting extends across line breaks: **bold, this is still bold. This line deliberately does not end in star-star. Not bold. Character formatting does not cross paragraph boundaries. You can use [[internal links]] or [[http://www.wikicreole.org|external links]], give the link a [[internal links|different]] name. ^D ## Wiki Creole You can make things **bold** or *italic* or ***both*** or ***both***. Character formatting extends across line breaks: \*\*bold, this is still bold. This line deliberately does not end in star-star. Not bold. Character formatting does not cross paragraph boundaries. You can use [internal links](internal links) or [external links](http://www.wikicreole.org), give the link a [different](internal links) name. ``` # Example: parsing JSON from an API This custom reader consumes the JSON output of and produces a document containing the current top articles on the Haskell subreddit. It assumes that the `pandoc.json` library is available, which ships with pandoc versions after (not including) 3.1. It's still possible to use this with older pandoc version by using a different JSON library. E.g., `luajson` can be installed using `luarocks install luajson`---but be sure you are installing it for Lua 5.4, which is the version packaged with pandoc. ```lua -- consumes the output of https://www.reddit.com/r/haskell.json local json = require 'pandoc.json' local function read_inlines(raw) local doc = pandoc.read(raw, "commonmark") return pandoc.utils.blocks_to_inlines(doc.blocks) end local function read_blocks(raw) local doc = pandoc.read(raw, "commonmark") return doc.blocks end function Reader(input) local parsed = json.decode(tostring(input)) local blocks = {} for _,entry in ipairs(parsed.data.children) do local d = entry.data table.insert(blocks, pandoc.Header(2, pandoc.Link(read_inlines(d.title), d.url))) for _,block in ipairs(read_blocks(d.selftext)) do table.insert(blocks, block) end end return pandoc.Pandoc(blocks) end ``` Similar code can be used to consume JSON output from other APIs. Note that the content of the text fields is markdown, so we convert it using `pandoc.read()`. # Example: syntax-highlighted code files This is a reader that puts the content of each input file into a code block, sets the file's extension as the block's class to enable code highlighting, and places the filename as a header above each code block. ``` lua function to_code_block (source) local _, lang = pandoc.path.split_extension(source.name) return pandoc.Div{ pandoc.Header(1, source.name == '' and '' or source.name), pandoc.CodeBlock(source.text, {class=lang}), } end function Reader (input, opts) return pandoc.Pandoc(input:map(to_code_block)) end ``` # Example: extracting the content from web pages This reader uses the command-line program `readable` (install via `npm install -g readability-cli`) to clean out parts of HTML input that have to do with navigation, leaving only the content. ``` lua -- Custom reader that extracts the content from HTML documents, -- ignoring navigation and layout elements. This preprocesses input -- through the 'readable' program (which can be installed using -- 'npm install -g readability-cli') and then calls the HTML reader. -- In addition, Divs that seem to have only a layout function are removed -- to avoid clutter. function make_readable(source) local result if not pcall(function () local name = source.name if not name:match("http") then name = "file:///" .. name end result = pandoc.pipe("readable", {"--keep-classes","--base",name}, source.text) end) then io.stderr:write("Error running 'readable': do you have it installed?\n") io.stderr:write("npm install -g readability-cli\n") os.exit(1) end return result end local boring_classes = { row = true, page = true, container = true } local boring_attributes = { "role" } local function is_boring_class(cl) return boring_classes[cl] or cl:match("col%-") or cl:match("pull%-") end local function handle_div(el) for i,class in ipairs(el.classes) do if is_boring_class(class) then el.classes[i] = nil end end for i,k in ipairs(boring_attributes) do el.attributes[k] = nil end if el.identifier:match("readability%-") then el.identifier = "" end if #el.classes == 0 and #el.attributes == 0 and #el.identifier == 0 then return el.content else return el end end function Reader(sources) local readable = '' for _,source in ipairs(sources) do readable = readable .. make_readable(source) end local doc = pandoc.read(readable, "html", PANDOC_READER_OPTIONS) -- Now remove Divs used only for layout return doc:walk{ Div = handle_div } end ``` Example of use: ``` pandoc -f readable.lua -t markdown https://pandoc.org ``` and compare the output to ``` pandoc -f html -t markdown https://pandoc.org ``` ================================================ FILE: doc/custom-writers.md ================================================ --- author: - John MacFarlane date: 'November 21, 2021' title: Creating Custom Pandoc Writers in Lua --- # Introduction If you need to render a format not already handled by pandoc, or you want to change how pandoc renders a format, you can create a custom writer using the [Lua] language. Pandoc has a built-in Lua interpreter, so you needn't install any additional software to do this. [Lua]: https://www.lua.org A custom writer is a Lua file that defines how to render the document. Writers must define just a single function, named either `Writer` or `ByteStringWriter`, which gets passed the document and writer options, and then handles the conversion of the document, rendering it into a string. This interface was introduced in pandoc 2.17.2, with ByteString writers becoming available in pandoc 3.0. Pandoc also supports "classic" custom writers, where a Lua function must be defined for each AST element type. Classic style writers are *deprecated* and should be replaced with new-style writers if possible. # Writers Custom writers using the new style must contain a global function named `Writer` or `ByteStringWriter`. Pandoc calls this function with the document and writer options as arguments, and expects the function to return a UTF-8 encoded string. ``` lua function Writer (doc, opts) -- ... end ``` Writers that do not return text but binary data should define a function with name `ByteStringWriter` instead. The function must still return a string, but it does not have to be UTF-8 encoded and can contain arbitrary binary data. If both `Writer` and `ByteStringWriter` functions are defined, then only the `Writer` function will be used. ## Format extensions Writers can be customized through format extensions, such as `smart`, `citations`, or `hard_line_breaks`. The global `Extensions` table indicates supported extensions with a key. Extensions enabled by default are assigned a true value, while those that are supported but disabled are assigned a false value. Example: A writer with the following global table supports the extensions `smart`, `citations`, and `foobar`, with `smart` enabled and the others disabled by default: ``` lua Extensions = { smart = true, citations = false, foobar = false } ``` The users control extensions as usual, e.g., `pandoc -t my-writer.lua+citations`. The extensions are accessible through the writer options' `extensions` field, e.g.: ``` lua function Writer (doc, opts) print( 'The citations extension is', opts.extensions:includes 'citations' and 'enabled' or 'disabled' ) -- ... end ``` ## Default template The default template of a custom writer is defined by the return value of the global function `Template`. Pandoc uses the default template for rendering when the user has not specified a template, but invoked with the `-s`/`--standalone` flag. The `Template` global can be left undefined, in which case pandoc will throw an error when it would otherwise use the default template. ## Example: modified Markdown writer Writers have access to all modules described in the [Lua filters documentation][]. This includes `pandoc.write`, which can be used to render a document in a format already supported by pandoc. The document can be modified before this conversion, as demonstrated in the following short example. It renders a document as GitHub Flavored Markdown, but always uses fenced code blocks, never indented code. ``` lua function Writer (doc, opts) local filter = { CodeBlock = function (cb) -- only modify if code block has no attributes if cb.attr == pandoc.Attr() then local delimited = '```\n' .. cb.text .. '\n```' return pandoc.RawBlock('markdown', delimited) end end } return pandoc.write(doc:walk(filter), 'gfm', opts) end Template = pandoc.template.default 'gfm' ``` [Lua filters documentation]: https://pandoc.org/lua-filters.html ## Reducing boilerplate with `pandoc.scaffolding.Writer` The `pandoc.scaffolding.Writer` structure is a custom writer scaffold that serves to avoid common boilerplate code when defining a custom writer. The object can be used as a function and allows to skip details like metadata and template handling, requiring only the render functions for each AST element type. The value of `pandoc.scaffolding.Writer` is a function that should usually be assigned to the global `Writer`: ``` lua Writer = pandoc.scaffolding.Writer ``` The render functions for Block and Inline values can then be added to `Writer.Block` and `Writer.Inline`, respectively. The functions are passed the element and the WriterOptions. ``` lua Writer.Inline.Str = function (str) return str.text end Writer.Inline.SoftBreak = function (_, opts) return opts.wrap_text == "wrap-preserve" and cr or space end Writer.Inline.LineBreak = cr Writer.Block.Para = function (para) return {Writer.Inlines(para.content), pandoc.layout.blankline} end ``` The render functions must return a string, a pandoc.layout *Doc* element, or a list of such elements. In the latter case, the values are concatenated as if they were passed to `pandoc.layout.concat`. If the value does not depend on the input, a constant can be used as well. The tables `Writer.Block` and `Writer.Inline` can be used as functions; they apply the right render function for an element of the respective type. E.g., `Writer.Block(pandoc.Para 'x')` will delegate to the `Writer.Para` render function and will return the result of that call. Similarly, the functions `Writer.Blocks` and `Writer.Inlines` can be used to render lists of elements, and `Writer.Pandoc` renders the document's blocks. The function `Writer.Blocks` can take a separator as an optional second argument, e.g., `Writer.Blocks(blks, pandoc.layout.cr)`; the default block separator is `pandoc.layout.blankline`. All predefined functions can be overwritten when needed. The resulting Writer uses the render functions to handle metadata values and converts them to template variables. The template is applied automatically if one is given. # Classic style A writer using the classic style defines rendering functions for each element of the pandoc AST. Note that this style is *deprecated* and may be removed in later versions. For example, ``` lua function Para(s) return "" .. s .. "" end ``` ## Template variables New template variables can be added, or existing ones modified, by returning a second value from function `Doc`. For example, the following will add the current date in variable `date`, unless `date` is already defined as either a metadata value or a variable: ``` lua function Doc (body, meta, vars) vars.date = vars.date or meta.date or os.date '%B %e, %Y' return body, vars end ``` ## Changes in pandoc 3.0 Custom writers were reworked in pandoc 3.0. For technical reasons, the global variables `PANDOC_DOCUMENT` and `PANDOC_WRITER_OPTIONS` are set to the empty document and default values, respectively. The old behavior can be restored by adding the following snippet, which turns a classic into a new style writer. ``` lua function Writer (doc, opts) PANDOC_DOCUMENT = doc PANDOC_WRITER_OPTIONS = opts loadfile(PANDOC_SCRIPT_FILE)() return pandoc.write_classic(doc, opts) end ``` ================================================ FILE: doc/customizing-pandoc.md ================================================ --- author: - Mauro Bieg - John MacFarlane title: Customizing Pandoc --- This document provides a quick overview over the various ways to customize pandoc's output, with links to fuller documentation and some examples. ## Templates When the `-s`/`--standalone` option is used, pandoc will generate a standalone document rather than a fragment. For example, in HTML output this will include the `` element; in LaTeX output, it will include the preamble. Pandoc comes with a default template for (almost) every output format. A template is a plain text file containing variables that are replaced by text generated by pandoc. For example, the variable `$body$` will be replaced by the document body, and `$title$` by the title from metadata. To look at the default template for an output format, you can do `pandoc -D FORMAT`, where `FORMAT` is replaced by the name of the format. For example `pandoc -D latex`. You can also use your own template instead, either by using the `--template` option or by putting the custom template in your user data directory (on Linux and macOS, `~/.pandoc/templates/`). Note that in many cases you can avoid the need for a custom template by including a file with the `--include-in-header`, `--include-before-body`, or `--include-after-body` option. Or you can set the corresponding template variable directly. ### Template variables There are several ways to set template variables: | | [`--variable`] | [`--metadata`] | [YAML metadata] and [`--metadata-file`] | |:---------------|:------------------|:------------------|:----------------------------| | values can be… | strings and bools | strings and bools | also YAML objects and lists | | strings are… | inserted verbatim | escaped | interpreted as markdown | | accessible by filters: | no | yes | yes | [`--variable`]: https://pandoc.org/MANUAL.html#option--variable [`--metadata`]: https://pandoc.org/MANUAL.html#option--metadata [YAML metadata]: https://pandoc.org/MANUAL.html#extension-yaml_metadata_block [`--metadata-file`]: https://pandoc.org/MANUAL.html#option--metadata-file For more information, see [Templates](https://pandoc.org/MANUAL.html#templates) in the pandoc manual. ### Example: adding structured author data to HTML TODO ### Example: generating documents from YAML metadata TODO ## Reference docx/pptx/odt For `docx`, `pptx` or `odt` documents, things are a bit more complicated. Instead of a single template file, you need to provide a customized `reference.docx/pptx/odt`. See the manual for the [`--reference-doc`](https://pandoc.org/MANUAL.html#option--reference-doc) option. ### Example: changing the font and line spacing in a Word docx TODO ## Filters Templates are very powerful, but they are only a sort of scaffold to place your document's body text in. You cannot directly change the body text using the template. If you need to affect the output of the actual body text, you can use a pandoc filter. A filter is a small program that transforms the document, between the parsing and the writing phase, while it is still in pandoc's native format. For example, a filter might find all the Header elements of a document and capitalize their text. Pandoc's native representation of a document is an abstract syntax tree (AST), not unlike the HTML DOM. It is documented [here](https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Definition.html). A `Pandoc` document is a chunk of metadata (`Meta`) and a list of `Block`s. The `Block`s, in turn, are composed of other `Block`s and `Inline` elements. (`Block` elements are things like paragraphs, lists, headers, and code blocks. `Inline` elements are individual words, links, emphasis, and so on.) Filters operate on these elements. You can use `pandoc -t native` to learn about the AST's structure. There are two kinds of filters: JSON filters (which transform a JSON serialization of the pandoc AST, and may be written in any language that can parse and emit JSON), and Lua filters (which use an interface built directly into pandoc, and must be written in the Lua language). If you are writing your own filters, it is best to use Lua filters, which are more portable (they require only pandoc itself) and more efficient. See [Lua filters](https://pandoc.org/lua-filters.html) for documentation and examples. If you would prefer to write your filter in another language, see [Filters](https://pandoc.org/filters.html) for a gentle introduction to JSON filters. There's a repository of lua filters at [pandoc/lua-filters](https://github.com/pandoc/lua-filters) on GitHub. A number of pandoc filters, written in Haskell, are available on [Hackage](https://hackage.haskell.org/packages/search?terms=pandoc+filter) and can be installed using the `stack` or `cabal` tools. The wiki also lists [third party filters](https://github.com/jgm/pandoc/wiki/Pandoc-Filters). ### Example: capitalizing headers TODO ### Example: code extractor TODO ## Generic Divs and Spans TODO [Divs and Spans](https://pandoc.org/MANUAL.html#divs-and-spans): generic blocks that can be transformed with filters ### Example: colored text ### Example: custom styles in docx [Custom Styles in Docx](https://pandoc.org/MANUAL.html#custom-styles-in-docx) ## Raw attributes TODO [Generic raw attributes](https://pandoc.org/MANUAL.html#generic-raw-attribute): to include raw snippets ## Custom writers TODO [Custom writers](https://pandoc.org/MANUAL.html#custom-writers) ## Custom syntax highlighting TODO [Custom syntax highlighting](https://pandoc.org/MANUAL.html#syntax-highlighting), provided by the [skylighting library](https://github.com/jgm/skylighting) including highlighting styles ================================================ FILE: doc/epub.md ================================================ --- title: Creating an ebook with pandoc author: John MacFarlane --- Starting with version 1.6, pandoc can produce output in the [EPUB] electronic book format. EPUB books can be viewed on iPads, Nooks, and other electronic book readers, including many smart phones. (They can also be converted to Kindle books using the GUI only [KindlePreviewer] on Windows and Mac OSX. [KindleGen] – which offers a command line interface and supports [Linux][KindleGenLinux], [Mac OSX][KindleGenMacOSX] and [Windows][KindleGenWindows] – has been deprecated, but binaries can still be found on the internet.) This means that it's now very easy to produce an electronic book! Let's try it. # A toy example Use your text editor to create a file `mybook.txt`, with the following contents: % My Book % Sam Smith This is my book! # Chapter One Chapter one is over. # Chapter Two Chapter two has just begun. To make this into an ebook takes only one command: pandoc mybook.txt -o mybook.epub You can upload `mybook.epub` to your ebook reader and try it out. Note that if your markdown file contains links to local images, for example ![Juliet](images/sun.jpg) pandoc will automatically include the images in the generated epub. # A real book To see what this would look like for a real book, let's convert Scott Chacon's book [Pro Git], which he wrote using pandoc's markdown variant and released under a [Creative Commons] license. (If you use the book, please consider [buying a copy] to help support his excellent work.) You can find the markdown source for the book on its [github site]. Let's get a copy of the whole repository:[^1] git clone https://github.com/progit/progit.git [^1]: If you don't have [git], you can browse to the [github site] and click "Download Source" to get the same files in a zip or tar archive. This command will create a working directory called `progit` on your machine. The actual markdown sources for the English version of the book are in the `en` subdirectory, so start by changing to that directory: cd progit/en As you can see, each chapter is a single text file in its own directory. Chacon does some postprocessing on these files, for example, to insert images. This is a placeholder for Figure 1-1, for example: Insert 18333fig0101.png Figure 1-1. Local version control diagram. The actual image file is called `18333fig0101-tn.png` and lives in the `figures` subdirectory of the repository, as you can verify. For demonstration purposes, we want pure markdown files, so let's change this placeholder into a markdown image link. Pandoc will treat a paragraph containing a single image as a figure with a caption, which is what we want: ![Figure 1-1. Local version control diagram.](../figures/18333fig0101-tn.png) We can make this change in all the files with a perl one-liner: perl -i -0pe \ 's/^Insert\s*(.*)\.png\s*\n([^\n]*)$/!\[\2](..\/figures\/\1-tn.png)/mg' \ */*.markdown This will modify the files in place. (We won't worry about backing them up; if we mess up, we can get the original files back with `git reset --hard`.) OK! Now we're almost ready to make an ebook. We have the chapters, each in its own file, but we still need a title. Create a file, `title.txt`, with a pandoc YAML metadata block: ``` --- title: Pro Git author: Scott Chacon rights: Creative Commons Non-Commercial Share Alike 3.0 language: en-US ... ``` See the [User's Guide](https://pandoc.org/MANUAL.html#epub-metadata) for more information about these fields. Now run pandoc to make the ebook, using our title page and modified chapter files as sources: pandoc -o progit.epub title.txt \ 01-introduction/01-chapter1.markdown \ 02-git-basics/01-chapter2.markdown \ 03-git-branching/01-chapter3.markdown \ 04-git-server/01-chapter4.markdown \ 05-distributed-git/01-chapter5.markdown \ 06-git-tools/01-chapter6.markdown \ 07-customizing-git/01-chapter7.markdown \ 08-git-and-other-scms/01-chapter8.markdown \ 09-git-internals/01-chapter9.markdown That's it! The ebook, `progit.epub`, is ready to be uploaded to your reader. ## Changing the format You can use the `--css` option to specify a CSS file for the book. The default CSS is minimal and can be found [on GitHub](https://github.com/jgm/pandoc/blob/main/data/epub.css) or in the `epub.css` file in your data directory (see `--data-dir` in the [User's Guide]). You can even embed fonts in the EPUB if you want; see the [User's Guide] under `--epub-embed-font` for instructions. ## Math Pandoc has an EPUB3 writer. It renders LaTeX math into MathML, which EPUB3 readers are supposed to support (but unfortunately few do). Of course, this isn't much help if you want EPUB2 output (`pandoc -t epub2`) or target readers that don't support MathML. Then you have two options: 1. Use the [`--webtex`](https://pandoc.org/MANUAL.html#option--webtex) option, which will use a web service to convert the TeX to an image. 2. Use the [`--gladtex`](https://pandoc.org/MANUAL.html#option--gladtex) option to convert maths into SVG images on your local machine. Both GladTeX and WebTeX add the LaTeX source of the formula as alternative text of the image, increasing accessibility for blind users. [KindlePreviewer]: https://www.amazon.com/Kindle-Previewer/b?node=21381691011 [KindleGen]: https://www.amazon.com/gp/feature.html?docId=1000765211 [KindleGenLinux]: https://archive.org/details/kindlegen2.9 [KindleGenMacOSX]: https://web.archive.org/web/20190905040839/https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211 [KindleGenWindows]: https://archive.org/details/kindlegen_win32_v2_9 [EPUB]: https://en.wikipedia.org/wiki/EPUB [Pro Git]: https://git-scm.com/book/en/v2 [Creative Commons]: https://creativecommons.org/ [buying a copy]: https://git-scm.com/book/en/v2 [github site]: https://github.com/progit/progit [git]: https://git-scm.com [Dublin Core metadata elements]: https://dublincore.org/documents/dces/ [User's Guide]: https://pandoc.org/MANUAL.html ================================================ FILE: doc/extras.md ================================================ --- title: Pandoc Extras author: John MacFarlane --- This is a curated list of some of the most useful third-party software that extends pandoc's capabilities. For a more complete list, see the [Pandoc Extras wiki page](https://github.com/jgm/pandoc/wiki/Pandoc-Extras). ### GUI - [PanWriter](https://github.com/mb21/panwriter/#panwriter) is a Markdown editor with live preview that can import and export using pandoc. - [Pandoc Mac OS X Services](https://github.com/mb21/Pandoc-Mac-OS-X-Services) allows you to invoke pandoc from any text editor with the opened file as input. ### Editor support - [pandoc-mode](http://joostkremers.github.io/pandoc-mode) for Emacs - [vim-pandoc](https://github.com/vim-pandoc) for Vim ### CLI wrappers - [panzer](https://github.com/msprev/panzer) allows you to specify command-line options and styles in the Markdown file's YAML metadata. - [pandocomatic](https://heerdebeer.org/Software/markdown/pandocomatic/) is similar to panzer but also provides support for converting directory trees. - [panrun](https://github.com/mb21/panrun) is a more minimalistic alternative to panzer and pandocomatic. ### Editing/document changes - [pandiff](https://github.com/davidar/pandiff) creates prose diffs for any document format supported by pandoc. - [pancritic](https://github.com/ickc/pancritic) allows using [CriticMarkup](http://criticmarkup.com/spec.php#caveats) with pandoc. ### Charts and data - [R Markdown](https://rmarkdown.rstudio.com) allows you to process Markdown documents with integrated data and charts (integrates pandoc). - [pandoc-plot](https://github.com/LaurentRDC/pandoc-plot) allows you to integrate programmatically generated plots generated by various tools. ### Citations - [zotxt](https://github.com/egh/zotxt) is Zotero extension for working with pandoc ### Numbering and cross-references - [pandoc-crossref](https://github.com/lierdakil/pandoc-crossref) is a filter that adds support for figure, table, and equation numbers and cross-references. - [pandoc-xnos](https://github.com/tomduck/pandoc-xnos) is a suite of filters supporting numbering and cross-referencing figures, equations, tables, and sections. ### Academic publishing workflows - [Quarto](https://quarto.org/) is an open-source scientific and technical publishing system to make reproducible, production quality articles, presentations, websites, blogs, and books. It supports equations, citations, crossrefs, figure panels, callouts, advanced layout, etc. - [Manubot](https://manubot.org) is a workflow and set of tools for the next generation of scholarly publishing, including citation support and support for multiple output formats. It's especially suited for papers with a very large set of authors. - [pandoc-ext](https://github.com/pandoc-ext) includes goodies such as putting the Abstract into a section, multiple bibliographies, support the Citation Typing Ontology (CiTO), etc. - [Baseprinter](https://try.perm.pub/baseprinter/) is an open-source pandoc wrapper to generate Baseprint document snapshots and HTML/PDF previews. These snapshots can be self-published/archived as editions of a [Baseprint document succession](https://try.perm.pub/document_succession/). - [Pandoc Scholar](https://github.com/pandoc-scholar/pandoc-scholar) is a set of utilities to make publishing of scientific articles as simple and pleasant as possible. It simplifies setting authors' metadata in YAML blocks, allows to add semantic annotation to citations, and only requires the programs `pandoc` and `make`. Please note that it is no longer maintained and refers to `Quarto` and `pandoc-ext` ### Containers - [pandoc docker images](https://github.com/pandoc/dockerfiles) ### Lua filters (For a fuller list, see [the lua-filters repository](https://github.com/pandoc/lua-filters).) - [Spell checking](https://github.com/pandoc/lua-filters/tree/master/spellcheck). - [Word count](https://github.com/pandoc/lua-filters/tree/master/wordcount). - [Embed textually-specified diagrams](https://github.com/pandoc-ext/diagram) in Mermaid, Dot/GraphViz, PlantUML, Asymptote, CeTZ, and TikZ. - [Create subfigures in pandoc's Markdown](https://github.com/rnwst/pandoc-subfigs). - [Handle scholarly metadata](https://github.com/pandoc/lua-filters/tree/master/scholarly-metadata). - [Panda](http://christophe.delord.free.fr/panda/) provides conditionals, code file inclusion, string expansion, and diagrams from code blocks. ================================================ FILE: doc/faqs.md ================================================ % FAQs ::: faqs ## How can I convert a whole directory of files from Markdown to RTF? On linux or OSX: for f in *.txt; do pandoc "$f" -s -o "${f%.txt}.rtf"; done In Windows Powershell: gci -r -i *.txt |foreach{$rtf=$_.directoryname+"\"+$_.basename+".rtf";pandoc -f markdown -s $_.fullname -o $rtf} ## I used pandoc to convert a document to ICML (or OPML or RTF), and when I try to open it I'm told it's invalid. What have I done wrong? Be sure to use the `-s` or `--standalone` flag, or you just get a fragment, not a full document with the required header: pandoc -s -f markdown -t icml -o my.icml my.md ## I get a blank document when I try to convert a markdown document in Chinese to PDF. By default, pandoc uses pdflatex to generate the PDF, and pdflatex doesn't handle Chinese characters. But you can change the default to use xelatex instead. You should also make sure you're using a font with Chinese glyphs. For example: pandoc -o c.pdf --pdf-engine=xelatex -V mainfont='Adobe Ming Std' ## The Windows installer does a single user install, rather than installing pandoc for all users. How can I install pandoc for all users? Run the following command as admin: msiexec /i pandoc-VERSION.msi ALLUSERS=1 This will put pandoc in `C:\Program Files\Pandoc`. You can install Pandoc to a different directory by setting APPLICATIONFOLDER parameter, for example: msiexec /i pandoc-1.11.1.msi ALLUSERS=1 APPLICATIONFOLDER="C:\Pandoc" ## How do I change the margins in PDF output? The option -V geometry:margin=1in will set the margins to one inch on each side. If you don't want uniform margins, you can do something like -V geometry:"top=2cm, bottom=1.5cm, left=1cm, right=1cm" Or -V geometry:"left=3cm, width=10cm" For more options, see the documentation for the LaTeX [geometry package](https://www.ctan.org/pkg/geometry). ## How does pandoc compare to multimarkdown? Here is a [wiki page](https://github.com/jgm/pandoc/wiki/Pandoc-vs-Multimarkdown) comparing the two. ## When I specify an image width of 50% and convert to LaTeX, pandoc sets the height to textheight and the aspect ratio isn't preserved. How can I prevent this? For example, if you convert an image with `{width="50%"}`, the LaTeX produced will be `\includegraphics[width=0.5\textwidth,height=\textheight]`. This output presupposes the following code in pandoc's default latex template: ``` % Scale images if necessary, so that they will not overflow the page % margins by default, and it is still possible to overwrite the defaults % using explicit options in \includegraphics[width, height, ...]{} \setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio} ``` If you don't have this in your custom template, you should add it. If we didn't set the `height` explicitly in this way, the image would not be resized correctly unless it was being resized to smaller than its original size. ## Pandoc sometimes uses too much memory. How can I limit the memory used by pandoc? `pandoc +RTS -M30m -RTS` will limit heap memory to 30MB. When converting a document requires more than this, an out of memory error will be issued. ## When using `--include-in-header` with PDF or LaTeX output, how do I reference tex declarations coming after `$header-includes$` in the default template? For various reasons, the `$header-includes$` are not at the very end of the LaTeX preamble. This poses a problem when the code you are inserting depends on declarations in the preamble coming after the `$header-includes$` location. For example, you might want to reference the `\author` and `\title` metadata values (set at the very bottom of the preamble) and present them in margins. In that case you can wrap your code in `etoolbox`'s `\AtEndPreamble`. The technique is demonstrated in [this gist](https://gist.github.com/JohnLukeBentley/9dda6166b9ee5c4127afd2b8cd16b70a). When using `\AtEndPreamble`, keep any `makeatletter` or `makeatother` outside of the `\AtEndPreamble`, as shown in the example. ## How can I convert PDFs to other formats using pandoc? You can't. You can try opening the PDF in Word or Google Docs and saving in a format from which pandoc can convert directly. ## Do I really need to install a 1 GB TeX installation to produce a PDF using pandoc? No. You can get by with a relatively small TeX installation, for example, by starting with MacTeX's Basic TeX distribution and using the `tlmgr` tool to install a few packages required by pandoc (see [the manual](https://pandoc.org/MANUAL.html#creating-a-pdf)). Or, you can produce PDFs via HTML and `wkhtmltopdf`, or via groff ms and `pdfroff`. (These don't produce as nice typography as TeX, particularly when it comes to math, but they may be fine for many purposes.) ## Converting to PDF on an M1 Mac, I get a "Cannot allocate memory" error. We are not sure why this happens, but we have found that fully specifying the `pdflatex` path avoids the error. For example, pandoc -o my.pdf --pdf-engine=/Library/TeX/texbin/pdflatex ## When I convert from ipynb, some visualizations aren't showing up. First, unless your target is a binary format (docx, odt, epub), you must use either `--extract-media` or (for HTML only) `--embed-resources` to make the images in the ipynb container available to your output file. Second, some Jupyter extensions, especially those that use JavaScript for visualizations, assume the presence of [`require.js`](https://github.com/requirejs/requirejs). To ensure that this script is available in your HTML output, you can use: ``` pandoc -s -o output.html input.ipynb \ -V header-includes='' ``` ## How can I get BibTeX references to work when converting from LaTeX? Use the `--citeproc` option. If it still doesn't work, you may need to tell pandoc where your bibliography file is using `--bibliography`. Your references may not be formatted the same as they are when you use `latex` and `bibtex`; you can change the format of the citations by specifying an appropriate CSL bibliography style using `--csl` (see [the manual](https://pandoc.org/MANUAL.html#specifying-a-citation-style)). ### How can I produce PDF/A with pandoc? The simplest approach is via ConTeXt: ``` pandoc --pdf-engine=context -V pdfa ``` Alternatively, `--pdf-engine=pdflatex` can be used with the following in `header-includes` in metadata (or included from a file using `--include-in-header`): ``` \usepackage[a-2u,mathxmp]{pdfx} \usepackage[pdfa]{hyperref} ``` Or `--pdf-engine=lualatex` can be used with the following: ``` \usepackage{hyperxmp} \hypersetup{pdfapart=3,pdfaconformance=B} \immediate\pdfobj stream attr{/N 3} file{sRGB.icc} \pdfcatalog{/OutputIntents [<< /Type /OutputIntent /S /GTS_PDFA1 /DestOutputProfile \the\pdflastobj\space 0 R /OutputConditionIdentifier (sRGB) /Info (sRGB) >>]} ``` ### Pandoc adds column widths to pipe tables when any line is wider than the setting for `--columns`. How can I prevent this? Save this filter as `nowidths.lua` and then pass `--lua-filter nowidths.lua` as an additional option to pandoc. (See [issue 8139](https://github.com/jgm/pandoc/issues/8139).) ``` lua -- Unset the width attribute of HTML colspecs in tables -- See https://github.com/jgm/pandoc/issues/8139 function Table (tbl) if PANDOC_VERSION[1] >= 2 and PANDOC_VERSION[2] >= 10 then tbl.colspecs = tbl.colspecs:map(function (colspec) local align = colspec[1] local width = nil -- default width return {align, width} end) else for i, w in ipairs(tbl.widths) do tbl.widths[i] = 0 end end return tbl end ``` ### How can I use pandoc to read Word files in the old .DOC format? Install `antiword` and use it to convert the doc to DocBook, which can be read by pandoc. ``` antiword -x db input.doc | pandoc -f docbook ``` ::: ================================================ FILE: doc/filters.md ================================================ % Pandoc filters % John MacFarlane # Summary Pandoc provides an interface for users to write programs (known as filters) which act on pandoc’s AST. Pandoc consists of a set of readers and writers. When converting a document from one format to another, text is parsed by a reader into pandoc’s intermediate representation of the document---an "abstract syntax tree" or AST---which is then converted by the writer into the target format. The pandoc AST format is defined in the module [`Text.Pandoc.Definition` in the `pandoc-types` package ](https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Definition.html). A "filter" is a program that modifies the AST, between the reader and the writer. INPUT --reader--> AST --filter--> AST --writer--> OUTPUT Pandoc supports two kinds of filters: - **Lua filters** use the Lua language to define transformations on the pandoc AST. They are described in a [separate document](lua-filters.html). - **JSON filters**, described here, are pipes that read from standard input and write to standard output, consuming and producing a JSON representation of the pandoc AST: source format ↓ (pandoc) ↓ JSON-formatted AST ↓ (JSON filter) ↓ JSON-formatted AST ↓ (pandoc) ↓ target format Lua filters have a couple of advantages. They use a Lua interpreter that is embedded in pandoc, so you don't need to have any external software installed. And they are usually faster than JSON filters. But if you wish to write your filter in a language other than Lua, you may prefer to use a JSON filter. JSON filters may be written in any programming language. You can use a JSON filter directly in a pipeline: pandoc -s input.txt -t json | \ pandoc-citeproc | \ pandoc -s -f json -o output.html But it is more convenient to use the `--filter` option, which handles the plumbing automatically: pandoc -s input.txt --filter pandoc-citeproc -o output.html For a gentle introduction into writing your own filters, continue this guide. There’s also a [list of third party filters on the wiki](https://github.com/jgm/pandoc/wiki/Pandoc-Filters). # A simple example Suppose you wanted to replace all level 2+ headings in a markdown document with regular paragraphs, with text in italics. How would you go about doing this? A first thought would be to use regular expressions. Something like this: perl -pe 's/^##+ (.*)$/\*\1\*/' source.txt This should work most of the time. But don't forget that ATX style headings can end with a sequence of `#`s that is not part of the heading text: ## My heading ## And what if your document contains a line starting with `##` in an HTML comment or delimited code block? ~~~~ ### A third level heading in standard markdown ~~~~ We don't want to touch *these* lines. Moreover, what about Setext style second-level heading? A heading --------- We need to handle those too. Finally, can we be sure that adding asterisks to each side of our string will put it in italics? What if the string already contains asterisks around it? Then we'll end up with bold text, which is not what we want. And what if it contains a regular unescaped asterisk? How would you modify your regular expression to handle these cases? It would be hairy, to say the least. A better approach is to let pandoc handle the parsing, and then modify the AST before the document is written. For this, we can use a filter. To see what sort of AST is produced when pandoc parses our text, we can use pandoc's `native` output format: ~~~~ % cat test.txt ## my heading text with *italics* % pandoc -s -t native test.txt Pandoc (Meta {unMeta = fromList []}) [Header 2 ("my-heading",[],[]) [Str "My",Space,Str "heading"] , Para [Str "text",Space,Str "with",Space,Emph [Str "italics"]] ] ~~~~ A `Pandoc` document consists of a `Meta` block (containing metadata like title, authors, and date) and a list of `Block` elements. In this case, we have two `Block`s, a `Header` and a `Para`. Each has as its content a list of `Inline` elements. For more details on the pandoc AST, see the [haddock documentation for `Text.Pandoc.Definition`]. [haddock documentation for `Text.Pandoc.Definition`]: https://hackage.haskell.org/package/pandoc-types We can use Haskell to create a JSON filter that transforms this AST, replacing each `Header` block with level >= 2 with a `Para` with its contents wrapped inside an `Emph` inline: ~~~~ {.haskell} #!/usr/bin/env runhaskell -- behead.hs import Text.Pandoc.JSON main :: IO () main = toJSONFilter behead behead :: Block -> Block behead (Header n _ xs) | n >= 2 = Para [Emph xs] behead x = x ~~~~ The `toJSONFilter` function does two things. First, it lifts the `behead` function (which maps `Block -> Block`) onto a transformation of the entire `Pandoc` AST, walking the AST and transforming each block. Second, it wraps this `Pandoc -> Pandoc` transformation with the necessary JSON serialization and deserialization, producing an executable that consumes JSON from stdin and produces JSON to stdout. To use the filter, make it executable: chmod +x behead.hs and then pandoc -f SOURCEFORMAT -t TARGETFORMAT --filter ./behead.hs (It is also necessary that `pandoc-types` be installed in the local package repository. To do this using cabal-install, `cabal v2-update && cabal v2-install --lib pandoc-types --package-env .`.) Alternatively, we could compile the filter: ghc -package-env=default --make behead.hs pandoc -f SOURCEFORMAT -t TARGETFORMAT --filter ./behead Note that if the filter is placed in the system PATH, then the initial `./` is not needed. Note also that the command line can include multiple instances of `--filter`: the filters will be applied in sequence. # LaTeX for WordPress Another easy example. WordPress blogs require a special format for LaTeX math. Instead of `$e=mc^2$`, you need: `$LaTeX e=mc^2$`. How can we convert a markdown document accordingly? Again, it's difficult to do the job reliably with regexes. A `$` might be a regular currency indicator, or it might occur in a comment or code block or inline code span. We just want to find the `$`s that begin LaTeX math. If only we had a parser... We do. Pandoc already extracts LaTeX math, so: ~~~~ {.haskell} #!/usr/bin/env runhaskell -- wordpressify.hs import Text.Pandoc.JSON main = toJSONFilter wordpressify where wordpressify (Math x y) = Math x ("LaTeX " ++ y) wordpressify x = x ~~~~ Mission accomplished. (I've omitted type signatures here, just to show it can be done.) # But I don't want to learn Haskell! While it's easiest to write pandoc filters in Haskell, it is fairly easy to write them in python using the `pandocfilters` package. The package is in PyPI and can be installed using `pip install pandocfilters` or `easy_install pandocfilters`. Here's our "beheading" filter in python: ~~~ {.python} #!/usr/bin/env python """ Pandoc filter to convert all level 2+ headings to paragraphs with emphasized text. """ from pandocfilters import toJSONFilter, Emph, Para def behead(key, value, format, meta): if key == 'Header' and value[0] >= 2: return Para([Emph(value[2])]) if __name__ == "__main__": toJSONFilter(behead) ~~~ `toJSONFilter(behead)` walks the AST and applies the `behead` action to each element. If `behead` returns nothing, the node is unchanged; if it returns an object, the node is replaced; if it returns a list, the new list is spliced in. Note that, although these parameters are not used in this example, `format` provides access to the target format, and `meta` provides access to the document's metadata. There are many examples of python filters in [the pandocfilters repository](https://github.com/jgm/pandocfilters). For a more Pythonic alternative to pandocfilters, see the [panflute](https://pypi.org/project/panflute) library. Don't like Python? There are also ports of pandocfilters in - [PHP](https://github.com/vinai/pandocfilters-php), - [perl](https://metacpan.org/pod/Pandoc::Filter), - TypeScript/JavaScript via Node.js - [pandoc-filter](https://github.com/mvhenderson/pandoc-filter-node), - [node-pandoc-filter](https://github.com/mu-io/node-pandoc-filter), - [Groovy](https://github.com/dfrommi/groovy-pandoc), and - [Ruby](https://heerdebeer.org/Software/markdown/paru/). Starting with pandoc 2.0, pandoc includes built-in support for writing filters in lua. The lua interpreter is built in to pandoc, so a lua filter does not require any additional software to run. See the [documentation on lua filters](https://pandoc.org/lua-filters.html). # Include files So none of our transforms have involved IO. How about a script that reads a markdown document, finds all the inline code blocks with attribute `include`, and replaces their contents with the contents of the file given? ~~~~ {.haskell} #!/usr/bin/env runhaskell -- includes.hs import Text.Pandoc.JSON import qualified Data.Text.IO as TIO import qualified Data.Text as T doInclude :: Block -> IO Block doInclude cb@(CodeBlock (id, classes, namevals) contents) = case lookup (T.pack "include") namevals of Just f -> CodeBlock (id, classes, namevals) <$> TIO.readFile (T.unpack f) Nothing -> return cb doInclude x = return x main :: IO () main = toJSONFilter doInclude ~~~~ Try this on the following: Here's the pandoc README: ~~~~ {include="README"} this will be replaced by contents of README ~~~~ # Removing links What if we want to remove every link from a document, retaining the link's text? ~~~~ {.haskell} #!/usr/bin/env runhaskell -- delink.hs import Text.Pandoc.JSON main = toJSONFilter delink delink :: Inline -> [Inline] delink (Link _ txt _) = txt delink x = [x] ~~~~ Note that `delink` can't be a function of type `Inline -> Inline`, because the thing we want to replace the link with is not a single `Inline` element, but a list of them. So we make `delink` a function from an `Inline` element to a list of `Inline` elements. `toJSONFilter` can still lift this function to a transformation of type `Pandoc -> Pandoc`. # A filter for ruby text Finally, here's a nice real-world example, developed on the [pandoc-discuss](https://groups.google.com/group/pandoc-discuss/browse_thread/thread/7baea325565878c8) list. Qubyte wrote: > I'm interested in using pandoc to turn my markdown notes on Japanese > into nicely set HTML and (Xe)LaTeX. With HTML5, ruby (typically used to > phonetically read chinese characters by placing text above or to the > side) is standard, and support from browsers is emerging (Webkit based > browsers appear to fully support it). For those browsers that don't > support it yet (notably Firefox) the feature falls back in a nice way > by placing the phonetic reading inside brackets to the side of each > Chinese character, which is suitable for other output formats too. As > for (Xe)LaTeX, ruby is not an issue. > > At the moment, I use inline HTML to achieve the result when the > conversion is to HTML, but it's ugly and uses a lot of keystrokes, for > example > > ~~~ {.xml} > はん > ~~~ > > sets ご飯 "gohan" with "han" spelt phonetically above the second > character, or to the right of it in brackets if the browser does not > support ruby. I'd like to have something more like > > r[はん](飯) > > or any keystroke saving convention would be welcome. We came up with the following script, which uses the convention that a markdown link with a URL beginning with a hyphen is interpreted as ruby: [はん](-飯) ~~~ {.haskell} {-# LANGUAGE OverloadedStrings #-} -- handleruby.hs import Text.Pandoc.JSON import System.Environment (getArgs) import qualified Data.Text as T handleRuby :: Maybe Format -> Inline -> Inline handleRuby (Just format) x@(Link attr [Str ruby] (src,_)) = case T.uncons src of Just ('-',kanji) | format == Format "html" -> RawInline format $ "" <> kanji <> "(" <> ruby <> ")" | format == Format "latex" -> RawInline format $ "\\ruby{" <> kanji <> "}{" <> ruby <> "}" | otherwise -> Str ruby _ -> x handleRuby _ x = x main :: IO () main = toJSONFilter handleRuby ~~~ Note that, when a script is called using `--filter`, pandoc passes it the target format as the first argument. When a function's first argument is of type `Maybe Format`, `toJSONFilter` will automatically assign it `Just` the target format or `Nothing`. We compile our script: # first, make sure pandoc-types is installed: cabal install --lib pandoc-types --package-env . ghc --make handleRuby Then run it: % pandoc -F ./handleRuby -t html [はん](-飯) ^D

(はん)

% pandoc -F ./handleRuby -t latex [はん](-飯) ^D \ruby{飯}{はん} Note: to use this to generate PDFs via LaTeX, you'll need to use `--pdf-engine=xelatex`, specify a `mainfont` that has the Japanese characters (e.g. "[Noto Sans CJK JP](https://fonts.google.com/noto/specimen/Noto+Sans+JP)"), and add `\usepackage{ruby}` to your template or header-includes. # Exercises 1. Put all the regular text in a markdown document in ALL CAPS (without touching text in URLs or link titles). 2. Remove all horizontal rules from a document. 3. Renumber all enumerated lists with roman numerals. 4. Replace each delimited code block with class `dot` with an image generated by running `dot -Tpng` (from graphviz) on the contents of the code block. 5. Find all code blocks with class `python` and run them using the python interpreter, printing the results to the console. # Technical details of JSON filters A JSON filter is any program which can consume and produce a valid pandoc JSON document representation. This section describes the technical details surrounding the invocation of filters. ## Arguments The program will always be called with the target format as the only argument. A pandoc invocation like pandoc --filter demo --to=html will cause pandoc to call the program `demo` with argument `html`. ## Environment variables Pandoc sets additional environment variables before calling a filter. `PANDOC_VERSION` : The version of the pandoc binary used to process the document. Example: `2.11.1`. `PANDOC_READER_OPTIONS` : JSON object representation of the options passed to the input parser. Object fields: `abbreviations` : set of known abbreviations (array of strings). `columns` : number of columns in terminal; an integer. `default-image-extension` : default extension for images; a string. `extensions` : integer representation of the syntax extensions bit field. `indented-code-classes` : default classes for indented code blocks; array of strings. `standalone` : whether the input was a standalone document with header; either `true` or `false`. `strip-comments` : HTML comments are stripped instead of parsed as raw HTML; either `true` or `false`. `tab-stop` : width (i.e. equivalent number of spaces) of tab stops; integer. `track-changes` : track changes setting for docx; one of `"accept-changes"`, `"reject-changes"`, and `"all-changes"`. ## Supported interpreters Files passed to the `--filter`/`-F` parameter are expected to be executable. However, if the executable bit is not set, then pandoc tries to guess a suitable interpreter from the file extension. file extension interpreter ---------------- -------------- .py `python` .hs `runhaskell` .pl `perl` .rb `ruby` .php `php` .js `node` .r `Rscript` ================================================ FILE: doc/getting-started.md ================================================ --- title: Getting started with pandoc author: John MacFarlane --- This document is for people who are unfamiliar with command line tools. Command-line experts can go straight to the [User's Guide](https://pandoc.org/MANUAL.html) or the pandoc man page. # Step 1: Install pandoc First, install pandoc, following the [instructions for your platform](https://pandoc.org/installing.html). # Step 2: Open a terminal Pandoc is a command-line tool. There is no graphic user interface. So, to use it, you'll need to open a terminal window: - On OS X, the Terminal application can be found in `/Applications/Utilities`. Open a Finder window and go to `Applications`, then `Utilities`. Then double click on `Terminal`. (Or, click the spotlight icon in the upper right hand corner of your screen and type `Terminal` -- you should see `Terminal` under `Applications`.) - On Windows, you can use either the classic command prompt or the more modern PowerShell terminal. If you use Windows in desktop mode, run the `cmd` or `powershell` command from the Start menu. If you use the Windows 8 start screen instead, simply type `cmd` or `powershell`, and then run either the "Command Prompt" or "Windows Powershell" application. If you are using `cmd`, type `chcp 65001` before using pandoc, to set the encoding to UTF-8. - On Linux, there are many possible configurations, depending on what desktop environment you're using: * In Unity, use the search function on the `Dash`, and search for `Terminal`. Or, use the keyboard shortcut `Ctrl-Alt-T`. * In Gnome, go to `Applications`, then `Accessories`, and select `Terminal`, or use `Ctrl-Alt-T`. * In XFCE, go to `Applications`, then `System`, then `Terminal`, or use `Super-T`. * In KDE, go to `KMenu`, then `System`, then `Terminal Program (Konsole)`. You should now see a rectangle with a "prompt" (possibly just a symbol like `%`, but probably including more information, such as your username and directory), and a blinking cursor. Let's verify that pandoc is installed. Type pandoc --version and hit enter. You should see a message telling you which version of pandoc is installed, and giving you some additional information. # Step 3: Changing directories First, let's see where we are. Type pwd on Linux or OSX, or echo %cd% on Windows, and hit enter. Your terminal should print your current working directory. (Guess what `pwd` stands for?) This should be your home directory. Let's navigate now to our `Documents` directory: type cd Documents and hit enter. Now type pwd (or `echo %cd%` on Windows) again. You should be in the `Documents` subdirectory of your home directory. To go back to your home directory, you could type cd .. The `..` means "one level up." Go back to your `Documents` directory if you're not there already. Let's try creating a subdirectory called `pandoc-test`: mkdir pandoc-test Now change to the `pandoc-test` directory: cd pandoc-test If the prompt doesn't tell you what directory you're in, you can confirm that you're there by doing pwd (or `echo %cd%`) again. OK, that's all you need to know for now about using the terminal. But here's a secret that will save you a lot of typing. You can always type the up-arrow key to go back through your history of commands. So if you want to use a command you typed earlier, you don't need to type it again: just use up-arrow until it comes up. Try this. (You can use down-arrow as well, to go the other direction.) Once you have the command, you can also use the left and right arrows and the backspace/delete key to edit it. Most terminals also support tab completion of directories and filenames. To try this, let's first go back up to our `Documents` directory: cd .. Now, type cd pandoc- and hit the tab key instead of enter. Your terminal should fill in the rest (`test`), and then you can hit enter. To review: - `pwd` (or `echo %cd%` on Windows) to see what the current working directory is. - `cd foo` to change to the `foo` subdirectory of your working directory. - `cd ..` to move up to the parent of the working directory. - `mkdir foo` to create a subdirectory called `foo` in the working directory. - up-arrow to go back through your command history. - tab to complete directories and file names. # Step 4: Using pandoc as a filter Type pandoc and hit enter. You should see the cursor just sitting there, waiting for you to type something. Type this: Hello *pandoc*! - one - two When you're finished (the cursor should be at the beginning of the line), type `Ctrl-D` on OS X or Linux, or `Ctrl-Z` followed by `Enter` on Windows. You should now see your text converted to HTML!

Hello pandoc!

  • one
  • two
What just happened? When pandoc is invoked without specifying any input files, it operates as a "filter," taking input from the terminal and sending its output back to the terminal. You can use this feature to play around with pandoc. By default, input is interpreted as pandoc markdown, and output is HTML. But we can change that. Let's try converting *from* HTML *to* markdown: pandoc -f html -t markdown Now type:

Hello pandoc!

and hit `Ctrl-D` (or `Ctrl-Z` followed by `Enter` on Windows). You should see: Hello *pandoc*! Now try converting something from markdown to LaTeX. What command do you think you should use? # Step 5: Text editor basics You'll probably want to use pandoc to convert a file, not to read text from the terminal. That's easy, but first we need to create a text file in our `pandoc-test` subdirectory. **Important:** To create a text file, you'll need to use a text editor, *not* a word processor like Microsoft Word. On Windows, you can use Notepad (in `Accessories`). On OS X, you can use `TextEdit` (in `Applications`). On Linux, different platforms come with different text editors: Gnome has `GEdit`, and KDE has `Kate`. Start up your text editor. Type the following: --- title: Test ... # Test! This is a test of *pandoc*. - list one - list two Now save your file as `test1.md` in the directory `Documents/pandoc-test`. Note: If you use plain text a lot, you'll want a better editor than `Notepad` or `TextEdit`. You might want to look at [Visual Studio Code](https://code.visualstudio.com/) or [Sublime Text](https://www.sublimetext.com/) or (if you're willing to put in some time learning an unfamiliar interface) [Vim](https://www.vim.org) or [Emacs](https://www.gnu.org/software/emacs). # Step 6: Converting a file Go back to your terminal. We should still be in the `Documents/pandoc-test` directory. Verify that with `pwd`. Now type ls (or `dir` if you're on Windows). This will list the files in the current directory. You should see the file you created, `test1.md`. To convert it to HTML, use this command: pandoc test1.md -f markdown -t html -s -o test1.html The filename `test1.md` tells pandoc which file to convert. The `-s` option says to create a "standalone" file, with a header and footer, not just a fragment. And the `-o test1.html` says to put the output in the file `test1.html`. Note that we could have omitted `-f markdown` and `-t html`, since the default is to convert from markdown to HTML, but it doesn't hurt to include them. Check that the file was created by typing `ls` again. You should see `test1.html`. Now open this in a browser. On OS X, you can type open test1.html On Windows, type .\test1.html You should see a browser window with your document. To create a LaTeX document, you just need to change the command slightly: pandoc test1.md -f markdown -t latex -s -o test1.tex Try opening `test1.tex` in your text editor. Pandoc can often figure out the input and output formats from the filename extensions. So, you could have just used: pandoc test1.md -s -o test1.tex Pandoc knows you're trying to create a LaTeX document, because of the `.tex` extension. Now try creating a Word document (with extension `docx`). If you want to create a PDF, you'll need to have LaTeX installed. (See [MacTeX](https://tug.org/mactex/) on OS X, [MiKTeX](https://miktex.org) on Windows, or install the texlive package on Linux.) Then do pandoc test1.md -s -o test1.pdf # Step 7: Command-line options You now know the basics. Pandoc has a lot of options. At this point you can start to learn more about them by reading the [User's Guide](https://pandoc.org/MANUAL.html). Here's an example. The `--mathml` option causes pandoc to convert TeX math into MathML. Type pandoc --mathml then enter this text, followed by `Ctrl-D` (`Ctrl-Z` followed by `Enter` on Windows): $x = y^2$ Now try the same thing without `--mathml`. See the difference in output? If you forget an option, or forget which formats are supported, you can always do pandoc --help to get a list of all the supported options. On OS X or Linux systems, you can also do man pandoc to get the pandoc manual page. All of this information is also in the User's Guide. If you get stuck, you can always ask questions on the [discussion forum](https://github.com/jgm/pandoc/discussions). But be sure to check the [FAQs](https://pandoc.org/faqs.html) first, and search through the forum to see if your question has been answered before. ================================================ FILE: doc/jats.md ================================================ --- title: JATS author: Albert Krewinkel --- This document describes pandoc's handling of [JATS]. Metadata Values =============== `abstract` : Article summary. Added via the document's front matter via the [``][elem:abstract] element. `author` : list of article contributors. Each author should have a surname and a given name listed in the entry; if the author has no `surname` value, then the item will be used as the contributors [`string-name`][elem:string-name]. `orcid` : the contributor's ORCID identifier. `surname` : surname of the contributor. Usually the family name in western names. See [``][elem:surname]. `given-names` : personal names of the contributor; this includes middle names (if any) in western-style names. See [``][elem:given-names]. `name` : full name of the author; included only as a fallback if `author.surname` is not available. Tagged with [``][elem:string-name]. `email` : the contributor's email address. Used as the contents of the [``][elem:given-names] element. `affiliation` : either full affiliation entries as described in field `affiliation`, or a list of affiliation identifiers. The identifiers link to the organizations with which an author is affiliated. Each identifier in this list must also occur as the `id` of an affiliation listed in the top-level `affiliation` list. If the top-level `affiliation` field is set, then this entry assumed to be a list of identifiers, and a list of full entries if that field is unset. Full entries must be given if the articleauthoring tag set it used, as affiliation links are not allowed in that schema. `roles` : a list of dictionaries describing the author's role(s). Each role is added as an [``] element to the author's [``] element. The following examples illustrate: An ad-hoc role: ```yaml roles: - name: Dolphin Catcher ``` A role specified with CRediT. ```yaml roles: - credit: writing-review-editing ``` The `credit-name` is automatically looked up from the CRediT taxonomy, but you can also specify it yourself: ```yaml roles: - credit: writing-review-editing credit-name: Writing – review & editing ``` A role specified with CRediT, including an optional degree of contribution. Note that specifying the degree only is allowed when using CRediT roles and not ad-hoc roles. ```yaml roles: - credit: writing-review-editing degree: Lead ``` A role specified with CRediT with a label override, useful for internationalization: ```yaml roles: - credit: writing-review-editing name: Escrita – revisão e edição ``` The value for `credit` and `credit-name` must be from one of the 14 terms from the Contribution Role Taxonomy (CRediT): | `credit` | `credit-name` | |--------------------------|----------------------------| | `conceptualization` | Conceptualization | | `data-curation` | Data curation | | `formal-analysis` | Formal analysis | | `funding-acquisition` | Funding acquisition | | `investigation` | Investigation | | `methodology` | Methodology | | `project-administration` | Project administration | | `resources` | Resources | | `software` | Software | | `supervision` | Supervision | | `validation` | Validation | | `visualization` | Visualization | | `writing-original-draft` | Writing – original draft | | `writing-review-editing` | Writing – review & editing | JATS suggests in [``] to use one of the following three values when specifying the degree of contribution: 1. `Lead` 2. `Equal` 3. `Supporting` `equal-contrib` : boolean attribute used to mark authors who contributed equally to the work. The [`equal-contrib`][attr:equal-contrib] attribute, set to `yes`, is added to the author's [``] element if this is set to a truthy value. `cor-id` : identifier linking to the contributor's correspondence information. The info itself must be stored as an item in `article.author-notes.corresp`. If the `cor-id` value is set, then an [``][elem:xref] link of [`ref-type`][attr:ref-type] `corresp` is added. The [`rid`][attr:rid] attribute is set to `cor-`, where `` is the stringified value of this attribute. Furthermore, the [`corresp`][attr:corresp] attribute on the author's [``] element is set to `yes` if this attribute is set to a truthy. `affiliation` : the list of organizations with which contributors are affiliated. Each institution is added as an [``] element to the author's contrib-group. The fields are given in the order in which they are included in the output. `id` : internal identifier; used as the [``] element's `id` value, prefixed with `aff-`. `group` : name of the research group or other low-level organizational structure; used as value of an [``] element with [`content-type`][attr:content-type] set to `group`. `department` : name of the department or other mid-level organizational structure; used as value of an [``] element with [`content-type`][attr:content-type] set to `dept`. `organization` : name of the company, university, or other top-level organizational structure; used as value of an [``] element. The institution element is wrapped in an [``] element; any identifiers, like `ringgold` or `ror`, are added to the wrapper and must hence belong to this organization (not the department or group). `isni` : International Standard Name Identifier of the organization. Added via an [``] element with [`institution-id-type`](attr:institution-id-type) set to `ISNI`. `ringgold` : [Ringgold] identifier of the organization. Added via an [``] element with [`institution-id-type`](attr:institution-id-type) set to `Ringgold`. `ror` : Research Organization Registry identifier of the organization. Added via an [``] element with [`institution-id-type`](attr:institution-id-type) set to `ROR`. `pid` : Array of persistent identifiers which are added as [``] elements. Each item must contain a map with keys `type`, used as [`institution-id-type`](attr:institution-id-type), and `id`, used as element content. `street-address` : The organization's street address; each list item is wrapped in an [``] element, separated by a comma and space (`, `). `city` : City in which the organization is located; used only if `street-address` is not given, in which case the value is wrapped in a [``] element. `country` : Country in which the organization is located; used as the value of a [``] element. `country-code` : Two letter ISO-3166-1 country identifier; used as the [`country`][attr:country] attribute in element [``] (if the latter is present). `copyright` : Copyright and licensing information. This information is rendered via the [``][elem:permissions] element. It is recommended to use the `license` field (described below) for licensing information. If licensing information is included below `copyright`, then the variables `type`, `link`, and `text` should always be used together. `statement` : copyright notice or statement; used as content of the [``][elem:copyright-statement]. Use a list for multiple statements. `year` : the year of copyright; used as content of the [``][elem:copyright-year]. Use a list to for multiple copyright years. The JATS documentation states that this field need not to be used if the year is included in the copyright statement. `holder` : the copyright holder; included via the [``][elem:copyright-holder] element. Use a list for multiple copyright holders. `text` : inline text setting the license under which the text is published; included via the [``][elem:copyright-holder] element. `type` : type of the license; used as value of the [`license-type`][attr:license-type] attribute. `link` : external link describing the license; used as value of a `xlink:href` attribute in the `` element. `date` : publication date. This value should usually be a string representation of a date. Pandoc will parse and deconstruct the date into the components given below. It is also possible to pass these components directly. The publication date is recorded in the document via the [``] element and its sub-elements. The [`publication-format`][attr:publication-format] attribute is always set to `electronic`. `iso-8601` : ISO-8601 representation of the publication date. Used as the value of the [``] element's [`iso-8601-date`][attr:iso-8601-date] attribute. This value is set automatically if pandoc can parse the `date` value as a date. `day`, `month`, `year` : Day, month, and year of the publication date. Only the publication year is required. The values are used as the contents of the elements with the respective names. The values are set automatically if pandoc can parse the `date` value as a date. `type` : The type of event marked by this date. The value is set as the [`date-type`][attr:date-type] attribute on the [``] element and defaults to "pub" if not specified. `article` : information concerning the article that identifies or describes it. The key-value pairs within this map are typically used within the [``][elem:article-meta] element. `publisher-id` : external article identifier assigned by the publisher. Used as the content of the [``][elem:article-id] element with attribute [`pub-id-type`][attr:pub-id-type] set to `publisher-id`. `doi` : Digital Object Identifier (DOI) assigned to the article. Used as the content of the [``][elem:article-id] element with attribute [`pub-id-type`][attr:pub-id-type] set to `doi`. `pmid` : PubMed Identifier (PubMed ID) assigned to the article. Used as the content of the [``][elem:article-id] element with attribute [`pub-id-type`][attr:pub-id-type] set to `pmid`. `pmcid` : PubMed Central Identifier assigned to the article. Used as the content of the [``][elem:article-id] element with attribute [`pub-id-type`][attr:pub-id-type] set to `pmcid`. `art-access-id` : generic article accession identifier. Used as the content of the [``][elem:article-id] element with attribute [`pub-id-type`][attr:pub-id-type] set to `art-access-id`. `heading` : name of a subject or topic describing the article. Used as the content of the [``][elem:subject] element, nested in a [``][elem:subj-group] element which has `heading` as its [`subj-group-type`][attr:subj-group-type] attribute. `categories` : list a subject or topic describing the article. Items are each used as the content a the [``][elem:subject] element, grouped in a single [``][elem:subj-group] element with its [`subj-group-type`][attr:subj-group-type] attribute set to `categories`. `author-notes` : Additional information about authors, like conflict of interest statements and corresponding author contact info. Wrapped in an [``][elem:author-notes] element. `conflict` : Conflict of interest statement. Rendered as a footnote ([``][elem:fn]) of [`fn-type`](attr:fn-type) `conflict`. `con` : Contributed-by information. Rendered as a footnote ([``][elem:fn]) of [`fn-type`](attr:fn-type) `con`. `corresp` : Correspondence information. This must be a list of contributor correspondence items, where each item must have the properties `id` and `email`. The info is then rendered via a [``][elem:corresp] element. `funding-statement` : Prose describing the funding. Added to the article's frontmatter via the [`funding-statement`][elem:funding-statement] element. `journal` : information on the journal in which the article is published. This must be a map; the following key/value pairs are recognized. `publisher-id` : journal identifier assigned by the publisher. Used as content of element [``][elem:journal-id] with attribute [`journal-id-type`][attr:journal-id-type] set to `publisher-id`. `nlm-ta` : journal identifier assigned by PubMed. Used as content of element [``][elem:journal-id] with attribute [`journal-id-type`][attr:journal-id-type] set to `nlm-ta`. `pmc` : journal identifier assigned by PubMed Central. Used as content of element [``][elem:journal-id] with attribute [`journal-id-type`][attr:journal-id-type] set to `pmc`. `title` : full title of the journal in which the article is published. Used as content of the [``][elem:journal-title] element. `abbrev-title` : short form of the journal title. Used as content of the [``][elem:abbrev-journal-title] element. `pissn` : ISSN identifier of the publication's print version. Used as content of the [``][elem:issn] element with the [`publication-format`][attr:publication-format] attribute set to `print`. `eissn` : ISSN identifier of the publication's electronic version. Used as content of the [``][elem:issn] element with the [`publication-format`][attr:publication-format] attribute set to `electronic`. `publisher-name` : name of the publishing entity (person, company, or other). Used as the content of the [``][elem:publisher-name] element. `publisher-loc` : place of publication. Used as the content of the [``][elem:publisher-loc] element. `license` : Article licensing information. Each item of this field is rendered as a `` element within the [``][elem:permissions] element. Item content should be either a single paragraph, or a map with the fields listed below. `text` : inline text describing a license under which the text is published; included via the [``][elem:copyright-holder] element. `type` : type of the license; used as value of the [`license-type`][attr:license-type] attribute. `link` : external link describing the license; used as value of a `xlink:href` attribute in the `` element. `notes` : Additional notes concerning the whole article. Added to the article's frontmatter via the [``][elem:notes] element. `subtitle` : Subordinate part of the document title. Added to the document's front matter as a [``][elem:article-title] element. `tags` : list of keywords. Items are used as contents of the [``][elem:kwd] element; the elements are grouped in a [``][elem:kwd-group] with the [`kwd-group-type`][attr:kwd-group-type] value `author`. `title` : The article title. Added to the document's front matter via the [``][elem:article-title] element. `supplementary-material` : Supplementary metadata. Added to the document's front matter via the [``][elem:supplementary-material] element. Only available with `jats_articlepublishing`. `floats-group` : List of floating objects, such as figures, tables, text boxes, etc., that are not part of the main text. The value is rendered by nesting it below a [``][elem:floats-group] element. Only available with `jats_publishing` and `jats_archiving`. Required Metadata ----------------- Pandoc will try to generate a valid JATS document even when information is missing, filling in placeholders or using empty values. This circumvents the intend to ensure a minimum set of information being present in documents of a certain tag set. It is hence recommended to always provide the information listed below. ### Publishing Tag Set Required metadata values: - One or more of `journal.publisher-id`, `journal.nlm-ta`, `journal.pmc`. - One or more of `journal.pissn`, `journal.eissn`. [JATS]: https://jats.nlm.nih.gov/ [Ringgold]: https://ringgold.com/ [attr:content-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/content-type.html [attr:corresp]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/corresp.html [attr:date-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/date-type.html [attr:equal-contrib]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/equal-contrib.html [attr:fn-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/fn-type.html [attr:institution-id-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/institution-id-type.html [attr:iso-8601-date]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/iso-8601-date.html [attr:journal-id-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/journal-id-type.html [attr:kwd-group-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/kwd-group-type.html [attr:license-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/license-type.html [attr:pub-id-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/pub-id-type.html [attr:publication-format]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/publication-format.html [attr:ref-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/ref-type.html [attr:rid]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/rid.html [attr:subj-group-type]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/subj-group-type.html [elem:abbrev-journal-title]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/abbrev-journal-title.html [elem:abstract]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/abstract.html [elem:article-id]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/article-id.html [elem:article-meta]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/article-meta.html [elem:article-title]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/article-title.html [elem:supplementary-material]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/supplementary-material.html [elem:copyright-holder]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/copyright-holder.html [elem:copyright-statement]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/copyright-statement.html [elem:copyright-year]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/copyright-year.html [elem:corresp]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/corresp.html [elem:email]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/email.html [elem:floats-group]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/floats-group.html [elem:fn]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/fn.html [elem:funding-statement]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/funding-statement.html [elem:given-names]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/given-names.html [elem:issn]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/issn.html [elem:journal-id]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/journal-id.html [elem:journal-title]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/journal-title.html [elem:kwd-group]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/kwd-group.html [elem:kwd]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/kwd.html [elem:license-p]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/license-p.html [elem:license]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/license.html [elem:notes]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/notes.html [elem:permissions]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/permissions.html [elem:publisher-loc]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/publisher-loc.html [elem:publisher-name]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/publisher-name.html [elem:string-name]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/string-name.html [elem:subj-group]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/subj-group.html [elem:subject]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/subject.html [elem:subtitle]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/subtitle.html [elem:surname]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/surname.html [elem:xref]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/xref.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/addr-line.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/aff.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/city.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/contrib.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/country.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-id.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution-wrap.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/institution.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/pub-date.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/role.html [``]: https://jats.nlm.nih.gov/publishing/tag-library/1.2/attribute/degree-contribution.html ================================================ FILE: doc/libraries.md ================================================ --- title: The pandoc family of libraries author: John MacFarlane --- The following Haskell libraries have been developed to support pandoc: [citeproc] : Citation processing using CSL stylesheets. [commonmark], [commonmark-extensions], and [commonmark-pandoc] : Efficient, standards-compliant parser for commonmark and extensions. [djot] : Parser and renderer for djot light markup syntax. [doclayout] : Combinators for laying out a textual document, with support for line wrapping, tabular layout, and more. [doctemplates] : Supports pandoc's templates. [emojis] : Conversion between emoji characters and aliases. [gridtables] : Support for parsing grid style textual tables. [hslua-aeson] : Converter from aeson data types to Lua objects. [hslua-cli] : Command-line interface mimicking the default `lua` executable. [hslua-module-doclayout] : Lua bindings to the doclayout library mentioned above. [hslua-module-path], [-system], [-text], and [-version] : Lua modules that expose functionality of basic Haskell libraries to Lua. [hslua-objectorientation], [hslua-packaging] : Bindings, wrappers, and helper functions to access Haskell data types from Lua via an object-oriented interface. [ipynb] : Representation of Jupyter notebooks and conversion to and from JSON. [jira-wiki-markup] : Support for parsing Jira wiki syntax. [rfc5051] : Simple unicode collation (used for citation sorting). [skylighting-core] and [skylighting] : Syntax highlighting engine supporting over 140 languages. [texmath] : Conversion of math between tex, Word equation, MathML, and GNU eqn. [typst] : Parsing and evaluating typst syntax. [typst-symbol] : Symbol and emoji lookup for typst language. [unicode-collation] : Proper Unicode collation (sorting). [zip-archive] : A pure zip file creator and extractor, used by pandoc for docx, ODT, and EPUB. [citeproc]: https://hackage.haskell.org/package/citeproc [commonmark-extensions]: https://hackage.haskell.org/package/commonmark-extensions [commonmark-pandoc]: https://hackage.haskell.org/package/commonmark-pandoc [commonmark]: https://hackage.haskell.org/package/commonmark [djot]: https://hackage.haskell.org/package/djot [doclayout]: https://hackage.haskell.org/package/doclayout [doctemplates]: https://hackage.haskell.org/package/doctemplates [emojis]: https://hackage.haskell.org/package/emojis [gridtables]: https://hackage.haskell.org/package/gridtables [hslua-aeson]: https://hackage.haskell.org/package/hslua-aeson [hslua-cli]: https://hackage.haskell.org/package/hslua-cli [hslua-module-doclayout]: https://hackage.haskell.org/package/hslua-module-doclayout [hslua-module-path]: https://hackage.haskell.org/package/hslua-module-path [-system]: https://hackage.haskell.org/package/hslua-module-system [-text]: https://hackage.haskell.org/package/hslua-module-text [-version]: https://hackage.haskell.org/package/hslua-module-version [hslua-objectorientation]: https://hackage.haskell.org/package/hslua-objectorientation [hslua-packaging]: https://hackage.haskell.org/package/hslua-packaging [ipynb]: https://hackage.haskell.org/package/ipynb [jira-wiki-markup]: https://hackage.haskell.org/package/jira-wiki-markup [rfc5051]: https://hackage.haskell.org/package/rfc5051 [skylighting-core]: https://hackage.haskell.org/package/skylighting-core [skylighting]: https://hackage.haskell.org/package/skylighting [texmath]: https://hackage.haskell.org/package/texmath [typst-symbol]: https://hackage.haskell.org/package/typst-symbol [typst]: https://hackage.haskell.org/package/typst [unicode-collation]: https://hackage.haskell.org/package/unicode-collation [zip-archive]: https://hackage.haskell.org/package/zip-archive ================================================ FILE: doc/lua-filters.md ================================================ --- author: - Albert Krewinkel - John MacFarlane date: 'January 10, 2020' title: Pandoc Lua Filters --- # Introduction Pandoc has long supported filters, which allow the pandoc abstract syntax tree (AST) to be manipulated between the parsing and the writing phase. [Traditional pandoc filters](https://pandoc.org/filters.html) accept a JSON representation of the pandoc AST and produce an altered JSON representation of the AST. They may be written in any programming language, and invoked from pandoc using the `--filter` option. Although traditional filters are very flexible, they have a couple of disadvantages. First, there is some overhead in writing JSON to stdout and reading it from stdin (twice, once on each side of the filter). Second, whether a filter will work will depend on details of the user's environment. A filter may require an interpreter for a certain programming language to be available, as well as a library for manipulating the pandoc AST in JSON form. One cannot simply provide a filter that can be used by anyone who has a certain version of the pandoc executable. Starting with version 2.0, pandoc makes it possible to write filters in Lua without any external dependencies at all. A Lua interpreter (version 5.4) and a Lua library for creating pandoc filters is built into the pandoc executable. Pandoc data types are marshaled to Lua directly, avoiding the overhead of writing JSON to stdout and reading it from stdin. Here is an example of a Lua filter that converts strong emphasis to small caps: ``` lua return { Strong = function (elem) return pandoc.SmallCaps(elem.content) end, } ``` or equivalently, ``` lua function Strong(elem) return pandoc.SmallCaps(elem.content) end ``` This says: walk the AST, and when you find a Strong element, replace it with a SmallCaps element with the same content. To run it, save it in a file, say `smallcaps.lua`, and invoke pandoc with `--lua-filter=smallcaps.lua`. Here's a quick performance comparison, converting the pandoc manual (MANUAL.txt) to HTML, with versions of the same JSON filter written in compiled Haskell (`smallcaps`) and interpreted Python (`smallcaps.py`): Command Time --------------------------------------- ------- `pandoc` 1.01s `pandoc --filter ./smallcaps` 1.36s `pandoc --filter ./smallcaps.py` 1.40s `pandoc --lua-filter ./smallcaps.lua` 1.03s As you can see, the Lua filter avoids the substantial overhead associated with marshaling to and from JSON over a pipe. # Lua filter structure Lua filters are tables with element names as keys and values consisting of functions acting on those elements. Filters are expected to be put into separate files and are passed via the `--lua-filter` command-line argument. For example, if a filter is defined in a file `current-date.lua`, then it would be applied like this: pandoc --lua-filter=current-date.lua -f markdown MANUAL.txt The `--lua-filter` option may be supplied multiple times. Pandoc applies all filters (including JSON filters specified via `--filter` and Lua filters specified via `--lua-filter`) in the order they appear on the command line. Pandoc expects each Lua file to return a filter. If there is no value returned by the filter script, then pandoc will try to generate a single filter by collecting all top-level functions whose names correspond to those of pandoc elements (e.g., `Str`, `Para`, `Meta`, or `Pandoc`). (That is why the two examples above are equivalent.) It is currently also possible to return a list of filters from a Lua file which are called sequentially. Before the [walk](#type-pandoc:walk) method was made available, this was the only way to run multiple filters from one Lua file. However, returning a list of filters is now discouraged in favor of using the [walk](#type-pandoc:walk) method, and this functionality may be removed at some point. For each filter, the document is traversed and each element subjected to the filter. Elements for which the filter contains an entry (i.e. a function of the same name) are passed to Lua element filtering function. In other words, filter entries will be called for each corresponding element in the document, getting the respective element as input. The return value of a filter function must be one of the following: - nil: this means that the object should remain unchanged. - a pandoc object: this must be of the same type as the input and will replace the original object. - a list of pandoc objects: these will replace the original object; the list is merged with the neighbors of the original objects (spliced into the list the original object belongs to); returning an empty list deletes the object. The function's output must result in an element of the same type as the input. This means a filter function acting on an inline element must return either nil, an inline, or a list of inlines, and a function filtering a block element must return one of nil, a block, or a list of block elements. Pandoc will throw an error if this condition is violated. If there is no function matching the element's node type, then the filtering system will look for a more general fallback function. Two fallback functions are supported, `Inline` and `Block`. Each matches elements of the respective type. Elements without matching functions are left untouched. See [module documentation](#module-pandoc) for a list of pandoc elements. ## Filters on element sequences For some filtering tasks, it is necessary to know the order in which elements occur in the document. It is not enough then to inspect a single element at a time. There are two special function names, which can be used to define filters on lists of blocks or lists of inlines. [`Inlines (inlines)`]{#inlines-filter} : If present in a filter, this function will be called on all lists of inline elements, like the content of a [Para] (paragraph) block, or the description of an [Image]. The `inlines` argument passed to the function will be a [List] of [Inline] elements for each call. [`Blocks (blocks)`]{#blocks-filter} : If present in a filter, this function will be called on all lists of block elements, like the content of a [MetaBlocks] meta element block, on each item of a list, and the main content of the [Pandoc] document. The `blocks` argument passed to the function will be a [List] of [Block] elements for each call. These filter functions are special in that the result must either be nil, in which case the list is left unchanged, or must be a list of the correct type, i.e., the same type as the input argument. Single elements are **not** allowed as return values, as a single element in this context usually hints at a bug. See ["Remove spaces before normal citations"][Inlines filter example] for an example. This functionality has been added in pandoc 2.9.2. [Inlines filter example]: #remove-spaces-before-citations ## Traversal order The traversal order of filters can be selected by setting the key `traverse` to either `'topdown'` or `'typewise'`; the default is `'typewise'`. Example: ``` lua local filter = { traverse = 'topdown', -- ... filter functions ... } return filter ``` Support for this was added in pandoc 2.17; previous versions ignore the `traverse` setting. ### Typewise traversal Element filter functions within a filter set are called in a fixed order, skipping any which are not present: 1. functions for [*Inline* elements](#type-inline), 2. the [`Inlines`](#inlines-filter) filter function, 2. functions for [*Block* elements](#type-block) , 2. the [`Blocks`](#inlines-filter) filter function, 3. the [`Meta`](#type-meta) filter function, and last 4. the [`Pandoc`](#type-pandoc) filter function. It is still possible to force a different order by manually running the filters using the [walk](#type-pandoc:walk) method. For example, if the filter for *Meta* is to be run before that for *Str*, one can write ``` lua function Pandoc(doc) doc = doc:walk { Meta = Meta } -- (1) return doc:walk { Str = Str } -- (2) end ``` ### Topdown traversal It is sometimes more natural to traverse the document tree depth-first from the root towards the leaves, and all in a single run. For example, a block list `[Plain [Str "a"], Para [Str "b"]]`{.haskell} will try the following filter functions, in order: `Blocks`, `Plain`, `Inlines`, `Str`, `Para`, `Inlines`, `Str`. Topdown traversals can be cut short by returning `false` as a second value from the filter function. No child-element of the returned element is processed in that case. For example, to exclude the contents of a footnote from being processed, one might write ``` lua traverse = 'topdown' function Note (n) return n, false end ``` ## Global variables Pandoc passes additional data to Lua filters by setting global variables. `FORMAT` : The global `FORMAT` is set to the format of the pandoc writer being used (`html5`, `latex`, etc.), so the behavior of a filter can be made conditional on the eventual output format. `PANDOC_READER_OPTIONS` : Table of the options which were provided to the parser. ([ReaderOptions](#type-readeroptions)) `PANDOC_WRITER_OPTIONS` : Table of the options that will be passed to the writer. While the object can be modified, the changes will **not** be picked up by pandoc. ([WriterOptions](#type-writeroptions)) Accessing this variable in **custom writers** is **deprecated**. Starting with pandoc 3.0, it is set to a placeholder value (the default options) in custom writers. Access to the actual writer options is provided via the `Writer` or `ByteStringWriter` function, to which the options are passed as the second function argument. *Since: pandoc 2.17* `PANDOC_VERSION` : Contains the pandoc version as a [Version] object which behaves like a numerically indexed table, most significant number first. E.g., for pandoc 2.7.3, the value of the variable is equivalent to a table `{2, 7, 3}`. Use `tostring(PANDOC_VERSION)` to produce a version string. This variable is also set in custom writers. `PANDOC_API_VERSION` : Contains the version of the pandoc-types API against which pandoc was compiled. It is given as a numerically indexed table, most significant number first. E.g., if pandoc was compiled against pandoc-types 1.17.3, then the value of the variable will behave like the table `{1, 17, 3}`. Use `tostring(PANDOC_API_VERSION)` to produce a version string. This variable is also set in custom writers. `PANDOC_SCRIPT_FILE` : The name used to involve the filter. This value can be used to find files relative to the script file. This variable is also set in custom writers. `PANDOC_STATE` : The state shared by all readers and writers. It is used by pandoc to collect and pass information. The value of this variable is of type [CommonState] and is read-only. `pandoc` : The *pandoc* module, described in the next section, is available through the global `pandoc`. The other modules described herein are loaded as subfields under their respective name. `lpeg` : This variable holds the `lpeg` module, a package based on Parsing Expression Grammars (PEG). It provides excellent parsing utilities and is documented on the official [LPeg homepage]. Pandoc uses a built-in version of the library, unless it has been configured by the package maintainer to rely on a system-wide installation. `re` : Contains the LPeg.re module, which is built on top of LPeg and offers an implementation of a [regex engine]. Pandoc uses a built-in version of the library, unless it has been configured by the package maintainer to rely on a system-wide installation. [LPeg homepage]: http://www.inf.puc-rio.br/~roberto/lpeg/ [regex engine]: http://www.inf.puc-rio.br/~roberto/lpeg/re.html # Pandoc Module The `pandoc` Lua module is loaded into the filter's Lua environment and provides a set of functions and constants to make creation and manipulation of elements easier. The global variable `pandoc` is bound to the module and should generally not be overwritten for this reason. Two major functionalities are provided by the module: element creator functions and access to some of pandoc's main functionalities. ## Element creation Element creator functions like `Str`, `Para`, and `Pandoc` are designed to allow easy creation of new elements that are simple to use and can be read back from the Lua environment. Internally, pandoc uses these functions to create the Lua objects which are passed to element filter functions. This means that elements created via this module will behave exactly as those elements accessible through the filter function parameter. ## Exposed pandoc functionality Some pandoc functions have been made available in Lua: - [`walk_block`](#pandoc.walk_block) and [`walk_inline`](#pandoc.walk_inline) allow filters to be applied inside specific block or inline elements; - [`read`](#pandoc.read) allows filters to parse strings into pandoc documents; - [`pipe`](#pandoc.pipe) runs an external command with input from and output to strings; - the [`pandoc.mediabag`](#module-pandoc.mediabag) module allows access to the "mediabag," which stores binary content such as images that may be included in the final document; - the [`pandoc.utils`](#module-pandoc.utils) module contains various utility functions. # Lua interpreter initialization Initialization of pandoc's Lua interpreter can be controlled by placing a file `init.lua` in pandoc's data directory. A common use-case would be to load additional modules, or even to alter default modules. The following snippet is an example of code that might be useful when added to `init.lua`. The snippet adds all Unicode-aware functions defined in the [`text` module](#module-pandoc.text) to the default `string` module, prefixed with the string `uc_`. ``` lua for name, fn in pairs(require 'text') do string['uc_' .. name] = fn end ``` This makes it possible to apply these functions on strings using colon syntax (`mystring:uc_upper()`). # Debugging Lua filters Many errors can be avoided by performing static analysis. [`luacheck`](https://github.com/lunarmodules/luacheck) may be used for this purpose. A Luacheck configuration file for pandoc filters is available at . William Lupton has written a Lua module with some handy functions for debugging Lua filters, including functions that can pretty-print the Pandoc AST elements manipulated by the filters: it is available at . It is possible to use a debugging interface to halt execution and step through a Lua filter line by line as it is run inside Pandoc. This is accomplished using the remote-debugging interface of the package [`mobdebug`](https://github.com/pkulchenko/MobDebug). Although mobdebug can be run from the terminal, it is more useful run within the donation-ware Lua editor and IDE, [ZeroBrane Studio](https://studio.zerobrane.com/). ZeroBrane offers a REPL console and UI to step-through and view all variables and state. ZeroBrane doesn't come with Lua 5.4 bundled, but it can debug it, so you should install Lua 5.4, and then add [`mobdebug`](https://luarocks.org/modules/paulclinger/mobdebug) and its dependency [`luasocket`](https://luarocks.org/modules/luasocket/luasocket) using [`luarocks`](https://luarocks.org). ZeroBrane can use your Lua 5.4 install by adding `path.lua = "/path/to/your/lua"` in your ZeroBrane settings file. Next, open your Lua filter in ZeroBrane, and add `require('mobdebug').start()` at the line where you want your breakpoint. Then make sure the Project > Lua Interpreter is set to the "Lua" you added in settings and enable "Start Debugger Server" [see detailed instructions here](https://studio.zerobrane.com/doc-remote-debugging). Run Pandoc as you normally would, and ZeroBrane should break at the correct line. ## Common pitfalls AST elements not updated : A filtered element will only be updated if the filter function returns a new element to replace it. A function like the below has no effect, as the function returns no value: ``` lua function Str (str) str.text = string.upper(str.text) end ``` The correct version would be ``` lua function Str (str) str.text = string.upper(str.text) return str end ``` Pattern behavior is locale dependent : The character classes in Lua's pattern library depend on the current locale: E.g., the character `©` will be treated as punctuation, and matched by the pattern `%p`, on CP-1252 locales, but not on systems using a UTF-8 locale. A reliable way to ensure unified handling of patterns and character classes is to use the "C" locale by adding `os.setlocale 'C'` to the top of the Lua script. String library is not Unicode aware : Lua's `string` library treats each byte as a single character. A function like `string.upper` will not have the intended effect when applied to words with non-ASCII characters. Similarly, a pattern like `[☃]` will match *any* of the bytes `\240`, `\159`, `\154`, and `\178`, but **won't** match the "snowman" Unicode character. Use the [pandoc.text](#module-pandoc.text) module for Unicode-aware transformation, and consider using using the lpeg or re library for pattern matching. # Examples The following filters are presented as examples. A repository of useful Lua filters (which may also serve as good examples) is available at . ## Macro substitution The following filter converts the string `{{helloworld}}` into emphasized text "Hello, World". ``` lua return { Str = function (elem) if elem.text == "{{helloworld}}" then return pandoc.Emph {pandoc.Str "Hello, World"} else return elem end end, } ``` ## Center images in LaTeX and HTML output For LaTeX, wrap an image in LaTeX snippets which cause the image to be centered horizontally. In HTML, the image element's style attribute is used to achieve centering. ``` lua -- Filter images with this function if the target format is LaTeX. if FORMAT:match 'latex' then function Image (elem) -- Surround all images with image-centering raw LaTeX. return { pandoc.RawInline('latex', '\\hfill\\break{\\centering'), elem, pandoc.RawInline('latex', '\\par}') } end end -- Filter images with this function if the target format is HTML if FORMAT:match 'html' then function Image (elem) -- Use CSS style to center image elem.attributes.style = 'margin:auto; display: block;' return elem end end ``` ## Setting the date in the metadata This filter sets the date in the document's metadata to the current date, if a date isn't already set: ``` lua function Meta(m) if m.date == nil then m.date = os.date("%B %e, %Y") return m end end ``` ## Remove spaces before citations This filter removes all spaces preceding an "author-in-text" citation. In Markdown, author-in-text citations (e.g., `@citekey`), must be preceded by a space. If these spaces are undesired, they must be removed with a filter. ``` lua local function is_space_before_author_in_text(spc, cite) return spc and spc.t == 'Space' and cite and cite.t == 'Cite' -- there must be only a single citation, and it must have -- mode 'AuthorInText' and #cite.citations == 1 and cite.citations[1].mode == 'AuthorInText' end function Inlines (inlines) -- Go from end to start to avoid problems with shifting indices. for i = #inlines-1, 1, -1 do if is_space_before_author_in_text(inlines[i], inlines[i+1]) then inlines:remove(i) end end return inlines end ``` ## Replacing placeholders with their metadata value Lua filter functions are run in the order > *Inlines → Blocks → Meta → Pandoc*. Passing information from a higher level (e.g., metadata) to a lower level (e.g., inlines) is still possible by using two filters living in the same file: ``` lua local vars = {} local function get_vars (meta) for k, v in pairs(meta) do if pandoc.utils.type(v) == 'Inlines' then vars["%" .. k .. "%"] = {table.unpack(v)} end end end local function replace (el) if vars[el.text] then return pandoc.Span(vars[el.text]) else return el end end function Pandoc(doc) return doc:walk { Meta = get_vars }:walk { Str = replace } end ``` If the contents of file `occupations.md` are ``` markdown --- name: Samuel Q. Smith occupation: Professor of Oenology --- Name : %name% Occupation : %occupation% ``` then running `pandoc --lua-filter=meta-vars.lua occupations.md` will output: ``` html
Name

Samuel Q. Smith

Occupation

Professor of Oenology

``` Note that the placeholders must not contain any spaces, otherwise they will turn into two separate Str elements and the filter won't work. ## Modifying pandoc's `MANUAL.txt` for man pages This is the filter we use when converting `MANUAL.txt` to man pages. It converts level-1 headers to uppercase (using [`walk`](#type-block:walk) to transform inline elements inside headers), removes footnotes, and replaces links with regular text. ``` lua -- we use pandoc.text to get a UTF-8 aware 'upper' function local text = pandoc.text function Header(el) if el.level == 1 then return el:walk { Str = function(el) return pandoc.Str(text.upper(el.text)) end } end end function Link(el) return el.content end function Note(el) return {} end ``` ## Creating a handout from a paper This filter extracts all the numbered examples, section headers, block quotes, and figures from a document, in addition to any divs with class `handout`. (Note that only blocks at the "outer level" are included; this ignores blocks inside nested constructs, like list items.) ``` lua -- creates a handout from an article, using its headings, -- blockquotes, numbered examples, figures, and any -- Divs with class "handout" function Pandoc(doc) local hblocks = {} for i,el in pairs(doc.blocks) do if (el.t == "Div" and el.classes[1] == "handout") or (el.t == "BlockQuote") or (el.t == "OrderedList" and el.style == "Example") or (el.t == "Para" and #el.c == 1 and el.c[1].t == "Image") or (el.t == "Header") then table.insert(hblocks, el) end end return pandoc.Pandoc(hblocks, doc.meta) end ``` ## Counting words in a document This filter counts the words in the body of a document (omitting metadata like titles and abstracts), including words in code. It should be more accurate than `wc -w` run directly on a Markdown document, since the latter will count markup characters, like the `#` in front of an ATX header, or tags in HTML documents, as words. To run it, `pandoc --lua-filter wordcount.lua myfile.md`. ``` lua -- counts words in a document words = 0 wordcount = { Str = function(el) -- we don't count a word if it's entirely punctuation: if el.text:match("%P") then words = words + 1 end end, Code = function(el) _,n = el.text:gsub("%S+","") words = words + n end, CodeBlock = function(el) _,n = el.text:gsub("%S+","") words = words + n end } function Pandoc(el) -- skip metadata, just count body: el.blocks:walk(wordcount) print(words .. " words in body") os.exit(0) end ``` ## Creating a table This filter creates a document that contains the following table with 5 columns. It serves as a working example of how to use the [`pandoc.Table`](#pandoc.Table) constructor. +--------+--------+--------+--------+---+ | This | is my | table | header | | +========+:=======+:======:+=======:+===+ | Cell 1 | Cell 2 | Cell 3 | | | +--------+--------+--------+--------+---+ | Cell 4 | Cell 5 | Cell 6 | | | +========+========+========+========+===+ | This is my table footer. | | +===================================+===+ : This is my table caption. Note that: - The number of columns in the resulting Table element is equal to the number of entries in the `colspecs` parameter. - A [ColSpec] object must contain the cell alignment, but the column width is optional. - A [TableBody] object is specified using a Lua table in the `bodies` parameter because there is no `pandoc.TableBody` constructor. ```lua function Pandoc () local caption = pandoc.Caption( "This is my table caption." ) local colspecs = { { pandoc.AlignLeft }, { pandoc.AlignDefault }, { pandoc.AlignCenter }, { pandoc.AlignRight }, { pandoc.AlignDefault } } local head = pandoc.TableHead{ pandoc.Row{ pandoc.Cell( "This" ), pandoc.Cell( "is my" ), pandoc.Cell( "table" ), pandoc.Cell( "header" ) } } local bodies = { { attr={}, body={ pandoc.Row{ pandoc.Cell( "Cell 1" ), pandoc.Cell( "Cell 2" ), pandoc.Cell( "Cell 3" ) }, pandoc.Row{ pandoc.Cell( "Cell 4" ), pandoc.Cell( "Cell 5" ), pandoc.Cell( "Cell 6" ) } }, head={}, row_head_columns=0 } } local foot = pandoc.TableFoot{ pandoc.Row{ pandoc.Cell( "This is my table footer.", pandoc.AlignDefault, 1, 4 ) } } return pandoc.Pandoc { pandoc.Table(caption, colspecs, head, bodies, foot) } end ``` ## Extracting links from a document This filter creates a document containing a table that lists the URLs the input document links to, together with the number of links to each URL. ```lua links = {} function Link (el) if links[el.target] then links[el.target] = links[el.target] + 1 else links[el.target] = 1 end return el end function Pandoc () local caption = pandoc.Caption("Link count.") local colspecs = { { pandoc.AlignDefault, 0.8 }, { pandoc.AlignLeft, 0.2 } } local head = pandoc.TableHead{ pandoc.Row{ pandoc.Cell("Target"), pandoc.Cell("Count") } } local foot = pandoc.TableFoot() local rows = {} for link, count in pairs(links) do rows[#rows + 1] = pandoc.Row{ pandoc.Cell( link ), pandoc.Cell( pandoc.utils.stringify(count) ) } end local bodies = { { attr={}, body=rows, head={}, row_head_columns=0 } } return pandoc.Pandoc { pandoc.Table(caption, colspecs, head, bodies, foot) } end ``` ## Converting ABC code to music notation This filter replaces code blocks with class `abc` with images created by running their contents through `abcm2ps` and ImageMagick's `convert`. (For more on ABC notation, see .) Images are added to the mediabag. For output to binary formats, pandoc will use images in the mediabag. For textual formats, use `--extract-media` to specify a directory where the files in the mediabag will be written, or (for HTML only) use `--embed-resources`. ``` lua -- Pandoc filter to process code blocks with class "abc" containing -- ABC notation into images. -- -- * Assumes that abcm2ps and ImageMagick's convert are in the path. -- * For textual output formats, use --extract-media=abc-images -- * For HTML formats, you may alternatively use --embed-resources local filetypes = { html = {"png", "image/png"} , latex = {"pdf", "application/pdf"} } local filetype = filetypes[FORMAT][1] or "png" local mimetype = filetypes[FORMAT][2] or "image/png" local function abc2eps(abc, filetype) local eps = pandoc.pipe("abcm2ps", {"-q", "-O", "-", "-"}, abc) local final = pandoc.pipe("convert", {"-", filetype .. ":-"}, eps) return final end function CodeBlock(block) if block.classes[1] == "abc" then local img = abc2eps(block.text, filetype) local fname = pandoc.sha1(img) .. "." .. filetype pandoc.mediabag.insert(fname, mimetype, img) return pandoc.Para{ pandoc.Image({pandoc.Str("abc tune")}, fname) } end end ``` ## Building images with Ti*k*Z This filter converts raw LaTeX Ti*k*Z environments into images. It works with both PDF and HTML output. The Ti*k*Z code is compiled to an image using `pdflatex`, and the image is converted from pdf to svg format using [`pdf2svg`](https://github.com/dawbarton/pdf2svg), so both of these must be in the system path. Converted images are cached in the working directory and given filenames based on a hash of the source, so that they need not be regenerated each time the document is built. (A more sophisticated version of this might put these in a special cache directory.) ``` lua local system = require 'pandoc.system' local tikz_doc_template = [[ \documentclass{standalone} \usepackage{xcolor} \usepackage{tikz} \begin{document} \nopagecolor %s \end{document} ]] local function tikz2image(src, filetype, outfile) system.with_temporary_directory('tikz2image', function (tmpdir) system.with_working_directory(tmpdir, function() local f = io.open('tikz.tex', 'w') f:write(tikz_doc_template:format(src)) f:close() os.execute('pdflatex tikz.tex') if filetype == 'pdf' then os.rename('tikz.pdf', outfile) else os.execute('pdf2svg tikz.pdf ' .. outfile) end end) end) end extension_for = { html = 'svg', html4 = 'svg', html5 = 'svg', latex = 'pdf', beamer = 'pdf' } local function file_exists(name) local f = io.open(name, 'r') if f ~= nil then io.close(f) return true else return false end end local function starts_with(start, str) return str:sub(1, #start) == start end function RawBlock(el) if starts_with('\\begin{tikzpicture}', el.text) then local filetype = extension_for[FORMAT] or 'svg' local fbasename = pandoc.sha1(el.text) .. '.' .. filetype local fname = system.get_working_directory() .. '/' .. fbasename if not file_exists(fname) then tikz2image(el.text, filetype, fname) end return pandoc.Para({pandoc.Image({}, fbasename)}) else return el end end ``` Example of use: pandoc --lua-filter tikz.lua -s -o cycle.html <, >=latex] ({360/\n * (\s - 1)+\margin}:\radius) arc ({360/\n * (\s - 1)+\margin}:{360/\n * (\s)-\margin}:\radius); } \end{tikzpicture} EOF # Lua type reference This section describes the types of objects available to Lua filters. See the [pandoc module](#module-pandoc) for functions to create these objects. ## Shared Properties ### `clone` `clone ()` All instances of the types listed here, with the exception of read-only objects, can be cloned via the `clone()` method. Usage: local emph = pandoc.Emph {pandoc.Str 'important'} local cloned_emph = emph:clone() -- note the colon ## Pandoc {#type-pandoc} Pandoc document Values of this type can be created with the [`pandoc.Pandoc`](#pandoc.Pandoc) constructor. Pandoc values are equal in Lua if and only if they are equal in Haskell. `blocks` : document content ([Blocks][]) `meta` : document meta information ([Meta] object) ### Methods {#type-pandoc-methods} #### normalize `normalize(self)` Perform a normalization of Pandoc documents. E.g., multiple successive spaces are collapsed, and tables are normalized, so that all rows and columns contain the same number of cells. Parameters: `self` : the element ([Pandoc][]) Results: - cloned and normalized document. ([Pandoc][]) ### walk {#type-pandoc:walk} `walk(self, lua_filter)` Applies a Lua filter to the Pandoc element. Just as for full-document filters, the order in which elements are traversed can be controlled by setting the `traverse` field of the filter; see the section on [traversal order][Traversal order]. Returns a (deep) copy on which the filter has been applied: the original element is left untouched. Parameters: `self` : the element ([Pandoc](#type-pandoc)) `lua_filter` : map of filter functions (table) Result: - filtered document ([Pandoc][]) Usage: -- returns `pandoc.Pandoc{pandoc.Para{pandoc.Str 'Bye'}}` return pandoc.Pandoc{pandoc.Para('Hi')}:walk { Str = function (_) return 'Bye' end, } ## Meta {#type-meta} Meta information on a document; string-indexed collection of [MetaValues]. Values of this type can be created with the [`pandoc.Meta`](#pandoc.meta) constructor. Meta values are equal in Lua if and only if they are equal in Haskell. ## MetaValue {#type-metavalue} Document meta information items. This is not a separate type, but describes a set of types that can be used in places were a MetaValue is expected. The types correspond to the following Haskell type constructors: - boolean → MetaBool - string or number → MetaString - Inlines → MetaInlines - Blocks → MetaBlocks - List/integer indexed table → MetaList - string-indexed table → MetaMap The corresponding constructors [`pandoc.MetaBool`](#pandoc.metabool), [`pandoc.MetaString`](#pandoc.metastring), [`pandoc.MetaInlines`](#pandoc.metainlines), [`pandoc.MetaBlocks`](#pandoc.metablocks), [`pandoc.MetaList`](#pandoc.metalist), and [`pandoc.MetaMap`](#pandoc.metamap) can be used to ensure that a value is treated in the intended way. E.g., an empty table is normally treated as a `MetaMap`, but can be made into an empty `MetaList` by calling `pandoc.MetaList{}`. However, the same can be accomplished by using the generic functions like `pandoc.List`, `pandoc.Inlines`, or `pandoc.Blocks`. Use the function [`pandoc.utils.type`](#pandoc.utils.type) to get the type of a metadata value. ## Block {#type-block} Block values are equal in Lua if and only if they are equal in Haskell. ### Common methods #### walk {#type-block:walk} `walk(self, lua_filter)` Applies a Lua filter to the block element. Just as for full-document filters, the order in which elements are traversed can be controlled by setting the `traverse` field of the filter; see the section on [traversal order][Traversal order]. Returns a (deep) copy on which the filter has been applied: the original element is left untouched. Note that the filter is applied to the subtree, but not to the `self` block element. The rationale is that otherwise the element could be deleted by the filter, or replaced with multiple block elements, which might lead to possibly unexpected results. Parameters: `self` : the element ([Block](#type-block)) `lua_filter` : map of filter functions (table) Result: - filtered block ([Block][]) Usage: -- returns `pandoc.Para{pandoc.Str 'Bye'}` return pandoc.Para('Hi'):walk { Str = function (_) return 'Bye' end, } ### BlockQuote {#type-blockquote} A block quote element. Values of this type can be created with the [`pandoc.BlockQuote`](#pandoc.BlockQuote) constructor. Fields: `content` : block content ([Blocks][]) `tag`, `t` : the literal `BlockQuote` (string) ### BulletList {#type-bulletlist} A bullet list. Values of this type can be created with the [`pandoc.BulletList`](#pandoc.BulletList) constructor. Fields: `content` : list items ([List] of items, i.e., [List] of [Blocks][]) `tag`, `t` : the literal `BulletList` (string) ### CodeBlock {#type-codeblock} Block of code. Values of this type can be created with the [`pandoc.CodeBlock`](#pandoc.CodeBlock) constructor. Fields: `text` : code string (string) `attr` : element attributes ([Attr]) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) `tag`, `t` : the literal `CodeBlock` (string) ### DefinitionList {#type-definitionlist} Definition list, containing terms and their explanation. Values of this type can be created with the [`pandoc.DefinitionList`](#pandoc.DefinitionList) constructor. Fields: `content` : list of items `tag`, `t` : the literal `DefinitionList` (string) ### Div {#type-div} Generic block container with attributes. Values of this type can be created with the [`pandoc.Div`](#pandoc.Div) constructor. Fields: `content` : block content ([Blocks][]) `attr` : element attributes ([Attr]) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) `tag`, `t` : the literal `Div` (string) ### Figure {#type-figure} Figure with caption and arbitrary block contents. Values of this type can be created with the [`pandoc.Figure`](#pandoc.Figure) constructor. Fields: `content` : block content ([Blocks][]) `caption` : figure caption ([Caption][]) `attr` : element attributes ([Attr][]) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List][] of strings) `attributes` : alias for `attr.attributes` ([Attributes][]) `tag`, `t` : the literal `Figure` (string) ### Header {#type-header} Creates a header element. Values of this type can be created with the [`pandoc.Header`](#pandoc.Header) constructor. Fields: `level` : header level (integer) `content` : inline content ([Inlines][]) `attr` : element attributes ([Attr]) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) `tag`, `t` : the literal `Header` (string) ### HorizontalRule {#type-horizontalrule} A horizontal rule. Values of this type can be created with the [`pandoc.HorizontalRule`](#pandoc.HorizontalRule) constructor. Fields: `tag`, `t` : the literal `HorizontalRule` (string) ### LineBlock {#type-lineblock} A line block, i.e. a list of lines, each separated from the next by a newline. Values of this type can be created with the [`pandoc.LineBlock`](#pandoc.LineBlock) constructor. Fields: `content` : inline content ([List] of lines, i.e. [List] of [Inlines][]) `tag`, `t` : the literal `LineBlock` (string) ### OrderedList {#type-orderedlist} An ordered list. Values of this type can be created with the [`pandoc.OrderedList`](#pandoc.OrderedList) constructor. Fields: `content` : list items ([List] of items, i.e., [List] of [Blocks][]) `listAttributes` : list parameters ([ListAttributes]) `start` : alias for `listAttributes.start` (integer) `style` : alias for `listAttributes.style` (string) `delimiter` : alias for `listAttributes.delimiter` (string) `tag`, `t` : the literal `OrderedList` (string) ### Para {#type-para} A paragraph. Values of this type can be created with the [`pandoc.Para`](#pandoc.Para) constructor. Fields: `content` : inline content ([Inlines][]) `tag`, `t` : the literal `Para` (string) ### Plain {#type-plain} Plain text, not a paragraph. Values of this type can be created with the [`pandoc.Plain`](#pandoc.Plain) constructor. Fields: `content` : inline content ([Inlines][]) `tag`, `t` : the literal `Plain` (string) ### RawBlock {#type-rawblock} Raw content of a specified format. Values of this type can be created with the [`pandoc.RawBlock`](#pandoc.RawBlock) constructor. Fields: `format` : format of content (string) `text` : raw content (string) `tag`, `t` : the literal `RawBlock` (string) ### Table {#type-table} A table. Values of this type can be created with the [`pandoc.Table`](#pandoc.Table) constructor. Fields: `attr` : table attributes ([Attr]) `caption` : table caption ([Caption]) `colspecs` : column specifications, i.e., alignments and widths ([List] of [ColSpec]s) `head` : table head ([TableHead]) `bodies` : table bodies ([List] of [TableBody]s) `foot` : table foot ([TableFoot]) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) `tag`, `t` : the literal `Table` (string) A [table cell]{#type-table-cell} is a list of blocks. *[Alignment]{#type-alignment}* is a string value indicating the horizontal alignment of a table column. `AlignLeft`, `AlignRight`, and `AlignCenter` leads cell content to be left-aligned, right-aligned, and centered, respectively. The default alignment is `AlignDefault` (often equivalent to centered). ## Blocks {#type-blocks} List of [Block] elements, with the same methods as a generic [List](#type-list). It is usually not necessary to create values of this type in user scripts, as pandoc can convert other types into Blocks wherever a value of this type is expected: - a list of [Block] (or Block-like) values is used directly; - a single [Inlines][] value is wrapped into a [Plain] element; - string values are turned into an [Inlines][] value by splitting the string into words (see [Inlines](#type-inlines)), and then wrapping the result into a Plain singleton. ### Methods Lists of type `Blocks` share all methods available in generic lists, see the [`pandoc.List` module](#module-pandoc.list). Additionally, the following methods are available on Blocks values: #### walk {#type-blocks:walk} `walk(self, lua_filter)` Applies a Lua filter to the Blocks list. Just as for full-document filters, the order in which elements are traversed can be controlled by setting the `traverse` field of the filter; see the section on [traversal order][Traversal order]. Returns a (deep) copy on which the filter has been applied: the original list is left untouched. Parameters: `self` : the list ([Blocks][]) `lua_filter` : map of filter functions (table) Result: - filtered list ([Blocks][]) Usage: -- returns `pandoc.Blocks{pandoc.Para('Salve!')}` return pandoc.Blocks{pandoc.Plain('Salve!)}:walk { Plain = function (p) return pandoc.Para(p.content) end, } ## Inline {#type-inline} Inline values are equal in Lua if and only if they are equal in Haskell. ### Common methods #### walk {#type-inline:walk} `walk(self, lua_filter)` Applies a Lua filter to the Inline element. Just as for full-document filters, the order in which elements are traversed can be controlled by setting the `traverse` field of the filter; see the section on [traversal order][Traversal order]. Returns a (deep) copy on which the filter has been applied: the original element is left untouched. Note that the filter is applied to the subtree, but not to the `self` inline element. The rationale is that otherwise the element could be deleted by the filter, or replaced with multiple inline elements, which might lead to possibly unexpected results. Parameters: `self` : the element ([Inline](#type-inline)) `lua_filter` : map of filter functions (table) Result: - filtered inline element ([Inline][]) Usage: -- returns `pandoc.SmallCaps('SPQR)` return pandoc.SmallCaps('spqr'):walk { Str = function (s) return string.upper(s.text) end, } ### Cite {#type-cite} Citation. Values of this type can be created with the [`pandoc.Cite`](#pandoc.Cite) constructor. Fields: `content` : ([Inlines][]) `citations` : citation entries ([List] of [Citations]) `tag`, `t` : the literal `Cite` (string) ### Code {#type-code} Inline code Values of this type can be created with the [`pandoc.Code`](#pandoc.Code) constructor. Fields: `text` : code string (string) `attr` : attributes ([Attr]) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) `tag`, `t` : the literal `Code` (string) ### Emph {#type-emph} Emphasized text Values of this type can be created with the [`pandoc.Emph`](#pandoc.Emph) constructor. Fields: `content` : inline content ([Inlines][]) `tag`, `t` : the literal `Emph` (string) ### Image {#type-image} Image: alt text (list of inlines), target Values of this type can be created with the [`pandoc.Image`](#pandoc.Image) constructor. Fields: `caption` : text used to describe the image ([Inlines][]) `src` : path to the image file (string) `title` : brief image description (string) `attr` : attributes ([Attr]) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) `tag`, `t` : the literal `Image` (string) ### LineBreak {#type-linebreak} Hard line break Values of this type can be created with the [`pandoc.LineBreak`](#pandoc.LineBreak) constructor. Fields: `tag`, `t` : the literal `LineBreak` (string) ### Link {#type-link} Hyperlink: alt text (list of inlines), target Values of this type can be created with the [`pandoc.Link`](#pandoc.Link) constructor. Fields: `attr` : attributes ([Attr]) `content` : text for this link ([Inlines][]) `target` : the link target (string) `title` : brief link description `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) `tag`, `t` : the literal `Link` (string) ### Math {#type-math} TeX math (literal) Values of this type can be created with the [`pandoc.Math`](#pandoc.Math) constructor. Fields: `mathtype` : specifier determining whether the math content should be shown inline (`InlineMath`) or on a separate line (`DisplayMath`) (string) `text` : math content (string) `tag`, `t` : the literal `Math` (string) ### Note {#type-note} Footnote or endnote Values of this type can be created with the [`pandoc.Note`](#pandoc.Note) constructor. Fields: `content` : ([Blocks][]) `tag`, `t` : the literal `Note` (string) ### Quoted {#type-quoted} Quoted text Values of this type can be created with the [`pandoc.Quoted`](#pandoc.Quoted) constructor. Fields: `quotetype` : type of quotes to be used; one of `SingleQuote` or `DoubleQuote` (string) `content` : quoted text ([Inlines][]) `tag`, `t` : the literal `Quoted` (string) ### RawInline {#type-rawinline} Raw inline Values of this type can be created with the [`pandoc.RawInline`](#pandoc.RawInline) constructor. Fields: `format` : the format of the content (string) `text` : raw content (string) `tag`, `t` : the literal `RawInline` (string) ### SmallCaps {#type-smallcaps} Small caps text Values of this type can be created with the [`pandoc.SmallCaps`](#pandoc.Smallcaps) constructor. Fields: `content` : ([Inlines][]) `tag`, `t` : the literal `SmallCaps` (string) ### SoftBreak {#type-softbreak} Soft line break Values of this type can be created with the [`pandoc.SoftBreak`](#pandoc.Softbreak) constructor. Fields: `tag`, `t` : the literal `SoftBreak` (string) ### Space {#type-space} Inter-word space Values of this type can be created with the [`pandoc.Space`](#pandoc.Space) constructor. Fields: `tag`, `t` : the literal `Space` (string) ### Span {#type-span} Generic inline container with attributes Values of this type can be created with the [`pandoc.Span`](#pandoc.Span) constructor. Fields: `attr` : attributes ([Attr]) `content` : wrapped content ([Inlines][]) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) `tag`, `t` : the literal `Span` (string) ### Str {#type-str} Text Values of this type can be created with the [`pandoc.Str`](#pandoc.Str) constructor. Fields: `text` : content (string) `tag`, `t` : the literal `Str` (string) ### Strikeout {#type-strikeout} Strikeout text Values of this type can be created with the [`pandoc.Strikeout`](#pandoc.Strikeout) constructor. Fields: `content` : inline content ([Inlines][]) `tag`, `t` : the literal `Strikeout` (string) ### Strong {#type-strong} Strongly emphasized text Values of this type can be created with the [`pandoc.Strong`](#pandoc.Strong) constructor. Fields: `content` : inline content ([Inlines][]) `tag`, `t` : the literal `Strong` (string) ### Subscript {#type-subscript} Subscripted text Values of this type can be created with the [`pandoc.Subscript`](#pandoc.Subscript) constructor. Fields: `content` : inline content ([Inlines][]) `tag`, `t` : the literal `Subscript` (string) ### Superscript {#type-superscript} Superscripted text Values of this type can be created with the [`pandoc.Superscript`](#pandoc.Superscript) constructor. Fields: `content` : inline content ([Inlines][]) `tag`, `t` : the literal `Superscript` (string) ### Underline {#type-underline} Underlined text Values of this type can be created with the [`pandoc.Underline`](#pandoc.Underline) constructor. Fields: `content` : inline content ([Inlines][]) `tag`, `t` : the literal `Underline` (string) ## Inlines {#type-inlines} List of [Inline] elements, with the same methods as a generic [List](#type-list). It is usually not necessary to create values of this type in user scripts, as pandoc can convert other types into Inlines wherever a value of this type is expected: - lists of [Inline] (or Inline-like) values are used directly; - single [Inline] values are converted into a list containing just that element; - String values are split into words, converting line breaks into [SoftBreak](#type-softbreak) elements, and other whitespace characters into [Spaces](#type-space). ### Methods Lists of type `Inlines` share all methods available in generic lists, see the [`pandoc.List` module](#module-pandoc.list). Additionally, the following methods are available on *Inlines* values: #### walk {#type-inlines:walk} `walk(self, lua_filter)` Applies a Lua filter to the Inlines list. Just as for full-document filters, the order in which elements are handled are Inline → Inlines → Block → Blocks. The filter is applied to all list items *and* to the list itself. Returns a (deep) copy on which the filter has been applied: the original list is left untouched. Parameters: `self` : the list ([Inlines](#type-inlines)) `lua_filter` : map of filter functions (table) Result: - filtered list ([Inlines](#type-inlines)) Usage: -- returns `pandoc.Inlines{pandoc.SmallCaps('SPQR')}` return pandoc.Inlines{pandoc.Emph('spqr')}:walk { Str = function (s) return string.upper(s.text) end, Emph = function (e) return pandoc.SmallCaps(e.content) end, } ## Element components ### Attr {#type-attr} A set of element attributes. Values of this type can be created with the [`pandoc.Attr`](#pandoc.Attr) constructor. For convenience, it is usually not necessary to construct the value directly if it is part of an element, and it is sufficient to pass an HTML-like table. E.g., to create a span with identifier "text" and classes "a" and "b", one can write: local span = pandoc.Span('text', {id = 'text', class = 'a b'}) This also works when using the `attr` setter: local span = pandoc.Span 'text' span.attr = {id = 'text', class = 'a b', other_attribute = '1'} Attr values are equal in Lua if and only if they are equal in Haskell. Fields: `identifier` : element identifier (string) `classes` : element classes ([List] of strings) `attributes` : collection of key/value pairs ([Attributes]) ### Attributes {#type-attributes} List of key/value pairs. Values can be accessed by using keys as indices to the list table. Attributes values are equal in Lua if and only if they are equal in Haskell. ### Caption {#type-caption} The caption of a table, with an optional short caption. Fields: `long` : long caption ([Blocks][]) `short` : short caption ([Inlines][]) ### Cell {#type-cell} A table cell. Fields: `attr` : cell attributes `alignment` : individual cell alignment ([Alignment]). `contents` : cell contents ([Blocks][]). `col_span` : number of columns spanned by the cell; the width of the cell in columns (integer). `row_span` : number of rows spanned by the cell; the height of the cell in rows (integer). `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List] of strings) `attributes` : alias for `attr.attributes` ([Attributes]) ### Citation {#type-citation} Single citation entry Values of this type can be created with the [`pandoc.Citation`](#pandoc.citation) constructor. Citation values are equal in Lua if and only if they are equal in Haskell. Fields: `id` : citation identifier, e.g., a bibtex key (string) `mode` : citation mode, one of `AuthorInText`, `SuppressAuthor`, or `NormalCitation` (string) `prefix` : citation prefix ([Inlines][]) `suffix` : citation suffix ([Inlines][]) `note_num` : note number (integer) `hash` : hash (integer) ### ColSpec {#type-colspec} Column alignment and width specification for a single table column. This is a pair, i.e., a plain table, with the following components: 1. cell alignment ([Alignment]). 2. table column width, as a fraction of the page width (number). ### ListAttributes {#type-listattributes} List attributes Values of this type can be created with the [`pandoc.ListAttributes`](#pandoc.ListAttributes) constructor. Fields: `start` : number of the first list item (integer) `style` : style used for list numbers; possible values are `DefaultStyle`, `Example`, `Decimal`, `LowerRoman`, `UpperRoman`, `LowerAlpha`, and `UpperAlpha` (string) `delimiter` : delimiter of list numbers; one of `DefaultDelim`, `Period`, `OneParen`, and `TwoParens` (string) ### Row {#type-row} A table row. Fields: `attr` : element attributes ([Attr][]) `cells` : list of table cells ([List][] of [Cell][]s) ### TableBody {#type-tablebody} A body of a table, with an intermediate head and the specified number of row header columns. Fields: `attr` : table body attributes ([Attr][]) `body` : table body rows ([List][] of [Row][]s) `head` : intermediate head ([List][] of [Row][]s) `row_head_columns` : number of columns taken up by the row head of each row of a [TableBody][]. The row body takes up the remaining columns. ### TableFoot {#type-tablefoot} The foot of a table. Fields: `attr` : element attributes ([Attr][]) `rows` : list of rows ([List][] of [Row][]s) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List][] of strings) `attributes` : alias for `attr.attributes` ([Attributes][]) ### TableHead {#type-tablehead} The head of a table. Fields: `attr` : element attributes ([Attr][]) `rows` : list of rows ([List][] of [Row][]s) `identifier` : alias for `attr.identifier` (string) `classes` : alias for `attr.classes` ([List][] of strings) `attributes` : alias for `attr.attributes` ([Attributes][]) ## ReaderOptions {#type-readeroptions} Pandoc reader options Fields: `abbreviations` : set of known abbreviations (set of strings) `columns` : number of columns in terminal (integer) `default_image_extension` : default extension for images (string) `extensions` : string representation of the syntax extensions bit field (sequence of strings) `indented_code_classes` : default classes for indented code blocks (list of strings) `standalone` : whether the input was a standalone document with header (boolean) `strip_comments` : HTML comments are stripped instead of parsed as raw HTML (boolean) `tab_stop` : width (i.e. equivalent number of spaces) of tab stops (integer) `track_changes` : track changes setting for docx; one of `accept-changes`, `reject-changes`, and `all-changes` (string) ## WriterOptions {#type-writeroptions} Pandoc writer options Fields: `chunk_template` : Template used to generate chunked HTML filenames (string) `cite_method` : How to print cites -- one of 'citeproc', 'natbib', or 'biblatex' (string) `columns` : Characters in a line (for text wrapping) (integer) `dpi` : DPI for pixel to/from inch/cm conversions (integer) `email_obfuscation` : How to obfuscate emails -- one of 'none', 'references', or 'javascript' (string) `epub_chapter_level` : Header level for chapters, i.e., how the document is split into separate files (integer) `epub_fonts` : Paths to fonts to embed (sequence of strings) `epub_metadata` : Metadata to include in EPUB (string|nil) `epub_subdirectory` : Subdir for epub in OCF (string) `extensions` : Markdown extensions that can be used (sequence of strings) `highlight_style` : Style to use for highlighting; see the output of `pandoc --print-highlight-style=...` for an example structure. The value `nil` means that no highlighting is used. (table|nil) `html_math_method` : How to print math in HTML; one of 'plain', 'mathjax', 'mathml', 'webtex', 'katex', 'gladtex', or a table with keys `method` and `url`. (string|table) `html_q_tags` : Use `` tags for quotes in HTML (boolean) `identifier_prefix` : Prefix for section & note ids in HTML and for footnote marks in markdown (string) `incremental` : True if lists should be incremental (boolean) `listings` : Use listings package for code (boolean) `number_offset` : Starting number for section, subsection, ... (sequence of integers) `number_sections` : Number sections in LaTeX (boolean) `prefer_ascii` : Prefer ASCII representations of characters when possible (boolean) `reference_doc` : Path to reference document if specified (string|nil) `reference_links` : Use reference links in writing markdown, rst (boolean) `reference_location` : Location of footnotes and references for writing markdown; one of 'end-of-block', 'end-of-section', 'end-of-document'. The common prefix may be omitted when setting this value. (string) `section_divs` : Put sections in div tags in HTML (boolean) `setext_headers` : Use setext headers for levels 1-2 in markdown (boolean) `slide_level` : Force header level of slides (integer\|nil) `tab_stop` : Tabstop for conversion btw spaces and tabs (integer) `table_of_contents` : Include table of contents (boolean) `template` : Template to use ([Template](#type-template)|nil) `toc_depth` : Number of levels to include in TOC (integer) `top_level_division` : Type of top-level divisions; one of 'top-level-part', 'top-level-chapter', 'top-level-section', or 'top-level-default'. The prefix `top-level` may be omitted when setting this value. (string) `variables` : Variables to set in template; string-indexed table (table) `wrap_text` : Option for wrapping text; one of 'wrap-auto', 'wrap-none', or 'wrap-preserve'. The `wrap-` prefix may be omitted when setting this value. (string) ## CommonState {#type-commonstate} The state used by pandoc to collect information and make it available to readers and writers. Fields: `input_files` : List of input files from command line ([List] of strings) `output_file` : Output file from command line (string or nil) `log` : A list of log messages ([List] of [LogMessage]s) `request_headers` : Headers to add for HTTP requests; table with header names as keys and header contents as value (table) `resource_path` : Path to search for resources like included images ([List] of strings) `source_url` : Absolute URL or directory of first source file (string or nil) `user_data_dir` : Directory to search for data files (string or nil) `trace` : Whether tracing messages are issued (boolean) `verbosity` : Verbosity level; one of `INFO`, `WARNING`, `ERROR` (string) ## Doc {#type-doc} Reflowable plain-text document. A Doc value can be rendered and reflown to fit a given column width. The [`pandoc.layout`](#module-pandoc.layout) module can be used to create and modify Doc values. All functions in that module that take a Doc value as their first argument are also available as Doc methods. E.g., `(pandoc.layout.literal 'text'):render()`. If a string is passed to a function expecting a Doc, then the string is treated as a literal value. I.e., the following two lines are equivalent: ``` lua test = pandoc.layout.quotes(pandoc.layout.literal 'this') test = pandoc.layout.quotes('this') ``` ### Operators {#type-doc-operators} #### `..` {#type-doc.__concat} Concatenate two `Doc` elements. #### `+` {#type-doc.__add} Concatenate two `Doc`s, inserting a reflowable space between them. #### `/` {#type-doc.__div} If `a` and `b` are `Doc` elements, then `a / b` puts `a` above `b`. #### `//` {#type-doc.__idiv} If `a` and `b` are `Doc` elements, then `a // b` puts `a` above `b`, inserting a blank line between them. ## List {#type-list} A list is any Lua table with integer indices. Indices start at one, so if `alist = {'value'}` then `alist[1] == 'value'`. Lists, when part of an element, or when generated during marshaling, are made instances of the `pandoc.List` type for convenience. The `pandoc.List` type is defined in the [*pandoc.List*](#module-pandoc.list) module. See there for available methods. Values of this type can be created with the [`pandoc.List`](#pandoc.list) constructor, turning a normal Lua table into a List. ## LogMessage {#type-logmessage} A pandoc log message. Objects have no fields, but can be converted to a string via `tostring`. ## SimpleTable {#type-simpletable} A simple table is a table structure which resembles the old (pre pandoc 2.10) Table type. Bi-directional conversion from and to [Tables](#type-table) is possible with the [`pandoc.utils.to_simple_table`](#pandoc.utils.to_simple_table) and [`pandoc.utils.from_simple_table`](#pandoc.utils.from_simple_table) function, respectively. Instances of this type can also be created directly with the [`pandoc.SimpleTable`](#pandoc.simpletable) constructor. Fields: `caption` : [Inlines][] `aligns` : column alignments ([List] of [Alignments](#type-alignment)) `widths` : column widths; a ([List] of numbers) `headers` : table header row ([List] of simple cells, i.e., [List] of [Blocks][]) `rows` : table rows ([List] of rows, where a row is a list of simple cells, i.e., [List] of [Blocks][]) ## Template {#type-template} Opaque type holding a compiled template. ## Version {#type-version} A version object. This represents a software version like "2.7.3". The object behaves like a numerically indexed table, i.e., if `version` represents the version `2.7.3`, then version[1] == 2 version[2] == 7 version[3] == 3 #version == 3 -- length Comparisons are performed element-wise, i.e. Version '1.12' > Version '1.9' Values of this type can be created with the [`pandoc.types.Version`](#pandoc.types.Version) constructor. ### `must_be_at_least` `must_be_at_least(actual, expected [, error_message])` Raise an error message if the actual version is older than the expected version; does nothing if `actual` is equal to or newer than the expected version. Parameters: `actual` : actual version specifier ([Version]) `expected` : minimum expected version ([Version]) `error_message` : optional error message template. The string is used as format string, with the expected and actual versions as arguments. Defaults to `"expected version %s or newer, got %s"`. Usage: PANDOC_VERSION:must_be_at_least '2.7.3' PANDOC_API_VERSION:must_be_at_least( '1.17.4', 'pandoc-types is too old: expected version %s, got %s' ) [Attributes]: #type-attributes [Cells]: #type-cell [Citation]: #type-citation [Citations]: #type-citation [ColSpec]: #type-colspec [CommonState]: #type-commonstate [Div]: #type-div [Image]: #type-image [List]: #type-list [ListAttributes]: #type-listattributes [Meta]: #type-meta [MetaBlocks]: #type-metablocks [MetaValue]: #type-metavalue [MetaValues]: #type-metavalue [LogMessage]: #type-logmessage [Pandoc]: #type-pandoc [Para]: #type-para [Plain]: #type-plain [Rows]: #type-row [SimpleTable]: #type-simpletable [Version]: #type-version ## Chunk {#type-chunk} Part of a document; usually chunks are each written to a separate file. Fields: `heading` : heading text ([Inlines][]) `id` : identifier (string) `level` : level of topmost heading in chunk (integer) `number` : chunk number (integer) `section_number` : hierarchical section number (string) `path` : target filepath for this chunk (string) `up` : link to the enclosing section, if any ([Chunk][]|nil) `prev` : link to the previous section, if any ([Chunk][]|nil) `next` : link to the next section, if any ([Chunk][]|nil) `unlisted` : whether the section in this chunk should be listed in the TOC even if the chunk has no section number. (boolean) `contents` : the chunk's block contents ([Blocks][]) ## ChunkedDoc {#type-chunkeddoc} A Pandoc document divided into [Chunks]{#type-chunk}. The table of contents info in field `toc` is rose-tree structure represented as a list. The node item is always placed at index `0`; subentries make up the rest of the list. Each node item contains the fields `title` ([Inlines][]), `number` (string|nil), `id` (string), `path` (string), and `level` (integer). Fields: `chunks` : list of chunks that make up the document (list of [Chunks](#type-chunk)). `meta` : the document's metadata ([Meta][]) `toc` : table of contents information (table) # Module pandoc Fields and functions for pandoc scripts; includes constructors for document tree elements, functions to parse text in a given format, and functions to filter and modify a subtree. ## Fields {#pandoc-fields} ### readers {#pandoc.readers} Set of formats that pandoc can parse. All keys in this table can be used as the `format` value in `pandoc.read`. (table) ### writers {#pandoc.writers} Set of formats that pandoc can generate. All keys in this table can be used as the `format` value in `pandoc.write`. (table) ## Functions {#pandoc-functions} ### Pandoc {#pandoc.Pandoc} `Pandoc (blocks[, meta])` Parameters: `blocks` : document contents ([Blocks]) `meta` : document metadata ([Meta]) Returns: - new Pandoc document ([Pandoc]) ### Meta {#pandoc.Meta} `Meta (meta)` Parameters: `meta` : table containing meta information (table) Returns: - new Meta table (table) ### MetaBlocks {#pandoc.MetaBlocks} `MetaBlocks (content)` Creates a value to be used as a MetaBlocks value in meta data; creates a copy of the input list via `pandoc.Blocks`, discarding all non-list keys. Parameters: `content` : block content ([Blocks]) Returns: - list of Block elements ([Blocks]) ### MetaBool {#pandoc.MetaBool} `MetaBool (bool)` Parameters: `bool` : true or false (boolean) Returns: - input, unchanged (boolean) ### MetaInlines {#pandoc.MetaInlines} `MetaInlines (inlines)` Creates a value to be used as a MetaInlines value in meta data; creates a copy of the input list via `pandoc.Inlines`, discarding all non-list keys. Parameters: `inlines` : inline elements ([Inlines]) Returns: - list of Inline elements ([Inlines]) ### MetaList {#pandoc.MetaList} `MetaList (values)` Creates a value to be used as a MetaList in meta data; creates a copy of the input list via `pandoc.List`, discarding all non-list keys. Parameters: `values` : value, or list of values ([MetaValue]\|{[MetaValue],\...}) Returns: - list of meta values ([List]{unknown-type="List"}) ### MetaMap {#pandoc.MetaMap} `MetaMap (key_value_map)` Creates a value to be used as a MetaMap in meta data; creates a copy of the input table, keeping only pairs with string keys and discards all other keys. Parameters: `key_value_map` : a string-indexed map of meta values (table) Returns: - map of meta values (table) ### MetaString {#pandoc.MetaString} `MetaString (s)` Creates a value to be used as a MetaString in meta data; this is the identity function for boolean values and exists only for completeness. Parameters: `s` : string value (string) Returns: - unchanged input (string) ### BlockQuote {#pandoc.BlockQuote} `BlockQuote (content)` Creates a block quote element Parameters: `content` : block content ([Blocks]) Returns: - BlockQuote element ([Block]) ### BulletList {#pandoc.BulletList} `BulletList (items)` Creates a bullet list. Parameters: `items` : list items ({[Blocks],\...}) Returns: - BulletList element ([Block]) ### CodeBlock {#pandoc.CodeBlock} `CodeBlock (text[, attr])` Creates a code block element. Parameters: `text` : code string (string) `attr` : element attributes ([Attr]) Returns: - CodeBlock element ([Block]) ### DefinitionList {#pandoc.DefinitionList} `DefinitionList (content)` Creates a definition list, containing terms and their explanation. Parameters: `content` : definition items ([{{Inlines, {Blocks,\...}},\...}]{unknown-type="{{Inlines, {Blocks,...}},...}"}) Returns: - DefinitionList element ([Block]) ### Div {#pandoc.Div} `Div (content[, attr])` Creates a div element Parameters: `content` : block content ([Blocks]) `attr` : element attributes ([Attr]) Returns: - Div element ([Block]) ### Figure {#pandoc.Figure} `Figure (content[, caption[, attr]])` Creates a [Figure] element. Parameters: `content` : figure block content ([Blocks]) `caption` : figure caption ([Caption]) `attr` : element attributes ([Attr]) Returns: - Figure object ([Block]) ### Header {#pandoc.Header} `Header (level, content[, attr])` Creates a header element. Parameters: `level` : heading level ([integer]{unknown-type="integer"}) `content` : inline content ([Inlines]) `attr` : element attributes ([Attr]) Returns: - Header element ([Block]) ### HorizontalRule {#pandoc.HorizontalRule} `HorizontalRule ()` Creates a horizontal rule. Returns: - HorizontalRule element ([Block]) ### LineBlock {#pandoc.LineBlock} `LineBlock (content)` Creates a line block element. Parameters: `content` : lines ({[Inlines],\...}) Returns: - LineBlock element ([Block]) ### OrderedList {#pandoc.OrderedList} `OrderedList (items[, listAttributes])` Creates an ordered list. Parameters: `items` : list items ({[Blocks],\...}) `listAttributes` : list parameters ([ListAttributes]) Returns: - OrderedList element ([Block]) ### Para {#pandoc.Para} `Para (content)` Creates a para element. Parameters: `content` : inline content ([Inlines]) Returns: - Para element ([Block]) ### Plain {#pandoc.Plain} `Plain (content)` Creates a plain element. Parameters: `content` : inline content ([Inlines]) Returns: - Plain element ([Block]) ### RawBlock {#pandoc.RawBlock} `RawBlock (format, text)` Creates a raw content block of the specified format. Parameters: `format` : format of content (string) `text` : raw content (string) Returns: - RawBlock element ([Block]) ### Table {#pandoc.Table} `Table (caption, colspecs, head, bodies, foot[, attr])` Creates a table element. Parameters: `caption` : table caption ([Caption]) `colspecs` : column alignments and widths ({[ColSpec],\...}) `head` : table head ([TableHead]) `bodies` : table bodies ({[TableBody],\...}) `foot` : table foot ([TableFoot]) `attr` : element attributes ([Attr]) Returns: - Table element ([Block]) ### Blocks {#pandoc.Blocks} `Blocks (block_like_elements)` Creates a [Blocks] list. Parameters: `block_like_elements` : List where each element can be treated as a [Block] value, or a single such value. ([Blocks]) Returns: - list of block elements ([Blocks]) ### Cite {#pandoc.Cite} `Cite (content, citations)` Creates a Cite inline element Parameters: `content` : placeholder content ([Inlines]) `citations` : List of Citations ({[Citation]{unknown-type="Citation"},\...}) Returns: - cite element ([Inline]) ### Code {#pandoc.Code} `Code (code[, attr])` Creates a Code inline element Parameters: `code` : code string (string) `attr` : additional attributes ([Attr]) Returns: - code element ([Inline]) ### Emph {#pandoc.Emph} `Emph (content)` Creates an inline element representing emphasized text. Parameters: `content` : inline content ([Inlines]) Returns: - new object ([Inline]) ### Image {#pandoc.Image} `Image (caption, src[, title[, attr]])` Creates an Image element Parameters: `caption` : text used to describe the image ([Inlines]) `src` : path to the image file (string) `title` : brief image description (string) `attr` : image attributes ([Attr]) Returns: - Image element ([Inline]) ### LineBreak {#pandoc.LineBreak} `LineBreak ()` Create a LineBreak inline element Returns: - line break ([Inline]) ### Link {#pandoc.Link} `Link (content, target[, title[, attr]])` Creates a link inline element, usually a hyperlink. Parameters: `content` : text for this link ([Inlines]) `target` : the link target (string) `title` : brief link description (string) `attr` : link attributes ([Attr]) Returns: - link element ([Inline]) ### Math {#pandoc.Math} `Math (mathtype, text)` Creates a Math element, either inline or displayed. Parameters: `mathtype` : rendering specifier ([MathType]{unknown-type="MathType"}) `text` : math content (string) Returns: - math element ([Inline]) ### Note {#pandoc.Note} `Note (content)` Creates a Note inline element Parameters: `content` : footnote block content ([Blocks]) Returns: - note ([Inline]) ### Quoted {#pandoc.Quoted} `Quoted (quotetype, content)` Creates a Quoted inline element given the quote type and quoted content. Parameters: `quotetype` : type of quotes ([QuoteType]{unknown-type="QuoteType"}) `content` : inlines in quotes ([Inlines]) Returns: - quoted element ([Inline]) ### RawInline {#pandoc.RawInline} `RawInline (format, text)` Creates a raw inline element Parameters: `format` : format of content (string) `text` : string content (string) Returns: - raw inline element ([Inline]) ### SmallCaps {#pandoc.SmallCaps} `SmallCaps (content)` Creates text rendered in small caps Parameters: `content` : inline content ([Inlines]) Returns: - new object ([Inline]) ### SoftBreak {#pandoc.SoftBreak} `SoftBreak ()` Creates a SoftBreak inline element. Returns: - soft break ([Inline]) ### Space {#pandoc.Space} `Space ()` Create a Space inline element Returns: - new space ([Inline]) ### Span {#pandoc.Span} `Span (content[, attr])` Creates a Span inline element Parameters: `content` : inline content ([Inlines]) `attr` : additional attributes ([Attr]) Returns: - [Span] object ([Inline]) ### Str {#pandoc.Str} `Str (text)` Creates a Str inline element Parameters: `text` : (string) Returns: - [Str] object ([Inline]) ### Strikeout {#pandoc.Strikeout} `Strikeout (content)` Creates text which is struck out. Parameters: `content` : inline content ([Inlines]) Returns: - new object ([Inline]) ### Strong {#pandoc.Strong} `Strong (content)` Creates a Strong element, whose text is usually displayed in a bold font. Parameters: `content` : inline content ([Inlines]) Returns: - new object ([Inline]) ### Subscript {#pandoc.Subscript} `Subscript (content)` Creates a Subscript inline element Parameters: `content` : inline content ([Inlines]) Returns: - new object ([Inline]) ### Superscript {#pandoc.Superscript} `Superscript (content)` Creates a Superscript inline element Parameters: `content` : inline content ([Inlines]) Returns: - new object ([Inline]) ### Underline {#pandoc.Underline} `Underline (content)` Creates an Underline inline element Parameters: `content` : inline content ([Inlines]) Returns: - new object ([Inline]) ### Inlines {#pandoc.Inlines} `Inlines (inline_like_elements)` Converts its argument into an [Inlines] list: - copies a list of [Inline] elements into a fresh list; any string `s` within the list is treated as `pandoc.Str(s)`; - turns a single [Inline] into a singleton list; - splits a string into `Str`-wrapped words, treating interword spaces as `Space`s or `SoftBreak`s. Parameters: `inline_like_elements` : List where each element can be treated as an [Inline] value, or just a single such value. ([Inlines]) Returns: - list of inline elements ([Inlines]) ### Attr {#pandoc.Attr} `Attr ([identifier[, classes[, attributes]]])` Create a new set of attributes Parameters: `identifier` : element identifier (string\|table\|[Attr]) `classes` : element classes ({string,\...}) `attributes` : table containing string keys and values (table\|[AttributeList]) Returns: - new Attr object ([Attr]) ### Caption {#pandoc.Caption} `Caption ([long[, short]])` Creates a new Caption object. Parameters: `long` : full caption ([Blocks]) `short` : short summary caption ([Inlines]) Returns: - new Caption object ([Caption]) *Since: 3.6.1* ### Cell {#pandoc.Cell} `Cell (blocks[, align[, rowspan[, colspan[, attr]]]])` Create a new table cell. Parameters: `blocks` : cell contents ([Blocks]) `align` : text alignment; defaults to `AlignDefault` ([Alignment]) `rowspan` : number of rows occupied by the cell; defaults to `1` ([integer]{unknown-type="integer"}) `colspan` : number of columns occupied by the cell; defaults to `1` ([integer]{unknown-type="integer"}) `attr` : cell attributes ([Attr]) Returns: - new Cell object ([Cell]) ### AttributeList {#pandoc.AttributeList} `AttributeList (attribs)` Parameters: `attribs` : an attribute list (table\|[AttributeList]) Returns: - new AttributeList object ([AttributeList]) ### Citation {#pandoc.Citation} `Citation (id, mode[, prefix[, suffix[, note_num[, hash]]]])` Creates a single citation. Parameters: `id` : citation ID (e.g. BibTeX key) (string) `mode` : citation rendering mode ([CitationMode]{unknown-type="CitationMode"}) `prefix` : ([Inlines]) `suffix` : ([Inlines]) `note_num` : note number ([integer]{unknown-type="integer"}) `hash` : hash number ([integer]{unknown-type="integer"}) Returns: - new citation object ([Citation]{unknown-type="Citation"}) ### ListAttributes {#pandoc.ListAttributes} `ListAttributes ([start[, style[, delimiter]]])` Creates a new ListAttributes object. Parameters: `start` : number of the first list item ([integer]{unknown-type="integer"}) `style` : style used for list numbering (string) `delimiter` : delimiter of list numbers (string) Returns: - new ListAttributes object ([ListAttributes]) ### Row {#pandoc.Row} `Row ([cells[, attr]])` Creates a table row. Parameters: `cells` : list of table cells in this row ({[Cell],\...}) `attr` : row attributes ([Attr]) Returns: - new Row object ([Row]) ### TableFoot {#pandoc.TableFoot} `TableFoot ([rows[, attr]])` Creates a table foot. Parameters: `rows` : list of table rows ({[Row],\...}) `attr` : table foot attributes ([Attr]) Returns: - new TableFoot object ([TableFoot]) ### TableHead {#pandoc.TableHead} `TableHead ([rows[, attr]])` Creates a table head. Parameters: `rows` : list of table rows ({[Row],\...}) `attr` : table head attributes ([Attr]) Returns: - new TableHead object ([TableHead]) ### SimpleTable {#pandoc.SimpleTable} `SimpleTable (caption, align, widths, header, rows)` Usage: local caption = "Overview" local aligns = {pandoc.AlignDefault, pandoc.AlignDefault} local widths = {0, 0} -- let pandoc determine col widths local headers = {{pandoc.Plain({pandoc.Str "Language"})}, {pandoc.Plain({pandoc.Str "Typing"})}} local rows = { {{pandoc.Plain "Haskell"}, {pandoc.Plain "static"}}, {{pandoc.Plain "Lua"}, {pandoc.Plain "Dynamic"}}, } simple_table = pandoc.SimpleTable( caption, aligns, widths, headers, rows ) Parameters: `caption` : table caption ([Inlines]) `align` : column alignments ({[Alignment],\...}) `widths` : relative column widths ({number,\...}) `header` : table header row ({[Blocks],\...}) `rows` : table rows ({{[Blocks],\...},\...}) Returns: - new SimpleTable object ([SimpleTable]) ## Constants [`AuthorInText`]{#pandoc.authorintext} : Author name is mentioned in the text. See also: [Citation](#type-citation) [`SuppressAuthor`]{#pandoc.suppressauthor} : Author name is suppressed. See also: [Citation](#type-citation) [`NormalCitation`]{#pandoc.normalcitation} : Default citation style is used. See also: [Citation](#type-citation) [`DisplayMath`]{#pandoc.displaymath} : Math style identifier, marking that the formula should be show in "display" style, i.e., on a separate line. See also: [Math](#type-math) [`InlineMath`]{#pandoc.inlinemath} : Math style identifier, marking that the formula should be show inline. See also: [Math](#type-math) [`SingleQuote`]{#pandoc.singlequote} : Quote type used with [Quoted](#type-quoted), indicating that the string is enclosed in *single* quotes. See also: [Quoted](#type-quoted) [`DoubleQuote`]{#pandoc.doublequote} : Quote type used with [Quoted](#type-quoted), indicating that the string is enclosed in *double* quotes. See also: [Quoted](#type-quoted) [`AlignLeft`]{#pandoc.alignleft} : Table cells aligned left. See also: [Table](#type-alignment) [`AlignRight`]{#pandoc.alignright} : Table cells right-aligned. See also: [Table](#type-alignment) [`AlignCenter`]{#pandoc.aligncenter} : Table cell content is centered. See also: [Table](#type-alignment) [`AlignDefault`]{#pandoc.aligndefault} : Table cells are alignment is unaltered. See also: [Table](#type-alignment) [`DefaultDelim`]{#pandoc.defaultdelim} : Default list number delimiters are used. See also: [ListAttributes](#type-listattributes) [`Period`]{#pandoc.period} : List numbers are delimited by a period. See also: [ListAttributes](#type-listattributes) [`OneParen`]{#pandoc.oneparen} : List numbers are delimited by a single parenthesis. See also: [ListAttributes](#type-listattributes) [`TwoParens`]{#pandoc.twoparens} : List numbers are delimited by a double parentheses. See also: [ListAttributes](#type-listattributes) [`DefaultStyle`]{#pandoc.defaultstyle} : List are numbered in the default style See also: [ListAttributes](#type-listattributes) [`Example`]{#pandoc.example} : List items are numbered as examples. See also: [ListAttributes](#type-listattributes) [`Decimal`]{#pandoc.decimal} : List are numbered using decimal integers. See also: [ListAttributes](#type-listattributes) [`LowerRoman`]{#pandoc.lowerroman} : List are numbered using lower-case roman numerals. See also: [ListAttributes](#type-listattributes) [`UpperRoman`]{#pandoc.upperroman} : List are numbered using upper-case roman numerals See also: [ListAttributes](#type-listattributes) [`LowerAlpha`]{#pandoc.loweralpha} : List are numbered using lower-case alphabetic characters. See also: [ListAttributes](#type-listattributes) [`UpperAlpha`]{#pandoc.upperalpha} : List are numbered using upper-case alphabetic characters. See also: [ListAttributes](#type-listattributes) [`sha1`]{#pandoc.sha1} : Alias for [`pandoc.utils.sha1`](#pandoc.utils.sha1) (DEPRECATED, use `pandoc.utils.sha1` instead). ## Other constructors ### `ReaderOptions (opts)` {#pandoc.readeroptions} Creates a new [ReaderOptions] value. Parameters `opts` : Either a table with a subset of the properties of a [ReaderOptions] object, or another ReaderOptions object. Uses the defaults specified in the manual for all properties that are not explicitly specified. Throws an error if a table contains properties which are not present in a ReaderOptions object. ([ReaderOptions]|table) Returns: new [ReaderOptions] object Usage: -- copy of the reader options that were defined on the command line. local cli_opts = pandoc.ReaderOptions(PANDOC_READER_OPTIONS) -- default reader options, but columns set to 66. local short_colums_opts = pandoc.ReaderOptions {columns = 66} ### `WriterOptions (opts)` {#pandoc.writeroptions} Creates a new [WriterOptions][] value. Parameters `opts` : Either a table with a subset of the properties of a [WriterOptions] object, or another WriterOptions object. Uses the defaults specified in the manual for all properties that are not explicitly specified. Throws an error if a table contains properties which are not present in a WriterOptions object. ([WriterOptions]|table) Returns: new [WriterOptions] object Usage: -- copy of the writer options that were defined on the command line. local cli_opts = pandoc.WriterOptions(PANDOC_WRITER_OPTIONS) -- default writer options, but DPI set to 300. local short_colums_opts = pandoc.WriterOptions {dpi = 300} ## Helper functions ### `pipe (command, args, input)` {#pandoc.pipe} Runs command with arguments, passing it some input, and returns the output. Parameters: `command` : program to run; the executable will be resolved using default system methods (string). `args` : list of arguments to pass to the program (list of strings). `input` : data which is piped into the program via stdin (string). Returns: - Output of command, i.e. data printed to stdout (string) Raises: - A table containing the keys `command`, `error_code`, and `output` is thrown if the command exits with a non-zero error code. Usage: local output = pandoc.pipe("sed", {"-e","s/a/b/"}, "abc") ### `walk_block (element, filter)` {#pandoc.walk_block} Apply a filter inside a block element, walking its contents. Returns a (deep) copy on which the filter has been applied: the original element is left untouched. Parameters: `element` : the block element `filter` : a Lua filter (table of functions) to be applied within the block element Returns: the transformed block element ### `walk_inline (element, filter)` {#pandoc.walk_inline} Apply a filter inside an inline element, walking its contents. Returns a (deep) copy on which the filter has been applied: the original element is left untouched. Parameters: `element` : the inline element `filter` : a Lua filter (table of functions) to be applied within the inline element Returns: the transformed inline element ### `with_state (options, callback)` {#pandoc.with_state} Runs a function with a modified pandoc state. The given callback is invoked after setting the pandoc state to the given values. The modifiable options are restored to their original values once the callback has returned. The following state variables can be controlled: - `request_headers` (list of key-value tuples) - `resource_path` (list of filepaths) - `user_data_dir` (string) Other options are ignored, and the rest of the state is not modified. Usage: local opts = { request_headers = { {'Authorization', 'Basic my-secret'} } } pandoc.with_state(opts, function () local mime, contents = pandoc.mediabag.fetch(image_url) ) ### `read (markup[, format[, reader_options[, read_env]]])` {#pandoc.read} Parse the given string into a Pandoc document. The parser is run in the same environment that was used to read the main input files; it has full access to the file-system and the mediabag. This means that if the document specifies files to be included, as is possible in formats like LaTeX, reStructuredText, and Org, then these will be included in the resulting document. Any media elements are added to those retrieved from the other parsed input files. Use the `read_env` parameter to modify this behavior. The `format` parameter defines the format flavor that will be parsed. This can be either a string, using `+` and `-` to enable and disable extensions, or a table with fields `format` (string) and `extensions` (table). The `extensions` table can be a list of all enabled extensions, or a table with extensions as keys and their activation status as values (`true` or `'enable'` to enable an extension, `false` or `'disable'` to disable it). Note: The extensions field in `reader_options` is ignored, as the function will always use the format extensions specified via the `format` parameter. Parameters: `markup` : the markup to be parsed (string|Sources) `format` : format specification; defaults to `"markdown"`. See the description above for a complete description of this parameter. (string|table) `reader_options` : options passed to the reader; may be a ReaderOptions object or a table with a subset of the keys and values of a ReaderOptions object; defaults to the default values documented in the manual. ([ReaderOptions]|table) `read_env` : If the value is not given or `nil`, then the global environment is used. Passing a list of filenames causes the reader to be run in a sandbox. The given files are read from the file system and provided to the sandbox via an ersatz file system. The table can also contain mappings from filenames to contents, which will be used to populate the ersatz file system. Returns: pandoc document ([Pandoc](#type-pandoc)) Usage: local org_markup = "/emphasis/" -- Input to be read local document = pandoc.read(org_markup, "org") -- Get the first block of the document local block = document.blocks[1] -- The inline element in that block is an `Emph` assert(block.content[1].t == "Emph") [ReaderOptions]: #type-readeroptions ### `write (doc[, format[, writer_options]])` {#pandoc.write} Converts a document to the given target format. Note: The extensions field in `writer_options` is ignored, as the function will always use the format extensions specified via the `format` parameter. Parameters: `doc` : document to convert ([Pandoc](#type-pandoc)) `format` : format specification; defaults to `"html"`. See the documentation of [`pandoc.read`](#pandoc.read) for a complete description of this parameter. (string|table) `writer_options` : options passed to the writer; may be a WriterOptions object or a table with a subset of the keys and values of a WriterOptions object; defaults to the default values documented in the manual. ([WriterOptions]|table) Returns: - converted document (string) Usage: local doc = pandoc.Pandoc( {pandoc.Para {pandoc.Strong 'Tea'}} ) local html = pandoc.write(doc, 'html') assert(html == "

Tea

") ### `write_classic (doc[, writer_options])` {#pandoc.write_custom} Runs a classic custom Lua writer, using the functions defined in the current environment. Parameters: `doc` : document to convert ([Pandoc](#type-pandoc)) `writer_options` : options passed to the writer; may be a [WriterOptions] object or a table with a subset of the keys and values of a WriterOptions object; defaults to the default values documented in the manual. ([WriterOptions]|table) Returns: - converted document (string) Usage: -- Adding this function converts a classic writer into a -- new-style custom writer. function Writer (doc, opts) PANDOC_DOCUMENT = doc PANDOC_WRITER_OPTIONS = opts loadfile(PANDOC_SCRIPT_FILE)() return pandoc.write_classic(doc, opts) end [WriterOptions]: #type-writeroptions # Module pandoc.cli Command line options and argument parsing. ## Fields {#pandoc.cli-fields} ### default_options {#pandoc.cli.default_options} Default CLI options, using a JSON-like representation. (table) ## Functions {#pandoc.cli-functions} ### parse_options {#pandoc.cli.parse_options} `parse_options (args)` Parses command line arguments into pandoc options. Typically this function will be used in stand-alone pandoc Lua scripts, taking the list of arguments from the global `arg`. Parameters: `args` : list of command line arguments ({string,\...}) Returns: - parsed options, using their JSON-like representation. (table) *Since: 3.0* ### repl {#pandoc.cli.repl} `repl ([env])` Starts a read-eval-print loop (REPL). The function returns all values of the last evaluated input. Exit the REPL by pressing `ctrl-d` or `ctrl-c`; press `F1` to get a list of all key bindings. The REPL is started in the global namespace, unless the `env` parameter is specified. In that case, the global namespace is merged into the given table and the result is used as `_ENV` value for the repl. Specifically, local variables *cannot* be accessed, unless they are explicitly passed via the `env` parameter; e.g. function Pandoc (doc) -- start repl, allow to access the `doc` parameter -- in the repl return pandoc.cli.repl{ doc = doc } end **Note**: it seems that the function exits immediately on Windows, without prompting for user input. Parameters: `env` : Extra environment; the global environment is merged into this table. (table) Returns: The result(s) of the last evaluated input, or nothing if the last input resulted in an error. *Since: 3.1.2* # Module pandoc.utils This module exposes internal pandoc functions and utility functions. ## Functions {#pandoc.utils-functions} ### blocks_to_inlines {#pandoc.utils.blocks_to_inlines} `blocks_to_inlines (blocks[, sep])` Squash a list of blocks into a list of inlines. Usage local blocks = { pandoc.Para{ pandoc.Str 'Paragraph1' }, pandoc.Para{ pandoc.Emph 'Paragraph2' } } local inlines = pandoc.utils.blocks_to_inlines(blocks) assert( inlines == pandoc.Inlines { pandoc.Str 'Paragraph1', pandoc.Linebreak(), pandoc.Emph{ pandoc.Str 'Paragraph2' } } ) Parameters: `blocks` : List of [Block] elements to be flattened. ([Blocks]) `sep` : List of [Inline] elements inserted as separator between two consecutive blocks; defaults to `{pandoc.LineBreak()}`. ([Inlines]) Returns: - ([Inlines]) *Since: 2.2.3* ### citeproc {#pandoc.utils.citeproc} `citeproc (doc)` Process the citations in the file, replacing them with rendered citations and adding a bibliography. See the manual section on citation rendering for details. Usage: -- Lua filter that behaves like `--citeproc` function Pandoc (doc) return pandoc.utils.citeproc(doc) end Parameters: `doc` : document ([Pandoc]) Returns: - processed document ([Pandoc]) *Since: 2.19.1* ### documentation {#pandoc.utils.documentation} `documentation (object[, format])` Return the documentation for a function or module defined by pandoc. Throws an error if there is no documentation for the given object. The result format can be any textual format accepted by `pandoc.write`, and the documentation will be returned in that format. Additionally, the special format `blocks` is accepted, in which case the documentation is returned as [Blocks]. Parameters: `object` : Retrieve documentation for this object (any) `format` : result format; defaults to `'ansi'` (string\|table) Returns: - rendered documentation (string\|[Blocks]) *Since: 3.8.4* ### equals {#pandoc.utils.equals} `equals (element1, element2)` Test equality of AST elements. Elements in Lua are considered equal if and only if the objects obtained by unmarshaling are equal. **This function is deprecated.** Use the normal Lua `==` equality operator instead. Parameters: `element1` : (any) `element2` : (any) Returns: - Whether the two objects represent the same element (boolean) *Since: 2.5* ### from_simple_table {#pandoc.utils.from_simple_table} `from_simple_table (simple_tbl)` Creates a [Table] block element from a [SimpleTable]. This is useful for dealing with legacy code which was written for pandoc versions older than 2.10. Usage: local simple = pandoc.SimpleTable(table) -- modify, using pre pandoc 2.10 methods simple.caption = pandoc.SmallCaps(simple.caption) -- create normal table block again table = pandoc.utils.from_simple_table(simple) Parameters: `simple_tbl` : ([SimpleTable]) Returns: - table block element ([Block]) *Since: 2.11* ### make_sections {#pandoc.utils.make_sections} `make_sections (number_sections, baselevel, blocks)` Converts a list of [Block] elements into sections. `Div`s will be created beginning at each `Header` and containing following content until the next `Header` of comparable level. If `number_sections` is true, a `number` attribute will be added to each `Header` containing the section number. If `base_level` is non-null, `Header` levels will be reorganized so that there are no gaps, and so that the base level is the level specified. Parameters: `number_sections` : whether section divs should get an additional `number` attribute containing the section number. (boolean) `baselevel` : shift top-level headings to this level (integer\|nil) `blocks` : list of blocks to process ([Blocks]) Returns: - blocks with sections ([Blocks]) *Since: 2.8* ### normalize_date {#pandoc.utils.normalize_date} `normalize_date (date)` Parse a date and convert (if possible) to "YYYY-MM-DD" format. We limit years to the range 1601-9999 (ISO 8601 accepts greater than or equal to 1583, but MS Word only accepts dates starting 1601). Returns nil instead of a string if the conversion failed. Parameters: `date` : the date string (string) Returns: - normalized date, or nil if normalization failed. (string\|nil) *Since: 2.0.6* ### references {#pandoc.utils.references} `references (doc)` Get references defined inline in the metadata and via an external bibliography. Only references that are actually cited in the document (either with a genuine citation or with `nocite`) are returned. URL variables are converted to links. The structure used represent reference values corresponds to that used in CSL JSON; the return value can be use as `references` metadata, which is one of the values used by pandoc and citeproc when generating bibliographies. Usage: -- Include all cited references in document function Pandoc (doc) doc.meta.references = pandoc.utils.references(doc) doc.meta.bibliography = nil return doc end Parameters: `doc` : document ([Pandoc]) Returns: - lift of references. (table) *Since: 2.17* ### run_json_filter {#pandoc.utils.run_json_filter} `run_json_filter (doc, filter[, args])` Filter the given doc by passing it through a JSON filter. Parameters: `doc` : the Pandoc document to filter ([Pandoc]) `filter` : filter to run (string) `args` : list of arguments passed to the filter. Defaults to `{FORMAT}`. ({string,\...}) Returns: - filtered document ([Pandoc]) *Since: 2.1.1* ### run_lua_filter {#pandoc.utils.run_lua_filter} `run_lua_filter (doc, filter[, env])` Filter the given doc by passing it through a Lua filter. The filter will be run in the current Lua process. Parameters: `doc` : the Pandoc document to filter ([Pandoc]) `filter` : filepath of the filter to run (string) `env` : environment to load and run the filter in (table) Returns: - filtered document ([Pandoc]) *Since: 3.2.1* ### sha1 {#pandoc.utils.sha1} `sha1 (input)` Computes the SHA1 hash of the given string input. Parameters: `input` : (string) Returns: - hexadecimal hash value (string) *Since: 2.0.6* ### stringify {#pandoc.utils.stringify} `stringify (element)` Converts the given element (Pandoc, Meta, Block, or Inline) into a string with all formatting removed. Parameters: `element` : some pandoc AST element ([Pandoc]\|[Block]\|[Inline]\|[Caption]\|[Cell]\|[MetaValue]) Returns: - A plain string representation of the given element. (string) *Since: 2.0.6* ### to_roman_numeral {#pandoc.utils.to_roman_numeral} `to_roman_numeral (n)` Converts an integer \< 4000 to uppercase roman numeral. Usage: local to_roman_numeral = pandoc.utils.to_roman_numeral local pandoc_birth_year = to_roman_numeral(2006) -- pandoc_birth_year == 'MMVI' Parameters: `n` : positive integer below 4000 (integer) Returns: - A roman numeral. (string) *Since: 2.0.6* ### to_simple_table {#pandoc.utils.to_simple_table} `to_simple_table (tbl)` Converts a table into an old/simple table. Usage: local simple = pandoc.utils.to_simple_table(table) -- modify, using pre pandoc 2.10 methods simple.caption = pandoc.SmallCaps(simple.caption) -- create normal table block again table = pandoc.utils.from_simple_table(simple) Parameters: `tbl` : a table ([Block]) Returns: - SimpleTable object ([SimpleTable]) *Since: 2.11* ### type {#pandoc.utils.type} `type (value)` Pandoc-friendly version of Lua's default `type` function, returning type information similar to what is presented in the manual. The function works by checking the metafield `__name`. If the argument has a string-valued metafield `__name`, then it returns that string. Otherwise it behaves just like the normal `type` function. Usage: -- Prints one of 'string', 'boolean', 'Inlines', 'Blocks', -- 'table', and 'nil', corresponding to the Haskell constructors -- MetaString, MetaBool, MetaInlines, MetaBlocks, MetaMap, -- and an unset value, respectively. function Meta (meta) print('type of metavalue `author`:', pandoc.utils.type(meta.author)) end Parameters: `value` : any Lua value (any) Returns: - type of the given value (string) *Since: 2.17* ### Version {#pandoc.utils.Version} `Version (v)` Creates a Version object. Parameters: `v` : version description ([Version]\|string\|{integer,\...}\|number) Returns: - new Version object ([Version]) # Module pandoc.mediabag The `pandoc.mediabag` module allows accessing pandoc's media storage. The "media bag" is used when pandoc is called with the `--extract-media` or (for HTML only) `--embed-resources` option. The module is loaded as part of module `pandoc` and can either be accessed via the `pandoc.mediabag` field, or explicitly required, e.g.: local mb = require 'pandoc.mediabag' ## Functions {#pandoc.mediabag-functions} ### delete {#pandoc.mediabag.delete} `delete (filepath)` Removes a single entry from the media bag. Parameters: `filepath` : Filename of the item to deleted. The media bag will be left unchanged if no entry with the given filename exists. (string) *Since: 2.7.3* ### empty {#pandoc.mediabag.empty} `empty ()` Clear-out the media bag, deleting all items. *Since: 2.7.3* ### fetch {#pandoc.mediabag.fetch} `fetch (source)` Fetches the given source from a URL or local file. Returns two values: the contents of the file and the MIME type (or an empty string). The function will first try to retrieve `source` from the mediabag; if that fails, it will try to download it or read it from the local file system while respecting pandoc's "resource path" setting. Usage: local diagram_url = 'https://pandoc.org/diagram.jpg' local mt, contents = pandoc.mediabag.fetch(diagram_url) Parameters: `source` : path to a resource; either a local file path or URI (string) Returns: - The entry's MIME type, or `nil` if the file was not found. (string) - Contents of the file, or `nil` if the file was not found. (string) *Since: 2.0* ### fill {#pandoc.mediabag.fill} `fill (doc)` Fills the mediabag with the images in the given document. An image that cannot be retrieved will be replaced with a Span of class "image" that contains the image description. Images for which the mediabag already contains an item will not be processed again. Parameters: `doc` : document from which to fill the mediabag ([Pandoc]) Returns: - modified document ([Pandoc]) *Since: 2.19* ### insert {#pandoc.mediabag.insert} `insert (filepath, mimetype, contents)` Adds a new entry to pandoc's media bag. Replaces any existing media bag entry the same `filepath`. Usage: local fp = 'media/hello.txt' local mt = 'text/plain' local contents = 'Hello, World!' pandoc.mediabag.insert(fp, mt, contents) Parameters: `filepath` : filename and path relative to the output folder. (string) `mimetype` : the item's MIME type; use `nil` if the MIME type is unknown or unavailable. (string\|nil) `contents` : the binary contents of the file. (string) *Since: 2.0* ### items {#pandoc.mediabag.items} `items ()` Returns an iterator triple to be used with Lua's generic `for` statement. The iterator returns the filepath, MIME type, and content of a media bag item on each invocation. Items are processed one-by-one to avoid excessive memory use. This function should be used only when full access to all items, including their contents, is required. For all other cases, [`list`] should be preferred. Usage: for fp, mt, contents in pandoc.mediabag.items() do -- print(fp, mt, contents) end Returns: Iterator triple: - The iterator function; must be called with the iterator state and the current iterator value. - Iterator state -- an opaque value to be passed to the iterator function. - Initial iterator value. *Since: 2.7.3* ### list {#pandoc.mediabag.list} `list ()` Get a summary of the current media bag contents. Usage: -- calculate the size of the media bag. local mb_items = pandoc.mediabag.list() local sum = 0 for i = 1, #mb_items do sum = sum + mb_items[i].length end print(sum) Returns: - A list of elements summarizing each entry in the media bag. The summary item contains the keys `path`, `type`, and `length`, giving the filepath, MIME type, and length of contents in bytes, respectively. (table) *Since: 2.0* ### lookup {#pandoc.mediabag.lookup} `lookup (filepath)` Lookup a media item in the media bag, and return its MIME type and contents. Usage: local filename = 'media/diagram.png' local mt, contents = pandoc.mediabag.lookup(filename) Parameters: `filepath` : name of the file to look up. (string) Returns: - The entry's MIME type, or nil if the file was not found. (string) - Contents of the file, or nil if the file was not found. (string) *Since: 2.0* ### make_data_uri {#pandoc.mediabag.make_data_uri} `make_data_uri (mime_type, raw_data)` Convert the input data into a data URI as defined by RFC 2397. Example: -- Embed an unofficial pandoc logo local pandoc_logo_url = 'https://raw.githubusercontent.com/' .. 'tarleb/pandoc-logo/main/pandoc.svg' local datauri = pandoc.mediabag.make_data_uri( pandoc.mediabag.fetch(pandoc_logo_url) ) local image = pandoc.Image('Logo', datauri) Parameters: `mime_type` : MIME type of the data (string) `raw_data` : data to encode (string) Returns: - data uri (string) *Since: 3.7.1* ### write {#pandoc.mediabag.write} `write (dir[, fp])` Writes the contents of mediabag to the given target directory. If `fp` is given, then only the resource with the given name will be extracted. Omitting that parameter means that the whole mediabag gets extracted. An error is thrown if `fp` is given but cannot be found in the mediabag. Parameters: `dir` : path of the target directory (string) `fp` : canonical name (relative path) of resource (string) *Since: 3.0* # Module pandoc.List This module defines pandoc's list type. It comes with useful methods and convenience functions. ## Constructor [`pandoc.List([table])`]{#pandoc.list} : Create a new List. If the optional argument `table` is given, set the metatable of that value to `pandoc.List`. This is an alias for [`pandoc.List:new([table])`](#pandoc.list:new). ## Metamethods ### `pandoc.List:__concat (list)` {#pandoc.list:__concat} Concatenates two lists. Parameters: `list` : second list concatenated to the first Returns: a new list containing all elements from list1 and list2 ### `pandoc.List:__eq (a, b)` {#pandoc.list:__eq} Compares two lists for equality. The lists are taken as equal if and only if they are of the same type (i.e., have the same non-nil metatable), have the same length, and if all elements are equal. Parameters: `a`, `b` : any Lua object Returns: - `true` if the two lists are equal, `false` otherwise. ## Methods ### `pandoc.List:at` {#pandoc.list:at} `:at (index[, default])` Returns the element at the given index, or `default` if the list contains no item at the given position. Negative integers count back from the last item in the list. Parameters: `index` : element position (integer) `default` : the default value that is returned if the index is out of range (any) Returns: - the list item at `index`, or `default`. ### `pandoc.List:clone ()` {#pandoc.list:clone} Returns a (shallow) copy of the list. (To get a deep copy of the list, use `walk` with an empty filter.) ### `pandoc.List:extend (list)` {#pandoc.list:extend} Adds the given list to the end of this list. Parameters: `list` : list to appended ### `pandoc.List:find (needle, init)` {#pandoc.list:find} Returns the value and index of the first occurrence of the given item. Parameters: `needle` : item to search for `init` : index at which the search is started Returns: first item equal to the needle, or nil if no such item exists. ### `pandoc.List:find_if (pred, init)` {#pandoc.list:find_if} Returns the value and index of the first element for which the predicate holds true. Parameters: `pred` : the predicate function `init` : index at which the search is started Returns: first item for which \`test\` succeeds, or nil if no such item exists. ### `pandoc.List:filter (pred)` {#pandoc.list:filter} Returns a new list containing all items satisfying a given condition. Parameters: `pred` : condition items must satisfy. Returns: a new list containing all items for which \`test\` was true. ### `pandoc.List:includes (needle, init)` {#pandoc.list:includes} Checks if the list has an item equal to the given needle. Parameters: `needle` : item to search for `init` : index at which the search is started Returns: true if a list item is equal to the needle, false otherwise ### `pandoc.List:insert ([pos], value)` {#pandoc.list:insert} Inserts element `value` at position `pos` in list, shifting elements to the next-greater index if necessary. This function is identical to [`table.insert`](https://www.lua.org/manual/5.4/manual.html#6.6). Parameters: `pos` : index of the new value; defaults to length of the list + 1 `value` : value to insert into the list ### `pandoc.List:iter ([step])` {#pandoc.list:iter} Create an iterator over the list. The resulting function returns the next value each time it is called. Usage: for item in List{1, 1, 2, 3, 5, 8}:iter() do -- process item end Parameters: `step` : step width with which to step through the list. Negative step sizes will cause the iterator to start from the end of the list. Defaults to 1. (integer) Returns: - iterator (function) ### `pandoc.List:map (fn)` {#pandoc.list:map} Returns a copy of the current list by applying the given function to all elements. Parameters: `fn` : function which is applied to all list items. ### `pandoc.List:new([table])` {#pandoc.list:new} Create a new List. If the optional argument `table` is given, set the metatable of that value to `pandoc.List`. The function also accepts an iterator, in which case it creates a new list from the return values of the iterator function. Parameters: `table` : table which should be treatable as a list; defaults to an empty table Returns: the updated input value ### `pandoc.List:remove ([pos])` {#pandoc.list:remove} Removes the element at position `pos`, returning the value of the removed element. This function is identical to [`table.remove`](https://www.lua.org/manual/5.4/manual.html#6.6). Parameters: `pos` : position of the list value that will be removed; defaults to the index of the last element Returns: the removed element ### `pandoc.List:sort ([comp])` {#pandoc.list:sort} Sorts list elements in a given order, in-place. If `comp` is given, then it must be a function that receives two list elements and returns true when the first element must come before the second in the final order (so that, after the sort, `i < j` implies `not comp(list[j],list[i]))`. If comp is not given, then the standard Lua operator `<` is used instead. Note that the comp function must define a strict partial order over the elements in the list; that is, it must be asymmetric and transitive. Otherwise, no valid sort may be possible. The sort algorithm is not stable: elements considered equal by the given order may have their relative positions changed by the sort. This function is identical to [`table.sort`](https://www.lua.org/manual/5.4/manual.html#6.6). Parameters: `comp` : Comparison function as described above. # Module pandoc.format Information about the formats supported by pandoc. ## Functions {#pandoc.format-functions} ### all_extensions {#pandoc.format.all_extensions} `all_extensions (format)` Returns the list of all valid extensions for a format. No distinction is made between input and output; an extension can have an effect when reading a format but not when writing it, or *vice versa*. Parameters: `format` : format name (string) Returns: - all extensions supported for `format` ([FormatExtensions]) *Since: 3.0* ### default_extensions {#pandoc.format.default_extensions} `default_extensions (format)` Returns the list of default extensions of the given format; this function does not check if the format is supported, it will return a fallback list of extensions even for unknown formats. Parameters: `format` : format name (string) Returns: - default extensions enabled for `format` ([FormatExtensions]) *Since: 3.0* ### extensions {#pandoc.format.extensions} `extensions (format)` Returns the extension configuration for the given format. The configuration is represented as a table with all supported extensions as keys and their default status as value, with `true` indicating that the extension is enabled by default, while `false` marks a supported extension that's disabled. This function can be used to assign a value to the `Extensions` global in custom readers and writers. Parameters: `format` : format identifier (string) Returns: - extensions config (table) *Since: 3.0* ### from_path {#pandoc.format.from_path} `from_path (path)` Parameters: `path` : file path, or list of paths (string\|{string,\...}) Returns: - format determined by heuristic (string\|nil) *Since: 3.1.2* # Module pandoc.image Basic image querying functions. ## Functions {#pandoc.image-functions} ### size {#pandoc.image.size} `size (image[, opts])` Returns a table containing the size and resolution of an image; throws an error if the given string is not an image, or if the size of the image cannot be determined. The resulting table has four entries: *width*, *height*, *dpi_horz*, and *dpi_vert*. The `opts` parameter, when given, should be either a WriterOptions object such as `PANDOC_WRITER_OPTIONS`, or a table with a `dpi` entry. It affects the calculation for vector image formats such as SVG. Parameters: `image` : image data (string) `opts` : writer options ([WriterOptions]\|table) Returns: - image size information or error message (table) *Since: 3.1.13* ### format {#pandoc.image.format} `format (image)` Returns the format of an image as a lowercase string. Formats recognized by pandoc include *png*, *gif*, *tiff*, *jpeg*, *pdf*, *svg*, *eps*, and *emf*. Parameters: `image` : binary image data (string) Returns: - image format, or nil if the format cannot be determined (string\|nil) *Since: 3.1.13* # Module pandoc.json JSON module to work with JSON; based on the Aeson Haskell package. ## Fields {#pandoc.json-fields} ### null {#pandoc.json.null} Value used to represent the `null` JSON value. (light userdata) ## Functions {#pandoc.json-functions} ### decode {#pandoc.json.decode} `decode (str[, pandoc_types])` Creates a Lua object from a JSON string. If the input can be decoded as representing an [Inline], [Block], [Pandoc], [Inlines], or [Blocks] element the function will return an object of the appropriate type. Otherwise, if the input does not represent any of the AST types, the default decoding is applied: Objects and arrays are represented as tables, the JSON `null` value becomes [null], and JSON booleans, strings, and numbers are converted using the Lua types of the same name. The special handling of AST elements can be disabled by setting `pandoc_types` to `false`. Parameters: `str` : JSON string (string) `pandoc_types` : whether to use pandoc types when possible. (boolean) Returns: - decoded object (any) *Since: 3.1.1* ### encode {#pandoc.json.encode} `encode (object)` Encodes a Lua object as JSON string. If the object has a metamethod with name `__tojson`, then the result is that of a call to that method with `object` passed as the sole argument. The result of that call is expected to be a valid JSON string, but this is not checked. Parameters: `object` : object to convert (any) Returns: - JSON encoding of the given `object` (string) *Since: 3.1.1* # Module pandoc.log Access to pandoc's logging system. ## Functions {#pandoc.log-functions} ### info {#pandoc.log.info} `info (message)` Reports a ScriptingInfo message to pandoc's logging system. Parameters: `message` : the info message (string) *Since: 3.2* ### silence {#pandoc.log.silence} `silence (fn)` Applies the function to the given arguments while preventing log messages from being added to the log. The warnings and info messages reported during the function call are returned as the first return value, with the results of the function call following thereafter. Parameters: `fn` : function to be silenced (function) Returns: List of log messages triggered during the function call, and any value returned by the function. *Since: 3.2* ### warn {#pandoc.log.warn} `warn (message)` Reports a ScriptingWarning to pandoc's logging system. The warning will be printed to stderr unless logging verbosity has been set to *ERROR*. Parameters: `message` : the warning message (string) *Since: 3.2* # Module pandoc.path Module for file path manipulations. ## Fields {#pandoc.path-fields} ### separator {#pandoc.path.separator} The character that separates directories. (string) ### search_path_separator {#pandoc.path.search_path_separator} The character that is used to separate the entries in the `PATH` environment variable. (string) ## Functions {#pandoc.path-functions} ### directory {#pandoc.path.directory} `directory (filepath)` Gets the directory name, i.e., removes the last directory separator and everything after from the given path. Parameters: `filepath` : path (string) Returns: - The filepath up to the last directory separator. (string) *Since: 2.12* ### exists {#pandoc.path.exists} `exists (path[, type])` Check whether there exists a filesystem object at the given path. If `type` is given and either *directory* or *file*, then the function returns `true` if and only if the file system object has the given type, or if it's a symlink pointing to an object of that type. Passing *symlink* as type requires the path itself to be a symlink. Types other than those will cause an error. Parameters: `path` : file path to check (string) `type` : the required type of the filesystem object (string) Returns: - whether a filesystem object of type `type` exists at `path`. (boolean) *Since: 3.7.1* ### filename {#pandoc.path.filename} `filename (filepath)` Get the file name. Parameters: `filepath` : path (string) Returns: - File name part of the input path. (string) *Since: 2.12* ### is_absolute {#pandoc.path.is_absolute} `is_absolute (filepath)` Checks whether a path is absolute, i.e. not fixed to a root. Parameters: `filepath` : path (string) Returns: - `true` iff `filepath` is an absolute path, `false` otherwise. (boolean) *Since: 2.12* ### is_relative {#pandoc.path.is_relative} `is_relative (filepath)` Checks whether a path is relative or fixed to a root. Parameters: `filepath` : path (string) Returns: - `true` iff `filepath` is a relative path, `false` otherwise. (boolean) *Since: 2.12* ### join {#pandoc.path.join} `join (filepaths)` Join path elements back together by the directory separator. Parameters: `filepaths` : path components ({string,\...}) Returns: - The joined path. (string) *Since: 2.12* ### make_relative {#pandoc.path.make_relative} `make_relative (path, root[, unsafe])` Contract a filename, based on a relative path. Note that the resulting path will never introduce `..` paths, as the presence of symlinks means `../b` may not reach `a/b` if it starts from `a/c`. For a worked example see [this blog post]. Parameters: `path` : path to be made relative (string) `root` : root path (string) `unsafe` : whether to allow `..` in the result. (boolean) Returns: - contracted filename (string) *Since: 2.12* ### normalize {#pandoc.path.normalize} `normalize (filepath)` Normalizes a path. - `//` makes sense only as part of a (Windows) network drive; elsewhere, multiple slashes are reduced to a single `path.separator` (platform dependent). - `/` becomes `path.separator` (platform dependent). - `./` is removed. - an empty path becomes `.` Parameters: `filepath` : path (string) Returns: - The normalized path. (string) *Since: 2.12* ### split {#pandoc.path.split} `split (filepath)` Splits a path by the directory separator. Parameters: `filepath` : path (string) Returns: - List of all path components. ({string,\...}) *Since: 2.12* ### split_extension {#pandoc.path.split_extension} `split_extension (filepath)` Splits the last extension from a file path and returns the parts. The extension, if present, includes the leading separator; if the path has no extension, then the empty string is returned as the extension. Parameters: `filepath` : path (string) Returns: - filepath without extension (string) - extension or empty string (string) *Since: 2.12* ### split_search_path {#pandoc.path.split_search_path} `split_search_path (search_path)` Takes a string and splits it on the `search_path_separator` character. Blank items are ignored on Windows, and converted to `.` on Posix. On Windows path elements are stripped of quotes. Parameters: `search_path` : platform-specific search path (string) Returns: - list of directories in search path ({string,\...}) *Since: 2.12* ### treat_strings_as_paths {#pandoc.path.treat_strings_as_paths} `treat_strings_as_paths ()` Augment the string module such that strings can be used as path objects. *Since: 2.12* # Module pandoc.structure Access to the higher-level document structure, including hierarchical sections and the table of contents. ## Functions {#pandoc.structure-functions} ### make_sections {#pandoc.structure.make_sections} `make_sections (blocks[, opts])` Puts [Blocks] into a hierarchical structure: a list of sections (each a Div with class "section" and first element a Header). The optional `opts` argument can be a table; two settings are recognized: If `number_sections` is true, a `number` attribute containing the section number will be added to each `Header`. If `base_level` is an integer, then `Header` levels will be reorganized so that there are no gaps, with numbering levels shifted by the given value. Finally, an integer `slide_level` value triggers the creation of slides at that heading level. Note that a [WriterOptions] object can be passed as the opts table; this will set the `number_section` and `slide_level` values to those defined on the command line. Usage: local blocks = { pandoc.Header(2, pandoc.Str 'first'), pandoc.Header(2, pandoc.Str 'second'), } local opts = PANDOC_WRITER_OPTIONS local newblocks = pandoc.structure.make_sections(blocks, opts) Parameters: `blocks` : document blocks to process ([Blocks]\|[Pandoc]) `opts` : options (table) Returns: - processed blocks ([Blocks]) *Since: 3.0* ### slide_level {#pandoc.structure.slide_level} `slide_level (blocks)` Find level of header that starts slides (defined as the least header level that occurs before a non-header/non-hrule in the blocks). Parameters: `blocks` : document body ([Blocks]\|[Pandoc]) Returns: - slide level (integer) *Since: 3.0* ### split_into_chunks {#pandoc.structure.split_into_chunks} `split_into_chunks (doc[, opts])` Converts a [Pandoc] document into a [ChunkedDoc]. Parameters: `doc` : document to split ([Pandoc]) `opts` : Splitting options. The following options are supported: `path_template` : template used to generate the chunks' filepaths `%n` will be replaced with the chunk number (padded with leading 0s to 3 digits), `%s` with the section number of the heading, `%h` with the (stringified) heading text, `%i` with the section identifier. For example, `"section-%s-%i.html"` might be resolved to `"section-1.2-introduction.html"`. Default is `"chunk-%n"` (string) `number_sections` : whether sections should be numbered; default is `false` (boolean) `chunk_level` : The heading level the document should be split into chunks. The default is to split at the top-level, i.e., `1`. (integer) `base_heading_level` : The base level to be used for numbering. Default is `nil` (integer|nil) (table) Returns: - ([ChunkedDoc]) *Since: 3.0* ### table_of_contents {#pandoc.structure.table_of_contents} `table_of_contents (toc_source[, opts])` Generates a table of contents for the given object. Parameters: `toc_source` : list of command line arguments ([Blocks]\|[Pandoc]\|[ChunkedDoc]) `opts` : options ([WriterOptions]) Returns: - Table of contents as a BulletList object ([Block]) *Since: 3.0* ### unique_identifier {#pandoc.structure.unique_identifier} `unique_identifier (inlines[, used[, exts]])` Generates a unique identifier from a list of inlines, similar to what's generated by the `auto_identifiers` extension. The method used to generated identifiers can be modified through `ext`, which is a list of format extensions. It can be used to generate IDs similar to what the `auto_identifiers` extension provides. Example: local used_ids = {} function Header (h) local id = pandoc.structure.unique_identifier(h.content, used_ids) used_ids[id] = true h.identifier = id return h end Parameters: `inlines` : base for identifier ([Inlines]) `used` : set of identifiers (string keys, boolean values) that have already been used. (table) `exts` : list of format extensions ({string,\...}) Returns: - unique identifier (string) *Since: 3.8* # Module pandoc.system Access to the system's information and file functionality. ## Fields {#pandoc.system-fields} ### arch {#pandoc.system.arch} The machine architecture on which the program is running. (string) ### os {#pandoc.system.os} The operating system on which the program is running. The most common values are `darwin` (macOS), `freebsd`, `linux`, `linux-android`, `mingw32` (Windows), `netbsd`, `openbsd`. (string) ## Functions {#pandoc.system-functions} ### cputime {#pandoc.system.cputime} `cputime ()` Returns the number of picoseconds CPU time used by the current program. The precision of this result may vary in different versions and on different platforms. Returns: - CPU time in picoseconds (integer) *Since: 3.1.1* ### command {#pandoc.system.command} `command (command, args[, input[, opts]])` Executes a system command with the given arguments and `input` on *stdin*. Parameters: `command` : command to execute (string) `args` : command arguments ({string,\...}) `input` : input on stdin (string) `opts` : process options (table) Returns: - exit code -- `false` on success, an integer otherwise (integer\|boolean) - stdout (string) - stderr (string) *Since: 3.7.1* ### copy {#pandoc.system.copy} `copy (source, target)` Copy a file with its permissions. If the destination file already exists, it is overwritten. Parameters: `source` : source file (string) `target` : target destination (string) *Since: 3.7.1* ### environment {#pandoc.system.environment} `environment ()` Retrieves the entire environment as a string-indexed table. Returns: - A table mapping environment variable names to their value. (table) *Since: 2.7.3* ### get_working_directory {#pandoc.system.get_working_directory} `get_working_directory ()` Obtain the current working directory as an absolute path. Returns: - The current working directory. (string) *Since: 2.8* ### list_directory {#pandoc.system.list_directory} `list_directory ([directory])` List the contents of a directory. Parameters: `directory` : Path of the directory whose contents should be listed. Defaults to `.`. (string) Returns: - A table of all entries in `directory`, except for the special entries (`.` and `..`). (table) *Since: 2.19* ### make_directory {#pandoc.system.make_directory} `make_directory (dirname[, create_parent])` Create a new directory which is initially empty, or as near to empty as the operating system allows. The function throws an error if the directory cannot be created, e.g., if the parent directory does not exist or if a directory of the same name is already present. If the optional second parameter is provided and truthy, then all directories, including parent directories, are created as necessary. Parameters: `dirname` : name of the new directory (string) `create_parent` : create parent directory if necessary (boolean) *Since: 2.19* ### read_file {#pandoc.system.read_file} `read_file (filepath)` Parameters: `filepath` : File to read (string) Returns: - file contents (string) *Since: 3.7.1* ### rename {#pandoc.system.rename} `rename (old, new)` Change the name of an existing path from `old` to `new`. If `old` is a directory and `new` is a directory that already exists, then `new` is atomically replaced by the `old` directory. On Win32 platforms, this function fails if `new` is an existing directory. If `old` does not refer to a directory, then neither may `new`. Renaming may not work across file system boundaries or due to other system-specific reasons. It's generally more robust to copy the source path to its destination before deleting the source. Parameters: `old` : original path (string) `new` : new path (string) *Since: 3.7.1* ### remove {#pandoc.system.remove} `remove (filename)` Removes the directory entry for an existing file. Parameters: `filename` : file to remove (string) *Since: 3.7.1* ### remove_directory {#pandoc.system.remove_directory} `remove_directory (dirname[, recursive])` Remove an existing, empty directory. If `recursive` is given, then delete the directory and its contents recursively. Parameters: `dirname` : name of the directory to delete (string) `recursive` : delete content recursively (boolean) *Since: 2.19* ### times {#pandoc.system.times} `times (filepath)` Obtain the modification and access time of a file or directory. The times are returned as strings using the ISO 8601 format. Parameters: `filepath` : file or directory path (string) Returns: - time at which the file or directory was last modified (table) - time at which the file or directory was last accessed (table) *Since: 3.7.1* ### with_environment {#pandoc.system.with_environment} `with_environment (environment, callback)` Run an action within a custom environment. Only the environment variables given by `environment` will be set, when `callback` is called. The original environment is restored after this function finishes, even if an error occurs while running the callback action. Parameters: `environment` : Environment variables and their values to be set before running `callback` (table) `callback` : Action to execute in the custom environment (function) Returns: The results of the call to `callback`. *Since: 2.7.3* ### with_temporary_directory {#pandoc.system.with_temporary_directory} `with_temporary_directory (parent_dir, templ, callback)` Create and use a temporary directory inside the given directory. The directory is deleted after the callback returns. Parameters: `parent_dir` : Parent directory to create the directory in. If this parameter is omitted, the system's canonical temporary directory is used. (string) `templ` : Directory name template. (string) `callback` : Function which takes the name of the temporary directory as its first argument. (function) Returns: The results of the call to `callback`. *Since: 2.8* ### with_working_directory {#pandoc.system.with_working_directory} `with_working_directory (directory, callback)` Run an action within a different directory. This function will change the working directory to `directory`, execute `callback`, then switch back to the original working directory, even if an error occurs while running the callback action. Parameters: `directory` : Directory in which the given `callback` should be executed (string) `callback` : Action to execute in the given directory (function) Returns: The results of the call to `callback`. *Since: 2.7.3* ### write_file {#pandoc.system.write_file} `write_file (filepath, contents)` Writes a string to a file. Parameters: `filepath` : path to target file (string) `contents` : file contents (string) *Since: 3.7.1* ### xdg {#pandoc.system.xdg} `xdg (xdg_directory_type[, filepath])` Access special directories and directory search paths. Special directories for storing user-specific application data, configuration, and cache files, as specified by the [XDG Base Directory Specification]. Parameters: `xdg_directory_type` : The type of the XDG directory or search path. Must be one of `config`, `data`, `cache`, `state`, `datadirs`, or `configdirs`. Matching is case-insensitive, and underscores and `XDG` prefixes are ignored, so a value like `XDG_DATA_DIRS` is also acceptable. The `state` directory might not be available, depending on the version of the underlying Haskell library. (string) `filepath` : relative path that is appended to the path; ignored if the result is a list of search paths. (string) Returns: - Either a single file path, or a list of search paths. (string\|{string,\...}) *Since: 3.7.1* # Module pandoc.layout Plain-text document layouting. ## Fields {#pandoc.layout-fields} ### blankline {#pandoc.layout.blankline} Inserts a blank line unless one exists already. ([Doc]) ### cr {#pandoc.layout.cr} A carriage return. Does nothing if we're at the beginning of a line; otherwise inserts a newline. ([Doc]) ### empty {#pandoc.layout.empty} The empty document. ([Doc]) ### space {#pandoc.layout.space} A breaking (reflowable) space. ([Doc]) ## Functions {#pandoc.layout-functions} ### after_break {#pandoc.layout.after_break} `after_break (text)` Creates a [Doc] which is conditionally included only if it comes at the beginning of a line. An example where this is useful is for escaping line-initial `.` in roff man. Parameters: `text` : content to include when placed after a break (string) Returns: - new doc ([Doc]) *Since: 2.18* ### before_non_blank {#pandoc.layout.before_non_blank} `before_non_blank (doc)` Conditionally includes the given `doc` unless it is followed by a blank space. Parameters: `doc` : document ([Doc]) Returns: - conditional doc ([Doc]) *Since: 2.18* ### blanklines {#pandoc.layout.blanklines} `blanklines (n)` Inserts blank lines unless they exist already. Parameters: `n` : number of blank lines ([integer]{unknown-type="integer"}) Returns: - conditional blank lines ([Doc]) *Since: 2.18* ### braces {#pandoc.layout.braces} `braces (doc)` Puts the `doc` in curly braces. Parameters: `doc` : document ([Doc]) Returns: - `doc` enclosed by {}. ([Doc]) *Since: 2.18* ### brackets {#pandoc.layout.brackets} `brackets (doc)` Puts the `doc` in square brackets Parameters: `doc` : document ([Doc]) Returns: - doc enclosed by \[\]. ([Doc]) *Since: 2.18* ### cblock {#pandoc.layout.cblock} `cblock (doc, width)` Creates a block with the given width and content, aligned centered. Parameters: `doc` : document ([Doc]) `width` : block width in chars ([integer]{unknown-type="integer"}) Returns: - doc, aligned centered in a block with max `width` chars per line. ([Doc]) *Since: 2.18* ### chomp {#pandoc.layout.chomp} `chomp (doc)` Chomps trailing blank space off of the `doc`. Parameters: `doc` : document ([Doc]) Returns: - `doc` without trailing blanks ([Doc]) *Since: 2.18* ### concat {#pandoc.layout.concat} `concat (docs[, sep])` Concatenates a list of `Doc`s. Parameters: `docs` : list of Docs ([\`{Doc,\...}\`]{unknown-type="`{Doc,...}`"}) `sep` : separator (default: none) ([Doc]) Returns: - concatenated doc ([Doc]) *Since: 2.18* ### double_quotes {#pandoc.layout.double_quotes} `double_quotes (doc)` Wraps a `Doc` in double quotes. Parameters: `doc` : document ([Doc]) Returns: - `doc` enclosed by `"` chars ([Doc]) *Since: 2.18* ### flush {#pandoc.layout.flush} `flush (doc)` Makes a `Doc` flush against the left margin. Parameters: `doc` : document ([Doc]) Returns: - flushed `doc` ([Doc]) *Since: 2.18* ### hang {#pandoc.layout.hang} `hang (doc, ind, start)` Creates a hanging indent. Parameters: `doc` : document ([Doc]) `ind` : indentation width ([integer]{unknown-type="integer"}) `start` : document ([Doc]) Returns: - `doc` prefixed by `start` on the first line, subsequent lines indented by `ind` spaces. ([Doc]) *Since: 2.18* ### inside {#pandoc.layout.inside} `inside (contents, start, end)` Encloses a [Doc] inside a start and end [Doc]. Parameters: `contents` : document ([Doc]) `start` : document ([Doc]) `end` : document ([Doc]) Returns: - enclosed contents ([Doc]) *Since: 2.18* ### lblock {#pandoc.layout.lblock} `lblock (doc, width)` Creates a block with the given width and content, aligned to the left. Parameters: `doc` : document ([Doc]) `width` : block width in chars ([integer]{unknown-type="integer"}) Returns: - doc put into block with max `width` chars per line. ([Doc]) *Since: 2.18* ### literal {#pandoc.layout.literal} `literal (text)` Creates a `Doc` from a string. Parameters: `text` : literal value (string) Returns: - doc contatining just the literal string ([Doc]) *Since: 2.18* ### nest {#pandoc.layout.nest} `nest (doc, ind)` Indents a `Doc` by the specified number of spaces. Parameters: `doc` : document ([Doc]) `ind` : indentation size ([integer]{unknown-type="integer"}) Returns: - `doc` indented by `ind` spaces ([Doc]) *Since: 2.18* ### nestle {#pandoc.layout.nestle} `nestle (doc)` Removes leading blank lines from a `Doc`. Parameters: `doc` : document ([Doc]) Returns: - `doc` with leading blanks removed ([Doc]) *Since: 2.18* ### nowrap {#pandoc.layout.nowrap} `nowrap (doc)` Makes a `Doc` non-reflowable. Parameters: `doc` : document ([Doc]) Returns: - same as input, but non-reflowable ([Doc]) *Since: 2.18* ### parens {#pandoc.layout.parens} `parens (doc)` Puts the `doc` in parentheses. Parameters: `doc` : document ([Doc]) Returns: - doc enclosed by (). ([Doc]) *Since: 2.18* ### prefixed {#pandoc.layout.prefixed} `prefixed (doc, prefix)` Uses the specified string as a prefix for every line of the inside document (except the first, if not at the beginning of the line). Parameters: `doc` : document ([Doc]) `prefix` : prefix for each line (string) Returns: - prefixed `doc` ([Doc]) *Since: 2.18* ### quotes {#pandoc.layout.quotes} `quotes (doc)` Wraps a `Doc` in single quotes. Parameters: `doc` : document ([Doc]) Returns: - doc enclosed in `'`. ([Doc]) *Since: 2.18* ### rblock {#pandoc.layout.rblock} `rblock (doc, width)` Creates a block with the given width and content, aligned to the right. Parameters: `doc` : document ([Doc]) `width` : block width in chars ([integer]{unknown-type="integer"}) Returns: - doc, right aligned in a block with max `width` chars per line. ([Doc]) *Since: 2.18* ### vfill {#pandoc.layout.vfill} `vfill (border)` An expandable border that, when placed next to a box, expands to the height of the box. Strings cycle through the list provided. Parameters: `border` : vertically expanded characters (string) Returns: - automatically expanding border Doc ([Doc]) *Since: 2.18* ### render {#pandoc.layout.render} `render (doc[, colwidth[, style]])` Render a [Doc]. The text is reflowed on breakable spaces to match the given line length. Text is not reflowed if the line length parameter is omitted or nil. Parameters: `doc` : document ([Doc]) `colwidth` : Maximum number of characters per line. A value of `nil`, the default, means that the text is not reflown. ([integer]{unknown-type="integer"}) `style` : Whether to generate plain text or ANSI terminal output. Must be either `'plain'` or `'ansi'`. Defaults to `'plain'`. (string) Returns: - rendered doc (string) *Since: 2.18* ### is_empty {#pandoc.layout.is_empty} `is_empty (doc)` Checks whether a doc is empty. Parameters: `doc` : document ([Doc]) Returns: - `true` iff `doc` is the empty document, `false` otherwise. (boolean) *Since: 2.18* ### height {#pandoc.layout.height} `height (doc)` Returns the height of a block or other Doc. Parameters: `doc` : document ([Doc]) Returns: - doc height ([integer]{unknown-type="integer"}\|string) *Since: 2.18* ### min_offset {#pandoc.layout.min_offset} `min_offset (doc)` Returns the minimal width of a [Doc] when reflowed at breakable spaces. Parameters: `doc` : document ([Doc]) Returns: - minimal possible width ([integer]{unknown-type="integer"}\|string) *Since: 2.18* ### offset {#pandoc.layout.offset} `offset (doc)` Returns the width of a [Doc] as number of characters. Parameters: `doc` : document ([Doc]) Returns: - doc width ([integer]{unknown-type="integer"}\|string) *Since: 2.18* ### real_length {#pandoc.layout.real_length} `real_length (str)` Returns the real length of a string in a monospace font: 0 for a combining character, 1 for a regular character, 2 for an East Asian wide character. Parameters: `str` : UTF-8 string to measure (string) Returns: - text length ([integer]{unknown-type="integer"}\|string) *Since: 2.18* ### update_column {#pandoc.layout.update_column} `update_column (doc, i)` Returns the column that would be occupied by the last laid out character. Parameters: `doc` : document ([Doc]) `i` : start column ([integer]{unknown-type="integer"}) Returns: - column number ([integer]{unknown-type="integer"}\|string) *Since: 2.18* ### bold {#pandoc.layout.bold} `bold (doc)` Puts a [Doc] in boldface. Parameters: `doc` : document ([Doc]) Returns: - bolded Doc ([Doc]) *Since: 3.4.1* ### italic {#pandoc.layout.italic} `italic (doc)` Puts a [Doc] in italics. Parameters: `doc` : document ([Doc]) Returns: - styled Doc ([Doc]) *Since: 3.4.1* ### underlined {#pandoc.layout.underlined} `underlined (doc)` Underlines a [Doc]. Parameters: `doc` : document ([Doc]) Returns: - styled Doc ([Doc]) *Since: 3.4.1* ### strikeout {#pandoc.layout.strikeout} `strikeout (doc)` Puts a line through the [Doc]. Parameters: `doc` : document ([Doc]) Returns: - styled Doc ([Doc]) *Since: 3.4.1* ### fg {#pandoc.layout.fg} `fg (doc, color)` Set the foreground color. Parameters: `doc` : document ([Doc]) `color` : One of 'black', 'red', 'green', 'yellow', 'blue', 'magenta' 'cyan', or 'white'. (string) Returns: - styled Doc ([Doc]) *Since: 3.4.1* ### bg {#pandoc.layout.bg} `bg (doc, color)` Set the background color. Parameters: `doc` : document ([Doc]) `color` : One of 'black', 'red', 'green', 'yellow', 'blue', 'magenta' 'cyan', or 'white'. (string) Returns: - styled Doc ([Doc]) *Since: 3.4.1* ## Types {#pandoc.layout-types} ### Doc {#type-pandoc.Doc} See the description [above][Doc]. # Module pandoc.scaffolding Scaffolding for custom writers. ## Fields {#pandoc.scaffolding-fields} ### Writer {#pandoc.scaffolding.Writer} An object to be used as a `Writer` function; the construct handles most of the boilerplate, expecting only render functions for all AST elements (table) # Module pandoc.text UTF-8 aware text manipulation functions, implemented in Haskell. The text module can also be loaded under the name `text`, although this is discouraged and deprecated. ``` lua -- uppercase all regular text in a document: function Str (s) s.text = pandoc.text.upper(s.text) return s end ``` ## Functions {#pandoc.text-functions} ### fromencoding {#pandoc.text.fromencoding} `fromencoding (s[, encoding])` Converts a string to UTF-8. The `encoding` parameter specifies the encoding of the input string. On Windows, that parameter defaults to the current ANSI code page; on other platforms the function will try to use the file system's encoding. The set of known encodings is system dependent, but includes at least `UTF-8`, `UTF-16BE`, `UTF-16LE`, `UTF-32BE`, and `UTF-32LE`. Note that the default code page on Windows is available through `CP0`. Parameters: `s` : string to be converted (string) `encoding` : target encoding (string) Returns: - UTF-8 string (string) *Since: 3.0* ### len {#pandoc.text.len} `len (s)` Returns the length of a UTF-8 string, i.e., the number of characters. Parameters: `s` : UTF-8 encoded string (string) Returns: - length (integer\|string) *Since: 2.0.3* ### lower {#pandoc.text.lower} `lower (s)` Returns a copy of a UTF-8 string, converted to lowercase. Parameters: `s` : UTF-8 string to convert to lowercase (string) Returns: - Lowercase copy of `s` (string) *Since: 2.0.3* ### reverse {#pandoc.text.reverse} `reverse (s)` Returns a copy of a UTF-8 string, with characters reversed. Parameters: `s` : UTF-8 string to revert (string) Returns: - Reversed `s` (string) *Since: 2.0.3* ### sub {#pandoc.text.sub} `sub (s, i[, j])` Returns a substring of a UTF-8 string, using Lua's string indexing rules. Parameters: `s` : UTF-8 string (string) `i` : substring start position (integer) `j` : substring end position (integer) Returns: - text substring (string) *Since: 2.0.3* ### subscript {#pandoc.text.subscript} `subscript (input)` Tries to convert the string into a Unicode subscript version of the string. Returns `nil` if not all characters of the input can be mapped to a subscript Unicode character. Supported characters include numbers, parentheses, and plus/minus. Parameters: `input` : string to convert to subscript characters (string) Returns: - Subscript version of the input, or `nil` if not all characters could be converted. (string\|nil) *Since: 3.8* ### superscript {#pandoc.text.superscript} `superscript (input)` Tries to convert the string into a Unicode superscript version of the string. Returns `nil` if not all characters of the input can be mapped to a superscript Unicode character. Supported characters include numbers, parentheses, and plus/minus. Parameters: `input` : string to convert to superscript characters (string) Returns: - Superscript version of the input, or `nil` if not all characters could be converted. (string\|nil) *Since: 3.8* ### toencoding {#pandoc.text.toencoding} `toencoding (s[, enc])` Converts a UTF-8 string to a different encoding. The `encoding` parameter defaults to the current ANSI code page on Windows; on other platforms it will try to guess the file system's encoding. The set of known encodings is system dependent, but includes at least `UTF-8`, `UTF-16BE`, `UTF-16LE`, `UTF-32BE`, and `UTF-32LE`. Note that the default code page on Windows is available through `CP0`. Parameters: `s` : UTF-8 string (string) `enc` : target encoding (string) Returns: - re-encoded string (string) *Since: 3.0* ### upper {#pandoc.text.upper} `upper (s)` Returns a copy of a UTF-8 string, converted to uppercase. Parameters: `s` : UTF-8 string to convert to uppercase (string) Returns: - Uppercase copy of `s` (string) *Since: 2.0.3* # Module pandoc.template Handle pandoc templates. ## Functions {#pandoc.template-functions} ### apply {#pandoc.template.apply} `apply (template, context)` Applies a context with variable assignments to a template, returning the rendered template. The `context` parameter must be a table with variable names as keys and [Doc], string, boolean, or table as values, where the table can be either be a list of the aforementioned types, or a nested context. Parameters: `template` : template to apply ([Template]) `context` : variable values (table) Returns: - rendered template ([Doc]) *Since: 3.0* ### compile {#pandoc.template.compile} `compile (template[, templates_path])` Compiles a template string into a [Template] object usable by pandoc. If the `templates_path` parameter is specified, then it should be the file path associated with the template. It is used when checking for partials. Partials will be taken only from the default data files if this parameter is omitted. An error is raised if compilation fails. Parameters: `template` : template string (string) `templates_path` : parameter to determine a default path and extension for partials; uses the data files templates path by default. (string) Returns: - compiled template ([Template]) *Since: 2.17* ### default {#pandoc.template.default} `default ([writer])` Returns the default template for a given writer as a string. An error is thrown if no such template can be found. Parameters: `writer` : name of the writer for which the template should be retrieved; defaults to the global `FORMAT`. (string) Returns: - raw template (string) *Since: 2.17* ### get {#pandoc.template.get} `get (filename)` Retrieve text for a template. This function first checks the resource paths for a file of this name; if none is found, the `templates` directory in the user data directory is checked. Returns the content of the file, or throws an error if no file is found. Parameters: `filename` : name of the template (string) Returns: - content of template file (string) *Since: 3.2.1* ### meta_to_context {#pandoc.template.meta_to_context} `meta_to_context (meta, blocks_writer, inlines_writer)` Creates template context from the document's [Meta] data, using the given functions to convert [Blocks] and [Inlines] to [Doc] values. Parameters: `meta` : document metadata ([Meta]) `blocks_writer` : converter from [Blocks] to [Doc] values (function) `inlines_writer` : converter from [Inlines] to [Doc] values (function) Returns: - template context (table) *Since: 3.0* ## Types {#pandoc.template-types} ### Template {#type-pandoc.template.Template} # Module pandoc.types Constructors for types that are not part of the pandoc AST. ## Functions {#pandoc.types-functions} ### Version {#pandoc.types.Version} `Version (version_specifier)` Parameters: `version_specifier` : A version string like `'2.7.3'`, a Lua number like `2.0`, a list of integers like `{2,7,3}`, or a Version object. (string\|number\|{integer,\...}\|[Version]) Returns: - New Version object. ([Version]) *Since: 2.7.3* ### Sources {#pandoc.types.Sources} `Sources (srcs)` Creates a new Sources element, i.e., a list of [Source] items. Pandoc's text readers expect the input text to be paired with information on where the text originated, e.g., a file name. This abstraction is provided via the `Sources` type. Pandoc accepts a range of objects wherever a *Sources* list is expected: - a list of [Source] items; - a simple string, which becomes an unnamed source; - a list of table objects, where each table contains the fields `name` (the filepath) and `text` (the file contents) A Sources list can be converted to a string via the default `tostring` Lua function. This will concatenate all source items. Parameters: `srcs` : sources (string\|{string,\...}\|table) Returns: - new Sources object ({[Source],\...}) *Since: 3.9.1* ## Types {#pandoc.types-types} ### Source {#type-pandoc.types.Source} ### Version {#type-pandoc.types.Version} #### Methods ##### Version.must_be_at_least {#Version.must_be_at_least} `Version.must_be_at_least (self, reference[, msg])` Parameters: `self` : version to check ([Version]) `reference` : minimum version ([Version]) `msg` : alternative message (string) Returns: Returns no result, and throws an error if this version is older than `reference`. # Module pandoc.zip Functions to create, modify, and extract files from zip archives. The module can be called as a function, in which case it behaves like the `zip` function described below. Zip options are optional; when defined, they must be a table with any of the following keys: - `recursive`: recurse directories when set to `true`; - `verbose`: print info messages to stdout; - `destination`: the value specifies the directory in which to extract; - `location`: value is used as path name, defining where files are placed. - `preserve_symlinks`: Boolean value, controlling whether symbolic links are preserved as such. This option is ignored on Windows. ## Functions {#pandoc.zip-functions} ### Archive {#pandoc.zip.Archive} `Archive ([bytestring_or_entries])` Reads an *Archive* structure from a raw zip archive or a list of Entry items; throws an error if the given string cannot be decoded into an archive. Parameters: `bytestring_or_entries` : binary archive data or list of entries; defaults to an empty list (string\|{[zip.Entry],\...}) Returns: - new Archive ([zip.Archive]) *Since: 3.0* ### Entry {#pandoc.zip.Entry} `Entry (path, contents[, modtime])` Generates a ZipEntry from a filepath, uncompressed content, and the file's modification time. Parameters: `path` : file path in archive (string) `contents` : uncompressed contents (string) `modtime` : modification time (integer) Returns: - a new zip archive entry ([zip.Entry]) *Since: 3.0* ### read_entry {#pandoc.zip.read_entry} `read_entry (filepath[, opts])` Generates a ZipEntry from a file or directory. Parameters: `filepath` : (string) `opts` : zip options (table) Returns: - a new zip archive entry ([zip.Entry]) *Since: 3.0* ### zip {#pandoc.zip.zip} `zip (filepaths[, opts])` Package and compress the given files into a new Archive. Parameters: `filepaths` : list of files from which the archive is created. ({string,\...}) `opts` : zip options (table) Returns: - a new archive ([zip.Archive]) *Since: 3.0* ## Types {#pandoc.zip-types} ### zip.Archive {#type-pandoc.zip.Archive} #### Properties {#type-pandoc.zip.Archive-properties} ##### entries {#type-pandoc.zip.Archive.entries} Files in this zip archive ({[zip.Entry],\...}) #### Methods {#type-pandoc.zip.Archive-methods} ##### bytestring {#pandoc.zip.Archive.bytestring} `bytestring (self)` Returns the raw binary string representation of the archive. Parameters: `self` : ([zip.Archive]) Returns: - bytes of the archive (string) ##### extract {#pandoc.zip.Archive.extract} `extract (self[, opts])` Extract all files from this archive, creating directories as needed. Note that the last-modified time is set correctly only in POSIX, not in Windows. This function fails if encrypted entries are present. Parameters: `self` : ([zip.Archive]) `opts` : zip options (table) ### zip.Entry {#type-pandoc.zip.Entry} #### Properties {#type-pandoc.zip.Entry-properties} ##### modtime {#type-pandoc.zip.Entry.modtime} Modification time (seconds since unix epoch) ([integer]{unknown-type="integer"}) ##### path {#type-pandoc.zip.Entry.path} Relative path, using `/` as separator ([zip.Entry]) #### Methods {#type-pandoc.zip.Entry-methods} ##### contents {#pandoc.zip.Entry.contents} `contents (self[, password])` Get the uncompressed contents of a zip entry. If `password` is given, then that password is used to decrypt the contents. An error is throws if decrypting fails. Parameters: `self` : ([zip.Entry]) `password` : password for entry (string) Returns: - binary contents (string) ##### symlink {#pandoc.zip.Entry.symlink} `symlink (self)` Returns the target if the Entry represents a symbolic link, and `nil` otherwise. Always returns `nil` on Windows. Parameters: `self` : ([zip.Entry]) Returns: - link target if entry represents a symbolic link (string\|nil) [zip.Entry]: #type-pandoc.zip.Entry [zip.Archive]: #type-pandoc.zip.Archive [Blocks]: #type-blocks [Meta]: #type-meta [Pandoc]: #type-pandoc [Inlines]: #type-inlines [MetaValue]: #type-metavalue [Block]: #type-block [Attr]: #type-attr [Figure]: #type-figure [Caption]: #type-caption [ListAttributes]: #type-listattributes [ColSpec]: #type-colspec [TableHead]: #type-tablehead [TableBody]: #type-tablebody [TableFoot]: #type-tablefoot [Inline]: #type-inline [Span]: #type-span [Str]: #type-str [AttributeList]: #type-attributes [Alignment]: #type-alignment [Cell]: #type-cell [Row]: #type-row [SimpleTable]: #type-simpletable [Table]: #type-table [Version]: #type-version [`list`]: #pandoc.mediabag.list [FormatExtensions]: #FormatExtensions [WriterOptions]: #type-writeroptions [null]: #pandoc.json.null [this blog post]: http://neilmitchell.blogspot.co.uk/2015/10/filepaths-are-subtle-symlinks-are-hard.html [ChunkedDoc]: #type-chunkeddoc [XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/latest/ [Doc]: #type-doc [Template]: #type-template [Source]: #type-pandoc.types.Source ================================================ FILE: doc/nix.md ================================================ --- title: Using NiX to develop pandoc author: John MacFarlane --- The source directory contains `shell.nix` and `flake.nix`, so if you have NiX installed, you can use either `nix shell` or `nix develop` can be used to obtain a shell with pandoc dependencies installed. To set up `direnv` so that the NiX shell is automatically activated whenever the directory is entered, add the following `.envrc` in the pandoc source directory: ``` if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" fi use flake ``` This uses [nix-direnv] for caching. See its web page for further help. [nix-direnv]: https://github.com/nix-community/nix-direnv ================================================ FILE: doc/org.md ================================================ --- title: Org-mode features and differences author: Albert Krewinkel --- Pandoc's handling of org files is similar to that of Emacs org-mode. This document aims to highlight the cases where this is not possible or just not the case yet. Export options ============== The following export keywords are supported. (Because they populate metadata fields, they will not generally affect the output unless you use the `-s/--standalone` option to generate a standalone document with metadata.) - AUTHOR: comma-separated list of author(s); fully supported. - CREATOR: output generator; passed as plain-text metadata entry `creator`, but not used by any default templates. - DATE: creation or publication date; well supported by pandoc. - EMAIL: author email address; passed as plain-text metadata field `email`, but not used by any default templates. - LANGUAGE: document language; included as plain-text metadata field `lang`. The value should be a [BCP47 language tag]. - SELECT_TAGS: tags which select a tree for export. - EXCLUDE\_TAGS: tags which prevent a subtree from being exported. Fully supported. - TITLE: document title; fully supported. - EXPORT\_FILE\_NAME: target filename; *unsupported*, the output defaults to stdout unless a target has to be given as a command line option. ::: {.alert .alert-info} Pandoc tries to be compatible with org-mode when exporting an org document. If you find some behavior confusing, please do refer to org-mode [Export-Settings](https://orgmode.org/manual/Export-Settings.html) documentation. For example, a common confusion ([#3214](https://github.com/jgm/pandoc/issues/3214 "Problem with headers lower then 3 in org-mode reader"), [#5169](https://github.com/jgm/pandoc/issues/5169 "org mode headings past level three converted to numbered outline list"), [#6145](https://github.com/jgm/pandoc/issues/6145 "Headers 4 levels deep render differently"), [#7236](https://github.com/jgm/pandoc/issues/7236 "In Org mode, Header with level > 3 are not recognized as headers")) is treatment of headers with level > 3 differently because org-mode sets `org-export-headline-levels` (configurable with `#+OPTIONS: H:3`) to 3 by default. ::: [BCP47 language tag]: https://tools.ietf.org/html/bcp47 Format-specific options ----------------------- Emacs Org-mode supports additional export options which work for specific export formats. Some of these options' behavior differs in Org-mode depending on the output format, while pandoc is format-agnostic when parsing; differences are noted where they occur. - DESCRIPTION: the document's description; pandoc parses this option as text with markup into the `description` metadata field. The field is not used in default templates. Pandoc follows the LaTeX exporter in that it allows markup in the description. In contrast, the Org-mode HTML exporter treats the description as plain text. - LATEX\_HEADER and LATEX_HEADER_EXTRA: arbitrary lines to add to the document's preamble. Contrary to Org-mode, these lines are not inserted before the hyperref settings, but close to the end of the preamble. The contents of this option are stored as a list of raw LaTeX lines in the `header-includes` metadata field. - LATEX\_CLASS: the LaTeX document class; like Org-mode, pandoc uses `article` as the default class. The contents of this option are stored as plain text in the `documentclass` metadata field. - LATEX\_CLASS\_OPTIONS: Options for the LaTeX document class; fully supported. The contents of this option are stored as plain text in the `classoption` metadata field. - SUBTITLE: the document's subtitle; fully supported. The content of this option is stored as inlines in the `subtitle` metadata field. - HTML\_HEAD and HTML\_HEAD\_EXTRA: arbitrary lines to add to the HTML document's head; fully supported. The contents of these options are stored as a list of raw HTML lines in the `header-includes` metadata field. Pandoc-specific options ----------------------- Pandoc recognizes some export options not used by Emacs Org. - NOCITE: this field adds the listed citations to the bibliography, without the need to mention them to the text. The special value `@*` causes all available references to be added the bibliography. - HEADER-INCLUDES: like HTML_HEAD and, LATEX_HEADER, but treats the option's value as normal text with markup. - INSTITUTE: Affiliation of the author; the value is read as text with markup and is stored in the `institute` metadata field. The field is included by default on the title slide of beamer presentations. Other options ------------- Any export option or directive not listed above has no effect when parsing with pandoc. However, the information is retained as a *raw block*. It can be accessed through a [filter](https://pandoc.org/filters.html) and will be included in org output. ### Directives as metadata As an example, we will restore an old behavior of pandoc versions prior to 2.10. Unknown keywords were treated as variable definitions, and were added the document's metadata. Typing `#+key: value` in the org-file used to have the same effect as running pandoc with the `--metadata key=value` option. Since pandoc 2.10, each unhandled line starting with `#+` is kept internally as a raw block with format `org`. This block can be inspected and processed by a filter. Below is a [Lua filter](https://pandoc.org/lua-filters.html) which converts these unhandled lines into metadata key-value pairs. ``` lua -- intermediate store for variables and their values local variables = {} --- Function called for each raw block element. function RawBlock (raw) -- Don't do anything unless the block contains *org* markup. if raw.format ~= 'org' then return nil end -- extract variable name and value local name, value = raw.text:match '#%+(%w+):%s*(.+)$' if name and value then variables[name] = value end end -- Add the extracted variables to the document's metadata. function Meta (meta) for name, value in pairs(variables) do meta[name] = value end return meta end ``` Tables ====== Pandoc supports normal org tables (sometimes called "pipe tables") and grid tables (tables created by [table.el]). Column widths ------------- Org mode tables don't allow line-breaks within cells, and lines which contain text can get very long. This often leads to tables which run off the page when exporting, especially when exporting to PDF via LaTeX. Overlong lines in the source text are this is usually hidden by setting a [column width], but the default Emacs exporters ignore that setting. Pandoc deviates from Emacs's behavior and uses this information to resize the table columns when exporting. Limitations ----------- There is no support yet for cells spanning multiple columns or rows. The table.el grid tables allows rowspans and colspans and so does pandoc's internal structure since 2.10, but the parser has not been updated yet. [table.el]: http://table.sourceforge.net/ [column width]: https://orgmode.org/manual/Column-Width-and-Alignment.html Emphasis rules ============== Org-mode uses complex rules to decide whether a string represents emphasized text. In Emacs, this can be customized via the variable `org-emphasis-regexp-components`. A variable like this doesn't fit well with pandoc's model. Instead, it is possible to use special lines to change these values: #+pandoc-emphasis-pre: "-\t ('\"{\x200B" #+pandoc-emphasis-post: "-\t\n .,:!?;'\")}[\x200B" The above describes the default values of these variables. The arguments must be valid (Haskell) strings. If interpretation of the argument as string fails, the default is restored. Changing emphasis rules only affect the part of the document following the special lines. They must be some of the first lines to alter parsing behavior for the whole document. It is also possible to change the values temporarily for selected sections only. The string `test` in the following snippet will be read as emphasized text, while the rest of the document will be parsed using default emphasis rules: #+pandoc-emphasis-pre: "[" #+pandoc-emphasis-post: "]" [/test/] #+pandoc-emphasis-pre: #+pandoc-emphasis-post: `smart` extension ================= Org-mode allows to insert certain characters via special character sequences. For example, instead of typing the Unicode /HORIZONTAL ELLISPIS/ character `…` by hand, one can instead type tree dots `...`. En dashes and em dashes can be written as `--` and `---` respectively. Furthermore, quotation marks (`"`) and apostrophe-quotes (`'`) can be treated in a "smart" way, potentially replacing them with proper, language specific unicode quotation characters. Like in Markdown, these behaviors can be turned on all-at-once by enabling the `smart` extension. However, disabling `smart` (the default) will *not* necessarily disable smart quotes and special strings. Instead, it will just result in the default Org mode behavior. The special string feature can be turned off via the `#+OPTIONS: -:nil` [export setting]. There are currently no command line flags which control these features. As a workaround, one can use process substitution, a feature supported by most shells. It allows to provide the options line on the command line: pandoc -f org <(printf "#+OPTIONS: -:nil\n") … [export setting]: https://orgmode.org/manual/Export-Settings.html `fancy_lists` extension ======================= Org-mode has a variable `org-list-allow-alphabetical` that when set to `t`, allows ordered lists with single-character alphabetical markers. Since this variable is `nil` by default, alphabetical markers can be optionally enabled in Pandoc by enabling the `fancy_lists` extension. When `fancy_lists` is enabled, Pandoc will also parse list markers starting with one lowercase or uppercase alphabetical character, like `a.` and `D)`. Contrary to the use of this extension in markdown, roman numerals or the `#` placeholder can't be used as markers as they are not allowed in Org-mode. One additional behavior that is enabled by the `fancy_lists` extension is that the `.` and `)` delimiters for list markers will be distinguished by Pandoc. In essence, this means that when converting Org into formats like LaTeX, Pandoc will respect the type of delimiter that you used in your Org file, instead of always using the default delimiter for the exported format. Currently unsupported features ============================== Library of babel ---------------- The library of babel translates between various programming languages. This is out-of-scope for pandoc. Use Emacs to run code, then feed the resulting org file to pandoc. ================================================ FILE: doc/pandoc-lua.md ================================================ --- title: pandoc-lua section: 1 date: September 22, 2022 --- # SYNOPSIS `pandoc-lua` [*options*] [*script* [*args*]] # DESCRIPTION `pandoc-lua` is a standalone Lua interpreter with behavior similar to that of the standard `lua` executable, but exposing all of pandoc's Lua libraries. All `pandoc.*` packages, as well as the packages `re` and `lpeg`, are available via global variables. Furthermore, the globals `PANDOC_VERSION`, `PANDOC_STATE`, and `PANDOC_API_VERSION` are set at startup. If no script argument is given, then the script is assumed to be passed in via *stdin*. When called without arguments, `pandoc-lua` behaves as `pandoc-lua -v -i` when the standard input (`stdin`) is a terminal, and as `pandoc-lua -` otherwise. On Windows the program will always behave as if it was connected to a terminal. When called without the option `-E`, the interpreter checks for an environment variable `LUA_INIT` before running any argument. If the variable content has the format *`@filename`*, then `pandoc-lua` executes the file. Otherwise, `pandoc-lua` executes the string itself. # OPTIONS `-e stat` : Execute statement `stat`. `-l mod` : If mod has the pattern `g=m`, then require library `m` into global `g`; otherwise require library `mod` into global `mod`. `-v` : Show version information. `-i` : Enter interactive mode after running *script*. `-E` : Ignore environment variables. This is not fully implemented yet and only ignores the `LUA_INIT` variable. Other variables like `LUA_PATH` and `LUA_CPATH` are **not** ignored. `-W` : Turn warnings on. # INTERACTIVE MODE In interactive mode, the Lua interpreter repeatedly prompts and waits for a line. After reading a line, Lua first tries to interpret the line as an expression. If it succeeds, it prints its value. Otherwise, it interprets the line as a statement. If you write an incomplete statement, the interpreter waits for its completion by issuing a different prompt. Exit the interactive mode by pressing `Ctrl-D` or `Ctrl-C`, or by typing `os.exit()`. The *Isocline* library is used for line editing. Press `F1` to get a list of available keybindings; the `ctrl` key is abbreviated as `^` in that list. # AUTHORS Copyright 2023 John MacFarlane (jgm@berkeley.edu) and contributors. Released under the [GPL], version 2 or later. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) Lua: Copyright 1994-2023 Lua.org, PUC-Rio. [GPL]: https://www.gnu.org/copyleft/gpl.html "GNU General Public License" ================================================ FILE: doc/pandoc-server.md ================================================ --- title: pandoc-server section: 1 date: August 15, 2022 --- # SYNOPSIS `pandoc-server` [*options*] # DESCRIPTION `pandoc-server` is a web server that can perform pandoc conversions. It can be used either as a running server or as a CGI program. To use `pandoc-server` as a CGI program, rename it (or symlink it) as `pandoc-server.cgi`. (Note: if you symlink it, you may need to adjust your webserver's configuration in order to allow it to follow symlinks for the CGI script.) All pandoc functions are run in the PandocPure monad, which ensures that they can do no I/O operations on the server. This should provide a high degree of security. This security does, however, impose certain limitations: - PDFs cannot be produced. - Filters are not supported. - Resources cannot be fetched via HTTP. - Any images, include files, or other resources needed for the document conversion must be explicitly included in the request, via the `files` field (see below under API). # OPTIONS `--port NUM` : HTTP port on which to run the server. Default: 3030. `--timeout SECONDS` : Timeout in seconds, after which a conversion is killed. Default: 2. When `pandoc-server` is run as a CGI program, this option can be set via the `PANDOC_SERVER_TIMEOUT` environment variable. `--help` : Print this help. `--version` : Print version. # API ## Root endpoint The root (`/`) endpoint accepts only POST requests. ### Response It returns a converted document in one of the following formats (in order of preference), depending on the `Accept` header: - `application/octet-stream` - `text/plain` - `application/json` If the result is a binary format (e.g., `epub` or `docx`) and the content is returned as plain text or JSON, the binary will be base64 encoded. If a JSON response is given, it will have one of the following formats. If the conversion is not successful: ``` { "error": string with the error message } ``` If the conversion is successful: ``` { "output": string with textual or base64-encoded binary output, "base64": boolean (true means the "output" is base64-encoded), "messages": array of message objects (see below) } ``` Each element of the "messages" array will have the format ``` { "message": string, "verbosity": string (either "WARNING" or "INFO") } ``` ### Request The body of the POST request should be a JSON object, with the following fields. Only the `text` field is required; all of the others can be omitted for default values. When there are several string alternatives, the first one given is the default. `text` (string) : The document to be converted. Note: if the `from` format is binary (e.g., `epub` or `docx`), then `text` should be a base64 encoding of the document. `from` (string, default `"markdown"`) : The input format, possibly with extensions, just as it is specified on the pandoc command line. `to` (string, default `"html"`) : The output format, possibly with extensions, just as it is specified on the pandoc command line. `shift-heading-level-by` (integer, default 0) : Increase or decrease the level of all headings. `indented-code-classes` (array of strings) : List of classes to be applied to indented Markdown code blocks. `default-image-extension` (string) : Extension to be applied to image sources that lack extensions (e.g. `".jpg"`). `metadata` (JSON map) : String-valued metadata. `tab-stop` (integer, default 4) : Tab stop (spaces per tab). `track-changes` (`"accept"|"reject"|"all"`) : Specifies what to do with insertions, deletions, and comments produced by the MS Word "Track Changes" feature. Only affects docx input. `abbreviations` (file path) : List of strings to be regarded as abbreviations when parsing Markdown. See `--abbreviations` in `pandoc(1)` for details. `standalone` (boolean, default false) : If true, causes a standalone document to be produced, using the default template or the custom template specified using `template`. If false, a fragment will be produced. `template` (string) : String contents of a document template (see Templates in `pandoc(1)` for the format). `variables` (JSON map) : Variables to be interpolated in the template. (See Templates in `pandoc(1)`.) `dpi` (integer, default 96) : Dots-per-inch to use for conversions between pixels and other measurements (for image sizes). `wrap` (`"auto"|"preserve"|"none"`) : Text wrapping option: either `"auto"` (automatic hard-wrapping to fit within a column width), `"preserve"` (insert newlines where they are present in the source), or `"none"` (don't insert any unnecessary newlines at all). `columns` (integer, default 72) : Column width (affects text wrapping and calculation of table column widths in plain text formats) `table-of-contents` (boolean, default false) : Include a table of contents (in supported formats). `toc-depth` (integer, default 3) : Depth of sections to include in the table of contents. `list-of-figures` (boolean, default false) : Include a list of figures (in supported formats). `list-of-tables` (boolean, default false) : Include a list of tables (in supported formats). `strip-comments` (boolean, default false) : Causes HTML comments to be stripped in Markdown or Textile source, instead of being passed through to the output format. `syntax-highlighting` (`"default"|"none"|"idiomatic"|style`) : The method used 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 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. Standard styles are `"pygments"` (the default), `"kate"`, `"monochrome"`, `"breezeDark"`, `"espresso"`, `"zenburn"`, `"haddock"`, and `"tango"`. Alternatively, the path of a `.theme` with a KDE syntax theme may be used (in this case, the relevant file contents must also be included in `files`, see below). 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. `embed-resources` : Embed images, scripts, styles and other resources in an HTML document using `data` URIs. Note that this will not work unless the contents of all external resources are included under `files`. `html-q-tags` (boolean, default false) : Use `` elements in HTML instead of literal quotation marks. `ascii` (boolean, default false) : Use entities and escapes when possible to avoid non-ASCII characters in the output. `reference-links` (boolean, default false) : Create reference links rather than inline links in Markdown output. `reference-location` (`"document"|"section"|"block"`) : Determines whether link references and footnotes are placed at the end of the document, the end of the section, or the end of the block (e.g. paragraph), in certain formats. (See `pandoc(1)` under `--reference-location`.) `setext-headers` (boolean, default false) : Use Setext (underlined) headings instead of ATX (`#`-prefixed) in Markdown output. `top-level-division` (`"default"|"part"|"chapter"|"section"`) : Determines how top-level headings are interpreted in LaTeX, ConTeXt, DocBook, and TEI. The `"default"` value tries to choose the best interpretation based on heuristics. `number-sections` (boolean, default false) : Automatically number sections (in supported formats). `number-offset` (array of integers) : Offsets to be added to each component of the section number. For example, `[1]` will cause the first section to be numbered "2" and the first subsection "2.1"; `[0,1]` will cause the first section to be numbered "1" and the first subsection "1.2." `html-math-method` (`"plain"|"webtex"|"gladtex"|"mathml"|"mathjax"|"katex"`) : Determines how math is represented in HTML. `listings` (boolean, default false) : Use the `listings` package to format code in LaTeX output. `incremental` (boolean, default false) : If true, lists appear incrementally by default in slide shows. `slide-level` (integer) : Heading level that deterimes slide divisions in slide shows. The default is to pick the highest heading level under which there is body text. `section-divs` (boolean, default false) : Arrange the document into a hierarchy of nested sections based on the headings. `email-obfuscation` (`"none"|"references"|"javascript"`) : Determines how email addresses are obfuscated in HTML. `identifier-prefix` (string) : Prefix to be added to all automatically-generated identifiers. `title-prefix` (string) : Prefix to be added to the title in the HTML header. `reference-doc` (file path) : Reference doc to use in creating `docx` or `odt` or `pptx`. See `pandoc(1)` under `--reference-doc` for details. The contents of the file must be included under `files`. `split-level` (integer, default 1) : Heading level at which documents are split in EPUB or chunked HTML. `epub-cover-image` (file path) : Cover image for EPUB. The contents of the file must be included under `files`. `epub-metadata` (file path) : Path of file containing Dublin core XML elements to be used for EPUB metadata. The contents of the file must be included under `files`. `epub-subdirectory` (string, default "EPUB") : Name of content subdirectory in the EPUB container. `epub-fonts` (array of file paths) : Fonts to include in the EPUB. The fonts themselves must be included in `files` (see below). `ipynb-output` (`"best"|"all"|"none"`) : Determines how ipynb output cells are treated. `all` means that all of the data formats included in the original are preserved. `none` means that the contents of data cells are omitted. `best` causes pandoc to try to pick the richest data block in each output cell that is compatible with the output format. `citeproc` (boolean, default false) : Causes citations to be processed using citeproc. See Citations in `pandoc(1)` for details. `bibliography` (array of file paths) : Files containing bibliographic data. The contents of the files must be included in `files`. `csl` (file path) : CSL style file. The contents of the file must be included in `files`. `cite-method` (`"citeproc"|"natbib"|"biblatex"`) : Determines how citations are formatted in LaTeX output. `files` (JSON mapping of file paths to base64-encoded strings) : Any files needed for the conversion, including images referred to in the document source, should be included here. Binary data must be base64-encoded. Textual data may be left as it is, unless it is *also* valid base 64 data, in which case it will be interpreted that way. ## `/batch` endpoint The `/batch` endpoint behaves like the root endpoint, except for these two points: - It accepts a JSON array, each element of which is a JSON object like the one expected by the root endpoint. - It returns a JSON array of JSON results. This endpoint can be used to convert a sequence of small snippets in one request. ## `/version` endpoint The `/version` endpoint accepts a GET request and returns the pandoc version as a plain or JSON-encoded string, depending on Accept headers. ## `/babelmark` endpoint The `/babelmark` endpoint accepts a GET request with the following query parameters: - `text` (required string) - `from` (optional string, default is `"markdown"`) - `to` (optional string, default is `"html"`) - `standalone` (optional boolean, default is `false`) It returns a JSON object with fields `html` and `version`. This endpoint is designed to support the [Babelmark](https://babelmark.github.io) website. # AUTHORS Copyright 2022 John MacFarlane (jgm@berkeley.edu). Released under the [GPL], version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) [GPL]: https://www.gnu.org/copyleft/gpl.html "GNU General Public License" ================================================ FILE: doc/press.md ================================================ % Press Here are some books, articles, and blogs that discuss pandoc: # Articles - [Pandoc](http://en.wikipedia.org/wiki/Pandoc) on Wikipedia. - Egon Willighagen, [Two years of explicit CiTO annotations](https://doi.org/10.1186/s13321-023-00683-2). Journal of Cheminformatics 15, 14 (2023). doi: 10.1186/s13321-023-00683-2. - Albert Krewinkel, Juanjo Bazán, and Afron M. Smith, ["JATS from Markdown: Developer friendly single-source scholarly publishing"](https://www.ncbi.nlm.nih.gov/books/NBK579698/), *JATS-Con Proceedings 2022*. - A. Ohri and T. Schmah, ["Machine Translation of Mathematical Text,"](https://doi.org/10.1109/ACCESS.2021.3063715) in *IEEE Access*, vol. 9, pp. 38078-38086, 2021, doi: 10.1109/ACCESS.2021.3063715. - Julien Dehut, ["En finir avec Word ! Pour une analyse des enjeux relatifs aux traitements de texte et à leur utilisation"](https://eriac.hypotheses.org/80), *L'Atelier des Savoirs*, January 23, 2018. - Phillips, Lee. ["Technical Writing with Pandoc and Panflute"](https://lee-phillips.org/panflute-gnuplot/). Reproduced with permission from *Linux Journal*, September, 2017. - Krewinkel, Albert and Robert Winkler. ["Formatting Open Science: agile creation of multiple document types by writing academic manuscripts in pandoc markdown."](https://peerj.com/preprints/2648/). *PeerJ Preprints*. - Krijnen, Jacco, Doaitse Swierstra, and Marcos O. Viera. ["Expand: Towards an Extensible Pandoc System."](http://dx.doi.org/10.1007/978-3-319-04132-2_14) *Practical Aspects of Declarative Languages* (Springer International Publishing, 2014), 200--215. - Kielhorn, Axel. ["Multi-target publishing-Generating ePub, PDF, and more, from Markdown using pandoc."](https://www.tug.org/TUGboat/tb32-3/tb102kielhorn.pdf) *TUGboat-TeX Users Group* 32, no. 3 (2011): 272. - Massimiliano Dominici. ["An Overview of Pandoc."](http://www.dw.tug.org/TUGboat/tb35-1/tb109dominici.pdf), *TUGboat* 32 (2014), n. 1: 44-50. - Gieben, R. ["Writing I-Ds and RFCs Using Pandoc and a Bit of XML."](https://tools.ietf.org/html/rfc7328.html) (RFC 7328, 2014). - Ovadia, Steven (2014). ["Markdown for Librarians and Academics".](http://www.tandfonline.com/doi/pdf/10.1080/01639269.2014.904696) *Behavioral & Social Sciences Librarian* 33 (2): 120–124. doi:10.1080/01639269.2014.904696 - Daniel Burgos and Alberto Corbí, "Semi-Automated Correction Tools for Mathematics-Based Exercises in MOOC Environments," [*International Journal of Artificial Intelligence and Interactive Multimedia*, 3](http://www.ijimai.org/journal/sites/default/files/journals/IJIMAI20153_3.pdf), 89-95. - Garnett A, Alperin JP, Willinsky J. "The Public Knowledge Project XML Publishing Service and meTypeset: Don't call it 'Yet Another Word-to-JATS Conversion Kit'." *Journal Article Tag Suite Conference (JATS-Con) Proceedings* 2015 [Internet]. Bethesda (MD): National Center for Biotechnology Information (US); 2015. Available from: # Books - Digital Publishing Toolkit Collective, [*From Print to EBooks: A Hybrid Publishing Toolkit for the Arts*](http://networkcultures.org/blog/publication/from-print-to-ebooks-a-hybrid-publishing-toolkit-for-the-arts/) (Institute of Network Cultures), ch. 6. - Hasecke, Jan Ulrich. [*Das ZEN von Pandoc: Bücher und E-Books einfach und professionell produzieren*](http://www.amazon.com/Das-ZEN-von-Pandoc-professionell-ebook/dp/B00TQ55D34). - Michael Kofler, [*Markdown und Pandoc*](http://www.amazon.com/Markdown-Pandoc-German-Michael-Kofler-ebook/dp/B00CX7HIOO/ref=sr_1_1?ie=UTF8&qid=1424543894&sr=8-1&keywords=pandoc). # Blog posts - Grec, Dan (2021), ["How I self-published a professional paperback and eBook using LaTeX and Pandoc"](http://theroadchoseme.com/how-i-self-published-a-professional-paperback-and-ebook-using-latex-and-pandoc), The Road Chose Me. - Grandesso, Piero (2018-03-23). ["A pandoc-based layout workflow for scholarly journals"](http://pierog.it/en/2018/03/markdown-workflow/). Piero G: Notes on Open Access, Scholarly Publishing etc. - Krewinkel, Albert (2017-12-23). ["Extending pandoc with Lua"](http://lua.space/general/extending-pandoc-with-lua). Lua.Space. - Pullum, Geoffrey (2017-03-13). ["Word-Processing Misery"](http://www.chronicle.com/blogs/linguafranca/2017/03/13/word-processing-hell/). The Chronicle of Higher Education Blogs: Lingua Franca. - Curiositry (2017-02-28). ["Pandoc for Writers"](http://www.autodidacts.io/convert-markdown-to-standard-manuscript-format-odts-docs-and-pdfs-with-pandoc/). The Autodidacts. - Tenen, Dennis and Grant Wythoff (2014-03-19). ["Sustainable Authorship in Plain Text using Pandoc and Markdown"](http://programminghistorian.org/lessons/sustainable-authorship-in-plain-text-using-pandoc-and-markdown). The Programming Historian. - Mullen, Lincoln (2012-02-23). ["Pandoc Converts All Your (Text) Documents"](http://chronicle.com/blogs/profhacker/pandoc-converts-all-your-text-documents/38700), The Chronicle of Higher Education Blogs: ProfHacker. - Mullen, Lincoln (2012-03-20). ["Make Your Own E-Books with Pandoc"](http://chronicle.com/blogs/profhacker/make-your-own-e-books-with-pandoc/39067). The Chronicle of Higher Education Blogs: ProfHacker. - Fenner, Martin (2013-12-12). ["From Markdown to JATS XML in one Step"](http://blog.martinfenner.org/2013/12/12/from-markdown-to-jats-xml-in-one-step/). Gobbledygook. - McDaniel, W. Caleb (2012-09-28). ["Why (and How) I Wrote My Academic Book in Plain Text"](http://wcm1.web.rice.edu/my-academic-book-in-plain-text.html). - Healy, Kieran (2014-01-23). ["Plain Text, Papers, Pandoc"](http://kieranhealy.org/blog/archives/2014/01/23/plain-text/). - Till, Kaitlyn, Shed Simas, and Velma Larkai (2014-04-14). ["The Flying Narwhal: Small mag workflow"](http://tkbr.ccsp.sfu.ca/mpub/2014/04/14/the-flying-narwhal-small-mag-workflow/#more-639). Publishing @ SFU. - Maxwell, John (2013-11-01). ["Building Publishing Workflows with Pandoc and Git"](http://www.ccsp.sfu.ca/2013/11/building-publishing-workflows-with-pandoc-and-git/). Publishing @ SFU. - Maxwell, John (2014-02-26). ["On Pandoc"](http://tkbr.ccsp.sfu.ca:5001/Slides/On%20Pandoc). eBound Canada: Digital Production Workshop, Vancouver, BC. - Puppet labs (2013-11-28). ["How We Automated our Ebook Builds with Pandoc and KindleGen."](http://puppetlabs.com/blog/automated-ebook-generation-convert-markdown-epub-mobi-pandoc-kindlegen). - Fenner, Martin (2014-08-25). ["Using Microsoft Word with git."](http://blog.martinfenner.org/2014/08/25/using-microsoft-word-with-git/). - Wouter Soudan (2015-12-02). ["From Word to Markdown to InDesign: Fully automated typesetting."](http://rhythmus.be/md2indd/) - Mattia Tezzele (2015-11-24), ["Typesetting automation: A Plain-text workflow for painless production of personal documents & offline correspondence---featuring Pandoc, LaTeX, and a simple makefile](http://mrzool.cc/writing/typesetting-automation/) # Talks - MacFarlane, John (2014-05-17). ["Pandoc for Haskell Hackers"](http://johnmacfarlane.net/BayHac2014/#/). BayHac 2014, Mountain View, CA. ================================================ FILE: doc/short-guide-to-pandocs-sources.md ================================================ --- title: Short guide to pandoc's sources subtitle: Laying a path for code wanderers author: Albert Krewinkel date: 2021-06-07 --- Pandoc, the universal document converter, can serve as a nice intro into functional programming with Haskell. For many contributors, including the author of this guide, pandoc was their first real exposure to this language. Despite its impressive size of more than 60.000 lines of Haskell code (excluding the test suite), pandoc is still very approachable due to its modular architecture. It can serve as an interesting subject for learning. This guide exists to navigate the large amount of sources, to lay-out a path that can be followed for learning, and to explain the underlying concepts. A basic understanding of Haskell and of pandoc's functionality is assumed. # Getting the code Pandoc has a publicly accessible git repository on GitHub: . To get a local copy of the source: git clone https://github.com/jgm/pandoc The source for the main pandoc program is `app/pandoc.hs`. The source for the pandoc library is in `src/`, the source for the tests is in `test/`, and the source for the benchmarks is in `benchmark/`. Core type definitions are in the separate [*pandoc-types* repo]. Get it with git clone https://github.com/jgm/pandoc-types The organization of library and test sources is identical to the main repo. [*pandoc-types* repo]: https://github.com/jgm/pandoc-types # Document representation The way documents are represented in pandoc is part of its success. Every document is read into one central data structure, the so-called *abstract syntax tree* (AST). The AST is defined in module `Text.Pandoc.Definition` in package [*pandoc-types*]. It is not necessary to understand the AST in detail, just check-out the following points: * The [`Pandoc`][def-Pandoc] type serves as the central structure. * A document has metadata and a list of "block" elements. * There are various types of [blocks][def-Block]; some contain raw text, others contain "Inline" elements. * [Inlines][def-Inline] are "running text", with many different types. The most important constructors are `Str` (a word), `Space` (a space char), `Emph` (emphasized text), and `Strong` (strongly emphasized text). It's worth checking their definitions. * Element attributes are captured as [`Attr`][def-Attr], which is a triple of the element identifier, its classes, and the key-value pairs.^[For plans to change this see [jgm/pandoc-types#88].] [*pandoc-types*]: https://hackage.haskell.org/package/pandoc-types [jgm/pandoc-types#88]: https://github.com/jgm/pandoc-types/issues/88 [def-Pandoc]: https://hackage.haskell.org/package/pandoc-types/docs/src/Text.Pandoc.Definition.html#Pandoc [def-Block]: https://hackage.haskell.org/package/pandoc-types/docs/src/Text.Pandoc.Definition.html#Block [def-Inline]: https://hackage.haskell.org/package/pandoc-types/docs/src/Text.Pandoc.Definition.html#Inline [def-Attr]: https://hackage.haskell.org/package/pandoc-types/docs/src/Text.Pandoc.Definition.html#Attr # Basic architecture Take a look at pandoc's source files. The code is below the `src` directory, in the `Text.Pandoc` module. The basic flow is: 1. Document is parsed into the internal representation by a *reader*; 2. the document AST is modified (optional); 3. then the internal representation is converted into the target format by a *writer*. The [*readers*] can be found in `Text.Pandoc.Readers`, while the [*writers*] are submodules of `Text.Pandoc.Writers`. The document modification step is powerful and used in different ways, e.g., in [*filters*]. These parts are the "muscles" of pandoc, which do the heavy lifting. Everything else can be thought of as the bones and fibers to which these parts are attached and which make them usable. # Writers Writers are usually simpler than readers and therefore easier to grasp. Broadly speaking, there are three kind of writers: 1. Text writers: these are used for lightweight markup languages and generate plain text output. Examples: Markdown, Org, reStructuredText. 2. XML writers, which convert the AST into structured XML. Examples: HTML, JATS. 3. Binary writers, which are like XML writers, but combine the output with other data and zip it into a single file. Examples: docx, epub. Most writers follow a common pattern and have three main functions: docTo*Format*, blockTo*Format* and inlineTo*Format*. Each converts the `Pandoc`, `Block`, and `Inline` elements, respectively. The *XWiki* and *TEI* writers are comparatively simple and suitable samples when taking a first look. Most writers are self-contained in that most of the conversion code is within a single module. However, newer writers often use a different setup: those are built around modules from an external package. The details of how to serialize the document are not in the writer module itself, but in an external module. The writer only has to convert pandoc's AST into the document representation used by the module. Good examples: commonmark, jira. ## DocLayout All writers build on the `doclayout` package. It can be thought of as a pretty printer with extra features suitable for lightweight markup languages. E.g., multiple blank lines are collapsed into a single blank line, unless multiple blank lines are specifically requested. This simplifies the code significantly. See the repo at https://github.com/jgm/doclayout, and the [hackage documentation](https://hackage.haskell.org/package/doclayout) # Readers The same distinction that applies to writers also applies to readers. Readers for XML formats use XML parsing libraries, while plain text formats are parsed with [parsec]. ## Builders The plain type constructors from the [`Text.Pandoc.Definition`] module can be difficult to use, which is why the module [`Text.Pandoc.Builder`] exists. It offers functions to conveniently build and combine AST elements. The most interesting and important types in `Builder` are [`Blocks`][def-Blocks] and [`Inlines`][def-Inlines]. All type constructors use simple lists for sequences of AST elements. Building lists can be awkward and often comes with bad performance characteristics, esp. when appending. The `Blocks` and `Inlines` types are better suited for these operations and are therefore used extensively in builder functions. The builder functions are named with the convention that the suffix `With` is added if the first argument is an `Attr`; there is usually another function without that suffix, creating an element with no attributes. [def-Blocks]: https://hackage.haskell.org/package/pandoc-types/docs/src/Text.Pandoc.Builder.html#Blocks [def-Inlines]: https://hackage.haskell.org/package/pandoc-types/docs/src/Text.Pandoc.Builder.html#Inlines [parsec]: https://hackage.haskell.org/package/parsec # PandocMonad Looking at the readers and writers, one will notice that they all operate within the `PandocMonad` type class. This class gives access to options, file operations, and other shared information. The typeclass has two main implementations: one operates in IO, so on the "real world", while the other provides a pure functional interface, suitable to "mock" an environment for testing. # Document modifications One of the big advantages of a central document structure is that it allows document modifications via a unified interface. This section describes the multiple ways in which the document can be altered. ## Walkable Document traversal happens through the `Walkable` class in module `Text.Pandoc.Walk` ([*pandoc-types* package]). ## Transformations Transformations are simple modifications controllable through command-line options. ## Filters Filters allow to use Lua or any external language to perform document transformations. [`Text.Pandoc.Builder`]: https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Builder.html [`Text.Pandoc.Definition`]: https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Definition.html # Module overview The library is structured as follows: - `Text.Pandoc` is a top-level module that exports what is needed by most users of the library. Any patches that add new readers or writers will need to make changes here, too. - `Text.Pandoc.Definition` (in `pandoc-types`) defines the types used for representing a pandoc document. - `Text.Pandoc.Builder` (in `pandoc-types`) provides functions for building pandoc documents programmatically. - `Text.Pandoc.Generics` (in `pandoc-types`) provides functions allowing you to promote functions that operate on parts of pandoc documents to functions that operate on whole pandoc documents, walking the tree automatically. - `Text.Pandoc.Readers.*` are the readers, and `Text.Pandoc.Writers.*` are the writers. - `Text.Pandoc.Citeproc.*` contain the code for citation handling, including an interface to the [citeproc] library. - `Text.Pandoc.Data` is used to embed data files when the `embed_data_files` cabal flag is used. - `Text.Pandoc.Emoji` is a thin wrapper around [emojis]. - `Text.Pandoc.Highlighting` contains the interface to the skylighting library, which is used for code syntax highlighting. - `Text.Pandoc.ImageSize` is a utility module containing functions for calculating image sizes from the contents of image files. - `Text.Pandoc.MIME` contains functions for associating MIME types with extensions. - `Text.Pandoc.Lua.*` implement Lua filters. - `Text.Pandoc.Options` defines reader and writer options. - `Text.Pandoc.PDF` contains functions for producing PDFs. - `Text.Pandoc.Parsing` contains parsing functions used in multiple readers. the needs of pandoc. - `Text.Pandoc.SelfContained` contains functions for making an HTML file "self-contained," by importing remotely linked images, CSS, and JavaScript and turning them into `data:` URLs. - `Text.Pandoc.Shared` is a grab-bag of shared utility functions. - `Text.Pandoc.Writers.Shared` contains utilities used in writers only. - `Text.Pandoc.Slides` contains functions for splitting a markdown document into slides, using the conventions described in the MANUAL. - `Text.Pandoc.Templates` defines pandoc's templating system. - `Text.Pandoc.UTF8` contains functions for converting text to and from UTF8 bytestrings (strict and lazy). - `Text.Pandoc.Asciify` contains functions to derive ascii versions of identifiers that use accented characters. - `Text.Pandoc.UUID` contains functions for generating UUIDs. - `Text.Pandoc.XML` contains functions for formatting XML. ================================================ FILE: doc/typst-property-output.md ================================================ --- title: Typst property output author: Gordon Woodhull --- Pandoc Typst property output ============================ In addition to the output of structural properties built into Pandoc's Typst Writer, the Writer can also output non-structural Typst properties. This is enabled by setting attributes with keys of the form `typst:prop` or `typst:text:prop` on supported elements. Typst properties ---------------- [Typst](https://typst.app/) allows specification of visual and layout properties as parameters to elements ```typst #block(fill=orange)[Hello] ``` and set-rules ```typst #set text(fill=blue); Hello ``` The parameter values are [Typst code](https://typst.app/docs/reference/syntax/#modes) that can use any features of the Typst language. Pandoc Typst property output ---------------------------- For the set of supported Pandoc elements, the Pandoc Typst Writer will output attributes as parameters to corresponding Typst elements or set-text rules. The Typst Writer looks for attributes with keys of the form `typst:prop` or `typst:text:prop` and assumes the values are raw Typst code. `prop` is the name of the property to set. For example, `pandoc -f html -t typst` with HTML input ```html
foo
``` produces Typst output ```typst #block(inset: 10pt)[ foo ] ``` and with HTML input ```html
foo
``` it produces Typst output ```typst #block[ #set text(fill: purple); foo ] ``` The Typst Writer does not check the validity of `prop` or the value. Since Typst is a statically typed language, improper property names or values usually result in compilation failure. Supported elements ------------------ The following Pandoc AST elements are currently supported. More may be supported in the future. - [Span](https://pandoc.org/lua-filters.html#type-span) `typst:text:prop` : The content is wrapped in a Typst [text element](https://typst.app/docs/reference/text/text/) with the specified properties set. - [Div](https://pandoc.org/lua-filters.html#type-div) `typst:prop` : The `prop` is output as a parameter to the Typst [block element](https://typst.app/docs/reference/layout/block/). `typst:text:prop` : The `prop` is output as a parameter to a set-text rule at the start of the block content. - [Table](https://pandoc.org/lua-filters.html#type-table) `typst:prop` : The `prop` is output as a parameter to the Typst [table element](https://typst.app/docs/reference/model/table/). `typst:text:prop` : The table is wrapped in a Typst [text element](https://typst.app/docs/reference/text/text/) with `prop` as one of its parameters. `typst:no-figure` (class) : By default, Pandoc will wrap the table in a Typst [figure element](https://typst.app/docs/reference/model/figure/). If the table has this class, only the table element itself will be emitted. This avoids Typst's crossreference counter of kind `table` from being incremented. `typst:figure:kind` : If this attribute is set, Pandoc will wrap the table in a Typst [figure element](https://typst.app/docs/reference/model/figure/) with the specified `kind` attribute. This is useful for tables that should be cross-referenced as something other than `Table ...` in the document. Typst will use the `kind` attribute to increment the corresponding counter: `raw` and `image`. - Table [Cell](https://pandoc.org/lua-filters.html#type-cell) `typst:prop` : The `prop` is output as a parameter to the Typst table [cell element](https://typst.app/docs/reference/model/table/#definitions-cell). `typst:text:prop` : The `prop` is output as a parameter to a set-text rule at the start of the cell content. Lua filter example ------------------ Here is a minimal example of a Lua filter which translates the CSS [color property](https://developer.mozilla.org/en-US/docs/Web/CSS/color) on a span element to the equivalent [fill parameter](https://typst.app/docs/reference/text/text/#parameters-fill) on a Typst text element. ```lua function styleToTable(style) if not style then return nil end local ret = {} for clause in style:gmatch('([^;]+)') do k,v = clause:match("([%w-]+)%s*:%s*(.*)$") ret[k] = v end return ret end function Span(span) local style = styleToTable(span.attributes['style']) if not style then return end if style['color'] then span.attributes['typst:text:fill'] = style['color'] end return span end ``` Given the HTML input ```html

Here is some orange text.

``` the command ```sh pandoc -f html -t typst --lua-filter ./typst-property-example.lua ``` will produce the Typst output ```typst Here is some #text(fill: orange)[orange text]. ``` Of course, this simple filter will only work for Typst's [predefined colors](https://typst.app/docs/reference/visualize/color/#predefined-colors). A more complete filter would need to translate the value as well. ================================================ FILE: doc/using-the-pandoc-api.md ================================================ % Using the pandoc API % John MacFarlane Pandoc can be used as a Haskell library, to write your own conversion tools or power a web application. This document offers an introduction to using the pandoc API. Detailed API documentation at the level of individual functions and types is available at . # Pandoc's architecture Pandoc is structured as a set of *readers*, which translate various input formats into an abstract syntax tree (the Pandoc AST) representing a structured document, and a set of *writers*, which render this AST into various output formats. Pictorially: ``` [input format] ==reader==> [Pandoc AST] ==writer==> [output format] ``` This architecture allows pandoc to perform $M \times N$ conversions with $M$ readers and $N$ writers. The Pandoc AST is defined in the [pandoc-types](https://hackage.haskell.org/package/pandoc-types) package. You should start by looking at the Haddock documentation for [Text.Pandoc.Definition]. As you'll see, a `Pandoc` is composed of some metadata and a list of `Block`s. There are various kinds of `Block`, including `Para` (paragraph), `Header` (section heading), and `BlockQuote`. Some of the `Block`s (like `BlockQuote`) contain lists of `Block`s, while others (like `Para`) contain lists of `Inline`s, and still others (like `CodeBlock`) contain plain text or nothing. `Inline`s are the basic elements of paragraphs. The distinction between `Block` and `Inline` in the type system makes it impossible to represent, for example, a link (`Inline`) whose link text is a block quote (`Block`). This expressive limitation is mostly a help rather than a hindrance, since many of the formats pandoc supports have similar limitations. The best way to explore the pandoc AST is to use `pandoc -t native`, which will display the AST corresponding to some Markdown input: ``` % echo -e "1. *foo*\n2. bar" | pandoc -t native [OrderedList (1,Decimal,Period) [[Plain [Emph [Str "foo"]]] ,[Plain [Str "bar"]]]] ``` # A simple example Here is a simple example of the use of a pandoc reader and writer to perform a conversion: ```haskell import Text.Pandoc import qualified Data.Text as T import qualified Data.Text.IO as TIO main :: IO () main = do result <- runIO $ do doc <- readMarkdown def (T.pack "[testing](url)") writeRST def doc rst <- handleError result TIO.putStrLn rst ``` Some notes: 1. The first part constructs a conversion pipeline: the input string is passed to `readMarkdown`, and the resulting Pandoc AST (`doc`) is then rendered by `writeRST`. The conversion pipeline is "run" by `runIO`---more on that below. 2. `result` has the type `Either PandocError Text`. We could pattern-match on this manually, but it's simpler in this context to use the `handleError` function from Text.Pandoc.Error. This exits with an appropriate error code and message if the value is a `Left`, and returns the `Text` if the value is a `Right`. # The PandocMonad class Let's look at the types of `readMarkdown` and `writeRST`: ```haskell readMarkdown :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m Text ``` The `PandocMonad m =>` part is a typeclass constraint. It says that `readMarkdown` and `writeRST` define computations that can be used in any instance of the `PandocMonad` type class. `PandocMonad` is defined in the module [Text.Pandoc.Class]. Two instances of `PandocMonad` are provided: `PandocIO` and `PandocPure`. The difference is that computations run in `PandocIO` are allowed to do IO (for example, read a file), while computations in `PandocPure` are free of any side effects. `PandocPure` is useful for sandboxed environments, when you want to prevent users from doing anything malicious. To run the conversion in `PandocIO`, use `runIO` (as above). To run it in `PandocPure`, use `runPure`. As you can see from the Haddocks, [Text.Pandoc.Class] exports many auxiliary functions that can be used in any instance of `PandocMonad`. For example: ```haskell -- | Get the verbosity level. getVerbosity :: PandocMonad m => m Verbosity -- | Set the verbosity level. setVerbosity :: PandocMonad m => Verbosity -> m () -- Get the accumulated log messages (in temporal order). getLog :: PandocMonad m => m [LogMessage] getLog = reverse <$> getsCommonState stLog -- | Log a message using 'logOutput'. Note that 'logOutput' is -- called only if the verbosity level exceeds the level of the -- message, but the message is added to the list of log messages -- that will be retrieved by 'getLog' regardless of its verbosity level. report :: PandocMonad m => LogMessage -> m () -- | Fetch an image or other item from the local filesystem or the net. -- Returns raw content and maybe mime type. fetchItem :: PandocMonad m => Text -> m (B.ByteString, Maybe MimeType) -- Set the resource path searched by 'fetchItem'. setResourcePath :: PandocMonad m => [FilePath] -> m () ``` If we wanted more verbose informational messages during the conversion we defined in the previous section, we could do this: ```haskell result <- runIO $ do setVerbosity INFO doc <- readMarkdown def (T.pack "[testing](url)") writeRST def doc ``` Note that `PandocIO` is an instance of `MonadIO`, so you can use `liftIO` to perform arbitrary IO operations inside a pandoc conversion chain. `readMarkdown` is polymorphic in its second argument, which can be any type that is an instance of the `ToSources` typeclass. You can use `Text`, as in the example above. But you can also use `[(FilePath, Text)]`, if the input comes from multiple files and you want to track source positions accurately. # Options The first argument of each reader or writer is for options controlling the behavior of the reader or writer: `ReaderOptions` for readers and `WriterOptions` for writers. These are defined in [Text.Pandoc.Options]. It is a good idea to study these options to see what can be adjusted. `def` (from Data.Default) denotes a default value for each kind of option. (You can also use `defaultWriterOptions` and `defaultReaderOptions`.) Generally you'll want to use the defaults and modify them only when needed, for example: ```haskell writeRST def{ writerReferenceLinks = True } ``` Some particularly important options to know about: 1. `writerTemplate`: By default, this is `Nothing`, which means that a document fragment will be produced. If you want a full document, you need to specify `Just template`, where `template` is a `Template Text` from [Text.Pandoc.Templates] containing the template's contents (not the path). 2. `readerExtensions` and `writerExtensions`: These specify the extensions to be used in parsing and rendering. Extensions are defined in [Text.Pandoc.Extensions]. # Builder Sometimes it's useful to construct a Pandoc document programmatically. To make this easier we provide the module [Text.Pandoc.Builder] `pandoc-types`. Because concatenating lists is slow, we use special types `Inlines` and `Blocks` that wrap a `Sequence` of `Inline` and `Block` elements. These are instances of the Monoid typeclass and can easily be concatenated: ```haskell import Text.Pandoc.Builder mydoc :: Pandoc mydoc = doc $ header 1 (text (T.pack "Hello!")) <> para (emph (text (T.pack "hello world")) <> text (T.pack ".")) main :: IO () main = print mydoc ``` If you use the `OverloadedStrings` pragma, you can simplify this further: ```haskell mydoc = doc $ header 1 "Hello!" <> para (emph "hello world" <> ".") ``` Here's a more realistic example. Suppose your boss says: write me a letter in Word listing all the filling stations in Chicago that take the Voyager card. You find some JSON data in this format (`fuel.json`): ```json [ { "state" : "IL", "city" : "Chicago", "fuel_type_code" : "CNG", "zip" : "60607", "station_name" : "Clean Energy - Yellow Cab", "cards_accepted" : "A D M V Voyager Wright_Exp CleanEnergy", "street_address" : "540 W Grenshaw" }, ... ``` And then use aeson and pandoc to parse the JSON and create the Word document: ```haskell {-# LANGUAGE OverloadedStrings #-} import Text.Pandoc.Builder import Text.Pandoc import Data.Monoid ((<>), mempty, mconcat) import Data.Aeson import Control.Applicative import Control.Monad (mzero) import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import Data.List (intersperse) data Station = Station{ address :: T.Text , name :: T.Text , cardsAccepted :: [T.Text] } deriving Show instance FromJSON Station where parseJSON (Object v) = Station <$> v .: "street_address" <*> v .: "station_name" <*> (T.words <$> (v .:? "cards_accepted" .!= "")) parseJSON _ = mzero createLetter :: [Station] -> Pandoc createLetter stations = doc $ para "Dear Boss:" <> para "Here are the CNG stations that accept Voyager cards:" <> simpleTable [plain "Station", plain "Address", plain "Cards accepted"] (map stationToRow stations) <> para "Your loyal servant," <> plain (image "JohnHancock.png" "" mempty) where stationToRow station = [ plain (text $ name station) , plain (text $ address station) , plain (mconcat $ intersperse linebreak $ map text $ cardsAccepted station) ] main :: IO () main = do json <- BL.readFile "fuel.json" let letter = case decode json of Just stations -> createLetter [s | s <- stations, "Voyager" `elem` cardsAccepted s] Nothing -> error "Could not decode JSON" docx <- runIO (writeDocx def letter) >>= handleError BL.writeFile "letter.docx" docx putStrLn "Created letter.docx" ``` Voila! You've written the letter without using Word and without looking at the data. # Data files Pandoc has a number of data files, which can be found in the `data/` subdirectory of the repository. These are installed with pandoc (or, if pandoc was compiled with the `embed_data_files` flag, they are embedded in the binary). You can retrieve data files using `readDataFile` from Text.Pandoc.Class. `readDataFile` will first look for the file in the "user data directory" (`setUserDataDir`, `getUserDataDir`), and if it is not found there, it will return the default installed with the system. To force the use of the default, `setUserDataDir Nothing`. # Metadata files Pandoc can add metadata to documents, as described in the User's Guide. Similar to data files, metadata YAML files can be retrieved using `readMetadataFile` from Text.Pandoc.Class. `readMetadataFile` will first look for the file in the working directory, and if it is not found there, it will look for it in the `metadata` subdirectory of the user data directory (`setUserDataDir`, `getUserDataDir`). # Templates Pandoc has its own template system, described in the User's Guide. To retrieve the default template for a system, use `getDefaultTemplate` from [Text.Pandoc.Templates]. Note that this looks first in the `templates` subdirectory of the user data directory, allowing users to override the system defaults. If you want to disable this behavior, use `setUserDataDir Nothing`. To render a template, use `renderTemplate'`, which takes two arguments, a template (Text) and a context (any instance of ToJSON). If you want to create a context from the metadata part of a Pandoc document, use `metaToJSON'` from [Text.Pandoc.Writers.Shared]. If you also want to incorporate values from variables, use `metaToJSON` instead, and make sure `writerVariables` is set in `WriterOptions`. # Handling errors and warnings `runIO` and `runPure` return an `Either PandocError a`. All errors raised in running a `PandocMonad` computation will be trapped and returned as a `Left` value, so they can be handled by the calling program. To see the constructors for `PandocError`, see the documentation for [Text.Pandoc.Error]. To raise a `PandocError` from inside a `PandocMonad` computation, use `throwError`. In addition to errors, which stop execution of the conversion pipeline, one can generate informational messages. Use `report` from [Text.Pandoc.Class] to issue a `LogMessage`. For a list of constructors for `LogMessage`, see [Text.Pandoc.Logging]. Note that each type of log message is associated with a verbosity level. The verbosity level (`setVerbosity`/`getVerbosity`) determines whether the report will be printed to stderr (when running in `PandocIO`), but regardless of verbosity level, all reported messages are stored internally and may be retrieved using `getLog`. # Walking the AST It is often useful to walk the Pandoc AST either to extract information (e.g., what are all the URLs linked to in this document?, do all the code samples compile?) or to transform a document (e.g., increase the level of every section header, remove emphasis, or replace specially marked code blocks with images). To make this easier and more efficient, `pandoc-types` includes a module [Text.Pandoc.Walk]. Here's the essential documentation: ```haskell class Walkable a b where -- | @walk f x@ walks the structure @x@ (bottom up) and replaces every -- occurrence of an @a@ with the result of applying @f@ to it. walk :: (a -> a) -> b -> b walk f = runIdentity . walkM (return . f) -- | A monadic version of 'walk'. walkM :: (Monad m, Functor m) => (a -> m a) -> b -> m b -- | @query f x@ walks the structure @x@ (bottom up) and applies @f@ -- to every @a@, appending the results. query :: Monoid c => (a -> c) -> b -> c ``` `Walkable` instances are defined for most combinations of Pandoc types. For example, the `Walkable Inline Block` instance allows you to take a function `Inline -> Inline` and apply it over every inline in a `Block`. And `Walkable [Inline] Pandoc` allows you to take a function `[Inline] -> [Inline]` and apply it over every maximal list of `Inline`s in a `Pandoc`. Here's a simple example of a function that promotes the levels of headers: ```haskell promoteHeaderLevels :: Pandoc -> Pandoc promoteHeaderLevels = walk promote where promote :: Block -> Block promote (Header lev attr ils) = Header (lev + 1) attr ils promote x = x ``` `walkM` is a monadic version of `walk`; it can be used, for example, when you need your transformations to perform IO operations, use PandocMonad operations, or update internal state. Here's an example using the State monad to add unique identifiers to each code block: ```haskell addCodeIdentifiers :: Pandoc -> Pandoc addCodeIdentifiers doc = evalState (walkM addCodeId doc) 1 where addCodeId :: Block -> State Int Block addCodeId (CodeBlock (_,classes,kvs) code) = do curId <- get put (curId + 1) return $ CodeBlock (show curId,classes,kvs) code addCodeId x = return x ``` `query` is used to collect information from the AST. Its argument is a query function that produces a result in some monoidal type (e.g. a list). The results are concatenated together. Here's an example that returns a list of the URLs linked to in a document: ```haskell listURLs :: Pandoc -> [Text] listURLs = query urls where urls (Link _ _ (src, _)) = [src] urls _ = [] ``` # Creating a front-end All of the functionality of the command-line program `pandoc` has been abstracted out in `convertWithOpts` in the module [Text.Pandoc.App]. Creating a GUI front-end for pandoc is thus just a matter of populating the `Opts` structure and calling this function. # Notes on using pandoc in web applications 1. Pandoc's parsers can exhibit pathological behavior on some inputs. So it is always a good idea to wrap uses of pandoc in a timeout function (e.g. `System.Timeout.timeout` from `base`) to prevent DoS attacks. 2. If pandoc generates HTML from untrusted user input, it is always a good idea to filter the generated HTML through a sanitizer (such as `xss-sanitize`) to avoid security problems. 3. Using `runPure` rather than `runIO` will ensure that pandoc's functions perform no IO operations (e.g. writing files). If some resources need to be made available, a "fake environment" is provided inside the state available to `runPure` (see `PureState` and its associated functions in [Text.Pandoc.Class]). It is also possible to write a custom instance of `PandocMonad` that, for example, makes wiki resources available as files in the fake environment, while isolating pandoc from the rest of the system. [Text.Pandoc.Definition]: https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Definition.html [Text.Pandoc.Walk]: https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Walk.html [Text.Pandoc.Class]: https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Class.html [Text.Pandoc.Options]: https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Options.html [Text.Pandoc.Extensions]: https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Extensions.html [Text.Pandoc.Builder]: https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Builder.html [Text.Pandoc.Templates]: https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Templates.html [Text.Pandoc.Logging]: https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Logging.html [Text.Pandoc.App]: https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-App.html [Text.Pandoc.Error]: https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Error.html [Text.Pandoc.Writers.Shared]: https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Writers-Shared.html ================================================ FILE: doc/xml.md ================================================ --- title: XML author: massifrg@gmail.com --- # Pandoc XML format This document describes Pandoc's `xml` format, a 1:1 equivalent of the `native` and `json` formats. Here's the xml version of the beginning of this document, to give you a glimpse of the format: ```xml massifrg@gmail.com XML
Pandoc XML format
This document describes Pandoc’s xml format, a 1:1 equivalentof the native and json formats. ...
``` ## The tags If you know [Pandoc types](https://hackage.haskell.org/package/pandoc-types-1.23.1/docs/Text-Pandoc-Definition.html), the XML conversion is fairly straightforward. These are the main rules: - `Str` inlines are usually converted to plain, UTF-8 text (see below for exceptions) - `Space` inlines are usually converted to " " chars (see below for exceptions) - every `Block` and `Inline` becomes an element with the same name and the same capitalization: a `Para` Block becomes a `` element, an `Emph` Inline becomes an `` element, and so on; - the root element is `` and it has a `api-version` attribute, whose value is a string of comma-separated integer numbers; it matches the `pandoc-api-version` field of the `json` format; - the root `` element has only two children: `` and `` (lowercase, as in `json` format); - blocks and inlines with an `Attr` are HTM-like, and they have: - the `id` attribute for the identifier - the `class` attribute, a string of space-separated classes - the other attributes of `Attr`, without any prefix (so no `data-` prefix, instead of HTML) - attributes are in lower (kebab) case: - `level` in Header - `start`, `number-style`, `number-delim` in OrderedList; style and delimiter values are capitalized exactly as in `Text.Pandoc.Definition`; - `format` in `RawBlock` and RawInline - `quote-type` in Quoted (values are `SingleQuote` and `DoubleQuote`) - `math-type` in Math (values are `InlineMath` and `DisplayMath`) - `title` and `src` in Image target - `title` and `href` in Link target - `alignment` and `col-width` in ColSpec (about `col-width` values, see below); (alignment values are capitalized as in `Text.Pandoc.Definition`) - `alignment`, `row-span` and `col-span` in Cell - `row-head-columns` in TableBody - `id`, `mode`, `note-num` and `hash` for Citation (about Cite elements, see below); (`mode` values are capitalized as in `Text.Pandoc.Definition`) The classes of items with an `Attr` are put in a `class` attribute, so that you can style the XML with CSS. ## Str and Space elements `Str` and `Space` usually result in text and normal " " spaces, but there are exceptions: - `Str ""`, an empty string, is not suppressed; instead it is converted into a `` element; - `Str "foo bar"`, a string containing a space, is converted as ``; - consecutive `Str` inlines, as in `[ ..., Str "foo", Str "bar", ... ]`, are encoded as `foo` to keep their individuality; - consecutive `Space` inlines, as in `[ ..., Space, Space, ... ]`, are encoded as `` - `Space` inlines at the start or at the end of their container element are always encoded with a `` element, instead of just a " " These encodings are necessary to ensure 1:1 equivalence of the `xml` format with the AST, or the `native` and `json` formats. Since the ones above are corner cases, usually you should not see those `` and `` elements in your documents. ## Added tags Some other elements have been introduced to better structure the resulting XML. Since they are not Pandoc Blocks or Inlines, or they have no constructor or type in Pandoc's haskell code, they are kept lowercased. ### BulletList and OrderedList items Items of those lists are embedded in `` elements. These snippets are from the `xml` version of `test/testsuite.native`: ```xml asterisk 1 asterisk 2 asterisk 3 ... First Second Third ``` ### DefinitionList items Definition lists have `` elements. Each `` term has only one `` child element, and one or more `` children elements. This snippet is from the `xml` version of `test/testsuite.native`: ```xml apple red fruit orange orange fruit banana yellow fruit ``` ### Figure and Table captions Figures and tables have a `` child element, which in turn may optionally have a `` child element. This snippet is from the `xml` version of `test/testsuite.native`: ```xml
lalune lalune
``` ### Tables A `` element has: - a `" , " " xlink:href=\"a.png\" />" , " " , "

" , " " , "" ] ] , testGroup "definition lists" [ "with internal link" =: definitionList [(link "#go" "" (str "testing"), [plain (text "hi there")])] =?> "\n\ \ \n\ \ testing\n\ \ \n\ \

hi there

\n\ \
\n\ \
\n\ \
" ] , testGroup "math" [ "escape |" =: para (math "\\sigma|_{\\{x\\}}") =?> "

\n\ \\n\ \σ|{x}

" ] , testGroup "headers" [ "unnumbered header" =: headerWith ("foo",["unnumbered"],[]) 1 (text "Header 1" <> note (plain $ text "note")) =?> "\n\ \ Header 1<xref ref-type=\"fn\" rid=\"fn1\">1</xref>\n\ \" , "unnumbered sub header" =: headerWith ("foo",["unnumbered"],[]) 1 (text "Header") <> headerWith ("foo",["unnumbered"],[]) 2 (text "Sub-Header") =?> "\n\ \ Header\n\ \ \n\ \ Sub-Header\n\ \ \n\ \" , "containing image" =: header 1 (image "imgs/foo.jpg" "" (text "Alt text")) =?> "\n\ \ <inline-graphic mimetype=\"image\" mime-subtype=\"jpeg\" xlink:href=\"imgs/foo.jpg\">\n\ \ <alt-text>Alt text</alt-text>\n\ \ </inline-graphic>\n\ \" ] , testGroup "ids" [ "non-ASCII in header ID" =: headerWith ("smørbrød",[],[]) 1 (text "smørbrød") =?> T.unlines [ "" , " smørbrød" , "" ] , "disallowed symbol in header id" =: headerWith ("i/o",[],[]) 1 (text "I/O") =?> T.unlines [ "" , " I/O" , "" ] , "disallowed symbols in internal link target" =: link "#foo:bar" "" "baz" =?> "

baz

" , "code id starting with a number" =: codeWith ("7y",[],[]) "print 5" =?> "

print 5

" ] , testGroup "spans" [ "unwrapped if no attributes given" =: spanWith nullAttr "text in span" =?> "

text in span

" , "converted to named-content element if class given" =: spanWith ("a", ["genus-species"], [("alt", "aa")]) "C. elegans" =?> ("

" <> "C. elegans

") , "unwrapped if styled-content element would have no attributes" =: spanWith ("", [], [("hidden", "true")]) "text in span" =?> "

text in span

" , "use content-type attribute if present" =: spanWith ("", [], [("content-type", "species")]) "E. coli" =?> "

E. coli

" ] ] ================================================ FILE: test/Tests/Writers/Jira.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Jira (tests) where import Data.Text (unpack) import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder jira :: (ToPandoc a) => a -> String jira = unpack . purely (writeJira def) . toPandoc infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test jira tests :: [TestTree] tests = [ testGroup "inlines" [ "underlined text" =: underline "underlined text" =?> "+underlined text+" , "image with attributes" =: imageWith ("", [], [("align", "right"), ("height", "50")]) "image.png" "" mempty =?> "!image.png|align=right, height=50!" , testGroup "links" [ "external link" =: link "https://example.com/test.php" "" "test" =?> "[test|https://example.com/test.php]" , "external link without description" =: link "https://example.com/tmp.js" "" "https://example.com/tmp.js" =?> "[https://example.com/tmp.js]" , "email link" =: link "mailto:me@example.com" "" "Jane" =?> "[Jane|mailto:me@example.com]" , "email link without description" =: link "mailto:me@example.com" "" "me@example.com" =?> "[mailto:me@example.com]" , "attachment link" =: linkWith ("", ["attachment"], []) "foo.txt" "" "My file" =?> "[My file^foo.txt]" , "attachment link without description" =: linkWith ("", ["attachment"], []) "foo.txt" "" "foo.txt" =?> "[^foo.txt]" , "user link" =: linkWith ("", ["user-account"], []) "~johndoe" "" "John Doe" =?> "[John Doe|~johndoe]" , "user link with user as description" =: linkWith ("", ["user-account"], []) "~johndoe" "" "~johndoe" =?> "[~johndoe]" , "'smart' link" =: para (linkWith ("", ["smart-link"], []) "http://example.com" "" "x") =?> "[x|http://example.com|smart-link]" , "'smart' card" =: para (linkWith ("", ["smart-card"], []) "http://example.org" "" "x") =?> "[x|http://example.org|smart-card]" ] , testGroup "spans" [ "id is used as anchor" =: spanWith ("unicorn", [], []) (str "Unicorn") =?> "{anchor:unicorn}Unicorn" , "use `color` attribute" =: spanWith ("",[],[("color","red")]) "ruby" =?> "{color:red}ruby{color}" ] , testGroup "code" [ "code block with known language" =: codeBlockWith ("", ["java"], []) "Book book = new Book(\"Algebra\")" =?> "{code:java}\nBook book = new Book(\"Algebra\")\n{code}" , "code block without language" =: codeBlockWith ("", [], []) "preformatted\n text.\n" =?> "{noformat}\npreformatted\n text.\n{noformat}" ] ] , testGroup "blocks" [ testGroup "div" [ "empty attributes" =: divWith nullAttr (para "interesting text") =?> "interesting text" , "just identifier" =: divWith ("a", [], []) (para "interesting text") =?> "{anchor:a}interesting text" , "with class 'panel'" =: divWith ("", ["panel"], []) (para "Contents!") =?> "{panel}\nContents\\!\n{panel}\n" , "panel with id" =: divWith ("b", ["panel"], []) (para "text") =?> "{panel}\n{anchor:b}text\n{panel}\n" , "title attribute" =: divWith ("", [], [("title", "Gimme!")]) (para "Contents!") =?> "{panel:title=Gimme!}\nContents\\!\n{panel}\n" , "nested panels" =: let panelAttr = ("", ["panel"], []) in divWith panelAttr (para "hi" <> divWith panelAttr (para "wassup?")) =?> "{panel}\nhi\n\nwassup?\n{panel}\n" ] ] ] ================================================ FILE: test/Tests/Writers/LaTeX.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.LaTeX (tests) where import Data.Text (pack, unpack) import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder latex :: (ToPandoc a) => a -> String latex = latexWithOpts def latexListing :: (ToPandoc a) => a -> String latexListing = latexWithOpts def{ writerHighlightMethod = IdiomaticHighlighting } latexWithOpts :: (ToPandoc a) => WriterOptions -> a -> String latexWithOpts opts = unpack . purely (writeLaTeX opts) . toPandoc beamerWithOpts :: (ToPandoc a) => WriterOptions -> a -> String beamerWithOpts opts = unpack . purely (writeBeamer opts) . toPandoc {- "my test" =: X =?> Y is shorthand for test latex "my test" $ X =?> Y which is in turn shorthand for test latex "my test" (X,Y) -} infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test latex tests :: [TestTree] tests = [ testGroup "code blocks" [ "in footnotes" =: note (para "hi" <> codeBlock "hi") =?> "\\footnote{hi\n\n\\begin{Verbatim}\nhi\n\\end{Verbatim}\n}" , test latexListing "identifier" $ codeBlockWith ("id",[],[]) "hi" =?> ("\\begin{lstlisting}[label=id]\nhi\n\\end{lstlisting}" :: String) , test latexListing "no identifier" $ codeBlock "hi" =?> ("\\begin{lstlisting}\nhi\n\\end{lstlisting}" :: String) ] , testGroup "definition lists" [ "with internal link" =: definitionList [(link "#go" "" (str "testing"), [plain (text "hi there")])] =?> "\\begin{description}\n\\tightlist\n\\item[{\\hyperref[go]{testing}}]\nhi there\n\\end{description}" ] , testGroup "math" [ "escape |" =: para (math "\\sigma|_{\\{x\\}}") =?> "\\(\\sigma|_{\\{x\\}}\\)" ] , testGroup "headers" [ "unnumbered header" =: headerWith ("foo",["unnumbered"],[]) 1 (text "Header 1" <> note (plain $ text "note")) =?> "\\section*{\\texorpdfstring{Header 1\\footnote{note}}{Header 1}}\\label{foo}\n\\addcontentsline{toc}{section}{Header 1}\n" , "in list item" =: bulletList [header 2 (text "foo")] =?> "\\begin{itemize}\n\\item ~\n \\subsection{foo}\n\\end{itemize}" , "in definition list item" =: definitionList [(text "foo", [header 2 (text "bar"), para $ text "baz"])] =?> "\\begin{description}\n\\item[foo] ~ \n\\subsection{bar}\n\nbaz\n\\end{description}" , "containing image" =: header 1 (image "imgs/foo.jpg" "" (text "Alt text")) =?> "\\section{\\texorpdfstring{\\protect\\pandocbounded{\\includegraphics[keepaspectratio,alt={Alt text}]{imgs/foo.jpg}}}{Alt text}}" ] , testGroup "inline code" [ "struck out and highlighted" =: strikeout (codeWith ("",["haskell"],[]) "foo" <> space <> str "bar") =?> "\\st{\\mbox{\\VERB|\\NormalTok{foo}|} bar}" , "struck out and not highlighted" =: strikeout (code "foo" <> space <> str "bar") =?> "\\st{\\mbox{\\texttt{foo}} bar}" , "single quotes" =: code "dog's" =?> "\\texttt{dog\\textquotesingle{}s}" , "backtick" =: code "`nu?`" =?> "\\texttt{\\textasciigrave{}nu?\\textasciigrave{}}" ] , testGroup "inline note" [ "Big note in emph" =: emph (str "This sentence" <> note (para (str "paragraph1") <> para (str "paragraph2")) <> str " has footnote.") =?> "\\emph{This sentence}\\footnote{paragraph1\n\n paragraph2}" <> "\\emph{ has footnote.}" , "Big note in strong" =: strong (str "This sentence" <> note (para (str "paragraph1") <> para (str "paragraph2")) <> str " has footnote.") =?> "\\textbf{This sentence}\\footnote{paragraph1\n\n paragraph2}" <> "\\textbf{ has footnote.}" , "Big note in underline" =: underline (str "This sentence" <> note (para (str "paragraph1") <> para (str "paragraph2")) <> str " has footnote.") =?> "\\ul{This sentence}\\footnote{paragraph1\n\n paragraph2}" <> "\\ul{ has footnote.}" , "Big note in strikeout" =: strikeout (str "This sentence" <> note (para (str "paragraph1") <> para (str "paragraph2")) <> str " has footnote.") =?> "\\st{This sentence}\\footnote{paragraph1\n\n paragraph2}" <> "\\st{ has footnote.}" , "Small note in emph" =: emph (str "This sentence" <> note (para (str "paragraph")) <> str " has footnote.") =?> "\\emph{This sentence\\footnote{paragraph} has footnote.}" , "Big note nested in emph and strong" =: emph (str "This " <> strong (str "nested sentence " <> note (para (str "paragraph1") <> para (str "paragraph2")) <> str "has ") <> str "footnote." ) =?> "\\emph{This \\textbf{nested sentence }}\\footnote{paragraph1\n\n" <> " paragraph2}\\emph{\\textbf{has }footnote.}" , "Two Big notes in emph" =: emph (str "This sentence" <> note (para (str "1-paragraph1") <> para (str "1-paragraph2")) <> str " has" <> note (para (str "2-paragraph1") <> para (str "2-paragraph2")) <> str " footnote.") =?> "\\emph{This sentence}\\footnote{1-paragraph1\n\n 1-paragraph2}" <> "\\emph{ has}\\footnote{2-paragraph1\n\n 2-paragraph2}" <> "\\emph{ footnote.}" ] , testGroup "writer options" [ testGroup "top-level division" $ let headers = header 1 (text "header1") <> header 2 (text "header2") <> header 3 (text "header3") latexTopLevelDiv :: (ToPandoc a) => TopLevelDivision -> a -> String latexTopLevelDiv division = latexWithOpts def{ writerTopLevelDivision = division } beamerTopLevelDiv :: (ToPandoc a) => TopLevelDivision -> a -> String beamerTopLevelDiv division = beamerWithOpts def { writerTopLevelDivision = division } in [ test (latexTopLevelDiv TopLevelSection) "sections as top-level" $ headers =?> unlines [ "\\section{header1}\n" , "\\subsection{header2}\n" , "\\subsubsection{header3}" ] , test (latexTopLevelDiv TopLevelChapter) "chapters as top-level" $ headers =?> unlines [ "\\chapter{header1}\n" , "\\section{header2}\n" , "\\subsection{header3}" ] , test (latexTopLevelDiv TopLevelPart) "parts as top-level" $ headers =?> unlines [ "\\part{header1}\n" , "\\chapter{header2}\n" , "\\section{header3}" ] , test (latexTopLevelDiv TopLevelDefault) "default top-level" $ headers =?> unlines [ "\\section{header1}\n" , "\\subsection{header2}\n" , "\\subsubsection{header3}" ] , test (beamerTopLevelDiv TopLevelSection) "sections as top-level in beamer" $ headers =?> unlines [ "\\section{header1}\n" , "\\subsection{header2}\n" , "\\subsubsection{header3}" ] , test (beamerTopLevelDiv TopLevelChapter) "chapters are as part in beamer" $ headers =?> unlines [ "\\part{header1}\n" , "\\section{header2}\n" , "\\subsection{header3}" ] , test (beamerTopLevelDiv TopLevelPart) "parts as top-level in beamer" $ headers =?> unlines [ "\\part{header1}\n" , "\\section{header2}\n" , "\\subsection{header3}" ] , test (beamerTopLevelDiv TopLevelDefault) "default top-level in beamer" $ headers =?> unlines [ "\\section{header1}\n" , "\\subsection{header2}\n" , "\\subsubsection{header3}" ] , test (latexTopLevelDiv TopLevelPart) "part top-level, section not in toc" $ ( headerWith ("", ["unnumbered"], []) 1 (text "header1") <> headerWith ("", ["unnumbered"], []) 2 (text "header2") <> headerWith ("", ["unnumbered"], []) 3 (text "header3") <> headerWith ("", ["unnumbered"], []) 4 (text "header4") <> headerWith ("", ["unnumbered"], []) 5 (text "header5") <> headerWith ("", ["unnumbered"], []) 6 (text "header6")) =?> unlines [ "\\part*{header1}" , "\\addcontentsline{toc}{part}{header1}\n" , "\\chapter*{header2}" , "\\addcontentsline{toc}{chapter}{header2}\n" , "\\section*{header3}" , "\\addcontentsline{toc}{section}{header3}\n" , "\\subsection*{header4}" , "\\addcontentsline{toc}{subsection}{header4}\n" , "\\subsubsection*{header5}" , "\\addcontentsline{toc}{subsubsection}{header5}\n" , "\\paragraph*{header6}" , "\\addcontentsline{toc}{paragraph}{header6}" ] ] ] , testGroup "figures" [ "placement" =: figureWith ("", [], [("latex-placement", "htbp")]) (simpleCaption $ plain "caption") (plain $ image (pack "img.jpg") (pack "") (text "alt text")) =?> "\\begin{figure}[htbp]\n\\centering\n" <> "\\pandocbounded{\\includegraphics[keepaspectratio,alt={alt text}]{img.jpg}}\n" <> "\\caption{caption}\n\\end{figure}" ] ] ================================================ FILE: test/Tests/Writers/Markdown.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} module Tests.Writers.Markdown (tests) where import Data.Text (unpack) import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder defopts :: WriterOptions defopts = def { writerExtensions = pandocExtensions , writerSetextHeaders = True } markdown :: (ToPandoc a) => a -> String markdown = unpack . purely (writeMarkdown defopts) . toPandoc markdownWithOpts :: (ToPandoc a) => WriterOptions -> a -> String markdownWithOpts opts x = unpack . purely (writeMarkdown opts) $ toPandoc x {- "my test" =: X =?> Y is shorthand for test markdown "my test" $ X =?> Y which is in turn shorthand for test markdown "my test" (X,Y) -} infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test markdown tests :: [TestTree] tests = [ "indented code after list" =: (orderedList [ para "one" <> para "two" ] <> codeBlock "test") =?> "1. one\n\n two\n\n\n\n test" , "list with tight sublist" =: bulletList [ plain "foo" <> bulletList [ plain "bar" ], plain "baz" ] =?> "- foo\n - bar\n- baz\n" , "emph/strong with spaces (#10696)" =: emph (str "f" <> strong (space <> str "d" <> space)) <> str "l" =?> "*f **d*** l" ] ++ [noteTests] ++ [shortcutLinkRefsTests] {- Testing with the following text: First Header ============ This is a footnote.[^1] And this is a [link](https://www.google.com). > A note inside a block quote.[^2] > > A second paragraph. Second Header ============= Some more text. [^1]: Down here. [^2]: The second note. -} noteTestDoc :: Blocks noteTestDoc = header 1 "First Header" <> para ("This is a footnote." <> note (para "Down here.") <> " And this is a " <> link "https://www.google.com" "" "link" <> ".") <> blockQuote (para ("A note inside a block quote." <> note (para "The second note.")) <> para "A second paragraph.") <> header 1 "Second Header" <> para "Some more text." noteTests :: TestTree noteTests = testGroup "note and reference location" [ test (markdownWithOpts defopts) "footnotes at the end of a document" $ noteTestDoc =?> unlines [ "First Header" , "============" , "" , "This is a footnote.[^1] And this is a [link](https://www.google.com)." , "" , "> A note inside a block quote.[^2]" , ">" , "> A second paragraph." , "" , "Second Header" , "=============" , "" , "Some more text." , "" , "[^1]: Down here." , "" , "[^2]: The second note." ] , test (markdownWithOpts defopts{writerReferenceLocation=EndOfBlock}) "footnotes at the end of blocks" $ noteTestDoc =?> unlines [ "First Header" , "============" , "" , "This is a footnote.[^1] And this is a [link](https://www.google.com)." , "" , "[^1]: Down here." , "" , "> A note inside a block quote.[^2]" , ">" , "> A second paragraph." , "" , "[^2]: The second note." , "" , "Second Header" , "=============" , "" , "Some more text." ] , test (markdownWithOpts defopts{writerReferenceLocation=EndOfBlock, writerReferenceLinks=True}) "footnotes and reference links at the end of blocks" $ noteTestDoc =?> unlines [ "First Header" , "============" , "" , "This is a footnote.[^1] And this is a [link]." , "" , "[^1]: Down here." , "" , " [link]: https://www.google.com" , "" , "> A note inside a block quote.[^2]" , ">" , "> A second paragraph." , "" , "[^2]: The second note." , "" , "Second Header" , "=============" , "" , "Some more text." ] , test (markdownWithOpts defopts{writerReferenceLocation=EndOfSection}) "footnotes at the end of section" $ noteTestDoc =?> unlines [ "First Header" , "============" , "" , "This is a footnote.[^1] And this is a [link](https://www.google.com)." , "" , "> A note inside a block quote.[^2]" , ">" , "> A second paragraph." , "" , "[^1]: Down here." , "" , "[^2]: The second note." , "" , "Second Header" , "=============" , "" , "Some more text." ] ] shortcutLinkRefsTests :: TestTree shortcutLinkRefsTests = let infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test (purely (writeMarkdown defopts{writerReferenceLinks = True}) . toPandoc) in testGroup "Shortcut reference links" [ "Simple link (shortcutable)" =: para (link "/url" "title" "foo") =?> "[foo]\n\n [foo]: /url \"title\"" , "Followed by another link (unshortcutable)" =: para (link "/url1" "title1" "first" <> link "/url2" "title2" "second") =?> unlines [ "[first][][second]" , "" , " [first]: /url1 \"title1\"" , " [second]: /url2 \"title2\"" ] , "Followed by space and another link (unshortcutable)" =: para (link "/url1" "title1" "first" <> " " <> link "/url2" "title2" "second") =?> unlines [ "[first][] [second]" , "" , " [first]: /url1 \"title1\"" , " [second]: /url2 \"title2\"" ] , "Reference link is used multiple times (unshortcutable)" =: para (link "/url1" "" "foo" <> link "/url2" "" "foo" <> link "/url3" "" "foo") =?> unlines [ "[foo][][foo][1][foo][2]" , "" , " [foo]: /url1" , " [1]: /url2" , " [2]: /url3" ] , "Reference link is used multiple times (unshortcutable)" =: para (link "/url1" "" "foo" <> " " <> link "/url2" "" "foo" <> " " <> link "/url3" "" "foo") =?> unlines [ "[foo][] [foo][1] [foo][2]" , "" , " [foo]: /url1" , " [1]: /url2" , " [2]: /url3" ] , "Reference link is followed by text in brackets" =: para (link "/url" "" "link" <> "[text in brackets]") =?> unlines [ "[link][]\\[text in brackets\\]" , "" , " [link]: /url" ] , "Reference link is followed by space and text in brackets" =: para (link "/url" "" "link" <> " [text in brackets]") =?> unlines [ "[link][] \\[text in brackets\\]" , "" , " [link]: /url" ] , "Reference link is followed by RawInline" =: para (link "/url" "" "link" <> rawInline "markdown" "[rawText]") =?> unlines [ "[link][][rawText]" , "" , " [link]: /url" ] , "Reference link is followed by space and RawInline" =: para (link "/url" "" "link" <> space <> rawInline "markdown" "[rawText]") =?> unlines [ "[link][] [rawText]" , "" , " [link]: /url" ] , "Reference link is followed by RawInline with space" =: para (link "/url" "" "link" <> rawInline "markdown" " [rawText]") =?> unlines [ "[link][] [rawText]" , "" , " [link]: /url" ] , "Reference link is followed by citation" =: para (link "/url" "" "link" <> cite [Citation "author" [] [] NormalCitation 0 0] (str "[@author]")) =?> unlines [ "[link][][@author]" , "" , " [link]: /url" ] , "Reference link is followed by space and citation" =: para (link "/url" "" "link" <> space <> cite [Citation "author" [] [] NormalCitation 0 0] (str "[@author]")) =?> unlines [ "[link][] [@author]" , "" , " [link]: /url" ] ] ================================================ FILE: test/Tests/Writers/Markua.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Markua (tests) where import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder {- "my test" =: X =?> Y is shorthand for test html "my test" $ X =?> Y which is in turn shorthand for test html "my test" (X,Y) -} infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test (purely (writeMarkua def) . toPandoc) tests :: [TestTree] tests = [ testGroup "simple blurb/aside" ["blurb" =: divWith ("",["blurb"],[]) (bulletList [para "blurb content"]) =?> "B> * blurb content" ,"aside" =: divWith ("",["aside"],[]) (bulletList [para "aside list"]) =?> "A> * aside list" ] ,testGroup "multiclass blurb/aside" ["blurb" =: divWith ("",["blurb", "otherclass"],[]) (bulletList [para "blurb content"]) =?> "B> * blurb content" ,"aside" =: divWith ("",["otherclass", "aside"],[]) (bulletList [para "aside list"]) =?> "A> * aside list" ] ] ================================================ FILE: test/Tests/Writers/Ms.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Ms (tests) where import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Builder infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test (purely (writeMs def . toPandoc)) tests :: [TestTree] tests = [ testGroup "code blocks" [ "basic" =: codeBlock "hello" =?> unlines [ ".IP" , ".nf" , "\\f[C]" , "hello" , "\\f[]" , ".fi"] , "escape starting ." =: codeBlock ". hello" =?> unlines [ ".IP" , ".nf" , "\\f[C]" , "\\&. hello" , "\\f[]" , ".fi"] ] ] ================================================ FILE: test/Tests/Writers/Muse.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Muse (tests) where import Prelude hiding (unlines) import Data.Text (Text, unlines) import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder defopts :: WriterOptions defopts = def{ writerWrapText = WrapPreserve, writerExtensions = extensionsFromList [Ext_amuse, Ext_auto_identifiers] } muse :: (ToPandoc a) => a -> Text muse = museWithOpts defopts museWithOpts :: (ToPandoc a) => WriterOptions -> a -> Text museWithOpts opts = purely (writeMuse opts) . toPandoc infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, Text) -> TestTree (=:) = test muse noteLocationTestDoc :: Blocks noteLocationTestDoc = header 1 "First Header" <> para ("This is a footnote." <> note (para "First note.")) <> blockQuote (para ("A note inside a block quote." <> note (para "The second note.")) <> para "A second paragraph.") <> header 1 "Second Header" <> para "Some more text." noteLocationTests :: TestTree noteLocationTests = testGroup "note location" [ test (museWithOpts defopts {writerReferenceLocation=EndOfDocument}) "footnotes at the end of document" $ noteLocationTestDoc =?> unlines [ "* First Header" , "" , "This is a footnote.[1]" , "" , "" , "A note inside a block quote.[2]" , "" , "A second paragraph." , "" , "" , "* Second Header" , "" , "Some more text." , "" , "[1] First note." , "" , "[2] The second note." ] , test (museWithOpts defopts {writerReferenceLocation=EndOfBlock}) "footnotes at the end of block" $ noteLocationTestDoc =?> unlines [ "* First Header" , "" , "This is a footnote.[1]" , "" , "[1] First note." , "" , "" , "A note inside a block quote.[2]" , "" , "[2] The second note." , "" , "A second paragraph." , "" , "" , "* Second Header" , "" , "Some more text." ] , test (museWithOpts defopts {writerReferenceLocation=EndOfSection}) "footnotes at the end of section" $ noteLocationTestDoc =?> unlines [ "* First Header" , "" , "This is a footnote.[1]" , "" , "" , "A note inside a block quote.[2]" , "" , "A second paragraph." , "" , "" , "[1] First note." , "" , "[2] The second note." , "" , "* Second Header" , "" , "Some more text." ] ] tests :: [TestTree] tests = [ testGroup "block elements" [ "plain" =: plain "Foo bar." =?> "Foo bar." , testGroup "paragraphs" [ "single paragraph" =: para "Sample paragraph." =?> "Sample paragraph." , "two paragraphs" =: para "First paragraph." <> para "Second paragraph." =?> unlines [ "First paragraph." , "" , "Second paragraph." ] ] , "line block" =: lineBlock ["Foo", "bar", "baz"] =?> unlines [ "> Foo" , "> bar" , "> baz" ] , "code block" =: codeBlock "int main(void) {\n\treturn 0;\n}" =?> unlines [ "" , "int main(void) {" , "\treturn 0;" , "}" , "" ] , "html raw block" =: rawBlock "html" "
" =?> unlines [ "" , "
" , "
" ] , "block quote" =: blockQuote (para "Foo") =?> unlines [ "" , "Foo" , "" ] , testGroup "lists" [ testGroup "simple lists" [ "ordered list" =: orderedList [ plain "first" , plain "second" , plain "third" ] =?> unlines [ " 1. first" , " 2. second" , " 3. third" ] , "ordered list with Roman numerals" =: orderedListWith (1, UpperRoman, DefaultDelim) [ plain "first" , plain "second" , plain "third" ] =?> unlines [ " I. first" , " II. second" , " III. third" ] , "bullet list" =: bulletList [ plain "first" , plain "second" , plain "third" ] =?> unlines [ " - first" , " - second" , " - third" ] , "definition list" =: definitionList [ ("first definition", [plain "first description"]) , ("second definition", [plain "second description"]) , ("third definition", [plain "third description"]) ] =?> unlines [ " first definition :: first description" , " second definition :: second description" , " third definition :: third description" ] , "definition list with multiple descriptions" =: definitionList [ ("first definition", [ plain "first description" , plain "second description" ]) , ("second definition", [plain "third description"]) ] =?> unlines [ " first definition :: first description" , " :: second description" , " second definition :: third description" ] , "definition list with empty term" =: definitionList [ ("first definition", [plain "first description"]) , (mempty, [plain "second description"]) , (str "", [plain "third description"]) ] =?> unlines [ " first definition :: first description" , " :: second description" , " :: third description" ] , "definition list terms starting with space" =: definitionList [ ("first definition", [plain "first description"]) , (space <> str "foo", [plain "second description"]) , (str " > bar", [plain "third description"]) ] =?> unlines [ " first definition :: first description" , " foo :: second description" , " > bar :: third description" ] , "definition list terms starting with list markers" =: definitionList [ ("first definition", [plain "first description"]) , (str "-", [plain "second description"]) , (str "1.", [plain "third description"]) ] =?> unlines [ " first definition :: first description" , " - :: second description" , " 1. :: third description" ] ] -- Test that lists of the same type and style are separated with two blanklines , testGroup "sequential lists" [ "bullet lists" =: bulletList [ para "First" , para "Second" , para "Third" ] <> bulletList [ para "Fourth" , para "Fifth" ] =?> unlines [ " - First" , " - Second" , " - Third" , "" , "" , " - Fourth" , " - Fifth" ] , "ordered lists of the same style" =: orderedListWith (1, UpperRoman, DefaultDelim) [ para "First" , para "Second" ] <> orderedListWith (1, UpperRoman, DefaultDelim) [ para "Third" , para "Fourth" ] =?> unlines [ " I. First" , " II. Second" , "" , "" , " I. Third" , " II. Fourth" ] , "ordered lists with equal styles" =: orderedList [ para "First" , para "Second" ] <> orderedListWith (1, Decimal, DefaultDelim) [ para "Third" , para "Fourth" ] =?> unlines [ " 1. First" , " 2. Second" , "" , "" , " 1. Third" , " 2. Fourth" ] , "bullet and ordered lists" =: bulletList [ para "First" , para "Second" ] <> orderedListWith (1, UpperRoman, DefaultDelim) [ para "Third" , para "Fourth" ] =?> unlines [ " - First" , " - Second" , "" , " I. Third" , " II. Fourth" ] , "different style ordered lists" =: orderedListWith (1, UpperRoman, DefaultDelim) [ para "First" , para "Second" ] <> orderedListWith (1, Decimal, DefaultDelim) [ para "Third" , para "Fourth" ] =?> unlines [ " I. First" , " II. Second" , "" , " 1. Third" , " 2. Fourth" ] ] , testGroup "nested lists" [ "nested ordered list" =: orderedList [ plain "First outer" , plain "Second outer:" <> orderedList [ plain "first" , plain "second" ] , plain "Third outer" ] =?> unlines [ " 1. First outer" , " 2. Second outer:" , " 1. first" , " 2. second" , " 3. Third outer" ] , "nested bullet lists" =: bulletList [ plain "First outer" , plain "Second outer:" <> bulletList [ plain "first" , plain "second" ] , plain "Third outer" ] =?> unlines [ " - First outer" , " - Second outer:" , " - first" , " - second" , " - Third outer" ] , "nested definition lists" =: definitionList [ ("first definition", [plain "first description"]) , ("second definition", [ plain "second description" <> definitionList [ ("first inner definition" , [plain "first inner description"]) , ( "second inner definition" , [plain "second inner description"]) ] ] ) ] =?> unlines [ " first definition :: first description" , " second definition :: second description" , " first inner definition :: first inner description" , " second inner definition :: second inner description" ] , "list item starting with list" =: bulletList [ bulletList [ plain "foo"] ] =?> " - - foo" ] -- Check that list is intended with one space even inside a quote , "List inside block quote" =: blockQuote (orderedList [ plain "first" , plain "second" , plain "third" ]) =?> unlines [ "" , " 1. first" , " 2. second" , " 3. third" , "" ] ] , testGroup "headings" [ "normal heading" =: header 1 "foo" =?> "* foo" , "heading levels" =: header 1 "First level" <> header 3 "Third level" =?> unlines [ "* First level" , "" , "*** Third level" ] , "heading with ID" =: headerWith ("bar", [], []) 2 "Foo" =?> unlines [ "#bar" , "** Foo" ] , "empty heading" =: header 4 mempty =?> "**** " ] , "horizontal rule" =: horizontalRule =?> "----" , "escape horizontal rule" =: para "----" =?> "----" , "escape long horizontal rule" =: para "----------" =?> "----------" , "don't escape horizontal inside paragraph" =: para "foo ---- bar" =?> "foo ---- bar" , "escape nonbreaking space" =: para "~~" =?> "~~" , "escape > in the beginning of line" =: para "> foo bar" =?> "> foo bar" , "escape string with > and space in the beginning of line" =: para (str "> foo bar") =?> "> foo bar" , testGroup "tables" [ "table without header" =: let rows = [[para "Para 1.1", para "Para 1.2"] ,[para "Para 2.1", para "Para 2.2"]] toRow = Row nullAttr . map simpleCell in table emptyCaption [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead nullAttr [toRow [mempty, mempty]]) [TableBody nullAttr 0 [] $ map toRow rows] (TableFoot nullAttr []) =?> unlines [ " Para 1.1 | Para 1.2" , " Para 2.1 | Para 2.2" ] , "table with header" =: let headers = [plain "header 1", plain "header 2"] rows = [[para "Para 1.1", para "Para 1.2"] ,[para "Para 2.1", para "Para 2.2"]] in simpleTable headers rows =?> unlines [ " header 1 || header 2" , " Para 1.1 | Para 1.2" , " Para 2.1 | Para 2.2" ] , "table with header and caption" =: let capt = simpleCaption $ plain "Table 1" toRow = Row nullAttr . map simpleCell headers = [toRow [plain "header 1", plain "header 2"]] rows = map toRow [[para "Para 1.1", para "Para 1.2"] ,[para "Para 2.1", para "Para 2.2"]] in table capt [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead nullAttr headers) [TableBody nullAttr 0 [] rows] (TableFoot nullAttr []) =?> unlines [ " header 1 || header 2" , " Para 1.1 | Para 1.2" , " Para 2.1 | Para 2.2" , " |+ Table 1 +|" ] , "table inside bullet list" =: bulletList [simpleTable [] [[para "foo", para "bar"] ,[para "bat", para "baz"]]] =?> unlines [ " - foo | bar" , " bat | baz" ] , "table with one column" =: let headers = [] rows = [[para "Para 1"] ,[para "Para 2"]] in simpleTable headers rows =?> unlines [ "+--------+" , "| Para 1 |" , "+--------+" , "| Para 2 |" , "+--------+" ] ] , "div with bullet list" =: divWith nullAttr (bulletList [para "foo"]) =?> unlines [ " - foo" ] -- Making sure bullets are indented ] , testGroup "inline elements" [ testGroup "string" [ "string" =: str "foo" =?> "foo" , "escape footnote" =: str "[1]" =?> "[1]" , "escape secondary note" =: str "{1}" =?> "{1}" , "do not escape brackets" =: str "[12ab]" =?> "[12ab]" , "escape verbatim close tag" =: str "foobar" =?> "foo</verbatim>bar" , "escape link-like text" =: str "[[https://www.example.org]]" =?> "[[https://www.example.org]]" , "escape pipe to avoid accidental tables" =: str "foo | bar" =?> "foo | bar" , "escape hash to avoid accidental anchors" =: text "#foo bar" =?> "#foo bar" , "escape definition list markers" =: str "::" =?> "::" , "normalize strings before escaping" =: fromList [Str ":", Str ":"] =?> "::" -- We don't want colons to be escaped if they can't be confused -- with definition list item markers. , "do not escape colon" =: str ":" =?> ":" , "escape - to avoid accidental unordered lists" =: text " - foo" =?> " - foo" , "escape - inside a list to avoid accidental nested unordered lists" =: bulletList [ para "foo" <> para "- bar" ] =?> unlines [ " - foo" , "" , " - bar" ] , "escape strings starting with - inside a list" =: bulletList [ para (str "foo") <> para (str "- bar") ] =?> unlines [ " - foo" , "" , " - bar" ] , "escape - inside a note" =: note (para "- foo") =?> unlines [ "[1]" , "" , "[1] - foo" ] , "escape - after softbreak in note" =: note (para (str "foo" <> softbreak <> str "- bar")) =?> unlines [ "[1]" , "" , "[1] foo" , " - bar" ] , "escape ; to avoid accidental comments" =: text "; foo" =?> "; foo" , "escape strings starting with ; and space" =: str "; foo" =?> "; foo" , "escape ; after softbreak" =: "foo" <> softbreak <> "; bar" =?> "foo\n; bar" , "escape ; after linebreak" =: "foo" <> linebreak <> "; bar" =?> "foo
\n; bar" , "do not escape ; inside paragraph" =: text "foo ; bar" =?> "foo ; bar" , "escape newlines" =: str "foo\nbar" =?> "foo bar" ] , testGroup "emphasis" [ "emphasis" =: emph "foo" =?> "*foo*" , "emphasis inside word" =: "foo" <> emph "bar" <> "baz" =?> "foobarbaz" , "emphasis before comma" =: emph "foo" <> ", bar" =?> "*foo*, bar" , "emphasis before period" =: emph "foobar" <> "." =?> "*foobar*." , "empty emphasis" =: emph mempty =?> "" , "empty strong" =: strong mempty =?> "" , "empty strong emphasis" =: strong (emph mempty) =?> "****" , "empty emphasized strong" =: emph (strong mempty) =?> "**" , "emphasized empty string" =: emph (str "") =?> "" , "strong empty string" =: strong (str "") =?> "" , "strong emphasized empty string" =: strong (emph (str "")) =?> "****" , "emphasized strong empty string" =: emph (strong (str "")) =?> "**" , "emphasized string with space" =: emph (str " ") =?> " " , "emphasized string ending with space" =: emph (str "foo ") =?> "foo " , "emphasized string with tab" =: emph (str "\t") =?> "\t" , "emphasized space between empty strings" =: emph (str "" <> space <> str "") =?> " " , "strong" =: strong "foo" =?> "**foo**" , "strong inside word" =: "foo" <> strong "bar" <> "baz" =?> "foobarbaz" , "strong emphasis" =: strong (emph "foo") =?> "***foo***" , "strong after emphasis" =: emph "foo" <> strong "bar" =?> "*foo*bar" , "strong emphasis after emphasis" =: emph "foo" <> strong (emph "bar") =?> "*foo**bar*" , "strong in the end of emphasis" =: emph ("foo" <> strong "bar") =?> "*foobar*" , "switch to lightweight markup after tag" =: strong (str "foo") <> emph (str "bar") <> strong (str "baz") =?> "**foo**bar**baz**" , "strikeout" =: strikeout "foo" =?> "foo" , "space at the beginning of emphasis" =: emph " foo" =?> " foo" , "space at the end of emphasis" =: emph "foo " =?> "foo " , "space at the beginning of strong" =: strong " foo" =?> " foo" , "space at the end of strong" =: strong "foo " =?> "foo " , "space at the beginning of strong emphasis" =: strong (emph " foo") =?> "** foo**" , "space at the end of strong emphasis" =: strong (emph "foo ") =?> "**foo **" , "space at the beginning of emphasiszed strong" =: emph (strong " foo") =?> "* foo*" , "space at the end of emphasized strong" =: emph (strong "foo ") =?> "*foo *" ] , "superscript" =: superscript "foo" =?> "foo" , "subscript" =: subscript "foo" =?> "foo" , "smallcaps" =: smallcaps "foo" =?> "*foo*" , "smallcaps near emphasis" =: emph (str "foo") <> smallcaps (str "bar") =?> "*foobar*" , "single quoted" =: singleQuoted "foo" =?> "‘foo’" , "double quoted" =: doubleQuoted "foo" =?> "“foo”" -- Cite is trivial , testGroup "code" [ "simple" =: code "foo" =?> "=foo=" , "empty" =: code "" =?> "" , "space" =: code " " =?> " " , "space at the beginning" =: code " foo" =?> " foo" , "space at the end" =: code "foo " =?> "foo " , "use tags for =" =: code "foo = bar" =?> "foo = bar" , "escape tag" =: code "foo = bar baz" =?> "foo = bar</code> baz" , "normalization with attributes" =: codeWith ("",["haskell"],[]) "foo" <> code "bar" =?> "=foobar=" , "code tag" =: code "foo" =?> "=foo=" , "normalization" =: code " code "de>" <> code "=" =?> "</code>=" , "normalization with empty string" =: code " str "" <> code "de>" <> code "=" =?> "</code>=" , "emphasized code" =: emph (code "foo") =?> "*=foo=*" , "strong code" =: strong (code "foo") =?> "**=foo=**" ] , testGroup "spaces" [ "space" =: "a" <> space <> "b" =?> "a b" , "soft break" =: "a" <> softbreak <> "b" =?> "a\nb" , test (museWithOpts def{ writerWrapText = WrapNone }) "remove soft break" $ "a" <> softbreak <> "b" =?> ("a b" :: String) , "line break" =: "a" <> linebreak <> "b" =?> "a
\nb" , "line break at the end" =: "a" <> linebreak =?> "a
" , "no newline after line break in header" =: header 1 ("a" <> linebreak <> "b") =?> "* a
b" , "no softbreak in header" =: header 1 ("a" <> softbreak <> "b") =?> "* a b" ] , testGroup "math" [ "inline math" =: math "2^3" =?> "23" , "display math" =: displayMath "2^3" =?> "23" , "multiple letters in inline math" =: math "abc" =?> "*abc*" , "expand math before normalization" =: math "[" <> str "2]" =?> "[2]" , "multiple math expressions inside one inline list" =: math "5_4" <> ", " <> displayMath "3^2" =?> "54, 32" ] , "raw inline" =: rawInline "html" "marked text" =?> "marked text" , testGroup "links" [ "link with description" =: link "https://example.com" "" (str "Link 1") =?> "[[https://example.com][Link 1]]" , "link without description" =: link "https://example.com" "" (str "https://example.com") =?> "[[https://example.com]]" -- Internal links in Muse include '#' , "link to anchor" =: link "#intro" "" (str "Introduction") =?> "[[#intro][Introduction]]" -- According to Emacs Muse manual, links to images should be prefixed with "URL:" , "link to image with description" =: link "1.png" "" (str "Link to image") =?> "[[URL:1.png][Link to image]]" , "link to image without description" =: link "1.png" "" (str "1.png") =?> "[[URL:1.png]]" , testGroup "escape brackets in links" [ "link with description" =: link "https://example.com/foo].txt" "" (str "Description") =?> "[[https://example.com/foo%5D.txt][Description]]" , "link without description" =: link "https://example.com/foo].txt" "" (str "https://example.com/foo].txt") =?> "[[https://example.com/foo%5D.txt][https://example.com/foo].txt]]" , "image link with description" =: link "foo]bar.png" "" (str "Image link") =?> "[[URL:foo%5Dbar.png][Image link]]" , "image link without description" =: link "foo]bar.png" "" (str "foo]bar.png") =?> "[[URL:foo%5Dbar.png][foo]bar.png]]" ] ] , "image" =: image "image.png" "Image 1" (str "") =?> "[[image.png][Image 1]]" , "image with width" =: imageWith ("", [], [("width", "60%")]) "image.png" "Image" (str "") =?> "[[image.png 60][Image]]" , "left-aligned image with width" =: imageWith ("", ["align-left"], [("width", "60%")]) "image.png" "Image" (str "") =?> "[[image.png 60 l][Image]]" , "right-aligned image with width" =: imageWith ("", ["align-right"], [("width", "60%")]) "image.png" "Image" (str "") =?> "[[image.png 60 r][Image]]" , "escape brackets in image title" =: image "image.png" "Foo]bar" (str "") =?> "[[image.png][Foo]bar]]" , "note" =: note (plain "Foo") =?> unlines [ "[1]" , "" , "[1] Foo" ] , noteLocationTests , "span with class" =: spanWith ("",["foobar"],[]) "Some text" =?> "Some text" , "span without class" =: spanWith ("",[],[]) "Some text" =?> "Some text" , "span with anchor" =: spanWith ("anchor", [], []) mempty <> "Foo bar" =?> "#anchor Foo bar" , "empty span with anchor" =: spanWith ("anchor", [], []) mempty =?> "#anchor" , "empty span without class and anchor" =: spanWith ("", [], []) mempty =?> "" , "span with class and anchor" =: spanWith ("anchor", ["foo"], []) "bar" =?> "#anchor bar" , "adjacent spans" =: spanWith ("", ["syllable"], []) (str "wa") <> spanWith ("", ["syllable"], []) (str "ter") =?> "water" , testGroup "RTL" [ "RTL span" =: spanWith ("",[],[("dir", "rtl")]) (text "foo bar") =?> "<<>>" , "LTR span" =: spanWith ("",[],[("dir", "ltr")]) (text "foo bar") =?> ">>>foo bar<<<" , "RTL span with a class" =: spanWith ("",["foobar"],[("dir", "rtl")]) (text "foo bar") =?> "<<>>" , "LTR span with a class" =: spanWith ("",["foobar"],[("dir", "ltr")]) (text "foo bar") =?> ">>>foo bar<<<" , "Escape <<< and >>>" =: plain (text "<<< foo bar >>>") =?> "<<< foo bar >>>" ] , testGroup "combined" [ "emph word before" =: para ("foo" <> emph "bar") =?> "foobar" , "emph word after" =: para (emph "foo" <> "bar") =?> "foobar" , "emph quoted" =: para (doubleQuoted (emph "foo")) =?> "“*foo*”" , "strong word before" =: para ("foo" <> strong "bar") =?> "foobar" , "strong word after" =: para (strong "foo" <> "bar") =?> "foobar" , "strong quoted" =: para (singleQuoted (strong "foo")) =?> "‘**foo**’" ] ] ] ================================================ FILE: test/Tests/Writers/Native.hs ================================================ module Tests.Writers.Native (tests) where import Data.Text (unpack) import Test.Tasty import Test.Tasty.QuickCheck import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () p_write_rt :: Pandoc -> Bool p_write_rt d = read (unpack $ purely (writeNative def{ writerTemplate = Just mempty }) d) == d p_write_blocks_rt :: [Block] -> Bool p_write_blocks_rt bs = read (unpack $ purely (writeNative def) (Pandoc nullMeta bs)) == bs tests :: [TestTree] tests = [ testProperty "p_write_rt" p_write_rt , testProperty "p_write_blocks_rt" $ mapSize (\x -> if x > 3 then 3 else x) p_write_blocks_rt ] ================================================ FILE: test/Tests/Writers/OOXML.hs ================================================ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.OOXML (ooxmlTest) where import Text.Pandoc hiding (Attr) import Test.Tasty import Test.Tasty.Golden.Advanced import Control.Applicative ((<|>)) import Codec.Archive.Zip import Text.XML.Light import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL import Data.Foldable (asum) import qualified Data.Text.IO as T import Data.List (isSuffixOf, sort, (\\), intercalate, union) import Data.Maybe (catMaybes, mapMaybe) import Tests.Helpers import Data.Algorithm.Diff import System.FilePath.Glob (compile, match) compareXML :: Content -> Content -> Maybe XMLDifference -- We make a special exception for times at the moment, and just pass -- them because we can't control the utctime when running IO. Besides, -- so long as we have two times, we're okay. compareXML (Elem goodElem) (Elem myElem) | (QName "created" _ (Just "dcterms")) <- elName myElem , (QName "created" _ (Just "dcterms")) <- elName goodElem = Nothing compareXML (Elem goodElem) (Elem myElem) | (QName "modified" _ (Just "dcterms")) <- elName myElem , (QName "modified" _ (Just "dcterms")) <- elName goodElem = Nothing compareXML (Elem goodElem) (Elem myElem) = (if elName myElem == elName goodElem then Nothing else Just (ElemNamesDiffer (Comparison {mine = elName myElem, good = elName goodElem})) ) <|> (if sort (elAttribs myElem) == sort (elAttribs goodElem) then Nothing else Just (ElemAttributesDiffer (Comparison { mine = sort (elAttribs myElem) , good = sort (elAttribs goodElem) }))) <|> (if length (elContent myElem) == length (elContent goodElem) then Nothing else Just (ElemChildrenDiffer (Comparison { mine = elContent myElem, good = elContent goodElem}))) <|> asum (zipWith compareXML (elContent myElem) (elContent goodElem)) compareXML (Text goodCData) (Text myCData) = (if cdVerbatim myCData == cdVerbatim goodCData && cdData myCData == cdData goodCData then Nothing else Just (CDatasDiffer (Comparison { mine = myCData, good = goodCData }))) compareXML (CRef goodStr) (CRef myStr) = if myStr == goodStr then Nothing else Just (CRefsDiffer (Comparison { mine = myStr, good = goodStr })) compareXML g m = Just (OtherContentsDiffer (Comparison {mine = m, good = g})) data XMLDifference = ElemNamesDiffer (Comparison QName) | ElemAttributesDiffer (Comparison [Attr]) | ElemChildrenDiffer (Comparison [Content]) | CDatasDiffer (Comparison CData) | CRefsDiffer (Comparison String) | OtherContentsDiffer (Comparison Content) deriving (Show) data Comparison a = Comparison { good :: a, mine :: a } deriving (Show) displayDiff :: Element -> Element -> String displayDiff elemA elemB = showDiff (1,1) (getDiff (lines $ ppElement elemA) (lines $ ppElement elemB)) goldenArchive :: FilePath -> IO Archive goldenArchive fp = toArchive . BL.fromStrict <$> BS.readFile fp testArchive :: (WriterOptions -> Pandoc -> PandocIO BL.ByteString) -> WriterOptions -> FilePath -> IO Archive testArchive writerFn opts fp = do txt <- T.readFile fp bs <- runIOorExplode $ do setTranslations "en-US" setVerbosity ERROR -- otherwise test output is confusingly noisy readNative def txt >>= writerFn opts return $ toArchive bs compareFileList :: FilePath -> Archive -> Archive -> Maybe String compareFileList goldenFP goldenArch testArch = let testFiles = filesInArchive testArch goldenFiles = filesInArchive goldenArch diffTestGolden = testFiles \\ goldenFiles diffGoldenTest = goldenFiles \\ testFiles results = [ if null diffGoldenTest then Nothing else Just $ "Files in " ++ goldenFP ++ " but not in generated archive:\n" ++ intercalate ", " diffGoldenTest , if null diffTestGolden then Nothing else Just $ "Files in generated archive but not in " ++ goldenFP ++ ":\n" ++ intercalate ", " diffTestGolden ] in if null $ catMaybes results then Nothing else Just $ intercalate "\n" $ catMaybes results compareXMLFile' :: FilePath -> Archive -> Archive -> Either String () compareXMLFile' fp goldenArch testArch = do testEntry <- case findEntryByPath fp testArch of Just entry -> Right entry Nothing -> Left $ "Can't extract " ++ fp ++ " from generated archive" testXMLDoc <- case parseXMLDoc $ fromEntry testEntry of Just doc -> Right doc Nothing -> Left $ "Can't parse xml in " ++ fp ++ " from generated archive" goldenEntry <- case findEntryByPath fp goldenArch of Just entry -> Right entry Nothing -> Left $ "Can't extract " ++ fp ++ " from archive in stored file" goldenXMLDoc <- case parseXMLDoc $ fromEntry goldenEntry of Just doc -> Right doc Nothing -> Left $ "Can't parse xml in " ++ fp ++ " from archive in stored file" let testContent = Elem testXMLDoc goldenContent = Elem goldenXMLDoc display difference = "Non-matching xml in " ++ fp ++ ":\n" ++ "* " ++ show difference ++ "\n" ++ displayDiff testXMLDoc goldenXMLDoc maybe (Right ()) (Left . display) (compareXML goldenContent testContent) compareXMLFile :: FilePath -> Archive -> Archive -> Maybe String compareXMLFile fp goldenArch testArch = case compareXMLFile' fp goldenArch testArch of Right _ -> Nothing Left s -> Just s compareAllXMLFiles :: Archive -> Archive -> Maybe String compareAllXMLFiles goldenArch testArch = let allFiles = filesInArchive goldenArch `union` filesInArchive testArch allXMLFiles = sort $ filter (\fp -> ".xml" `isSuffixOf` fp || ".rels" `isSuffixOf` fp) allFiles results = mapMaybe (\fp -> compareXMLFile fp goldenArch testArch) allXMLFiles in if null results then Nothing else Just $ unlines results compareMediaFile' :: FilePath -> Archive -> Archive -> Either String () compareMediaFile' fp goldenArch testArch = do testEntry <- case findEntryByPath fp testArch of Just entry -> Right entry Nothing -> Left $ "Can't extract " ++ fp ++ " from generated archive" goldenEntry <- case findEntryByPath fp goldenArch of Just entry -> Right entry Nothing -> Left $ "Can't extract " ++ fp ++ " from archive in stored file" if fromEntry testEntry == fromEntry goldenEntry then Right () else Left $ "Non-matching binary file: " ++ fp compareMediaFile :: FilePath -> Archive -> Archive -> Maybe String compareMediaFile fp goldenArch testArch = case compareMediaFile' fp goldenArch testArch of Right _ -> Nothing Left s -> Just s compareAllMediaFiles :: Archive -> Archive -> Maybe String compareAllMediaFiles goldenArch testArch = let allFiles = filesInArchive goldenArch `union` filesInArchive testArch mediaPattern = compile "*/media/*" allMediaFiles = sort $ filter (match mediaPattern) allFiles results = mapMaybe (\fp -> compareMediaFile fp goldenArch testArch) allMediaFiles in if null results then Nothing else Just $ unlines results ooxmlTest :: (WriterOptions -> Pandoc -> PandocIO BL.ByteString) -> String -> WriterOptions -> FilePath -> FilePath -> TestTree ooxmlTest writerFn testName opts nativeFP goldenFP = goldenTest testName (goldenArchive goldenFP) (testArchive writerFn opts nativeFP) (\goldenArch testArch -> let res = catMaybes [ compareFileList goldenFP goldenArch testArch , compareAllXMLFiles goldenArch testArch , compareAllMediaFiles goldenArch testArch ] in return $ if null res then Nothing else Just $ unlines res) (\a -> BL.writeFile goldenFP $ fromArchive a) ================================================ FILE: test/Tests/Writers/Org.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Org (tests) where import Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, Text) -> TestTree (=:) = test org defopts :: WriterOptions defopts = def { writerExtensions = getDefaultExtensions "org" } org :: (ToPandoc a) => a -> Text org = orgWithOpts defopts orgWithOpts :: (ToPandoc a) => WriterOptions -> a -> Text orgWithOpts opts x = purely (writeOrg opts) $ toPandoc x tests :: [TestTree] tests = [ testGroup "links" -- See http://orgmode.org/manual/Internal-links.html#Internal-links [ "simple link" =: link "/url" "" "foo" =?> "[[/url][foo]]" , "internal link to anchor" =: link "#my-custom-id" "" "#my-custom-id" =?> "[[#my-custom-id]]" ] , testGroup "lists" [ "bullet task list" =: bulletList [plain "☐ a", plain "☒ b"] =?> T.unlines [ "- [ ] a" , "- [X] b" ] , "ordered task list" =: orderedList [plain ("☐" <> space <> "a"), plain "☒ b"] =?> T.unlines [ "1. [ ] a" , "2. [X] b" ] , "ordered task list with starting number" =: orderedListWith (9, DefaultStyle, DefaultDelim) [plain ("☐" <> space <> "a"), plain "☒ b"] =?> T.unlines [ "9. [@9] [ ] a" , "10. [X] b" ] , test (orgWithOpts def) "bullet without task_lists" $ bulletList [plain "☐ a", plain "☒ b"] =?> T.unlines [ "- ☐ a" , "- ☒ b" ] ] , testGroup "code blocks" [ "identifier" =: codeBlockWith ("abc", ["python"], []) "return True" =?> T.unlines [ "#+name: abc" , "#+begin_src python" , "return True" , "#+end_src" ] , "attributes" =: codeBlockWith ("", ["python"], [("cache", "yes"), ("noweb", "yes")]) "'Hello'" =?> T.unlines [ "#+begin_src python :cache yes :noweb yes" , "'Hello'" , "#+end_src" ] ] ] ================================================ FILE: test/Tests/Writers/Plain.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Plain (tests) where import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test (purely (writePlain def{ writerExtensions = enableExtension Ext_gutenberg plainExtensions }) . toPandoc) tests :: [TestTree] tests = [ "strongly emphasized text to uppercase" =: strong "Straße" =?> "STRASSE" ] ================================================ FILE: test/Tests/Writers/Powerpoint.hs ================================================ module Tests.Writers.Powerpoint (tests) where import Control.Arrow ((***)) import Tests.Writers.OOXML (ooxmlTest) import Text.Pandoc import Test.Tasty import System.FilePath import Text.DocTemplates (ToContext(toVal), Context(..)) import qualified Data.Map as M import Data.Text (pack) -- templating is important enough, and can break enough things, that -- we want to run all our tests with both default formatting and a -- template. modifyPptxName :: FilePath -> String -> FilePath modifyPptxName fp suffix = addExtension (takeDirectory fp ++ suffix) "pptx" pptxTests :: String -> WriterOptions -> FilePath -> FilePath -> (TestTree, TestTree) pptxTests name opts native pptx = let referenceDoc = "pptx/reference-depth.pptx" in ( ooxmlTest writePowerpoint name opts{writerReferenceDoc=Nothing} native pptx , ooxmlTest writePowerpoint name opts{writerReferenceDoc=Just referenceDoc} native (modifyPptxName pptx "/templated") ) groupPptxTests :: [(TestTree, TestTree)] -> [TestTree] groupPptxTests pairs = let (noRefs, refs) = unzip pairs in [ testGroup "Default slide formatting" noRefs , testGroup "With `--reference-doc` pptx file" refs ] testGroup' :: String -> [(TestTree, TestTree)] -> (TestTree, TestTree) testGroup' descr = (testGroup descr *** testGroup descr) . unzip tests :: [TestTree] tests = let regularTests = groupPptxTests [ pptxTests "Inline formatting" def "pptx/inline-formatting/input.native" "pptx/inline-formatting/output.pptx" , pptxTests "Slide breaks (default slide-level)" def "pptx/slide-breaks/input.native" "pptx/slide-breaks/output.pptx" , pptxTests "slide breaks (slide-level set to 1)" def{ writerSlideLevel = Just 1 } "pptx/slide-breaks/input.native" "pptx/slide-breaks-slide-level-1/output.pptx" , pptxTests "lists" def "pptx/lists/input.native" "pptx/lists/output.pptx" , pptxTests "start ordered list at specified num" def "pptx/start-numbering-at/input.native" "pptx/start-numbering-at/output.pptx" , pptxTests "List continuation paragraph indentation" def "pptx/list-level/input.native" "pptx/list-level/output.pptx" , pptxTests "tables" def "pptx/tables/input.native" "pptx/tables/output.pptx" , pptxTests "table of contents" def{ writerTableOfContents = True } "pptx/slide-breaks/input.native" "pptx/slide-breaks-toc/output.pptx" , pptxTests "end notes" def "pptx/endnotes/input.native" "pptx/endnotes/output.pptx" , pptxTests "end notes, with table of contents" def { writerTableOfContents = True } "pptx/endnotes/input.native" "pptx/endnotes-toc/output.pptx" , pptxTests "images" def "pptx/images/input.native" "pptx/images/output.pptx" , pptxTests "two-column layout" def "pptx/two-column/all-text/input.native" "pptx/two-column/all-text/output.pptx" , pptxTests "two-column (not comparison)" def "pptx/two-column/text-and-image/input.native" "pptx/two-column/text-and-image/output.pptx" , pptxTests "single column text" def "pptx/single-column/text/input.native" "pptx/single-column/text/output.pptx" , pptxTests "single column image" def "pptx/single-column/image/input.native" "pptx/single-column/image/output.pptx" , pptxTests "speaker notes" def "pptx/speaker-notes/input.native" "pptx/speaker-notes/output.pptx" , pptxTests "speaker notes after a separating block" def "pptx/speaker-notes-afterseps/input.native" "pptx/speaker-notes-afterseps/output.pptx" , pptxTests "speaker notes after a separating header" def "pptx/speaker-notes-afterheader/input.native" "pptx/speaker-notes-afterheader/output.pptx" , pptxTests "speaker notes after metadata" 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" "pptx/remove-empty-slides/output.pptx" , pptxTests "raw ooxml" def "pptx/raw-ooxml/input.native" "pptx/raw-ooxml/output.pptx" , pptxTests "metadata, custom properties" def "pptx/document-properties/input.native" "pptx/document-properties/output.pptx" , pptxTests "metadata, short description" def "pptx/document-properties-short-desc/input.native" "pptx/document-properties-short-desc/output.pptx" , pptxTests "inline code and code blocks" def "pptx/code/input.native" "pptx/code/output.pptx" , pptxTests "inline code and code blocks, custom formatting" def { writerVariables = Context $ M.fromList [(pack "monofont", toVal $ pack "Consolas")] } "pptx/code/input.native" "pptx/code-custom/output.pptx" , testGroup' "Using slide level 0, if the first thing on a slide is" [ pptxTests ("a h1 it's used as the slide title") def { writerSlideLevel = Just 0 } "pptx/slide-level-0/h1-with-image/input.native" "pptx/slide-level-0/h1-with-image/output.pptx" , pptxTests ("a h2 it's used as the " <> "slide title") def { writerSlideLevel = Just 0 } "pptx/slide-level-0/h2-with-image/input.native" "pptx/slide-level-0/h2-with-image/output.pptx" , testGroup' "a heading it's used as the slide title" [ pptxTests "(works with a table)" def { writerSlideLevel = Just 0 } "pptx/slide-level-0/h1-with-table/input.native" "pptx/slide-level-0/h1-with-table/output.pptx" , pptxTests ("(content with caption layout)") def { writerSlideLevel = Just 0 } "pptx/slide-level-0/h1-h2-with-table/input.native" "pptx/slide-level-0/h1-h2-with-table/output.pptx" ] ] , testGroup' "comparison layout" [ testGroup' "comparison layout is used..." [ pptxTests "when two columns contain text + non-text" def "pptx/comparison/both-columns/input.native" "pptx/comparison/both-columns/output.pptx" , pptxTests "even when only one col contains text + non-text" def "pptx/comparison/one-column/input.native" "pptx/comparison/one-column/output.pptx" ] , testGroup' "extra ... in one column gets overlaid" [ pptxTests "text" def "pptx/comparison/extra-text/input.native" "pptx/comparison/extra-text/output.pptx" , pptxTests "image" def "pptx/comparison/extra-image/input.native" "pptx/comparison/extra-image/output.pptx" ] , pptxTests "is not used if the non-text comes first" def "pptx/comparison/non-text-first/input.native" "pptx/comparison/non-text-first/output.pptx" ] , testGroup' "Content with Caption layout is ..." [ pptxTests "used for heading, text, image on the same slide" def "pptx/content-with-caption/heading-text-image/input.native" "pptx/content-with-caption/heading-text-image/output.pptx" , pptxTests "used for text and an image on the same slide" def "pptx/content-with-caption/text-image/input.native" "pptx/content-with-caption/text-image/output.pptx" , pptxTests "not used if the image comes first" def "pptx/content-with-caption/image-text/input.native" "pptx/content-with-caption/image-text/output.pptx" ] , testGroup' "The Blank layout is used if a slide contains only..." [ pptxTests "speaker notes" def "pptx/blanks/just-speaker-notes/input.native" "pptx/blanks/just-speaker-notes/output.pptx" , pptxTests "an empty heading with a body of only NBSPs" def "pptx/blanks/nbsp-in-body/input.native" "pptx/blanks/nbsp-in-body/output.pptx" , pptxTests "a heading containing only non-breaking spaces" def "pptx/blanks/nbsp-in-heading/input.native" "pptx/blanks/nbsp-in-heading/output.pptx" ] , pptxTests ("Incremental lists are supported") def { writerIncremental = True } "pptx/incremental-lists/with-flag/input.native" "pptx/incremental-lists/with-flag/output.pptx" , pptxTests ("One-off incremental lists are supported") def "pptx/incremental-lists/without-flag/input.native" "pptx/incremental-lists/without-flag/output.pptx" , pptxTests "Background images" def "pptx/background-image/input.native" "pptx/background-image/output.pptx" ] referenceSpecificTests = [ ooxmlTest writePowerpoint "Basic footer" def { writerReferenceDoc = Just "pptx/footer/basic/reference.pptx"} "pptx/footer/input.native" "pptx/footer/basic/output.pptx" , ooxmlTest writePowerpoint "Footer with fixed date, replaced by meta block date" def { writerReferenceDoc = Just "pptx/footer/fixed-date/reference.pptx"} "pptx/footer/input.native" "pptx/footer/fixed-date/output.pptx" , ooxmlTest writePowerpoint "Footer not shown on title slide" def { writerReferenceDoc = Just "pptx/footer/no-title-slide/reference.pptx"} "pptx/footer/input.native" "pptx/footer/no-title-slide/output.pptx" , ooxmlTest writePowerpoint "Footer with slide number starting from 3" def { writerReferenceDoc = Just "pptx/footer/higher-slide-number/reference.pptx"} "pptx/footer/input.native" "pptx/footer/higher-slide-number/output.pptx" , ooxmlTest writePowerpoint "Layouts can be moved around in reference doc" def {writerReferenceDoc = Just "pptx/reference-moved-layouts.pptx"} "pptx/layouts/input.native" "pptx/layouts/moved.pptx" , ooxmlTest writePowerpoint "Layouts can be missing from the reference doc" def {writerReferenceDoc = Just "pptx/reference-deleted-layouts.pptx"} "pptx/layouts/input.native" "pptx/layouts/deleted.pptx" , ooxmlTest writePowerpoint "Slides can be missing from the reference doc" def {writerReferenceDoc = Just "pptx/reference-no-slides.pptx"} "pptx/reference-no-slides/add-slides/input.native" "pptx/reference-no-slides/add-slides/output.pptx" , ooxmlTest writePowerpoint "Notes are placed at the right position with a reference doc without slides" def {writerReferenceDoc = Just "pptx/reference-no-slides.pptx"} "pptx/reference-no-slides/with-notes/input.native" "pptx/reference-no-slides/with-notes/output.pptx" ] in regularTests <> referenceSpecificTests ================================================ FILE: test/Tests/Writers/RST.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.RST (tests) where import Control.Monad.Identity import Test.Tasty import Test.Tasty.HUnit import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder import Text.Pandoc.Writers.RST import qualified Data.Text as T infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test (purely (writeRST def . toPandoc)) testTemplate :: (ToString a, ToString c, ToPandoc a) => String -> String -> (a, c) -> TestTree testTemplate t = case runIdentity (compileTemplate [] (T.pack t)) of Left e -> error $ "Could not compile RST template: " ++ e Right templ -> test (purely (writeRST def{ writerTemplate = Just templ }) . toPandoc) bodyTemplate :: Template T.Text bodyTemplate = case runIdentity (compileTemplate [] "$body$\n") of Left e -> error $ "Could not compile RST bodyTemplate" ++ e Right templ -> templ tests :: [TestTree] tests = [ testGroup "rubrics" [ "in list item" =: bulletList [header 2 (text "foo")] =?> "- .. rubric:: foo" , "in definition list item" =: definitionList [(text "foo", [header 2 (text "bar"), para $ text "baz"])] =?> unlines [ "foo" , " .. rubric:: bar" , "" , " baz"] , "in block quote" =: blockQuote (header 1 (text "bar")) =?> " .. rubric:: bar" , "with id" =: blockQuote (headerWith ("foo",[],[]) 1 (text "bar")) =?> unlines [ " .. rubric:: bar" , " :name: foo"] , "with id class" =: blockQuote (headerWith ("foo",["baz"],[]) 1 (text "bar")) =?> unlines [ " .. rubric:: bar" , " :name: foo" , " :class: baz"] ] , testGroup "ligatures" -- handling specific sequences of blocks [ "a list is closed by a comment before a quote" =: -- issue 4248 bulletList [plain "bulleted"] <> blockQuote (plain "quoted") =?> unlines [ "- bulleted" , "" , ".." , "" , " quoted"] ] , testGroup "flatten" [ testCase "emerges nested styles as expected" $ flatten (Emph [Str "1", Strong [Str "2"], Str "3"]) @?= [Emph [Str "1"], Strong [Str "2"], Emph [Str "3"]] , testCase "could introduce trailing spaces" $ flatten (Emph [Str "f", Space, Strong [Str "2"]]) @?= [Emph [Str "f", Space], Strong [Str "2"]] -- the test above is the reason why we call -- stripLeadingTrailingSpace through transformNested after -- flatten , testCase "preserves empty parents" $ flatten (Image ("",[],[]) [] ("loc","title")) @?= [Image ("",[],[]) [] ("loc","title")] ] , testGroup "inlines" [ "are removed when empty" =: -- #4434 plain (strong (str "")) =?> "" , "do not cause the introduction of extra spaces when removed" =: plain (strong (str "") <> emph (str "text")) =?> "*text*" , "spaces are stripped at beginning and end" =: -- pandoc issue 4327 "The text within inline markup may not -- begin or end with whitespace" -- http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#inline-markup strong (space <> str "text" <> space <> space) =?> "**text**" , "single space stripped" =: strong space =?> "" , "give priority to strong style over emphasis" =: strong (emph (strong (str "s"))) =?> "**s**" , "links are not elided by outer style" =: strong (emph (link "loc" "" (str "text"))) =?> "`text `__" , "RST inlines cannot start nor end with spaces" =: emph (str "f" <> strong (space <> str "d" <> space) <> str "l") =?> "*f* **d** *l*" , "keeps quotes" =: strong (str "f" <> doubleQuoted (str "d") <> str "l") =?> "**f“d”l**" , "backslash inserted between str and code" =: str "/api?query=" <> code "foo" =?> "/api?query=\\ ``foo``" ] , testGroup "headings" [ "normal heading" =: header 1 (text "foo") =?> unlines [ "foo" , "==="] -- note: heading normalization is only done in standalone mode , test (purely (writeRST def{ writerTemplate = Just bodyTemplate }) . toPandoc) "heading levels" $ header 1 (text "Header 1") <> header 3 (text "Header 2") <> header 2 (text "Header 2") <> header 1 (text "Header 1") <> header 4 (text "Header 2") <> header 5 (text "Header 3") <> header 3 (text "Header 2") =?> unlines [ "Header 1" , "========" , "" , "Header 2" , "--------" , "" , "Header 2" , "--------" , "" , "Header 1" , "========" , "" , "Header 2" , "--------" , "" , "Header 3" , "~~~~~~~~" , "" , "Header 2" , "--------"] , test (purely (writeRST def{ writerTemplate = Just bodyTemplate }) . toPandoc) "minimal heading levels" $ header 2 (text "Header 1") <> header 3 (text "Header 2") <> header 2 (text "Header 1") <> header 4 (text "Header 2") <> header 5 (text "Header 3") <> header 3 (text "Header 2") =?> unlines [ "Header 1" , "========" , "" , "Header 2" , "--------" , "" , "Header 1" , "========" , "" , "Header 2" , "--------" , "" , "Header 3" , "~~~~~~~~" , "" , "Header 2" , "--------"] ] , testTemplate "$subtitle$\n" "subtitle" $ setMeta "subtitle" ("subtitle" :: Inlines) (doc $ plain "") =?> ("subtitle" :: String) ] ================================================ FILE: test/Tests/Writers/TEI.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.TEI (tests) where import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder {- "my test" =: X =?> Y is shorthand for test html "my test" $ X =?> Y which is in turn shorthand for test html "my test" (X,Y) -} infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test (purely (writeTEI def) . toPandoc) tests :: [TestTree] tests = [ testGroup "block elements" ["para" =: para "Lorem ipsum cetera." =?> "

Lorem ipsum cetera.

" ] , testGroup "inlines" [ "Emphasis" =: emph "emphasized" =?> "

emphasized

" ,"SingleQuoted" =: singleQuoted (text "quoted material") =?> "

quoted material

" ,"DoubleQuoted" =: doubleQuoted (text "quoted material") =?> "

quoted material

" ,"NestedQuoted" =: doubleQuoted (singleQuoted (text "quoted material")) =?> "

quoted material

" ] ] ================================================ FILE: test/Tests/XML.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | -- Module : Tests.XML -- Copyright : Copyright (C) 2025- Massimiliano Farinella and John MacFarlane -- License : GNU GPL, version 2 or above -- -- Maintainer : Massimiliano Farinella -- Stability : WIP -- Portability : portable Runs a roundtrip conversion of an AST trough the XML format: - first from AST to XML (XML Writer), - then back to AST (XML Reader), - and checks that the two ASTs are the same -} module Tests.XML (tests) where import Control.Monad ((>=>)) import Test.Tasty (TestTree) import Test.Tasty.QuickCheck import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () p_xml_roundtrip :: Pandoc -> Bool p_xml_roundtrip d = d == purely (writeXML def {writerTemplate = Just mempty} >=> readXML def) d tests :: [TestTree] tests = [testProperty "p_xml_roundtrip" p_xml_roundtrip] ================================================ FILE: test/ansi-test.ansi ================================================ LEVEL 1 HEADING Text Level 2 heading Text Level 3 heading Text Level 4 heading Text Level 5 heading Text • list • nested 1) numbered continuation paragraph ]8;;http://example.com\link]8;;\ ]8;;mailto:mail@example.com\mail@example.com]8;;\ │ Block quote │ │ │ Nested block quote Multiline table with caption: Centered Left Right Default aligned Header Aligned Aligned ─────────── ────────── ──────────── ────────────────────────── First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. ─────────── ────────── ──────────── ────────────────────────── Here’s the caption. It may span multiple lines. Multiline table without caption: Centered Left Right Default aligned Header Aligned Aligned ─────────── ────────── ──────────── ────────────────────────── First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. ─────────── ────────── ──────────── ────────────────────────── Table without column headers: ───────────────── ───────────────── ───────────────── ───────────────── 12 12 12 12 123 123 123 123 1 1 1 1 ───────────────── ───────────────── ───────────────── ───────────────── Footnote¹ Multiple blocks in a cell ────────────────── ─────────── ──────────── COL 1 COL 2 COL 3 col 1 col 2 col 3 r1 a • b c c 2 c 2 r1 bis • b 2 • b 2 ────────────────── ─────────── ──────────── East Asian characters have double width ── ──── 魚 fish ── ──── This is inline code:  x >>= y ++ 3 . code block  >>> ## `` Highlighted: <p><a href="foobar" title="title">This is a link  Math: $\pi^2 = \frac{1}{2}$ e = mc² ──────────────────── More text. boldface text, underlined text, emphasized text. small caps We see a log₁₀ reduction in 2⁹ seconds. Hello Goodbye ──────────────────── 1. Here’s the note.]8;;\ ================================================ FILE: test/ansi-test.txt ================================================ --- title: My Document author: John Doe date: July 25, 1933 ... # Level 1 heading Text ## Level 2 heading Text ### Level 3 heading Text #### Level 4 heading Text ##### Level 5 heading Text - list - nested 1) numbered continuation paragraph [link](http://example.com) > Block quote > > > Nested block quote Multiline table with caption: : Here's the caption. It may span multiple lines. --------------------------------------------------------------- Centered Left Right Header Aligned Aligned Default aligned ---------- --------- ----------- --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. --------------------------------------------------------------- Multiline table without caption: --------------------------------------------------------------- Centered Left Right Header Aligned Aligned Default aligned ---------- --------- ----------- --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. --------------------------------------------------------------- Table without column headers: ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 ------- ------ ---------- ------- Footnote^[Here's the note.] Multiple blocks in a cell +------------------+-----------+------------+ | # col 1 | # col 2 | # col 3 | | col 1 | col 2 | col 3 | +------------------+-----------+------------+ | r1 a | - b | c | | | - b 2 | c 2 | | r1 bis | - b 2 | c 2 | +------------------+-----------+------------+ East Asian characters have double width +--+----+ |魚|fish| +--+----+ This is inline code: `x >>= y ++ 3`. ``` code block >>> ## `` ``` Highlighted: ```html

This is a link

``` Math: $\pi^2 = \frac{1}{2}$ $$e = mc^2$$ ---- More text. **boldface text**, [underlined text]{.underline}, *emphasized text*. [small caps]{.smallcaps} We see a log~10~ reduction in 2^9^ seconds. | Hello | | Goodbye ================================================ FILE: test/asciidoc-reader-include.adoc ================================================ This is a test! . one .. two ================================================ FILE: test/asciidoc-reader-include.rb ================================================ # A function def foo return 42 end ================================================ FILE: test/asciidoc-reader.adoc ================================================ // Some comment here. = AsciiDoc reader test John MacFarlane ; John Doe v1.0, 2025-11-24 :custom: Foo bar \ baz :flag: :stem: latexmath [#firsty] == Inline markup === Characters and escapes Dog's has a curved apostrophe, but dog\'s does not. An escaped symbol: \*star\*. Character references: ä ⋠ === Line breaks This is a hard + break. Or set it for a whole paragraph [%hardbreaks] These are all hard breaks. === Autolinks http://example.com/foobar?a=333&b=no%20body, me@example.com , === Cross-reference See <> See <> Go to <> === Anchors An [[anch]]inline anchor. [[[bibanchor]]Doe, John. A Book. === Inline macros kbd:[F11] kbd:[Ctrl+Shift+F] menu:File[Save] menu:Reader[Markdown > Pandoc] btn:[Cancel] icon:heart[2x,role=red] anchor:tiger pass:[*bold*] link:downloads/report.pdf[Get Report] link:tools.html#editors[] link:file:///home/username[Your files] Tricky cases: link:pass:[My Documents/report.pdf][Get Report] link:My Documents/report.pdf[Get Report] link:My%20Documents/report.pdf[Get Report] link:++https://example.org/now_this__link_works.html++[] mailto:join@discuss.example.org[Subscribe] mailto:join@discuss.example.org["Click, subscribe, and participate!",role=mail] xref:link-macro-attributes[use attributes within the link macro] image::sunset.jpg[Sunset] image::name.png[] image::sunset.jpg[Sunset,300,400] image::sunset.jpg[alt=Sunset,width=300,height=400] [latexmath] ++++ e=mc^2 ++++ [asciimath] ++++ sin n / 3 ++++ [stem] ++++ e^i ++++ === Attribute substitutions {custom} {nonexistent} Built in: x{blank}y{empty}z{sp}a{nbsp}b{zwsp}c{apos}d{lsquo} === Bold and italic Constrained: *this is bold _and italic_*. Unconstrained: wild**content__with italic__stuff**. === Monospace `simple` `complex *with bold* text and a link:foo.html[]` unconstrained``wwow``okay === Span and inline attributes [.red]#Bonjour *monsieur*# Un[.red]##constrained##content With #no attribute# it's highlighted. === Sub and superscript H~2~O H~a{sp}b~O Not subscript: H~a b~O. H^2&O H^a{sp}b^O Not subscript: H^a b^O. === Passthrough Here the special characters just come through as literal: +*test*+ xx++*test*++xx But here they are passed through: xx+++*test*+++xx === Quoted "`double quoted`" '`single quoted`' === Footnotes Doublefootnote:[The double hail-and-rainbow level makes my toes tingle.] A bold statement!footnote:disclaimer[Opinions are my own.] Another outrageous statement.footnote:disclaimer[] == Block markup === Sections ==== Another level ===== Level 5 #### Markdown style ##### Level 5 === Discrete heading [discrete] ==== A discrete heading, not a section === Paragraph This is a paragraph whose source fits on two lines. {.This is my title} A paragraph with a title. === Example block .Optional title [example] This is an example of an example block. .Optional title ==== Paragraph *one*. Paragraph *two*. ==== === Admonition Simple form: WARNING: This is very dangerous. Don't do it unless you understand the risks. [IMPORTANT] .Title of the admonition ==== Remember: . Don't do this. . And don't do that. ==== === Sidebar [sidebar] A simple sidebar. .Optional Title *with strong emphasis* **** Here is a sidebar. TIP: It can contain any type of content. **** === Literal block Short indented code: $ ls -a $ cat /foo/bar/baz \ /bi/bim/bop [literal] This is a literal block too. .... Fenced $+ *a* literal **** not a sidebar **** .... === Listing [source,ruby] ---- require 'sinatra' get '/hi' do "Hello World!" end ---- Implied: [source,ruby] ---- require 'sinatra' get '/hi' do "Hello World!" end ---- [,ruby] ---- include::asciidoc-reader-include.rb[] ---- [#hello.haskell] ---- putStrLn $ unwords ["Hello", "world"] ---- Line numbering: [%linenums,ruby] ---- puts 1 puts 2 puts 3 ---- ---- This doesn't have a language. +="hi" ---- And with a callout list: [source,ruby] ---- require 'sinatra' <1> get '/hi' do <2> <3> "Hello World!" end ---- <1> Library import <2> URL mapping <3> Response block Markdown-style fenced: ```ruby def foo return 5 end ``` === Verse [verse,Carl Sandburg, two lines from the poem Fog] The fog comes on little cat feet. [verse,Carl Sandburg,Fog] ____ The fog comes on little cat feet. It sits looking over harbor and city on silent haunches and then moves on. ____ === Collapsible Click here for more. [%collapsible%open] ==== This is collapsible. It can be hidden. ==== .Click me! [%collapsible] This paragraph is also collapsible. === Quote [quote,Captain James T. Kirk,Star Trek IV: The Voyage Home] Everybody remember where we parked. [quote,Monty Python and the Holy Grail] ____ Dennis: Come and see the violence inherent in the system. Help! Help! I'm being repressed. King Arthur: Bloody peasant! Dennis: Oh, what a giveaway! Did you hear that? Did you hear that, eh? That's what I'm on about! Did you see him repressing me? You saw him, Didn't you? ____ [quote.movie#roads,Dr. Emmett Brown] ____ Roads? Where we're going, we don't need roads. ____ === Pass ++++

pass through

++++ === Open block .A title. [key="a value"] -- Any content can go here: . one . two -- === Anchor [[goals]] * one * two === Breaks Asciidoc thematic break: ''' Markdown style: --- - - - *** * * * Page breaks: <<< [%always] <<< === List * Edgar Allan Poe * Sheri S. Tepper * Bill Bryson // titled .Kizmet's Favorite Authors * Edgar Allan Poe * Sheri S. Tepper * Bill Bryson // hyphen - Edgar Allan Poe - Sheri S. Tepper - Bill Bryson . Nested list * West wood maze ** Maze heart *** Reflection pool ** Secret exit * Level 1 list item ** Level 2 list item *** Level 3 list item **** Level 4 list item ***** Level 5 list item ****** etc. * Level 1 list item // attributes [square] * one * two * three // ordered with actual numbers 1. Protons 2. Electrons 3. Neutrons // ordered with . . Protons . Electrons . Neutrons Start with 4: 4. Step four 5. Step five 6. Step six or [start=4] . Step four . Step five . Step six Reversed: [%reversed] .Parts of an atom . Protons . Electrons . Neutrons Nested . Step 1 . Step 2 .. Step 2a .. Step 2b . Step 3 Mixed nested . Linux * Fedora * Ubuntu * Slackware . BSD * FreeBSD * NetBSD With spacing . Linux * Fedora * Ubuntu * Slackware . BSD * FreeBSD * NetBSD With number styles [lowerroman,start=5] . Five . Six [loweralpha] .. a .. b .. c . Seven Checklist * [*] checked * [x] also checked * [ ] not checked * normal list item Separate lists with block attribute * Apples * Oranges [] . Wash . Slice Multiline items * Blah blah. Blah blah. * The document header in AsciiDoc is optional. If present, it must start with a document title. * Optional author and revision information lines immediately follow the document title. * The document header must be separated from the remainder of the document by one or more empty lines and it cannot contain empty lines. Complex item * The header in AsciiDoc must start with a document title. + ---- = Document Title ---- + Keep in mind that the header is optional. * Optional author and revision information lines immediately follow the document title. + ---- = Document Title Doc Writer v1.0, 2022-01-01 ---- * Second item Empty principle element: . {empty} + ---- test ---- === Table ==== Simple with column specs [cols="3,2,3"] |=== |This content is placed in the first cell of column 1 |This line starts with a vertical bar so this content is placed in a new cell in column 2 |When the processor encounters a whitespace followed by a vertical bar it ends the previous cell and starts a new cell |=== ==== Repeated column in specs [cols="2*"] |=== >s|This cell's specifier indicates that this cell's content is right-aligned and bold. |The cell specifier on this cell hasn't been set explicitly, so the default properties are applied. |=== ==== Simple without column specs |=== |Column 1, header row |Column 2, header row |Cell in column 1, row 2 |Cell in column 2, row 2 |Cell in column 1, row 3 |Cell in column 2, row 3 |=== ==== With caption .My cool table. |=== |Column 1, header row |Column 2, header row |Cell in column 1, row 2 |Cell in column 2, row 2 |Cell in column 1, row 3 |Cell in column 2, row 3 |=== ==== No header By default the first line should turn into the header, but this can be disabled: [%noheader] |=== |Cell in column 1, row 1 |Cell in column 2, row 1 |Cell in column 1, row 2 |Cell in column 2, row 2 |=== And also explicitly enabled: [%header%footer%autowidth,cols=2*~] |=== |Cell A1 |Cell B1 |Cell A2 |Cell B2 |Cell A3 |Cell B3 |=== ==== Footer [%header%footer,cols="2,2,1"] |=== |Column 1, header row |Column 2, header row |Column 3, header row |Cell in column 1, row 2 |Cell in column 2, row 2 |Cell in column 3, row 2 |Column 1, footer row |Column 2, footer row |Column 3, footer row |=== or [options="footer"] |=== |Column 1, header row |Column 2, header row |Cell in column 1, row 2 |Cell in column 2, row 2 |Cell in column 1, row 3 |Cell in column 2, row 3 |Column 1, footer row |Column 2, footer row |=== ==== Alignment |=== |Column Name |Column Name 2+^|This cell spans two columns, and its content is horizontally centered because the cell specifier includes the `+^+` operator. 2*^|This content is duplicated in two adjacent columns. Its content is horizontally centered because the cell specifier includes the `+^+` operator. |=== ==== Multiple paragraphs in cells |=== |Single paragraph on row 1 |First paragraph on row 2 Second paragraph on row 2 |=== ==== Complex table |=== 2*>m|This content is duplicated across two columns. It is aligned right horizontally. And it is monospaced. .3+^.>s|This cell spans 3 rows. The content is centered horizontally, aligned to the bottom of the cell, and strong. e|This content is emphasized. .^l|This content is aligned to the top of the cell and literal. a| [source] puts "This is a source block!" |=== ==== Column styles [cols="m,m"] |=== |monospace | mono d|default | mono |=== ==== Block elements in cells |=== |Normal Style |AsciiDoc Style |This cell isn't prefixed with an `a`, so the processor doesn't interpret the following lines as an AsciiDoc list. * List item 1 * List item 2 * List item 3 a|This cell is prefixed with an `a`, so the processor interprets the following lines as an AsciiDoc list. * List item 1 * List item 2 * List item 3 |This cell isn't prefixed with an `a`, so the processor doesn't interpret the listing block delimiters or the `source` style. [source,python] ---- import os print ("%s" %(os.uname())) ---- a|This cell is prefixed with an `a`, so the listing block is processed and rendered according to the `source` style rules. [source,python] ---- import os print "%s" %(os.uname()) ---- |=== ==== Col and rowspan |=== |Column 1, header row |Column 2, header row | Column 3, header row 2.2+|This cell spans 2 cols and 2 rows |Cell in column 3, row 2 |Cell in column 3, row 3 3+|Cell in column 1-3, row 4 |=== ==== CSV table [%header,format=csv] |=== Artist,Track,Genre Baauer,Harlem Shake,Hip Hop The Lumineers,Ho Hey,Folk Rock |=== or ,=== Artist,Track,Genre Baauer,Harlem Shake,Hip Hop ,=== ==== DSV table [format=dsv,separator=;] |=== a;b;c d;e;f |=== or :=== Artist:Track:Genre Robyn:Indestructible:Dance :=== === Definition list CPU:: The brain of the computer. Hard drive:: Permanent storage for operating system and/or user files. Mixed Dairy:: * Milk * Eggs Bakery:: * Bread Produce:: * Bananas With spaces Dairy:: * Milk * Eggs Bakery:: * Bread Produce:: * Bananas Nested Operating Systems:: Linux::: . Fedora * Desktop . Ubuntu * Desktop * Server BSD::: . FreeBSD . NetBSD Cloud Providers:: PaaS::: . OpenShift . CloudBees IaaS::: . Amazon EC2 This just affects the output: [horizontal,labelwidth=25,itemwidth=75] CPU:: The brain of the computer. RAM:: Temporarily stores information the CPU uses during operation. Q&A list [qanda] What is the answer?:: This is the answer. Are cameras allowed?:: Are backpacks allowed?:: No. Ordered description list (with numbers) [ordered] &:: ampersand >:: greater than === Block macros image::sunset.jpg[Sunset,300,200] video::mymovie.mp4[] audio::mysong.mp3[] toc::[] include::asciidoc-reader-include.adoc[] ================================================ FILE: test/asciidoc-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "John" , Space , Str "MacFarlane" , Space , Str "(" , Link ( "" , [] , [] ) [ Str "jgm@berkeley.edu" ] ( "mailto:jgm@berkeley.edu" , "" ) , Str ")" ] , MetaInlines [ Str "John" , Space , Str "Doe" , Space , Str "(" , Link ( "" , [] , [] ) [ Str "doe@example.com" ] ( "mailto:doe@example.com" , "" ) , Str ")" ] ] ) , ( "custom" , MetaString "Foo bar baz" ) , ( "date" , MetaString "2025-11-24" ) , ( "flag" , MetaBool True ) , ( "title" , MetaInlines [ Str "AsciiDoc" , Space , Str "reader" , Space , Str "test" ] ) , ( "version" , MetaString "1.0" ) ] } [ Header 1 ( "firsty" , [] , [] ) [ Str "Inline" , Space , Str "markup" ] , Header 2 ( "_characters_and_escapes" , [] , [] ) [ Str "Characters" , Space , Str "and" , Space , Str "escapes" ] , Para [ Str "Dog\8217s" , Space , Str "has" , Space , Str "a" , Space , Str "curved" , Space , Str "apostrophe," , Space , Str "but" , Space , Str "dog's" , Space , Str "does" , Space , Str "not." ] , Para [ Str "An" , Space , Str "escaped" , Space , Str "symbol:" , Space , Str "*star*." ] , Para [ Str "Character" , Space , Str "references:" , Space , Str "\228\160\8928" ] , Header 2 ( "_line_breaks" , [] , [] ) [ Str "Line" , Space , Str "breaks" ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "hard" , LineBreak , Str "break." ] , Para [ Str "Or" , Space , Str "set" , Space , Str "it" , Space , Str "for" , Space , Str "a" , Space , Str "whole" , Space , Str "paragraph" ] , Div ( "" , [] , [ ( "wrapper" , "1" ) , ( "options" , "hardbreaks" ) ] ) [ Para [ Str "These" , Space , Str "are" , LineBreak , Str "all" , LineBreak , Str "hard" , LineBreak , Str "breaks." ] ] , Header 2 ( "_autolinks" , [] , [] ) [ Str "Autolinks" ] , Para [ Link ( "" , [] , [] ) [ Str "http://example.com/foobar?a=333&b=no%20body" ] ( "http://example.com/foobar?a=333&b=no%20body" , "" ) , Str "," , Space , Link ( "" , [] , [] ) [ Str "me@example.com" ] ( "me@example.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "http://example.com/foobar?a=333&b=no%20body" ] ( "http://example.com/foobar?a=333&b=no%20body" , "" ) , Str "," , Space , Str "<" , Link ( "" , [] , [] ) [ Str "me@example.com" ] ( "me@example.com" , "" ) , Str ">" ] , Header 2 ( "_cross_reference" , [] , [] ) [ Str "Cross-reference" ] , Para [ Str "See" , Space , Link ( "" , [ "cross-reference" ] , [] ) [ Str "Inline" , Space , Str "markup" ] ( "#firsty" , "" ) ] , Para [ Str "See" , Space , Link ( "" , [ "cross-reference" ] , [] ) [ Str "My" , Space , Str "great" , Space , Str "section" , Space , Str "on" , Space , Str "Inline" , Space , Str "markup" ] ( "#firsty" , "" ) ] , Para [ Str "Go" , Space , Str "to" , Space , Link ( "" , [ "cross-reference" ] , [] ) [ Str "an" , Space , Str "inline" , Space , Str "anchor" ] ( "#anch" , "" ) ] , Header 2 ( "_anchors" , [] , [] ) [ Str "Anchors" ] , Para [ Str "An" , Space , Span ( "anch" , [] , [] ) [] , Str "inline" , Space , Str "anchor." ] , Para [ Span ( "[bibanchor" , [] , [] ) [] , Str "Doe," , Space , Str "John." , Space , Str "A" , Space , Str "Book." ] , Header 2 ( "_inline_macros" , [] , [] ) [ Str "Inline" , Space , Str "macros" ] , Para [ Span ( "" , [ "kbd" ] , [] ) [ Strong [ Str "F11" ] ] ] , Para [ Span ( "" , [ "kbd" ] , [] ) [ Strong [ Str "Ctrl" ] ] , Str "+" , Span ( "" , [ "kbd" ] , [] ) [ Strong [ Str "Shift" ] ] , Str "+" , Span ( "" , [ "kbd" ] , [] ) [ Strong [ Str "F" ] ] ] , Para [ Span ( "" , [ "menu" ] , [] ) [ Strong [ Str "File\160\8250\160Save" ] ] ] , Para [ Span ( "" , [ "menu" ] , [] ) [ Strong [ Str "Reader\160\8250\160Markdown\160\8250\160Pandoc" ] ] ] , Para [ Span ( "" , [ "button" ] , [] ) [ Strong [ Str "[Cancel]" ] ] ] , Para [ Image ( "" , [ "red icon" ] , [] ) [] ( "./images/icons/heart.png" , "" ) ] , Para [ Str "anchor:tiger" ] , Para [ Strong [ Str "*bold*" ] ] , Para [ Link ( "" , [] , [] ) [ Str "Get" , Space , Str "Report" ] ( "downloads/report.pdf" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "tools.html#editors" ] ( "tools.html#editors" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Your" , Space , Str "files" ] ( "file:///home/username" , "" ) ] , Para [ Str "Tricky" , Space , Str "cases:" ] , Para [ Link ( "" , [] , [] ) [ Str "Get" , Space , Str "Report" ] ( "My Documents/report.pdf" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Get" , Space , Str "Report" ] ( "My Documents/report.pdf" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Get" , Space , Str "Report" ] ( "My%20Documents/report.pdf" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "https://example.org/now_this__link_works.html" ] ( "https://example.org/now_this__link_works.html" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Subscribe" ] ( "join@discuss.example.org" , "" ) ] , Para [ Link ( "" , [ "mail" ] , [] ) [ Str "Click," , Space , Str "subscribe," , Space , Str "and" , Space , Str "participate!" ] ( "join@discuss.example.org" , "" ) ] , Para [ Link ( "" , [ "cross-reference" ] , [] ) [ Str "use" , Space , Str "attributes" , Space , Str "within" , Space , Str "the" , Space , Str "link" , Space , Str "macro" ] ( "#link-macro-attributes" , "" ) ] , Figure ( "" , [] , [] ) (Caption Nothing []) [ Plain [ Image ( "" , [] , [] ) [ Str "Sunset" ] ( "sunset.jpg" , "" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing []) [ Plain [ Image ( "" , [] , [] ) [] ( "name.png" , "" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing []) [ Plain [ Image ( "" , [] , [ ( "width" , "300px" ) , ( "height" , "400px" ) ] ) [ Str "Sunset" ] ( "sunset.jpg" , "" ) ] ] , Div ( "" , [] , [ ( "wrapper" , "1" ) , ( "alt" , "Sunset" ) , ( "height" , "400" ) , ( "width" , "300" ) ] ) [ Figure ( "" , [] , [] ) (Caption Nothing []) [ Plain [ Image ( "" , [] , [] ) [] ( "sunset.jpg" , "" ) ] ] ] , Para [ Math DisplayMath "e=mc^2\n" ] , Para [ Math DisplayMath "sin n / 3\n" ] , Para [ Math DisplayMath "e^i\n" ] , Header 2 ( "_attribute_substitutions" , [] , [] ) [ Str "Attribute" , Space , Str "substitutions" ] , Para [ Str "Foo" , Space , Str "bar" , Space , Str "baz" ] , Para [ Str "{nonexistent}" ] , Para [ Str "Built" , Space , Str "in:" , Space , Str "xyz" , Space , Str "a\160b\8203c'd\8216" ] , Header 2 ( "_bold_and_italic" , [] , [] ) [ Str "Bold" , Space , Str "and" , Space , Str "italic" ] , Para [ Str "Constrained:" , Space , Strong [ Str "this" , Space , Str "is" , Space , Str "bold" , Space , Emph [ Str "and" , Space , Str "italic" ] ] , Str "." ] , Para [ Str "Unconstrained:" , Space , Str "wild" , Strong [ Str "content" , Emph [ Str "with" , Space , Str "italic" ] , Str "stuff" ] , Str "." ] , Header 2 ( "_monospace" , [] , [] ) [ Str "Monospace" ] , Para [ Code ( "" , [] , [] ) "simple" ] , Para [ Code ( "" , [] , [] ) "complex" , Space , Strong [ Code ( "" , [] , [] ) "with" , Space , Code ( "" , [] , [] ) "bold" ] , Space , Code ( "" , [] , [] ) "text" , Space , Code ( "" , [] , [] ) "and" , Space , Code ( "" , [] , [] ) "a" , Space , Link ( "" , [] , [] ) [ Code ( "" , [] , [] ) "foo.html" ] ( "foo.html" , "" ) ] , Para [ Str "unconstrained" , Code ( "" , [] , [] ) "wwow" , Str "okay" ] , Header 2 ( "_span_and_inline_attributes" , [] , [] ) [ Str "Span" , Space , Str "and" , Space , Str "inline" , Space , Str "attributes" ] , Para [ Span ( "" , [ "red" ] , [] ) [ Str "Bonjour" , Space , Strong [ Str "monsieur" ] ] ] , Para [ Str "Un" , Span ( "" , [ "red" ] , [] ) [ Str "constrained" ] , Str "content" ] , Para [ Str "With" , Space , Span ( "" , [ "mark" ] , [] ) [ Str "no" , Space , Str "attribute" ] , Space , Str "it\8217s" , Space , Str "highlighted." ] , Header 2 ( "_sub_and_superscript" , [] , [] ) [ Str "Sub" , Space , Str "and" , Space , Str "superscript" ] , Para [ Str "H" , Subscript [ Str "2" ] , Str "O" ] , Para [ Str "H" , Subscript [ Str "a" , Space , Str "b" ] , Str "O" ] , Para [ Str "Not" , Space , Str "subscript:" , Space , Str "H~a" , Space , Str "b~O." ] , Para [ Str "H^2&O" ] , Para [ Str "H" , Superscript [ Str "a" , Space , Str "b" ] , Str "O" ] , Para [ Str "Not" , Space , Str "subscript:" , Space , Str "H^a" , Space , Str "b^O." ] , Header 2 ( "_passthrough" , [] , [] ) [ Str "Passthrough" ] , Para [ Str "Here" , Space , Str "the" , Space , Str "special" , Space , Str "characters" , Space , Str "just" , Space , Str "come" , Space , Str "through" , Space , Str "as" , Space , Str "literal:" ] , Para [ Str "*test*" ] , Para [ Str "xx*test*xx" ] , Para [ Str "But" , Space , Str "here" , Space , Str "they" , Space , Str "are" , Space , Str "passed" , Space , Str "through:" ] , Para [ Str "xx" , Strong [ Str "*test*" ] , Str "xx" ] , Header 2 ( "_quoted" , [] , [] ) [ Str "Quoted" ] , Para [ Quoted DoubleQuote [ Str "double" , Space , Str "quoted" ] ] , Para [ Quoted SingleQuote [ Str "single" , Space , Str "quoted" ] ] , Header 2 ( "_footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Double" , Note [ Para [ Str "The" , Space , Str "double" , Space , Str "hail-and-rainbow" , Space , Str "level" , Space , Str "makes" , Space , Str "my" , Space , Str "toes" , Space , Str "tingle." ] ] ] , Para [ Str "A" , Space , Str "bold" , Space , Str "statement!" , Note [ Para [ Str "Opinions" , Space , Str "are" , Space , Str "my" , Space , Str "own." ] ] , SoftBreak , Str "Another" , Space , Str "outrageous" , Space , Str "statement." , Note [ Para [ Str "Opinions" , Space , Str "are" , Space , Str "my" , Space , Str "own." ] ] ] , Header 1 ( "_block_markup" , [] , [] ) [ Str "Block" , Space , Str "markup" ] , Header 2 ( "_sections" , [] , [] ) [ Str "Sections" ] , Header 3 ( "_another_level" , [] , [] ) [ Str "Another" , Space , Str "level" ] , Header 4 ( "_level_5" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 3 ( "_markdown_style" , [] , [] ) [ Str "Markdown" , Space , Str "style" ] , Header 4 ( "_level_5_2" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 2 ( "_discrete_heading" , [] , [] ) [ Str "Discrete" , Space , Str "heading" ] , Header 3 ( "" , [] , [] ) [ Str "A" , Space , Str "discrete" , Space , Str "heading," , Space , Str "not" , Space , Str "a" , Space , Str "section" ] , Header 2 ( "_paragraph" , [] , [] ) [ Str "Paragraph" ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "paragraph" , SoftBreak , Str "whose" , Space , Str "source" , Space , Str "fits" , Space , Str "on" , Space , Str "two" , Space , Str "lines." ] , Para [ Str "{.This" , Space , Str "is" , Space , Str "my" , Space , Str "title}" , SoftBreak , Str "A" , Space , Str "paragraph" , Space , Str "with" , Space , Str "a" , Space , Str "title." ] , Header 2 ( "_example_block" , [] , [] ) [ Str "Example" , Space , Str "block" ] , Div ( "" , [] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Optional" , Space , Str "title" ] ] , Para [ Str "This" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "of" , Space , Str "an" , Space , Str "example" , Space , Str "block." ] ] , Div ( "" , [ "example" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Optional" , Space , Str "title" ] ] , Para [ Str "Paragraph" , Space , Strong [ Str "one" ] , Str "." ] , Para [ Str "Paragraph" , Space , Strong [ Str "two" ] , Str "." ] ] , Header 2 ( "_admonition" , [] , [] ) [ Str "Admonition" ] , Para [ Str "Simple" , Space , Str "form:" ] , Div ( "" , [ "warning" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Warning" ] ] , Para [ Str "This" , Space , Str "is" , Space , Str "very" , Space , Str "dangerous." , SoftBreak , Str "Don\8217t" , Space , Str "do" , Space , Str "it" , Space , Str "unless" , Space , Str "you" , Space , Str "understand" , Space , Str "the" , Space , Str "risks." ] ] , Div ( "" , [ "important" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Title" , Space , Str "of" , Space , Str "the" , Space , Str "admonition" ] ] , Para [ Str "Remember:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Don\8217t" , Space , Str "do" , Space , Str "this." ] ] , [ Para [ Str "And" , Space , Str "don\8217t" , Space , Str "do" , Space , Str "that." ] ] ] ] , Header 2 ( "_sidebar" , [] , [] ) [ Str "Sidebar" ] , Para [ Str "A" , Space , Str "simple" , Space , Str "sidebar." ] , Div ( "" , [ "sidebar" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Optional" , Space , Str "Title" , Space , Strong [ Str "with" , Space , Str "strong" , Space , Str "emphasis" ] ] ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "sidebar." ] , Div ( "" , [ "tip" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Tip" ] ] , Para [ Str "It" , Space , Str "can" , Space , Str "contain" , Space , Str "any" , Space , Str "type" , Space , Str "of" , Space , Str "content." ] ] ] , Header 2 ( "_literal_block" , [] , [] ) [ Str "Literal" , Space , Str "block" ] , Para [ Str "Short" , Space , Str "indented" , Space , Str "code:" ] , CodeBlock ( "" , [] , [] ) "$ ls -a\n$ cat /foo/bar/baz \\\n /bi/bim/bop\n" , CodeBlock ( "" , [] , [] ) "This is\n a literal block too.\n" , CodeBlock ( "" , [] , [] ) " Fenced\n $+ *a* literal\n\n****\nnot a sidebar\n****\n" , Header 2 ( "_listing" , [] , [] ) [ Str "Listing" ] , CodeBlock ( "" , [ "ruby" ] , [] ) "require 'sinatra'\n\nget '/hi' do\n \"Hello World!\"\nend" , Para [ Str "Implied:" ] , CodeBlock ( "" , [ "ruby" ] , [] ) "require 'sinatra'\n\nget '/hi' do\n \"Hello World!\"\nend" , CodeBlock ( "" , [ "ruby" ] , [] ) "# A function\ndef foo\n return 42\nend" , CodeBlock ( "hello" , [ "haskell" ] , [] ) "putStrLn $ unwords [\"Hello\", \"world\"]" , Para [ Str "Line" , Space , Str "numbering:" ] , CodeBlock ( "" , [] , [ ( "options" , "linenums" ) ] ) "puts 1\nputs 2\nputs 3" , CodeBlock ( "" , [] , [] ) "This doesn't have a language.\n +=\"hi\"" , Para [ Str "And" , Space , Str "with" , Space , Str "a" , Space , Str "callout" , Space , Str "list:" ] , CodeBlock ( "" , [ "ruby" ] , [] ) "require 'sinatra' \9312\n\nget '/hi' do \9313 \9314\n \"Hello World!\"\nend" , Div ( "" , [ "callout-list" ] , [] ) [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Library" , Space , Str "import" ] ] , [ Para [ Str "URL" , Space , Str "mapping" ] ] , [ Para [ Str "Response" , Space , Str "block" ] ] ] ] , Para [ Str "Markdown-style" , Space , Str "fenced:" ] , CodeBlock ( "" , [ "ruby" ] , [] ) "def foo\n return 5\nend" , Header 2 ( "_verse" , [] , [] ) [ Str "Verse" ] , BlockQuote [ Para [ Str "The" , Space , Str "fog" , Space , Str "comes" , LineBreak , Str "on" , Space , Str "little" , Space , Str "cat" , Space , Str "feet." ] , Para [ Str "\8212" , Space , Str "Carl" , Space , Str "Sandburg," , Space , Str "two" , Space , Str "lines" , Space , Str "from" , Space , Str "the" , Space , Str "poem" , Space , Str "Fog" ] ] , BlockQuote [ Para [ Str "The" , Space , Str "fog" , Space , Str "comes" , LineBreak , Str "on" , Space , Str "little" , Space , Str "cat" , Space , Str "feet." , LineBreak , Str "It" , Space , Str "sits" , Space , Str "looking" , LineBreak , Str "over" , Space , Str "harbor" , Space , Str "and" , Space , Str "city" , LineBreak , Str "on" , Space , Str "silent" , Space , Str "haunches" , LineBreak , Str "and" , Space , Str "then" , Space , Str "moves" , Space , Str "on." ] , Para [ Str "\8212" , Space , Str "Carl" , Space , Str "Sandburg," , Space , Str "Fog" ] ] , Header 2 ( "_collapsible" , [] , [] ) [ Str "Collapsible" ] , Para [ Str "Click" , Space , Str "here" , Space , Str "for" , Space , Str "more." ] , Div ( "" , [ "example" ] , [ ( "options" , "collapsible,open" ) ] ) [ Para [ Str "This" , Space , Str "is" , Space , Str "collapsible." ] , Para [ Str "It" , Space , Str "can" , Space , Str "be" , Space , Str "hidden." ] ] , Div ( "" , [] , [ ( "options" , "collapsible" ) ] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Click" , Space , Str "me!" ] ] , Para [ Str "This" , Space , Str "paragraph" , Space , Str "is" , SoftBreak , Str "also" , Space , Str "collapsible." ] ] , Header 2 ( "_quote" , [] , [] ) [ Str "Quote" ] , BlockQuote [ Para [ Str "Everybody" , Space , Str "remember" , Space , Str "where" , Space , Str "we" , Space , Str "parked." ] , Para [ Str "\8212" , Space , Str "Captain" , Space , Str "James" , Space , Str "T." , Space , Str "Kirk," , Space , Str "Star" , Space , Str "Trek" , Space , Str "IV:" , Space , Str "The" , Space , Str "Voyage" , Space , Str "Home" ] ] , BlockQuote [ Para [ Str "Dennis:" , Space , Str "Come" , Space , Str "and" , Space , Str "see" , Space , Str "the" , Space , Str "violence" , Space , Str "inherent" , Space , Str "in" , Space , Str "the" , Space , Str "system." , Space , Str "Help!" , Space , Str "Help!" , Space , Str "I\8217m" , Space , Str "being" , SoftBreak , Str "repressed." ] , Para [ Str "King" , Space , Str "Arthur:" , Space , Str "Bloody" , Space , Str "peasant!" ] , Para [ Str "Dennis:" , Space , Str "Oh," , Space , Str "what" , Space , Str "a" , Space , Str "giveaway!" , Space , Str "Did" , Space , Str "you" , Space , Str "hear" , Space , Str "that?" , Space , Str "Did" , Space , Str "you" , Space , Str "hear" , Space , Str "that," , Space , Str "eh?" , Space , Str "That\8217s" , Space , Str "what" , Space , Str "I\8217m" , SoftBreak , Str "on" , Space , Str "about!" , Space , Str "Did" , Space , Str "you" , Space , Str "see" , Space , Str "him" , Space , Str "repressing" , Space , Str "me?" , Space , Str "You" , Space , Str "saw" , Space , Str "him," , Space , Str "Didn\8217t" , Space , Str "you?" ] , Para [ Str "\8212" , Space , Str "Monty" , Space , Str "Python" , Space , Str "and" , Space , Str "the" , Space , Str "Holy" , Space , Str "Grail" ] ] , Div ( "roads" , [ "movie" ] , [ ( "wrapper" , "1" ) ] ) [ BlockQuote [ Para [ Str "Roads?" , Space , Str "Where" , Space , Str "we\8217re" , Space , Str "going," , Space , Str "we" , Space , Str "don\8217t" , Space , Str "need" , Space , Str "roads." ] , Para [ Str "\8212" , Space , Str "Dr." , Space , Str "Emmett" , Space , Str "Brown" ] ] ] , Header 2 ( "_pass" , [] , [] ) [ Str "Pass" ] , Para [ Str "pass" , Space , Emph [ Str "through" ] ] , Header 2 ( "_open_block" , [] , [] ) [ Str "Open" , Space , Str "block" ] , Div ( "" , [] , [ ( "key" , "a value" ) ] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "A" , Space , Str "title." ] ] , Para [ Str "Any" , Space , Str "content" , Space , Str "can" , Space , Str "go" , Space , Str "here:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] ] ] , Header 2 ( "_anchor" , [] , [] ) [ Str "Anchor" ] , Div ( "goals" , [] , [ ( "wrapper" , "1" ) ] ) [ BulletList [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] ] ] , Header 2 ( "_breaks" , [] , [] ) [ Str "Breaks" ] , Para [ Str "Asciidoc" , Space , Str "thematic" , Space , Str "break:" ] , HorizontalRule , Para [ Str "Markdown" , Space , Str "style:" ] , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , Para [ Str "Page" , Space , Str "breaks:" ] , Div ( "" , [ "page-break" ] , [ ( "wrapper" , "1" ) ] ) [ HorizontalRule ] , Div ( "" , [ "page-break" ] , [ ( "options" , "always" ) , ( "wrapper" , "1" ) ] ) [ HorizontalRule ] , Header 2 ( "_list" , [] , [] ) [ Str "List" ] , BulletList [ [ Para [ Str "Edgar" , Space , Str "Allan" , Space , Str "Poe" ] ] , [ Para [ Str "Sheri" , Space , Str "S." , Space , Str "Tepper" ] ] , [ Para [ Str "Bill" , Space , Str "Bryson" ] ] ] , Div ( "" , [] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Kizmet\8217s" , Space , Str "Favorite" , Space , Str "Authors" ] ] , BulletList [ [ Para [ Str "Edgar" , Space , Str "Allan" , Space , Str "Poe" ] ] , [ Para [ Str "Sheri" , Space , Str "S." , Space , Str "Tepper" ] ] , [ Para [ Str "Bill" , Space , Str "Bryson" ] ] ] ] , BulletList [ [ Para [ Str "Edgar" , Space , Str "Allan" , Space , Str "Poe" ] ] , [ Para [ Str "Sheri" , Space , Str "S." , Space , Str "Tepper" ] ] , [ Para [ Str "Bill" , Space , Str "Bryson" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Nested" , Space , Str "list" ] , BulletList [ [ Para [ Str "West" , Space , Str "wood" , Space , Str "maze" ] , BulletList [ [ Para [ Str "Maze" , Space , Str "heart" ] , BulletList [ [ Para [ Str "Reflection" , Space , Str "pool" ] ] ] ] , [ Para [ Str "Secret" , Space , Str "exit" ] ] ] ] , [ Para [ Str "Level" , Space , Str "1" , Space , Str "list" , Space , Str "item" ] , BulletList [ [ Para [ Str "Level" , Space , Str "2" , Space , Str "list" , Space , Str "item" ] , BulletList [ [ Para [ Str "Level" , Space , Str "3" , Space , Str "list" , Space , Str "item" ] , BulletList [ [ Para [ Str "Level" , Space , Str "4" , Space , Str "list" , Space , Str "item" ] , BulletList [ [ Para [ Str "Level" , Space , Str "5" , Space , Str "list" , Space , Str "item" ] , BulletList [ [ Para [ Str "etc." ] ] ] ] ] ] ] ] ] ] ] ] , [ Para [ Str "Level" , Space , Str "1" , Space , Str "list" , Space , Str "item" ] ] ] ] ] , BulletList [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] , [ Para [ Str "three" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Protons" ] ] , [ Para [ Str "Electrons" ] ] , [ Para [ Str "Neutrons" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Protons" ] ] , [ Para [ Str "Electrons" ] ] , [ Para [ Str "Neutrons" ] ] ] , Para [ Str "Start" , Space , Str "with" , Space , Str "4:" ] , OrderedList ( 4 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Step" , Space , Str "four" ] ] , [ Para [ Str "Step" , Space , Str "five" ] ] , [ Para [ Str "Step" , Space , Str "six" ] ] ] , Para [ Str "or" ] , OrderedList ( 4 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Step" , Space , Str "four" ] ] , [ Para [ Str "Step" , Space , Str "five" ] ] , [ Para [ Str "Step" , Space , Str "six" ] ] ] , Para [ Str "Reversed:" ] , Div ( "" , [] , [ ( "options" , "reversed" ) ] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Parts" , Space , Str "of" , Space , Str "an" , Space , Str "atom" ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Protons" ] ] , [ Para [ Str "Electrons" ] ] , [ Para [ Str "Neutrons" ] ] ] ] , Para [ Str "Nested" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Step" , Space , Str "1" ] ] , [ Para [ Str "Step" , Space , Str "2" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Step" , Space , Str "2a" ] ] , [ Para [ Str "Step" , Space , Str "2b" ] ] ] ] , [ Para [ Str "Step" , Space , Str "3" ] ] ] , Para [ Str "Mixed" , Space , Str "nested" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Linux" ] , BulletList [ [ Para [ Str "Fedora" ] ] , [ Para [ Str "Ubuntu" ] ] , [ Para [ Str "Slackware" ] ] ] ] , [ Para [ Str "BSD" ] , BulletList [ [ Para [ Str "FreeBSD" ] ] , [ Para [ Str "NetBSD" ] ] ] ] ] , Para [ Str "With" , Space , Str "spacing" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Linux" ] , BulletList [ [ Para [ Str "Fedora" ] ] , [ Para [ Str "Ubuntu" ] ] , [ Para [ Str "Slackware" ] ] ] ] , [ Para [ Str "BSD" ] , BulletList [ [ Para [ Str "FreeBSD" ] ] , [ Para [ Str "NetBSD" ] ] ] ] ] , Para [ Str "With" , Space , Str "number" , Space , Str "styles" ] , OrderedList ( 5 , LowerRoman , DefaultDelim ) [ [ Para [ Str "Five" ] ] , [ Para [ Str "Six" ] , OrderedList ( 1 , LowerAlpha , DefaultDelim ) [ [ Para [ Str "a" ] ] , [ Para [ Str "b" ] ] , [ Para [ Str "c" ] ] ] ] , [ Para [ Str "Seven" ] ] ] , Para [ Str "Checklist" ] , BulletList [ [ Para [ Str "\9746" , Space , Str "checked" ] ] , [ Para [ Str "\9746" , Space , Str "also" , Space , Str "checked" ] ] , [ Para [ Str "\9744" , Space , Str "not" , Space , Str "checked" ] ] , [ Para [ Str "normal" , Space , Str "list" , Space , Str "item" ] ] ] , Para [ Str "Separate" , Space , Str "lists" , Space , Str "with" , Space , Str "block" , Space , Str "attribute" ] , BulletList [ [ Para [ Str "Apples" ] ] , [ Para [ Str "Oranges" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Wash" ] ] , [ Para [ Str "Slice" ] ] ] ] ] , Para [ Str "Multiline" , Space , Str "items" ] , BulletList [ [ Para [ Str "Blah" , Space , Str "blah." , SoftBreak , Str "Blah" , Space , Str "blah." ] ] , [ Para [ Str "The" , Space , Str "document" , Space , Str "header" , Space , Str "in" , Space , Str "AsciiDoc" , Space , Str "is" , Space , Str "optional." , SoftBreak , Str "If" , Space , Str "present," , Space , Str "it" , Space , Str "must" , Space , Str "start" , Space , Str "with" , Space , Str "a" , Space , Str "document" , Space , Str "title." ] ] ] , BulletList [ [ Para [ Str "Optional" , Space , Str "author" , Space , Str "and" , Space , Str "revision" , Space , Str "information" , Space , Str "lines" , SoftBreak , Str "immediately" , Space , Str "follow" , Space , Str "the" , Space , Str "document" , Space , Str "title." ] ] ] , BulletList [ [ Para [ Str "The" , Space , Str "document" , Space , Str "header" , Space , Str "must" , Space , Str "be" , Space , Str "separated" , Space , Str "from" , SoftBreak , Str "the" , Space , Str "remainder" , Space , Str "of" , Space , Str "the" , Space , Str "document" , Space , Str "by" , Space , Str "one" , Space , Str "or" , Space , Str "more" , SoftBreak , Str "empty" , Space , Str "lines" , Space , Str "and" , Space , Str "it" , Space , Str "cannot" , Space , Str "contain" , Space , Str "empty" , Space , Str "lines." ] ] ] , Para [ Str "Complex" , Space , Str "item" ] , BulletList [ [ Para [ Str "The" , Space , Str "header" , Space , Str "in" , Space , Str "AsciiDoc" , Space , Str "must" , Space , Str "start" , Space , Str "with" , Space , Str "a" , Space , Str "document" , Space , Str "title." ] , CodeBlock ( "" , [] , [] ) "= Document Title" , Para [ Str "Keep" , Space , Str "in" , Space , Str "mind" , Space , Str "that" , Space , Str "the" , Space , Str "header" , Space , Str "is" , Space , Str "optional." ] ] , [ Para [ Str "Optional" , Space , Str "author" , Space , Str "and" , Space , Str "revision" , Space , Str "information" , Space , Str "lines" , Space , Str "immediately" , Space , Str "follow" , Space , Str "the" , Space , Str "document" , SoftBreak , Str "title." ] , CodeBlock ( "" , [] , [] ) "= Document Title\nDoc Writer \nv1.0, 2022-01-01" ] , [ Para [ Str "Second" , Space , Str "item" ] ] ] , Para [ Str "Empty" , Space , Str "principle" , Space , Str "element:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [] , CodeBlock ( "" , [] , [] ) "test" ] ] , Header 2 ( "_table" , [] , [] ) [ Str "Table" ] , Header 3 ( "_simple_with_column_specs" , [] , [] ) [ Str "Simple" , Space , Str "with" , Space , Str "column" , Space , Str "specs" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.375 ) , ( AlignDefault , ColWidth 0.25 ) , ( AlignDefault , ColWidth 0.375 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "content" , Space , Str "is" , Space , Str "placed" , Space , Str "in" , Space , Str "the" , Space , Str "first" , Space , Str "cell" , Space , Str "of" , Space , Str "column" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "line" , Space , Str "starts" , Space , Str "with" , Space , Str "a" , Space , Str "vertical" , Space , Str "bar" , Space , Str "so" , Space , Str "this" , Space , Str "content" , Space , Str "is" , Space , Str "placed" , Space , Str "in" , Space , Str "a" , Space , Str "new" , Space , Str "cell" , Space , Str "in" , SoftBreak , Str "column" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "When" , Space , Str "the" , Space , Str "processor" , Space , Str "encounters" , Space , Str "a" , Space , Str "whitespace" , Space , Str "followed" , Space , Str "by" , Space , Str "a" , Space , Str "vertical" , Space , Str "bar" , Space , Str "it" , SoftBreak , Str "ends" , Space , Str "the" , Space , Str "previous" , Space , Str "cell" , Space , Str "and" , Space , Str "starts" , Space , Str "a" , Space , Str "new" , Space , Str "cell" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) , Header 3 ( "_repeated_column_in_specs" , [] , [] ) [ Str "Repeated" , Space , Str "column" , Space , Str "in" , Space , Str "specs" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Strong [ Str "This" , Space , Str "cell\8217s" , Space , Str "specifier" , Space , Str "indicates" , Space , Str "that" , Space , Str "this" , Space , Str "cell\8217s" , Space , Str "content" , Space , Str "is" , Space , Str "right-aligned" , Space , Str "and" , Space , Str "bold." ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "The" , Space , Str "cell" , Space , Str "specifier" , Space , Str "on" , Space , Str "this" , Space , Str "cell" , Space , Str "hasn\8217t" , Space , Str "been" , Space , Str "set" , Space , Str "explicitly," , Space , Str "so" , Space , Str "the" , Space , Str "default" , SoftBreak , Str "properties" , Space , Str "are" , Space , Str "applied." ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) , Header 3 ( "_simple_without_column_specs" , [] , [] ) [ Str "Simple" , Space , Str "without" , Space , Str "column" , Space , Str "specs" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "1," , Space , Str "header" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "2," , Space , Str "header" , Space , Str "row" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "3" ] ] ] ]) , Header 3 ( "_with_caption" , [] , [] ) [ Str "With" , Space , Str "caption" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "My" , Space , Str "cool" , Space , Str "table." ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "1," , Space , Str "header" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "2," , Space , Str "header" , Space , Str "row" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "3" ] ] ] ]) , Header 3 ( "_no_header" , [] , [] ) [ Str "No" , Space , Str "header" ] , Para [ Str "By" , Space , Str "default" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "should" , Space , Str "turn" , Space , Str "into" , Space , Str "the" , Space , Str "header," , Space , Str "but" , Space , Str "this" , SoftBreak , Str "can" , Space , Str "be" , Space , Str "disabled:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "2" ] ] ] ]) , Para [ Str "And" , Space , Str "also" , Space , Str "explicitly" , Space , Str "enabled:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "A1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "B1" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "A2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "B2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "A3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "B3" ] ] ] ]) , Header 3 ( "_footer" , [] , [] ) [ Str "Footer" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.4 ) , ( AlignDefault , ColWidth 0.4 ) , ( AlignDefault , ColWidth 0.2 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "1," , Space , Str "header" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "2," , Space , Str "header" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "3," , Space , Str "header" , Space , Str "row" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "3," , Space , Str "row" , Space , Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "1," , Space , Str "footer" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "2," , Space , Str "footer" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "3," , Space , Str "footer" , Space , Str "row" ] ] ] ]) , Para [ Str "or" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "1," , Space , Str "header" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "2," , Space , Str "header" , Space , Str "row" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1," , Space , Str "row" , Space , Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "2," , Space , Str "row" , Space , Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "1," , Space , Str "footer" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "2," , Space , Str "footer" , Space , Str "row" ] ] ] ]) , Header 3 ( "_alignment" , [] , [] ) [ Str "Alignment" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "Name" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "Name" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 2) [ Para [ Str "This" , Space , Str "cell" , Space , Str "spans" , Space , Str "two" , Space , Str "columns," , Space , Str "and" , Space , Str "its" , Space , Str "content" , Space , Str "is" , Space , Str "horizontally" , Space , Str "centered" , Space , Str "because" , Space , Str "the" , SoftBreak , Str "cell" , Space , Str "specifier" , Space , Str "includes" , Space , Str "the" , Space , Code ( "" , [] , [] ) "^" , Space , Str "operator." ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "content" , Space , Str "is" , Space , Str "duplicated" , Space , Str "in" , Space , Str "two" , Space , Str "adjacent" , Space , Str "columns." , SoftBreak , Str "Its" , Space , Str "content" , Space , Str "is" , Space , Str "horizontally" , Space , Str "centered" , Space , Str "because" , Space , Str "the" , Space , Str "cell" , Space , Str "specifier" , SoftBreak , Str "includes" , Space , Str "the" , Space , Code ( "" , [] , [] ) "^" , Space , Str "operator." ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "content" , Space , Str "is" , Space , Str "duplicated" , Space , Str "in" , Space , Str "two" , Space , Str "adjacent" , Space , Str "columns." , SoftBreak , Str "Its" , Space , Str "content" , Space , Str "is" , Space , Str "horizontally" , Space , Str "centered" , Space , Str "because" , Space , Str "the" , Space , Str "cell" , Space , Str "specifier" , SoftBreak , Str "includes" , Space , Str "the" , Space , Code ( "" , [] , [] ) "^" , Space , Str "operator." ] ] ] ]) , Header 3 ( "_multiple_paragraphs_in_cells" , [] , [] ) [ Str "Multiple" , Space , Str "paragraphs" , Space , Str "in" , Space , Str "cells" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Single" , Space , Str "paragraph" , Space , Str "on" , Space , Str "row" , Space , Str "1" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "First" , Space , Str "paragraph" , Space , Str "on" , Space , Str "row" , Space , Str "2" ] , Para [ Str "Second" , Space , Str "paragraph" , Space , Str "on" , Space , Str "row" , Space , Str "2" ] ] ] ]) , Header 3 ( "_complex_table" , [] , [] ) [ Str "Complex" , Space , Str "table" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Code ( "" , [] , [] ) "This" , Space , Code ( "" , [] , [] ) "content" , Space , Code ( "" , [] , [] ) "is" , Space , Code ( "" , [] , [] ) "duplicated" , Space , Code ( "" , [] , [] ) "across" , Space , Code ( "" , [] , [] ) "two" , Space , Code ( "" , [] , [] ) "columns." ] , Para [ Code ( "" , [] , [] ) "It" , Space , Code ( "" , [] , [] ) "is" , Space , Code ( "" , [] , [] ) "aligned" , Space , Code ( "" , [] , [] ) "right" , Space , Code ( "" , [] , [] ) "horizontally." ] , Para [ Code ( "" , [] , [] ) "And" , Space , Code ( "" , [] , [] ) "it" , Space , Code ( "" , [] , [] ) "is" , Space , Code ( "" , [] , [] ) "monospaced." ] ] , Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Code ( "" , [] , [] ) "This" , Space , Code ( "" , [] , [] ) "content" , Space , Code ( "" , [] , [] ) "is" , Space , Code ( "" , [] , [] ) "duplicated" , Space , Code ( "" , [] , [] ) "across" , Space , Code ( "" , [] , [] ) "two" , Space , Code ( "" , [] , [] ) "columns." ] , Para [ Code ( "" , [] , [] ) "It" , Space , Code ( "" , [] , [] ) "is" , Space , Code ( "" , [] , [] ) "aligned" , Space , Code ( "" , [] , [] ) "right" , Space , Code ( "" , [] , [] ) "horizontally." ] , Para [ Code ( "" , [] , [] ) "And" , Space , Code ( "" , [] , [] ) "it" , Space , Code ( "" , [] , [] ) "is" , Space , Code ( "" , [] , [] ) "monospaced." ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 2) (ColSpan 1) [ Para [ Strong [ Str "This" , Space , Str "cell" , Space , Str "spans" , Space , Str "3" , Space , Str "rows." , Space , Str "The" , Space , Str "content" , Space , Str "is" , Space , Str "centered" , Space , Str "horizontally," , Space , Str "aligned" , Space , Str "to" , Space , Str "the" , Space , Str "bottom" , Space , Str "of" , Space , Str "the" , Space , Str "cell," , Space , Str "and" , Space , Str "strong." ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Emph [ Str "This" , Space , Str "content" , Space , Str "is" , Space , Str "emphasized." ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "This content is aligned to the top of the cell and literal.\n\n" ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "puts \"This is a source block!\"" ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) , Header 3 ( "_column_styles" , [] , [] ) [ Str "Column" , Space , Str "styles" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Code ( "" , [] , [] ) "monospace" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Code ( "" , [] , [] ) "mono" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "default" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Code ( "" , [] , [] ) "mono" ] ] ] ]) , Header 3 ( "_block_elements_in_cells" , [] , [] ) [ Str "Block" , Space , Str "elements" , Space , Str "in" , Space , Str "cells" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Normal" , Space , Str "Style" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "AsciiDoc" , Space , Str "Style" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "cell" , Space , Str "isn\8217t" , Space , Str "prefixed" , Space , Str "with" , Space , Str "an" , Space , Code ( "" , [] , [] ) "a" , Str "," , Space , Str "so" , Space , Str "the" , Space , Str "processor" , Space , Str "doesn\8217t" , Space , Str "interpret" , Space , Str "the" , SoftBreak , Str "following" , Space , Str "lines" , Space , Str "as" , Space , Str "an" , Space , Str "AsciiDoc" , Space , Str "list." ] , Para [ Str "*" , Space , Str "List" , Space , Str "item" , Space , Str "1" , SoftBreak , Str "*" , Space , Str "List" , Space , Str "item" , Space , Str "2" , SoftBreak , Str "*" , Space , Str "List" , Space , Str "item" , Space , Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "cell" , Space , Str "is" , Space , Str "prefixed" , Space , Str "with" , Space , Str "an" , Space , Code ( "" , [] , [] ) "a" , Str "," , Space , Str "so" , Space , Str "the" , Space , Str "processor" , Space , Str "interprets" , Space , Str "the" , Space , Str "following" , Space , Str "lines" , SoftBreak , Str "as" , Space , Str "an" , Space , Str "AsciiDoc" , Space , Str "list." ] , BulletList [ [ Para [ Str "List" , Space , Str "item" , Space , Str "1" ] ] , [ Para [ Str "List" , Space , Str "item" , Space , Str "2" ] ] , [ Para [ Str "List" , Space , Str "item" , Space , Str "3" ] ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "cell" , Space , Str "isn\8217t" , Space , Str "prefixed" , Space , Str "with" , Space , Str "an" , Space , Code ( "" , [] , [] ) "a" , Str "," , Space , Str "so" , Space , Str "the" , Space , Str "processor" , Space , Str "doesn\8217t" , Space , Str "interpret" , Space , Str "the" , Space , Str "listing" , SoftBreak , Str "block" , Space , Str "delimiters" , Space , Str "or" , Space , Str "the" , Space , Code ( "" , [] , [] ) "source" , Space , Str "style." ] , Para [ Str "----" , SoftBreak , Str "import" , Space , Str "os" , SoftBreak , Str "print" , Space , Str "(\"%s\"" , Space , Str "%(os.uname()))" , SoftBreak , Str "----" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "cell" , Space , Str "is" , Space , Str "prefixed" , Space , Str "with" , Space , Str "an" , Space , Code ( "" , [] , [] ) "a" , Str "," , Space , Str "so" , Space , Str "the" , Space , Str "listing" , Space , Str "block" , Space , Str "is" , Space , Str "processed" , Space , Str "and" , Space , Str "rendered" , SoftBreak , Str "according" , Space , Str "to" , Space , Str "the" , Space , Code ( "" , [] , [] ) "source" , Space , Str "style" , Space , Str "rules." ] , CodeBlock ( "" , [ "python" ] , [] ) "import os\nprint \"%s\" %(os.uname())" ] ] ]) , Header 3 ( "_col_and_rowspan" , [] , [] ) [ Str "Col" , Space , Str "and" , Space , Str "rowspan" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "1," , Space , Str "header" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "2," , Space , Str "header" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Column" , Space , Str "3," , Space , Str "header" , Space , Str "row" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 2) [ Para [ Str "This" , Space , Str "cell" , Space , Str "spans" , Space , Str "2" , Space , Str "cols" , Space , Str "and" , Space , Str "2" , Space , Str "rows" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "3," , Space , Str "row" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "3," , Space , Str "row" , Space , Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Para [ Str "Cell" , Space , Str "in" , Space , Str "column" , Space , Str "1-3," , Space , Str "row" , Space , Str "4" ] ] ] ]) , Header 3 ( "_csv_table" , [] , [] ) [ Str "CSV" , Space , Str "table" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Artist" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Track" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Genre" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Baauer" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Harlem" , Space , Str "Shake" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Hip" , Space , Str "Hop" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "The" , Space , Str "Lumineers" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Ho" , Space , Str "Hey" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Folk" , Space , Str "Rock" ] ] ] ]) , Para [ Str "or" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Artist" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Track" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Genre" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Baauer" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Harlem" , Space , Str "Shake" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Hip" , Space , Str "Hop" ] ] ] ]) , Header 3 ( "_dsv_table" , [] , [] ) [ Str "DSV" , Space , Str "table" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "b" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "c" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "f" ] ] ] ]) , Para [ Str "or" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Artist" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Track" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Genre" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Robyn" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Indestructible" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Dance" ] ] ] ]) , Header 2 ( "_definition_list" , [] , [] ) [ Str "Definition" , Space , Str "list" ] , DefinitionList [ ( [ Str "CPU" ] , [ [ Para [ Str "The" , Space , Str "brain" , Space , Str "of" , Space , Str "the" , Space , Str "computer." ] ] ] ) , ( [ Str "Hard" , Space , Str "drive" ] , [ [ Para [ Str "Permanent" , Space , Str "storage" , Space , Str "for" , Space , Str "operating" , Space , Str "system" , Space , Str "and/or" , Space , Str "user" , Space , Str "files." ] ] ] ) ] , Para [ Str "Mixed" ] , DefinitionList [ ( [ Str "Dairy" ] , [ [ BulletList [ [ Para [ Str "Milk" ] ] , [ Para [ Str "Eggs" ] , DefinitionList [ ( [ Str "Bakery" ] , [ [] ] ) ] ] , [ Para [ Str "Bread" ] , DefinitionList [ ( [ Str "Produce" ] , [ [] ] ) ] ] , [ Para [ Str "Bananas" ] ] ] ] ] ) ] , Para [ Str "With" , Space , Str "spaces" ] , DefinitionList [ ( [ Str "Dairy" ] , [ [] ] ) ] , BulletList [ [ Para [ Str "Milk" ] ] , [ Para [ Str "Eggs" ] ] ] , DefinitionList [ ( [ Str "Bakery" ] , [ [] ] ) ] , BulletList [ [ Para [ Str "Bread" ] ] ] , DefinitionList [ ( [ Str "Produce" ] , [ [] ] ) ] , BulletList [ [ Para [ Str "Bananas" ] ] ] , Para [ Str "Nested" ] , DefinitionList [ ( [ Str "Operating" , Space , Str "Systems" ] , [ [ DefinitionList [ ( [ Str "Linux" ] , [ [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Fedora" ] , BulletList [ [ Para [ Str "Desktop" ] ] ] ] , [ Para [ Str "Ubuntu" ] , BulletList [ [ Para [ Str "Desktop" ] ] , [ Para [ Str "Server" ] ] ] ] ] ] ] ) , ( [ Str "BSD" ] , [ [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "FreeBSD" ] ] , [ Para [ Str "NetBSD" ] , DefinitionList [ ( [ Str "Cloud" , Space , Str "Providers" ] , [ [ DefinitionList [ ( [ Str "PaaS" ] , [ [] ] ) ] ] ] ) ] ] , [ Para [ Str "OpenShift" ] ] , [ Para [ Str "CloudBees" ] ] ] ] ] ) , ( [ Str "IaaS" ] , [ [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Amazon" , Space , Str "EC2" ] ] ] ] ] ) ] , Para [ Str "This" , Space , Str "just" , Space , Str "affects" , Space , Str "the" , Space , Str "output:" ] ] ] ) ] , Div ( "" , [] , [ ( "wrapper" , "1" ) , ( "itemwidth" , "75" ) , ( "labelwidth" , "25" ) ] ) [ DefinitionList [ ( [ Str "CPU" ] , [ [ Para [ Str "The" , Space , Str "brain" , Space , Str "of" , Space , Str "the" , Space , Str "computer." ] ] ] ) , ( [ Str "RAM" ] , [ [ Para [ Str "Temporarily" , Space , Str "stores" , Space , Str "information" , Space , Str "the" , Space , Str "CPU" , Space , Str "uses" , Space , Str "during" , Space , Str "operation." ] ] ] ) ] ] , Para [ Str "Q&A" , Space , Str "list" ] , DefinitionList [ ( [ Str "What" , Space , Str "is" , Space , Str "the" , Space , Str "answer?" ] , [ [ Para [ Str "This" , Space , Str "is" , Space , Str "the" , Space , Str "answer." ] ] ] ) , ( [ Str "Are" , Space , Str "cameras" , Space , Str "allowed?" ] , [ [] ] ) , ( [ Str "Are" , Space , Str "backpacks" , Space , Str "allowed?" ] , [ [ Para [ Str "No." ] ] ] ) ] , Para [ Str "Ordered" , Space , Str "description" , Space , Str "list" , Space , Str "(with" , Space , Str "numbers)" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ DefinitionList [ ( [ Str "&" ] , [ [ Para [ Str "ampersand" ] ] ] ) ] ] , [ DefinitionList [ ( [ Str ">" ] , [ [ Para [ Str "greater" , Space , Str "than" ] ] ] ) ] ] ] , Header 2 ( "_block_macros" , [] , [] ) [ Str "Block" , Space , Str "macros" ] , Figure ( "" , [] , [] ) (Caption Nothing []) [ Plain [ Image ( "" , [] , [ ( "width" , "300px" ) , ( "height" , "200px" ) ] ) [ Str "Sunset" ] ( "sunset.jpg" , "" ) ] ] , Plain [ Image ( "" , [] , [] ) [ Str "mymovie.mp4" ] ( "mymovie.mp4" , "" ) ] , Plain [ Image ( "" , [] , [] ) [ Str "mysong.mp3" ] ( "mysong.mp3" , "" ) ] , Div ( "toc" , [] , [] ) [] , Div ( "" , [ "included" ] , [ ( "path" , "asciidoc-reader-include.adoc" ) ] ) [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "test!" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "one" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "two" ] ] ] ] ] ] ] ================================================ FILE: test/command/01.csv ================================================ "Column1";"Column2" "Data1";"- data1 - data2" ================================================ FILE: test/command/10002.md ================================================ ```` % pandoc -f native -t opendocument [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "First" , Space , Str "Header" , Space , Str "Row" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header - Table" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header - Body" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header - Row" ] ] ] ] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Table" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "First" , Space , Str "Footer" , Space , Str "Row" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Footer" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" ] ] ] ]) ] ^D First Header Row Second Header Row Header - Table Header - Body Header - Row Table Body Row First Footer Row Second Footer Row ```` ================================================ FILE: test/command/10057.md ================================================ ``` % pandoc --natbib -t latex We all love the famous proof by @BenjaminHornigold [p. 42]. ^D We all love the famous proof by \citet[p.~42]{BenjaminHornigold}. ``` ================================================ FILE: test/command/10062.md ================================================ ``` % pandoc -t asciidoc ### section ##### five ^D ==== section ====== five ``` ================================================ FILE: test/command/10071.md ================================================ Leading tabs must be preserved in org mode *src* blocks with the `-i` flag when pandoc is called with `-p`/`--preserve-tabs`. ``` % pandoc -f org -t native --preserve-tabs #+begin_src makefile -i %.o: %.cpp $(CXX) -o $@ $< #+end_src ^D [ CodeBlock ( "" , [ "makefile" ] , [] ) "%.o: %.cpp\n\t$(CXX)\t-o\t$@\t$<\n" ] ``` ================================================ FILE: test/command/10093.md ================================================ ``` % pandoc -f rst ========== ==== Function 1 ========== ==== a yes b ========== ==== ^D
` child element; - a `` child element, whose children are empty `` elements; - a `` child element; - one or more `` children elements, that in turn have two children: `
` and ``, whose children are `` elements; - a `` child element. This specification is debatable; I have these doubts: - is it necessary to enclose the `` elements in a `` element? - to discriminate between header and data cells in table bodies, there are the `row-head-columns` attribute, and the `
` and `` children of the `` element, but there's only one type of cell: every cell is a `` element - the specs are a tradeoff between consistency with pandoc types and CSS compatibility; this way bodies' header rows are easily stylable with CSS, while header columns are not The `ColWidthDefault` value becomes a "0" value for the attribute `col-width`; this way it's type-consistent with non-zero values, but I'm still doubtful whether to leave its value as a "ColWidthDefault" string. Here's an example from the `xml` version of `test/tables/planets.native`: ```xml Name Mass (10^24kg) ...
Terrestrial planets Mercury 0.330 4,879 5427 3.7 4222.6 57.9 167 0 Closest to the Sun ...
Data about the planets of our solar system.
``` ### Metadata and MetaMap entries Metadata entries are meta values (`MetaBool`, `MetaString`, `MetaInlines`, `MetaBlocks`, `MetaList` and `MetaMap` elements) inside `` elements. The `` and the `` elements have the same children elements (``), which have a `key` attribute. ``, ``, `` and `` elements all have children elements. `` elements have only text. `` elements are empty, they can be either `` or ``. This snippet is from the `xml` version of `test/testsuite.native`: ```xml John MacFarlane Anonymous July 17, 2006 Pandoc Test Suite ``` ### Cite elements `Cite` inlines are modeled with `` elements, whose first child is a `` element, that have only `` children elements. `` elements are empty, unless they have a prefix and/or a suffix. Here's an example from the `xml` version of `test/markdown-citations.native`: ```xml @item1 says blah. p. 30 @item1 [p. 30] says blah. A citation group see chap. 3 also p. 34-35 [see @item1 chap. 3; also @пункт3 p. 34-35]. ``` ================================================ FILE: flake.lock ================================================ { "nodes": { "flake-utils": { "inputs": { "systems": "systems" }, "locked": { "lastModified": 1731533236, "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { "owner": "numtide", "repo": "flake-utils", "type": "github" } }, "flake-utils_2": { "inputs": { "systems": "systems_2" }, "locked": { "lastModified": 1731533236, "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { "owner": "numtide", "repo": "flake-utils", "type": "github" } }, "ghc-wasm-meta": { "inputs": { "flake-utils": "flake-utils_2", "nixpkgs": "nixpkgs" }, "locked": { "host": "gitlab.haskell.org", "lastModified": 1769511593, "narHash": "sha256-zHh+dPvGHX6ZIk5XQm20y2Qg31tXeqX5REe06pBvMiU=", "owner": "haskell-wasm", "repo": "ghc-wasm-meta", "rev": "e950d94ea9a091d64ee38ff735e52d46191e76af", "type": "gitlab" }, "original": { "host": "gitlab.haskell.org", "owner": "haskell-wasm", "repo": "ghc-wasm-meta", "type": "gitlab" } }, "nixpkgs": { "locked": { "lastModified": 1769318308, "narHash": "sha256-Mjx6p96Pkefks3+aA+72lu1xVehb6mv2yTUUqmSet6Q=", "owner": "NixOS", "repo": "nixpkgs", "rev": "1cd347bf3355fce6c64ab37d3967b4a2cb4b878c", "type": "github" }, "original": { "owner": "NixOS", "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { "lastModified": 1769205588, "narHash": "sha256-ugJzenE0+X/5ILJEcl0+NavMQhqMro3cwPhR/NjOOzU=", "owner": "NixOS", "repo": "nixpkgs", "rev": "28528e01b00d737528b90d0ccb9cd468290fdc36", "type": "github" }, "original": { "owner": "NixOS", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "flake-utils": "flake-utils", "ghc-wasm-meta": "ghc-wasm-meta", "nixpkgs": "nixpkgs_2" } }, "systems": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "owner": "nix-systems", "repo": "default", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "type": "github" }, "original": { "owner": "nix-systems", "repo": "default", "type": "github" } }, "systems_2": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "owner": "nix-systems", "repo": "default", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "type": "github" }, "original": { "owner": "nix-systems", "repo": "default", "type": "github" } } }, "root": "root", "version": 7 } ================================================ FILE: flake.nix ================================================ { description = "pandoc"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; ghc-wasm-meta.url = "gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org"; }; outputs = { self, nixpkgs, flake-utils, ghc-wasm-meta }: flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; haskellPackages = pkgs.haskellPackages; # Or, override some dependencies as follows: # haskellPackages = (pkgs.haskellPackages.override { # all-cabal-hashes = pkgs.fetchurl { # # The hash in the URL is just a git commit hash # url = "https://github.com/commercialhaskell/all-cabal-hashes/archive/2e3f153549871ada6ecbf36719339a5da051bc76.tar.gz"; # sha256 = "ymHZb6wCZlrtKb+T+iOL17jyN8IzA7s52uiamUIkWNI="; # }; # }).extend(self: super: { # citeproc = pkgs.haskell.lib.dontCheck (self.callHackage "citeproc" "0.9.0.1" {}); # texmath = self.callHackage "texmath" "0.12.10.2" {}; # }); jailbreakUnbreak = pkg: pkgs.haskell.lib.doJailbreak (pkg.overrideAttrs (_: { meta = { }; })); # PUT YOUR PACKAGE NAME HERE: packageName = "pandoc"; wasmToolchain = ghc-wasm-meta.packages.${system}.default; # Alternatively, if you want a specific "bundle": # wasmToolchain = ghc-wasm-meta.packages.${system}.all_9_14; in { packages.${packageName} = haskellPackages.callCabal2nix packageName self rec { # Dependency overrides go here }; packages.default = self.packages.${system}.${packageName}; defaultPackage = self.packages.${system}.default; devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ wasmToolchain haskellPackages.haskell-language-server haskellPackages.hlint haskellPackages.cabal-install haskellPackages.cabal-plan haskellPackages.hpc haskellPackages.ghcid haskellPackages.stylish-haskell haskellPackages.eventlog2html haskellPackages.profiterole haskellPackages.profiteur zlib.dev git gnumake bashInteractive ripgrep unzip jq libxml2 # for xmllint epubcheck # for validate-epub nodejs # for validate-epub ]; inputsFrom = map (__getAttr "env") (__attrValues self.packages.${system}); }; devShell = self.devShells.${system}.default; }); } ================================================ FILE: hie.yaml ================================================ cradle: cabal: - path: "./xml-light" component: "pandoc:lib:xml-light" - path: "./src" component: "lib:pandoc" - path: "./test" component: "pandoc:test:test-pandoc" - path: "./benchmark/benchmark-pandoc.hs" component: "pandoc:bench:benchmark-pandoc" - path: "pandoc-cli/no-lua/pandoc.hs" component: "pandoc-cli:exe:pandoc" - path: "pandoc-cli/no-lua/PandocCLI/Lua.hs" component: "pandoc-cli:exe:pandoc" - path: "pandoc-cli/no-lua/PandocCLI/Server.hs" component: "pandoc-cli:exe:pandoc" - path: "pandoc-lua-engine/src" component: "lib:pandoc-lua-engine" - path: "pandoc-lua-engine/test" component: "pandoc-lua-engine:test:test-pandoc-lua-engine" - path: "pandoc-server/src" component: "lib:pandoc-server" ================================================ FILE: linux/control.in ================================================ Package: pandoc Version: VERSION Section: text Priority: optional Architecture: ARCHITECTURE Installed-Size: INSTALLED_SIZE Depends: libc6 (>= 2.13), libgmp10, zlib1g (>= 1:1.1.4) Replaces: pandoc-data Suggests: texlive-latex-recommended, texlive-xetex, texlive-fonts-recommended Maintainer: John MacFarlane Description: general markup converter Pandoc is a Haskell library for converting from one markup format to another, and a command-line tool that uses this library. The formats it can handle include light markup formats (many variants of Markdown, reStructuredText, AsciiDoc, Org-mode, Muse, Textile, txt2tags), HTML formats (HTML 4 and 5), ebook formats (EPUB v2 and v3, FB2), Documentation formats (GNU TexInfo, Haddock), Roff formats (man, ms), TeX formats (LaTeX, ConTeXt), XML formats (DocBook 4 and 5, JATS, TEI Simple, OpenDocument), outline formats (OPML), bibliography formats (BibTeX, BibLaTeX, CSL JSON, CSL YAML, RIS), word processor formats (Docx, RTF, ODT), interactive notebook formats (Jupyter notebook ipynb), page layout formats (InDesign ICML), wiki markup formats (MediaWiki, DokuWiki, TikiWiki, TWiki, Vimwiki, XWiki, ZimWiki, Jira wiki, Creole), slide show formats (LaTeX Beamer, PowerPoint, Slidy, reveal.js, Slideous, S5, DZSlides), data formats (CSV and TSV tables), and PDF (via external programs such as pdflatex or wkhtmltopdf). ================================================ FILE: linux/make_artifacts.sh ================================================ #!/bin/bash set -e cabal update cabal clean cabal build $CABALOPTS --ghc-options="$GHCOPTS" pandoc-cli BINPATH=$(cabal list-bin $CABALOPTS --ghc-options="$GHCOPTS" pandoc-cli) echo "Built executable: $BINPATH" WORK="$(pwd)" MACHINE=$(uname -m) case "$MACHINE" in x86_64) ARCHITECTURE=amd64;; i686) ARCHITECTURE=i386;; i386) ARCHITECTURE=i386;; aarch64) ARCHITECTURE=arm64;; armv6l | armv7l) ARCHITECTURE=armhf;; riscv64) ARCHITECTURE=riscv64;; *) ARCHITECTURE=unknown;; esac ARTIFACTS="$WORK/linux-${ARCHITECTURE}" echo "Creating $ARTIFACTS directory" mkdir -p $ARTIFACTS echo "Copying and stripping pandoc binary" cp "$BINPATH" "$ARTIFACTS/pandoc" strip "$ARTIFACTS/pandoc" echo "Checking that the binary is statically linked..." file "$ARTIFACTS/pandoc" | grep "statically linked" echo "Checking that the binary has +lua and +server support..." "$ARTIFACTS/pandoc" --version | grep -q '+server +lua' echo "Checking that the binary has data files baked in..." strings "$ARTIFACTS/pandoc" | grep -q '\$title\$' make_deb() { VERSION=$("$ARTIFACTS"/pandoc --version | awk '{print $2; exit;}') REVISION=${REVISION:-1} DEBVER=$VERSION-$REVISION BASE=pandoc-$DEBVER-$ARCHITECTURE DIST=$WORK/$BASE DEST=$DIST/usr COPYRIGHT=$DEST/share/doc/pandoc/copyright mkdir -p "$DEST/bin" mkdir -p "$DEST/share/man/man1" mkdir -p "$DEST/share/doc/pandoc" find "$DIST" -type d -exec chmod 755 {} \; cp "$ARTIFACTS/pandoc" "$DEST/bin/" pushd "$DEST/bin" ln -s pandoc pandoc-server ln -s pandoc pandoc-lua popd for manpage in pandoc.1 pandoc-lua.1 pandoc-server.1 do cp "$WORK/pandoc-cli/man/$manpage" "$DEST/share/man/man1/$manpage" gzip -9 "$DEST/share/man/man1/$manpage" done cp $WORK/COPYRIGHT "$COPYRIGHT" echo "" >> "$COPYRIGHT" INSTALLED_SIZE=$(du -k -s "$DEST" | awk '{print $1}') mkdir "$DIST/DEBIAN" perl -pe "s/VERSION/$DEBVER/" $WORK/linux/control.in | \ perl -pe "s/ARCHITECTURE/$ARCHITECTURE/" | \ perl -pe "s/INSTALLED_SIZE/$INSTALLED_SIZE/" \ > "$DIST/DEBIAN/control" # we limit compression to avoid OOM error fakeroot dpkg-deb -Zgzip -z9 --build "$DIST" rm -rf "$DIST" cp "$BASE.deb" "$ARTIFACTS/" echo "Created $BASE.deb" } # Make tarball for pandoc make_tarball() { TARGET=pandoc-$VERSION pushd "$ARTIFACTS" rm -rf "$TARGET" mkdir "$TARGET" mkdir "$TARGET/bin" "$TARGET/share" "$TARGET/share/man" "$TARGET/share/man/man1" cp $WORK/pandoc-cli/man/pandoc.1 $WORK/pandoc-cli/man/pandoc-server.1 $WORK/pandoc-cli/man/pandoc-lua.1 "$TARGET/share/man/man1" gzip -9 "$TARGET"/share/man/man1/*.1 mv pandoc "$TARGET/bin" pushd "$TARGET/bin" ln -s pandoc pandoc-server ln -s pandoc pandoc-lua popd tar cvzf "$TARGET-linux-$ARCHITECTURE.tar.gz" "$TARGET" echo "Created $TARGET-linux-$ARCHITECTURE.tar.gz" rm -r "$TARGET" popd } echo "Making debian package..." make_deb echo "Making tarball..." make_tarball echo "Finished!" exit 0 ================================================ FILE: macos/Makefile ================================================ ARCHITECTURE=$(shell cat architecture.txt) VERSION=$(shell cat version.txt) BASE=pandoc-$(VERSION)-$(ARCHITECTURE) DEST=pandoc/usr/local ALL=$(BASE)-macOS.pkg $(BASE)-macOS.zip TOOLPATH=/Applications/XCode.app/Contents/Developer/usr/bin all: $(ALL) notarize signed.txt: $(DEST) chmod +x $(DEST)/bin/pandoc codesign --force --options runtime -s "5U2WKE6DES" $(DEST)/bin/pandoc echo "SIGNED" > signed.txt $(DEST)/bin/pandoc-server: cd $(DEST)/bin && ln -s pandoc pandoc-server $(DEST)/bin/pandoc-lua: cd $(DEST)/bin && ln -s pandoc pandoc-lua pandoc.pkg: $(DEST)/bin/pandoc-server $(DEST)/bin/pandoc-lua signed.txt pkgbuild --root pandoc --identifier net.johnmacfarlane.pandoc --version $(VERSION) --ownership recommended $@ $(BASE)-macOS.pkg: pandoc.pkg productbuild --distribution distribution.xml --resources Resources --package-path $< --version $(VERSION) --sign 'Developer ID Installer: John Macfarlane' $@ $(BASE)-macOS.zip: $(DEST)/bin/pandoc-server $(DEST)/bin/pandoc-lua signed.txt mv $(DEST) $(BASE) zip --symlinks -r $@ $(BASE) notarize: $(BASE)-macOS.pkg $(TOOLPATH)/notarytool submit $< --keychain-profile=pandoc --wait && \ $(TOOLPATH)/stapler staple $< clean: rm signed.txt $(BASE) pandoc.pkg # gon.hcl distclean: clean rm $(ALL) .PHONY: all clean distclean ================================================ FILE: macos/distribution.xml.in ================================================ pandoc net.johnmacfarlane.pandoc pandoc.pkg ================================================ FILE: macos/make_macos_release.sh ================================================ #!/bin/sh -e MACHINE=$(uname -m) VERSION=$(grep '^[Vv]ersion:' pandoc.cabal | awk '{print $2;}') ARTIFACTS=macos-$MACHINE RESOURCES=$ARTIFACTS/Resources ROOT=$ARTIFACTS/pandoc DEST=$ROOT/usr/local ME=$(whoami) CABALOPTS="-fembed_data_files -fserver -flua -fhttp" # Build the pandoc binary and put it in . cabal update cabal build all $CABALOPTS BINPATH=$(cabal list-bin $CABALOPTS pandoc-cli) echo "Built executable..." # Create directories echo "Creating $ARTIFACTS directory..." mkdir -p $ARTIFACTS mkdir -p $RESOURCES mkdir -p $DEST/bin mkdir -p $DEST/share/man/man1 # Copy binary and strip it echo "Copying executable..." cp "$BINPATH" "$DEST/bin/" strip "$DEST/bin/pandoc" # Copy man pages and license echo "Copying manuals and license..." cp pandoc-cli/man/pandoc.1 "$DEST/share/man/man1/pandoc.1" cp pandoc-cli/man/pandoc-server.1 "$DEST/share/man/man1/pandoc-server.1" cp pandoc-cli/man/pandoc-lua.1 "$DEST/share/man/man1/pandoc-lua.1" "$BINPATH" -s COPYING.md -Vpagetitle=License -o "$RESOURCES/license.html" # Prepare distribution directory; after downloading, run 'make' to notarize echo "Preparing distribution directory..." chown -R "$ME:staff" "$ROOT" sed -e "s/PANDOCVERSION/$VERSION/; s/ARCHITECTURE/$MACHINE/;" macos/distribution.xml.in > "$ARTIFACTS/distribution.xml" cp macos/Makefile "$ARTIFACTS/" echo "$VERSION" > "$ARTIFACTS/version.txt" echo "$MACHINE" > "$ARTIFACTS/architecture.txt" ls -R $ARTIFACTS echo "Finished..." ================================================ FILE: macos/uninstall-pandoc.pl ================================================ #!/usr/bin/perl # Script to remove all files installed by the macOS pandoc installer # and unregister the package. Modified from a script contributed # by Daniel T. Staal. use warnings; use strict; use File::Spec; # The main info: this is the list of files to remove and the pkg_id. my $pkg_id = 'net.johnmacfarlane.pandoc'; # Find which, if any, volume Pandoc is installed on. my $volume; # First check /, then other volumes on the box. my $cur_test = `pkgutil --pkgs=$pkg_id`; if ( $cur_test =~ m/$pkg_id/ ) { $volume = '/'; } else { opendir( my $dh, '/Volumes' ) or die "Can't list Volumes: $!\n"; foreach my $dir ( readdir($dh) ) { next if $dir =~ m/^\./; # Skip dotfiles. my $path = File::Spec->rel2abs( $dir, '/Volumes' ); next if !( -d $path ); # Skip anything that isn't a directory. my $cur_test = `pkgutil --pkgs=$pkg_id --volume '$path'`; if ( $cur_test =~ m/$pkg_id/ ) { $volume = $path; last; } } } die "Pandoc not installed.\n" if !( defined($volume) ); # Get the list of files to remove. my @pkg_files = `pkgutil --volume '$volume' --only-files --files '$pkg_id'`; @pkg_files = map { chomp; File::Spec->rel2abs($_, $volume) } @pkg_files; # Confirm uninstall with the user. print "The following files will be deleted:\n\n"; print join("\n", @pkg_files); print "\n\n"; print "Do you want to proceed and uninstall pandoc (Y/N)?"; my $input = ; if ($input =~ m/^[Yy]/) { # Actually remove the files. foreach my $file (@pkg_files) { if ( -e $file ) { if ( system( 'sudo', 'rm', $file ) == 0 ) { warn "Deleted $file\n"; } else { warn "Unable to delete $file: $?\n"; die "Aborting Uninstall.\n"; } } else { warn "File $file does not exist. Skipping.\n"; } } # Clean up the install. if (system('sudo', 'pkgutil', '--forget', $pkg_id, '--volume', $volume) != 0) { die "Unable to clean up install: $?\n"; } } else { print "OK, aborting uninstall.\n"; exit; } print "Pandoc has been successfully uninstalled.\n"; exit; ================================================ FILE: man/manfilter.lua ================================================ -- we use preloaded text to get a UTF-8 aware 'upper' function local text = require('text') -- capitalize level 1 headers function Header(el) if el.level == 1 then return pandoc.walk_block(el, { Str = function(el) return pandoc.Str(text.upper(el.text)) end }) end end -- For portability with mandoc, which doesn't allow man commands -- inside table cells, we convert all tables to code blocks. function Table(el) local rendered = pandoc.write(pandoc.Pandoc({el}), "plain") local adjusted = rendered -- tame grid table lines :gsub("%+([=:][=:]+)", function(s) return " " .. string.rep("-", #s - 1) end) :gsub("(%+[-:][-:]+)", function(s) return "" end) :gsub("%+\n","\n") :gsub("\n| ","\n|") :gsub("|","") return { pandoc.RawBlock("man", ".RS -14n"), pandoc.CodeBlock(adjusted), pandoc.RawBlock("man", ".RE") } end -- replace links with link text function Link(el) return el.content end -- remove notes function Note(el) return {} end ================================================ FILE: man/pandoc.1.after ================================================ .PP The Pandoc source code may be downloaded from or . Further documentation is available at . ================================================ FILE: man/pandoc.1.before ================================================ .SH NAME pandoc - general markup converter ================================================ FILE: pandoc-cli/README.md ================================================ # pandoc-cli This package provides the command-line document-conversion program `pandoc`. There is not much to this package; all of the work is done by the libraries `pandoc` and `pandoc-server`. ## License © 2006-2024 John MacFarlane (jgm@berkeley.edu). Released under the [GPL](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html "GNU General Public License"), version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) ================================================ FILE: pandoc-cli/lua/PandocCLI/Lua.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : PandocCLI.Lua Copyright : © 2022-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Functions to run the pandoc Lua scripting engine. -} module PandocCLI.Lua (runLuaInterpreter, getEngine) where #ifdef REPL import Control.Monad ((<=<)) import System.Environment (lookupEnv) import System.IO.Temp (withSystemTempFile) import System.IO (hClose) import Text.Pandoc.Class (runIOorExplode) import Text.Pandoc.Error (handleError) import Text.Pandoc.Lua (runLua, runLuaNoEnv, getEngine) import Text.Pandoc.Version (pandocVersionText) import HsLua.CLI (EnvBehavior (..), Settings (..), runStandalone) #else import Text.Pandoc.Lua (getEngine) import System.IO (stderr, hPutStrLn) import System.Exit (exitWith, ExitCode(..)) #endif -- | Runs pandoc as a Lua interpreter that is (mostly) compatible with -- the default @lua@ program shipping with Lua. -- -- The filename for the history of the REPL is taken from the -- @PANDOC_REPL_HISTORY@ environment variable if possible. Otherwise a -- new temporary file is used; it is removed after the REPL finishes. runLuaInterpreter :: String -- ^ Program name -> [String] -- ^ Command line arguments -> IO () #ifdef REPL runLuaInterpreter progName args = do -- We need some kind of temp mbhistfile <- lookupEnv "PANDOC_REPL_HISTORY" case mbhistfile of Just histfile -> runStandaloneWithHistory histfile Nothing -> withSystemTempFile "pandoc-hist" $ \fp handle -> do -- We cannot pass a handle to the repl; the file will be re-opened -- there. hClose handle runStandaloneWithHistory fp where runStandaloneWithHistory histfile = do let settings = Settings { settingsVersionInfo = "\nEmbedded in pandoc " <> pandocVersionText <> " Copyright (C) 2006-2024 John MacFarlane" , settingsRunner = runner , settingsHistory = Just histfile } runStandalone settings progName args runner envBehavior = let runLua' = case envBehavior of IgnoreEnvVars -> runLuaNoEnv ConsultEnvVars -> runLua in handleError <=< runIOorExplode . runLua' #else runLuaInterpreter _ _ = do hPutStrLn stderr "Pandoc not compiled with Lua interpreter support." exitWith $ ExitFailure 4 #endif ================================================ FILE: pandoc-cli/man/pandoc-lua.1 ================================================ .\" Automatically generated by Pandoc 3.9.0.2 .\" .TH "pandoc-lua" "1" "September 22, 2022" "pandoc 3.9.0.2" "Pandoc User\[cq]s Guide" .SH SYNOPSIS \f[CR]pandoc\-lua\f[R] [\f[I]options\f[R]] [\f[I]script\f[R] [\f[I]args\f[R]]] .SH DESCRIPTION \f[CR]pandoc\-lua\f[R] is a standalone Lua interpreter with behavior similar to that of the standard \f[CR]lua\f[R] executable, but exposing all of pandoc\(cqs Lua libraries. All \f[CR]pandoc.*\f[R] packages, as well as the packages \f[CR]re\f[R] and \f[CR]lpeg\f[R], are available via global variables. Furthermore, the globals \f[CR]PANDOC_VERSION\f[R], \f[CR]PANDOC_STATE\f[R], and \f[CR]PANDOC_API_VERSION\f[R] are set at startup. .PP If no script argument is given, then the script is assumed to be passed in via \f[I]stdin\f[R]. When called without arguments, \f[CR]pandoc\-lua\f[R] behaves as \f[CR]pandoc\-lua \-v \-i\f[R] when the standard input (\f[CR]stdin\f[R]) is a terminal, and as \f[CR]pandoc\-lua \-\f[R] otherwise. On Windows the program will always behave as if it was connected to a terminal. .PP When called without the option \f[CR]\-E\f[R], the interpreter checks for an environment variable \f[CR]LUA_INIT\f[R] before running any argument. If the variable content has the format \f[I]\f[CI]\(atfilename\f[I]\f[R], then \f[CR]pandoc\-lua\f[R] executes the file. Otherwise, \f[CR]pandoc\-lua\f[R] executes the string itself. .SH OPTIONS .TP \f[CR]\-e stat\f[R] Execute statement \f[CR]stat\f[R]. .TP \f[CR]\-l mod\f[R] If mod has the pattern \f[CR]g=m\f[R], then require library \f[CR]m\f[R] into global \f[CR]g\f[R]; otherwise require library \f[CR]mod\f[R] into global \f[CR]mod\f[R]. .TP \f[CR]\-v\f[R] Show version information. .TP \f[CR]\-i\f[R] Enter interactive mode after running \f[I]script\f[R]. .TP \f[CR]\-E\f[R] Ignore environment variables. This is not fully implemented yet and only ignores the \f[CR]LUA_INIT\f[R] variable. Other variables like \f[CR]LUA_PATH\f[R] and \f[CR]LUA_CPATH\f[R] are \f[B]not\f[R] ignored. .TP \f[CR]\-W\f[R] Turn warnings on. .SH INTERACTIVE MODE In interactive mode, the Lua interpreter repeatedly prompts and waits for a line. After reading a line, Lua first tries to interpret the line as an expression. If it succeeds, it prints its value. Otherwise, it interprets the line as a statement. If you write an incomplete statement, the interpreter waits for its completion by issuing a different prompt. .PP Exit the interactive mode by pressing \f[CR]Ctrl\-D\f[R] or \f[CR]Ctrl\-C\f[R], or by typing \f[CR]os.exit()\f[R]. The \f[I]Isocline\f[R] library is used for line editing. Press \f[CR]F1\f[R] to get a list of available keybindings; the \f[CR]ctrl\f[R] key is abbreviated as \f[CR]\(ha\f[R] in that list. .SH AUTHORS Copyright 2023 John MacFarlane (jgm\(atberkeley.edu) and contributors. Released under the GPL, version 2 or later. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) .PP Lua: Copyright 1994\-2023 Lua.org, PUC\-Rio. .PP The Pandoc source code may be downloaded from or . Further documentation is available at . ================================================ FILE: pandoc-cli/man/pandoc-server.1 ================================================ .\" Automatically generated by Pandoc 3.9.0.2 .\" .TH "pandoc-server" "1" "August 15, 2022" "pandoc 3.9.0.2" "Pandoc User\[cq]s Guide" .SH SYNOPSIS \f[CR]pandoc\-server\f[R] [\f[I]options\f[R]] .SH DESCRIPTION \f[CR]pandoc\-server\f[R] is a web server that can perform pandoc conversions. It can be used either as a running server or as a CGI program. .PP To use \f[CR]pandoc\-server\f[R] as a CGI program, rename it (or symlink it) as \f[CR]pandoc\-server.cgi\f[R]. (Note: if you symlink it, you may need to adjust your webserver\(cqs configuration in order to allow it to follow symlinks for the CGI script.) .PP All pandoc functions are run in the PandocPure monad, which ensures that they can do no I/O operations on the server. This should provide a high degree of security. This security does, however, impose certain limitations: .IP \(bu 2 PDFs cannot be produced. .IP \(bu 2 Filters are not supported. .IP \(bu 2 Resources cannot be fetched via HTTP. .IP \(bu 2 Any images, include files, or other resources needed for the document conversion must be explicitly included in the request, via the \f[CR]files\f[R] field (see below under API). .SH OPTIONS .TP \f[CR]\-\-port NUM\f[R] HTTP port on which to run the server. Default: 3030. .TP \f[CR]\-\-timeout SECONDS\f[R] Timeout in seconds, after which a conversion is killed. Default: 2. .RS When \f[CR]pandoc\-server\f[R] is run as a CGI program, this option can be set via the \f[CR]PANDOC_SERVER_TIMEOUT\f[R] environment variable. .RE .TP \f[CR]\-\-help\f[R] Print this help. .TP \f[CR]\-\-version\f[R] Print version. .SH API .SS Root endpoint The root (\f[CR]/\f[R]) endpoint accepts only POST requests. .SS Response It returns a converted document in one of the following formats (in order of preference), depending on the \f[CR]Accept\f[R] header: .IP \(bu 2 \f[CR]application/octet\-stream\f[R] .IP \(bu 2 \f[CR]text/plain\f[R] .IP \(bu 2 \f[CR]application/json\f[R] .PP If the result is a binary format (e.g., \f[CR]epub\f[R] or \f[CR]docx\f[R]) and the content is returned as plain text or JSON, the binary will be base64 encoded. .PP If a JSON response is given, it will have one of the following formats. If the conversion is not successful: .IP .EX { \(dqerror\(dq: string with the error message } .EE .PP If the conversion is successful: .IP .EX { \(dqoutput\(dq: string with textual or base64\-encoded binary output, \(dqbase64\(dq: boolean (true means the \(dqoutput\(dq is base64\-encoded), \(dqmessages\(dq: array of message objects (see below) } .EE .PP Each element of the \(lqmessages\(rq array will have the format .IP .EX { \(dqmessage\(dq: string, \(dqverbosity\(dq: string (either \(dqWARNING\(dq or \(dqINFO\(dq) } .EE .SS Request The body of the POST request should be a JSON object, with the following fields. Only the \f[CR]text\f[R] field is required; all of the others can be omitted for default values. When there are several string alternatives, the first one given is the default. .TP \f[CR]text\f[R] (string) The document to be converted. Note: if the \f[CR]from\f[R] format is binary (e.g., \f[CR]epub\f[R] or \f[CR]docx\f[R]), then \f[CR]text\f[R] should be a base64 encoding of the document. .TP \f[CR]from\f[R] (string, default \f[CR]\(dqmarkdown\(dq\f[R]) The input format, possibly with extensions, just as it is specified on the pandoc command line. .TP \f[CR]to\f[R] (string, default \f[CR]\(dqhtml\(dq\f[R]) The output format, possibly with extensions, just as it is specified on the pandoc command line. .TP \f[CR]shift\-heading\-level\-by\f[R] (integer, default 0) Increase or decrease the level of all headings. .TP \f[CR]indented\-code\-classes\f[R] (array of strings) List of classes to be applied to indented Markdown code blocks. .TP \f[CR]default\-image\-extension\f[R] (string) Extension to be applied to image sources that lack extensions (e.g.\ \f[CR]\(dq.jpg\(dq\f[R]). .TP \f[CR]metadata\f[R] (JSON map) String\-valued metadata. .TP \f[CR]tab\-stop\f[R] (integer, default 4) Tab stop (spaces per tab). .TP \f[CR]track\-changes\f[R] (\f[CR]\(dqaccept\(dq|\(dqreject\(dq|\(dqall\(dq\f[R]) Specifies what to do with insertions, deletions, and comments produced by the MS Word \(lqTrack Changes\(rq feature. Only affects docx input. .TP \f[CR]abbreviations\f[R] (file path) List of strings to be regarded as abbreviations when parsing Markdown. See \f[CR]\-\-abbreviations\f[R] in \f[CR]pandoc(1)\f[R] for details. .TP \f[CR]standalone\f[R] (boolean, default false) If true, causes a standalone document to be produced, using the default template or the custom template specified using \f[CR]template\f[R]. If false, a fragment will be produced. .TP \f[CR]template\f[R] (string) String contents of a document template (see Templates in \f[CR]pandoc(1)\f[R] for the format). .TP \f[CR]variables\f[R] (JSON map) Variables to be interpolated in the template. (See Templates in \f[CR]pandoc(1)\f[R].) .TP \f[CR]dpi\f[R] (integer, default 96) Dots\-per\-inch to use for conversions between pixels and other measurements (for image sizes). .TP \f[CR]wrap\f[R] (\f[CR]\(dqauto\(dq|\(dqpreserve\(dq|\(dqnone\(dq\f[R]) Text wrapping option: either \f[CR]\(dqauto\(dq\f[R] (automatic hard\-wrapping to fit within a column width), \f[CR]\(dqpreserve\(dq\f[R] (insert newlines where they are present in the source), or \f[CR]\(dqnone\(dq\f[R] (don\(cqt insert any unnecessary newlines at all). .TP \f[CR]columns\f[R] (integer, default 72) Column width (affects text wrapping and calculation of table column widths in plain text formats) .TP \f[CR]table\-of\-contents\f[R] (boolean, default false) Include a table of contents (in supported formats). .TP \f[CR]toc\-depth\f[R] (integer, default 3) Depth of sections to include in the table of contents. .TP \f[CR]list\-of\-figures\f[R] (boolean, default false) Include a list of figures (in supported formats). .TP \f[CR]list\-of\-tables\f[R] (boolean, default false) Include a list of tables (in supported formats). .TP \f[CR]strip\-comments\f[R] (boolean, default false) Causes HTML comments to be stripped in Markdown or Textile source, instead of being passed through to the output format. .TP \f[CR]syntax\-highlighting\f[R] (\f[CR]\(dqdefault\(dq|\(dqnone\(dq|\(dqidiomatic\(dq|style\f[R]) The method used for code syntax highlighting. Setting a specific \f[I]style\f[R] causes highlighting to be performed with the internal highlighting engine, using KDE syntax definitions and styles. The \f[CR]\(dqidiomatic\(dq\f[R] 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 \f[CR]none\f[R] disables all syntax highlighting. The \f[CR]\(dqdefault\(dq\f[R] method uses a format\-specific default. .RS .PP Standard styles are \f[CR]\(dqpygments\(dq\f[R] (the default), \f[CR]\(dqkate\(dq\f[R], \f[CR]\(dqmonochrome\(dq\f[R], \f[CR]\(dqbreezeDark\(dq\f[R], \f[CR]\(dqespresso\(dq\f[R], \f[CR]\(dqzenburn\(dq\f[R], \f[CR]\(dqhaddock\(dq\f[R], and \f[CR]\(dqtango\(dq\f[R]. Alternatively, the path of a \f[CR].theme\f[R] with a KDE syntax theme may be used (in this case, the relevant file contents must also be included in \f[CR]files\f[R], see below). .PP 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\(cqs own syntax highlighting system by default. .RE .TP \f[CR]embed\-resources\f[R] Embed images, scripts, styles and other resources in an HTML document using \f[CR]data\f[R] URIs. Note that this will not work unless the contents of all external resources are included under \f[CR]files\f[R]. .TP \f[CR]html\-q\-tags\f[R] (boolean, default false) Use \f[CR]\f[R] elements in HTML instead of literal quotation marks. .TP \f[CR]ascii\f[R] (boolean, default false) Use entities and escapes when possible to avoid non\-ASCII characters in the output. .TP \f[CR]reference\-links\f[R] (boolean, default false) Create reference links rather than inline links in Markdown output. .TP \f[CR]reference\-location\f[R] (\f[CR]\(dqdocument\(dq|\(dqsection\(dq|\(dqblock\(dq\f[R]) Determines whether link references and footnotes are placed at the end of the document, the end of the section, or the end of the block (e.g.\ paragraph), in certain formats. (See \f[CR]pandoc(1)\f[R] under \f[CR]\-\-reference\-location\f[R].) .TP \f[CR]setext\-headers\f[R] (boolean, default false) Use Setext (underlined) headings instead of ATX (\f[CR]#\f[R]\-prefixed) in Markdown output. .TP \f[CR]top\-level\-division\f[R] (\f[CR]\(dqdefault\(dq|\(dqpart\(dq|\(dqchapter\(dq|\(dqsection\(dq\f[R]) Determines how top\-level headings are interpreted in LaTeX, ConTeXt, DocBook, and TEI. The \f[CR]\(dqdefault\(dq\f[R] value tries to choose the best interpretation based on heuristics. .TP \f[CR]number\-sections\f[R] (boolean, default false) Automatically number sections (in supported formats). .TP \f[CR]number\-offset\f[R] (array of integers) Offsets to be added to each component of the section number. For example, \f[CR][1]\f[R] will cause the first section to be numbered \(lq2\(rq and the first subsection \(lq2.1\(rq; \f[CR][0,1]\f[R] will cause the first section to be numbered \(lq1\(rq and the first subsection \(lq1.2.\(rq .TP \f[CR]html\-math\-method\f[R] (\f[CR]\(dqplain\(dq|\(dqwebtex\(dq|\(dqgladtex\(dq|\(dqmathml\(dq|\(dqmathjax\(dq|\(dqkatex\(dq\f[R]) Determines how math is represented in HTML. .TP \f[CR]listings\f[R] (boolean, default false) Use the \f[CR]listings\f[R] package to format code in LaTeX output. .TP \f[CR]incremental\f[R] (boolean, default false) If true, lists appear incrementally by default in slide shows. .TP \f[CR]slide\-level\f[R] (integer) Heading level that deterimes slide divisions in slide shows. The default is to pick the highest heading level under which there is body text. .TP \f[CR]section\-divs\f[R] (boolean, default false) Arrange the document into a hierarchy of nested sections based on the headings. .TP \f[CR]email\-obfuscation\f[R] (\f[CR]\(dqnone\(dq|\(dqreferences\(dq|\(dqjavascript\(dq\f[R]) Determines how email addresses are obfuscated in HTML. .TP \f[CR]identifier\-prefix\f[R] (string) Prefix to be added to all automatically\-generated identifiers. .TP \f[CR]title\-prefix\f[R] (string) Prefix to be added to the title in the HTML header. .TP \f[CR]reference\-doc\f[R] (file path) Reference doc to use in creating \f[CR]docx\f[R] or \f[CR]odt\f[R] or \f[CR]pptx\f[R]. See \f[CR]pandoc(1)\f[R] under \f[CR]\-\-reference\-doc\f[R] for details. The contents of the file must be included under \f[CR]files\f[R]. .TP \f[CR]split\-level\f[R] (integer, default 1) Heading level at which documents are split in EPUB or chunked HTML. .TP \f[CR]epub\-cover\-image\f[R] (file path) Cover image for EPUB. The contents of the file must be included under \f[CR]files\f[R]. .TP \f[CR]epub\-metadata\f[R] (file path) Path of file containing Dublin core XML elements to be used for EPUB metadata. The contents of the file must be included under \f[CR]files\f[R]. .TP \f[CR]epub\-subdirectory\f[R] (string, default \(lqEPUB\(rq) Name of content subdirectory in the EPUB container. .TP \f[CR]epub\-fonts\f[R] (array of file paths) Fonts to include in the EPUB. The fonts themselves must be included in \f[CR]files\f[R] (see below). .TP \f[CR]ipynb\-output\f[R] (\f[CR]\(dqbest\(dq|\(dqall\(dq|\(dqnone\(dq\f[R]) Determines how ipynb output cells are treated. \f[CR]all\f[R] means that all of the data formats included in the original are preserved. \f[CR]none\f[R] means that the contents of data cells are omitted. \f[CR]best\f[R] causes pandoc to try to pick the richest data block in each output cell that is compatible with the output format. .TP \f[CR]citeproc\f[R] (boolean, default false) Causes citations to be processed using citeproc. See Citations in \f[CR]pandoc(1)\f[R] for details. .TP \f[CR]bibliography\f[R] (array of file paths) Files containing bibliographic data. The contents of the files must be included in \f[CR]files\f[R]. .TP \f[CR]csl\f[R] (file path) CSL style file. The contents of the file must be included in \f[CR]files\f[R]. .TP \f[CR]cite\-method\f[R] (\f[CR]\(dqciteproc\(dq|\(dqnatbib\(dq|\(dqbiblatex\(dq\f[R]) Determines how citations are formatted in LaTeX output. .TP \f[CR]files\f[R] (JSON mapping of file paths to base64\-encoded strings) Any files needed for the conversion, including images referred to in the document source, should be included here. Binary data must be base64\-encoded. Textual data may be left as it is, unless it is \f[I]also\f[R] valid base 64 data, in which case it will be interpreted that way. .SS \f[CR]/batch\f[R] endpoint The \f[CR]/batch\f[R] endpoint behaves like the root endpoint, except for these two points: .IP \(bu 2 It accepts a JSON array, each element of which is a JSON object like the one expected by the root endpoint. .IP \(bu 2 It returns a JSON array of JSON results. .PP This endpoint can be used to convert a sequence of small snippets in one request. .SS \f[CR]/version\f[R] endpoint The \f[CR]/version\f[R] endpoint accepts a GET request and returns the pandoc version as a plain or JSON\-encoded string, depending on Accept headers. .SS \f[CR]/babelmark\f[R] endpoint The \f[CR]/babelmark\f[R] endpoint accepts a GET request with the following query parameters: .IP \(bu 2 \f[CR]text\f[R] (required string) .IP \(bu 2 \f[CR]from\f[R] (optional string, default is \f[CR]\(dqmarkdown\(dq\f[R]) .IP \(bu 2 \f[CR]to\f[R] (optional string, default is \f[CR]\(dqhtml\(dq\f[R]) .IP \(bu 2 \f[CR]standalone\f[R] (optional boolean, default is \f[CR]false\f[R]) .PP It returns a JSON object with fields \f[CR]html\f[R] and \f[CR]version\f[R]. This endpoint is designed to support the Babelmark website. .SH AUTHORS Copyright 2022 John MacFarlane (jgm\(atberkeley.edu). Released under the GPL, version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) .PP The Pandoc source code may be downloaded from or . Further documentation is available at . ================================================ FILE: pandoc-cli/man/pandoc.1 ================================================ .\" Automatically generated by Pandoc 3.9.0.2 .\" .TH "pandoc" "1" "2026\-03\-19" "pandoc 3.9.0.2" "Pandoc User\[cq]s Guide" .SH NAME pandoc - general markup converter .SH SYNOPSIS \f[CR]pandoc\f[R] [\f[I]options\f[R]] [\f[I]input\-file\f[R]]\&... .SH DESCRIPTION Pandoc is a Haskell library for converting from one markup format to another, and a command\-line tool that uses this library. .PP Pandoc can convert between numerous markup and word processing formats, including, but not limited to, various flavors of Markdown, HTML, LaTeX and Word docx. For the full lists of input and output formats, see the \f[CR]\-\-from\f[R] and \f[CR]\-\-to\f[R] options below. Pandoc can also produce PDF output: see creating a PDF, below. .PP Pandoc\(cqs enhanced version of Markdown includes syntax for tables, definition lists, metadata blocks, footnotes, citations, math, and much more. See below under Pandoc\(cqs Markdown. .PP Pandoc has a modular design: it consists of a set of readers, which parse text in a given format and produce a native representation of the document (an \f[I]abstract syntax tree\f[R] or AST), and a set of writers, which convert this native representation into a target format. Thus, adding an input or output format requires only adding a reader or writer. Users can also run custom pandoc filters to modify the intermediate AST. .PP Because pandoc\(cqs intermediate representation of a document is less expressive than many of the formats it converts between, one should not expect perfect conversions between every format and every other. Pandoc attempts to preserve the structural elements of a document, but not formatting details such as margin size. And some document elements, such as complex tables, may not fit into pandoc\(cqs simple document model. While conversions from pandoc\(cqs Markdown to all formats aspire to be perfect, conversions from formats more expressive than pandoc\(cqs Markdown can be expected to be lossy. .SS Using pandoc If no \f[I]input\-files\f[R] are specified, input is read from \f[I]stdin\f[R]. Output goes to \f[I]stdout\f[R] by default. For output to a file, use the \f[CR]\-o\f[R]/\f[CR]\-\-output\f[R] option: .IP .EX pandoc \-o output.html input.txt .EE .PP By default, pandoc produces a document fragment. To produce a standalone document (e.g.\ a valid HTML file including \f[CR]\f[R] and \f[CR]\f[R]), use the \f[CR]\-s\f[R] or \f[CR]\-\-standalone\f[R] flag: .IP .EX pandoc \-s \-o output.html input.txt .EE .PP For more information on how standalone documents are produced, see Templates below. .PP If multiple input files are given, pandoc will concatenate them all (with blank lines between them) before parsing. (Use \f[CR]\-\-file\-scope\f[R] to parse files individually.) .SS Specifying formats The format of the input and output can be specified explicitly using command\-line options. The input format can be specified using the \f[CR]\-f/\-\-from\f[R] option, the output format using the \f[CR]\-t/\-\-to\f[R] option. Thus, to convert \f[CR]hello.txt\f[R] from Markdown to LaTeX, you could type: .IP .EX pandoc \-f markdown \-t latex hello.txt .EE .PP To convert \f[CR]hello.html\f[R] from HTML to Markdown: .IP .EX pandoc \-f html \-t markdown hello.html .EE .PP Supported input and output formats are listed below under Options (see \f[CR]\-f\f[R] for input formats and \f[CR]\-t\f[R] for output formats). You can also use \f[CR]pandoc \-\-list\-input\-formats\f[R] and \f[CR]pandoc \-\-list\-output\-formats\f[R] to print lists of supported formats. .PP If the input or output format is not specified explicitly, pandoc will attempt to guess it from the extensions of the filenames. Thus, for example, .IP .EX pandoc \-o hello.tex hello.txt .EE .PP will convert \f[CR]hello.txt\f[R] from Markdown to LaTeX. If no output file is specified (so that output goes to \f[I]stdout\f[R]), or if the output file\(cqs extension is unknown, the output format will default to HTML. If no input file is specified (so that input comes from \f[I]stdin\f[R]), or if the input files\(cq extensions are unknown, the input format will be assumed to be Markdown. .SS Character encoding Pandoc uses the UTF\-8 character encoding for both input and output. If your local character encoding is not UTF\-8, you should pipe input and output through \f[CR]iconv\f[R]: .IP .EX iconv \-t utf\-8 input.txt | pandoc | iconv \-f utf\-8 .EE .PP Note that in some output formats (such as HTML, LaTeX, ConTeXt, RTF, OPML, DocBook, and Texinfo), information about the character encoding is included in the document header, which will only be included if you use the \f[CR]\-s/\-\-standalone\f[R] option. .SS Creating a PDF To produce a PDF, specify an output file with a \f[CR].pdf\f[R] extension: .IP .EX pandoc test.txt \-o test.pdf .EE .PP By default, pandoc will use LaTeX to create the PDF, which requires that a LaTeX engine be installed (see \f[CR]\-\-pdf\-engine\f[R] below). Alternatively, pandoc can use ConTeXt, roff ms, or HTML as an intermediate format. To do this, specify an output file with a \f[CR].pdf\f[R] extension, as before, but add the \f[CR]\-\-pdf\-engine\f[R] option or \f[CR]\-t context\f[R], \f[CR]\-t html\f[R], or \f[CR]\-t ms\f[R] to the command line. The tool used to generate the PDF from the intermediate format may be specified using \f[CR]\-\-pdf\-engine\f[R]. .PP You can control the PDF style using variables, depending on the intermediate format used: see variables for LaTeX, variables for ConTeXt, variables for \f[CR]wkhtmltopdf\f[R], variables for ms. When HTML is used as an intermediate format, the output can be styled using \f[CR]\-\-css\f[R]. .PP To debug the PDF creation, it can be useful to look at the intermediate representation: instead of \f[CR]\-o test.pdf\f[R], use for example \f[CR]\-s \-o test.tex\f[R] to output the generated LaTeX. You can then test it with \f[CR]pdflatex test.tex\f[R]. .PP When using LaTeX, the following packages need to be available (they are included with all recent versions of TeX Live): \f[CR]amsfonts\f[R], \f[CR]amsmath\f[R], \f[CR]lm\f[R], \f[CR]unicode\-math\f[R], \f[CR]iftex\f[R], \f[CR]listings\f[R] (if the \f[CR]\-\-listings\f[R] option is used), \f[CR]fancyvrb\f[R], \f[CR]longtable\f[R], \f[CR]booktabs\f[R], \f[CR]multirow\f[R] (if the document contains a table with cells that cross multiple rows), \f[CR]graphicx\f[R] (if the document contains images), \f[CR]bookmark\f[R], \f[CR]xcolor\f[R], \f[CR]soul\f[R], \f[CR]geometry\f[R] (with the \f[CR]geometry\f[R] variable set), \f[CR]setspace\f[R] (with \f[CR]linestretch\f[R]), and \f[CR]babel\f[R] (with \f[CR]lang\f[R]). If \f[CR]CJKmainfont\f[R] is set, \f[CR]xeCJK\f[R] is needed if \f[CR]xelatex\f[R] is used, else \f[CR]luatexja\f[R] is needed if \f[CR]lualatex\f[R] is used. \f[CR]framed\f[R] is required if code is highlighted in a scheme that use a colored background. The use of \f[CR]xelatex\f[R] or \f[CR]lualatex\f[R] as the PDF engine requires \f[CR]fontspec\f[R]. \f[CR]lualatex\f[R] uses \f[CR]selnolig\f[R] and \f[CR]lua\-ul\f[R]. \f[CR]xelatex\f[R] uses \f[CR]bidi\f[R] (with the \f[CR]dir\f[R] variable set). If the \f[CR]mathspec\f[R] variable is set, \f[CR]xelatex\f[R] will use \f[CR]mathspec\f[R] instead of \f[CR]unicode\-math\f[R]. The \f[CR]csquotes\f[R] package will be used for typography if the \f[CR]csquotes\f[R] variable or metadata field is set to a true value. The \f[CR]natbib\f[R], \f[CR]biblatex\f[R], \f[CR]bibtex\f[R], and \f[CR]biber\f[R] packages can optionally be used for citation rendering. If math with \f[CR]\(rscancel\f[R], \f[CR]\(rsbcancel\f[R], or \f[CR]\(rsxcancel\f[R] is used, the \f[CR]cancel\f[R] package is needed. The following packages will be used to improve output quality if present, but pandoc does not require them to be present: \f[CR]upquote\f[R] (for straight quotes in verbatim environments), \f[CR]microtype\f[R] (for better spacing adjustments), \f[CR]parskip\f[R] (for better inter\-paragraph spaces), \f[CR]xurl\f[R] (for better line breaks in URLs), and \f[CR]footnotehyper\f[R] or \f[CR]footnote\f[R] (to allow footnotes in tables). .SS Reading from the Web Instead of an input file, an absolute URI may be given. In this case pandoc will fetch the content using HTTP: .IP .EX pandoc \-f html \-t markdown https://www.fsf.org .EE .PP It is possible to supply a custom User\-Agent string or other header when requesting a document from a URL: .IP .EX pandoc \-f html \-t markdown \-\-request\-header User\-Agent:\(dqMozilla/5.0\(dq \(rs https://www.fsf.org .EE .SH OPTIONS .SS General options .TP \f[CR]\-f\f[R] \f[I]FORMAT\f[R], \f[CR]\-r\f[R] \f[I]FORMAT\f[R], \f[CR]\-\-from=\f[R]\f[I]FORMAT\f[R], \f[CR]\-\-read=\f[R]\f[I]FORMAT\f[R] Specify input format. \f[I]FORMAT\f[R] can be: .RS .IP \(bu 2 \f[CR]asciidoc\f[R] (AsciiDoc markup) .IP \(bu 2 \f[CR]bibtex\f[R] (BibTeX bibliography) .IP \(bu 2 \f[CR]biblatex\f[R] (BibLaTeX bibliography) .IP \(bu 2 \f[CR]bits\f[R] (BITS XML, alias for \f[CR]jats\f[R]) .IP \(bu 2 \f[CR]commonmark\f[R] (CommonMark Markdown) .IP \(bu 2 \f[CR]commonmark_x\f[R] (CommonMark Markdown with extensions) .IP \(bu 2 \f[CR]creole\f[R] (Creole 1.0) .IP \(bu 2 \f[CR]csljson\f[R] (CSL JSON bibliography) .IP \(bu 2 \f[CR]csv\f[R] (CSV table) .IP \(bu 2 \f[CR]tsv\f[R] (TSV table) .IP \(bu 2 \f[CR]djot\f[R] (Djot markup) .IP \(bu 2 \f[CR]docbook\f[R] (DocBook) .IP \(bu 2 \f[CR]docx\f[R] (Word docx) .IP \(bu 2 \f[CR]dokuwiki\f[R] (DokuWiki markup) .IP \(bu 2 \f[CR]endnotexml\f[R] (EndNote XML bibliography) .IP \(bu 2 \f[CR]epub\f[R] (EPUB) .IP \(bu 2 \f[CR]fb2\f[R] (FictionBook2 e\-book) .IP \(bu 2 \f[CR]gfm\f[R] (GitHub\-Flavored Markdown), or the deprecated and less accurate \f[CR]markdown_github\f[R]; use \f[CR]markdown_github\f[R] only if you need extensions not supported in \f[CR]gfm\f[R]. .IP \(bu 2 \f[CR]haddock\f[R] (Haddock markup) .IP \(bu 2 \f[CR]html\f[R] (HTML) .IP \(bu 2 \f[CR]ipynb\f[R] (Jupyter notebook) .IP \(bu 2 \f[CR]jats\f[R] (JATS XML) .IP \(bu 2 \f[CR]jira\f[R] (Jira/Confluence wiki markup) .IP \(bu 2 \f[CR]json\f[R] (JSON version of native AST) .IP \(bu 2 \f[CR]latex\f[R] (LaTeX) .IP \(bu 2 \f[CR]markdown\f[R] (Pandoc\(cqs Markdown) .IP \(bu 2 \f[CR]markdown_mmd\f[R] (MultiMarkdown) .IP \(bu 2 \f[CR]markdown_phpextra\f[R] (PHP Markdown Extra) .IP \(bu 2 \f[CR]markdown_strict\f[R] (original unextended Markdown) .IP \(bu 2 \f[CR]mediawiki\f[R] (MediaWiki markup) .IP \(bu 2 \f[CR]man\f[R] (roff man) .IP \(bu 2 \f[CR]mdoc\f[R] (mdoc manual page markup) .IP \(bu 2 \f[CR]muse\f[R] (Muse) .IP \(bu 2 \f[CR]native\f[R] (native Haskell) .IP \(bu 2 \f[CR]odt\f[R] (OpenDocument text document) .IP \(bu 2 \f[CR]opml\f[R] (OPML) .IP \(bu 2 \f[CR]org\f[R] (Emacs Org mode) .IP \(bu 2 \f[CR]pod\f[R] (Perl\(cqs Plain Old Documentation) .IP \(bu 2 \f[CR]pptx\f[R] (PowerPoint) .IP \(bu 2 \f[CR]ris\f[R] (RIS bibliography) .IP \(bu 2 \f[CR]rtf\f[R] (Rich Text Format) .IP \(bu 2 \f[CR]rst\f[R] (reStructuredText) .IP \(bu 2 \f[CR]t2t\f[R] (txt2tags) .IP \(bu 2 \f[CR]textile\f[R] (Textile) .IP \(bu 2 \f[CR]tikiwiki\f[R] (TikiWiki markup) .IP \(bu 2 \f[CR]twiki\f[R] (TWiki markup) .IP \(bu 2 \f[CR]typst\f[R] (typst) .IP \(bu 2 \f[CR]vimwiki\f[R] (Vimwiki) .IP \(bu 2 \f[CR]xlsx\f[R] (Excel spreadsheet) .IP \(bu 2 \f[CR]xml\f[R] (XML version of native AST) .IP \(bu 2 the path of a custom Lua reader, see Custom readers and writers below .PP Extensions can be individually enabled or disabled by appending \f[CR]+EXTENSION\f[R] or \f[CR]\-EXTENSION\f[R] to the format name. See Extensions below, for a list of extensions and their names. See \f[CR]\-\-list\-input\-formats\f[R] and \f[CR]\-\-list\-extensions\f[R], below. .RE .TP \f[CR]\-t\f[R] \f[I]FORMAT\f[R], \f[CR]\-w\f[R] \f[I]FORMAT\f[R], \f[CR]\-\-to=\f[R]\f[I]FORMAT\f[R], \f[CR]\-\-write=\f[R]\f[I]FORMAT\f[R] Specify output format. \f[I]FORMAT\f[R] can be: .RS .IP \(bu 2 \f[CR]ansi\f[R] (text with ANSI escape codes, for terminal viewing) .IP \(bu 2 \f[CR]asciidoc\f[R] (modern AsciiDoc as interpreted by AsciiDoctor) .IP \(bu 2 \f[CR]asciidoc_legacy\f[R] (AsciiDoc as interpreted by \f[CR]asciidoc\-py\f[R]). .IP \(bu 2 \f[CR]asciidoctor\f[R] (deprecated synonym for \f[CR]asciidoc\f[R]) .IP \(bu 2 \f[CR]bbcode\f[R] BBCode .IP \(bu 2 \f[CR]bbcode_fluxbb\f[R] BBCode (FluxBB) .IP \(bu 2 \f[CR]bbcode_phpbb\f[R] BBCode (phpBB) .IP \(bu 2 \f[CR]bbcode_steam\f[R] BBCode (Steam) .IP \(bu 2 \f[CR]bbcode_hubzilla\f[R] BBCode (Hubzilla) .IP \(bu 2 \f[CR]bbcode_xenforo\f[R] BBCode (xenForo) .IP \(bu 2 \f[CR]beamer\f[R] (LaTeX beamer slide show) .IP \(bu 2 \f[CR]bibtex\f[R] (BibTeX bibliography) .IP \(bu 2 \f[CR]biblatex\f[R] (BibLaTeX bibliography) .IP \(bu 2 \f[CR]chunkedhtml\f[R] (zip archive of multiple linked HTML files) .IP \(bu 2 \f[CR]commonmark\f[R] (CommonMark Markdown) .IP \(bu 2 \f[CR]commonmark_x\f[R] (CommonMark Markdown with extensions) .IP \(bu 2 \f[CR]context\f[R] (ConTeXt) .IP \(bu 2 \f[CR]csljson\f[R] (CSL JSON bibliography) .IP \(bu 2 \f[CR]djot\f[R] (Djot markup) .IP \(bu 2 \f[CR]docbook\f[R] or \f[CR]docbook4\f[R] (DocBook 4) .IP \(bu 2 \f[CR]docbook5\f[R] (DocBook 5) .IP \(bu 2 \f[CR]docx\f[R] (Word docx) .IP \(bu 2 \f[CR]dokuwiki\f[R] (DokuWiki markup) .IP \(bu 2 \f[CR]epub\f[R] or \f[CR]epub3\f[R] (EPUB v3 book) .IP \(bu 2 \f[CR]epub2\f[R] (EPUB v2) .IP \(bu 2 \f[CR]fb2\f[R] (FictionBook2 e\-book) .IP \(bu 2 \f[CR]gfm\f[R] (GitHub\-Flavored Markdown), or the deprecated and less accurate \f[CR]markdown_github\f[R]; use \f[CR]markdown_github\f[R] only if you need extensions not supported in \f[CR]gfm\f[R]. .IP \(bu 2 \f[CR]haddock\f[R] (Haddock markup) .IP \(bu 2 \f[CR]html\f[R] or \f[CR]html5\f[R] (HTML, i.e.\ HTML5/XHTML polyglot markup) .IP \(bu 2 \f[CR]html4\f[R] (XHTML 1.0 Transitional) .IP \(bu 2 \f[CR]icml\f[R] (InDesign ICML) .IP \(bu 2 \f[CR]ipynb\f[R] (Jupyter notebook) .IP \(bu 2 \f[CR]jats_archiving\f[R] (JATS XML, Archiving and Interchange Tag Set) .IP \(bu 2 \f[CR]jats_articleauthoring\f[R] (JATS XML, Article Authoring Tag Set) .IP \(bu 2 \f[CR]jats_publishing\f[R] (JATS XML, Journal Publishing Tag Set) .IP \(bu 2 \f[CR]jats\f[R] (alias for \f[CR]jats_archiving\f[R]) .IP \(bu 2 \f[CR]jira\f[R] (Jira/Confluence wiki markup) .IP \(bu 2 \f[CR]json\f[R] (JSON version of native AST) .IP \(bu 2 \f[CR]latex\f[R] (LaTeX) .IP \(bu 2 \f[CR]man\f[R] (roff man) .IP \(bu 2 \f[CR]markdown\f[R] (Pandoc\(cqs Markdown) .IP \(bu 2 \f[CR]markdown_mmd\f[R] (MultiMarkdown) .IP \(bu 2 \f[CR]markdown_phpextra\f[R] (PHP Markdown Extra) .IP \(bu 2 \f[CR]markdown_strict\f[R] (original unextended Markdown) .IP \(bu 2 \f[CR]markua\f[R] (Markua) .IP \(bu 2 \f[CR]mediawiki\f[R] (MediaWiki markup) .IP \(bu 2 \f[CR]ms\f[R] (roff ms) .IP \(bu 2 \f[CR]muse\f[R] (Muse) .IP \(bu 2 \f[CR]native\f[R] (native Haskell) .IP \(bu 2 \f[CR]odt\f[R] (OpenDocument text document) .IP \(bu 2 \f[CR]opml\f[R] (OPML) .IP \(bu 2 \f[CR]opendocument\f[R] (OpenDocument XML) .IP \(bu 2 \f[CR]org\f[R] (Emacs Org mode) .IP \(bu 2 \f[CR]pdf\f[R] (PDF) .IP \(bu 2 \f[CR]plain\f[R] (plain text) .IP \(bu 2 \f[CR]pptx\f[R] (PowerPoint slide show) .IP \(bu 2 \f[CR]rst\f[R] (reStructuredText) .IP \(bu 2 \f[CR]rtf\f[R] (Rich Text Format) .IP \(bu 2 \f[CR]texinfo\f[R] (GNU Texinfo) .IP \(bu 2 \f[CR]textile\f[R] (Textile) .IP \(bu 2 \f[CR]slideous\f[R] (Slideous HTML and JavaScript slide show) .IP \(bu 2 \f[CR]slidy\f[R] (Slidy HTML and JavaScript slide show) .IP \(bu 2 \f[CR]dzslides\f[R] (DZSlides HTML5 + JavaScript slide show) .IP \(bu 2 \f[CR]revealjs\f[R] (reveal.js HTML5 + JavaScript slide show) .IP \(bu 2 \f[CR]s5\f[R] (S5 HTML and JavaScript slide show) .IP \(bu 2 \f[CR]tei\f[R] (TEI Simple) .IP \(bu 2 \f[CR]typst\f[R] (typst) .IP \(bu 2 \f[CR]vimdoc\f[R] (Vimdoc) .IP \(bu 2 \f[CR]xml\f[R] (XML version of native AST) .IP \(bu 2 \f[CR]xwiki\f[R] (XWiki markup) .IP \(bu 2 \f[CR]zimwiki\f[R] (ZimWiki markup) .IP \(bu 2 the path of a custom Lua writer, see Custom readers and writers below .PP Note that \f[CR]odt\f[R], \f[CR]docx\f[R], \f[CR]epub\f[R], and \f[CR]pdf\f[R] output will not be directed to \f[I]stdout\f[R] unless forced with \f[CR]\-o \-\f[R]. .PP Extensions can be individually enabled or disabled by appending \f[CR]+EXTENSION\f[R] or \f[CR]\-EXTENSION\f[R] to the format name. See Extensions below, for a list of extensions and their names. See \f[CR]\-\-list\-output\-formats\f[R] and \f[CR]\-\-list\-extensions\f[R], below. .RE .TP \f[CR]\-o\f[R] \f[I]FILE\f[R], \f[CR]\-\-output=\f[R]\f[I]FILE\f[R] Write output to \f[I]FILE\f[R] instead of \f[I]stdout\f[R]. If \f[I]FILE\f[R] is \f[CR]\-\f[R], output will go to \f[I]stdout\f[R], even if a non\-textual format (\f[CR]docx\f[R], \f[CR]odt\f[R], \f[CR]epub2\f[R], \f[CR]epub3\f[R]) is specified. If the output format is \f[CR]chunkedhtml\f[R] and \f[I]FILE\f[R] has no extension, then instead of producing a \f[CR].zip\f[R] file pandoc will create a directory \f[I]FILE\f[R] and unpack the zip archive there (unless \f[I]FILE\f[R] already exists, in which case an error will be raised). .TP \f[CR]\-\-data\-dir=\f[R]\f[I]DIRECTORY\f[R] Specify the user data directory to search for pandoc data files. If this option is not specified, the default user data directory will be used. On *nix and macOS systems this will be the \f[CR]pandoc\f[R] subdirectory of the XDG data directory (by default, \f[CR]$HOME/.local/share\f[R], overridable by setting the \f[CR]XDG_DATA_HOME\f[R] environment variable). If that directory does not exist and \f[CR]$HOME/.pandoc\f[R] exists, it will be used (for backwards compatibility). On Windows the default user data directory is \f[CR]%APPDATA%\(rspandoc\f[R]. You can find the default user data directory on your system by looking at the output of \f[CR]pandoc \-\-version\f[R]. Data files placed in this directory (for example, \f[CR]reference.odt\f[R], \f[CR]reference.docx\f[R], \f[CR]epub.css\f[R], \f[CR]templates\f[R]) will override pandoc\(cqs normal defaults. (Note that the user data directory is not created by pandoc, so you will need to create it yourself if you want to make use of it.) .TP \f[CR]\-d\f[R] \f[I]FILE\f[R], \f[CR]\-\-defaults=\f[R]\f[I]FILE\f[R] Specify a set of default option settings. \f[I]FILE\f[R] 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 \f[CR]defaults\f[R] subdirectory of the user data directory (see \f[CR]\-\-data\-dir\f[R]). The \f[CR].yaml\f[R] extension will be added if \f[I]FILE\f[R] 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. .TP \f[CR]\-\-bash\-completion\f[R] Generate a bash completion script. To enable bash completion with pandoc, add this to your \f[CR].bashrc\f[R]: .RS .IP .EX eval \(dq$(pandoc \-\-bash\-completion)\(dq .EE .RE .TP \f[CR]\-\-verbose\f[R] Give verbose debugging output. .TP \f[CR]\-\-quiet\f[R] Suppress warning messages. .TP \f[CR]\-\-fail\-if\-warnings[=true|false]\f[R] Exit with error status if there are any warnings. .TP \f[CR]\-\-log=\f[R]\f[I]FILE\f[R] Write log messages in machine\-readable JSON format to \f[I]FILE\f[R]. All messages above DEBUG level will be written, regardless of verbosity settings (\f[CR]\-\-verbose\f[R], \f[CR]\-\-quiet\f[R]). .TP \f[CR]\-\-list\-input\-formats\f[R] List supported input formats, one per line. .TP \f[CR]\-\-list\-output\-formats\f[R] List supported output formats, one per line. .TP \f[CR]\-\-list\-extensions\f[R][\f[CR]=\f[R]\f[I]FORMAT\f[R]] List supported extensions for \f[I]FORMAT\f[R], one per line, preceded by a \f[CR]+\f[R] or \f[CR]\-\f[R] indicating whether it is enabled by default in \f[I]FORMAT\f[R]. If \f[I]FORMAT\f[R] is not specified, defaults for pandoc\(cqs Markdown are given. .TP \f[CR]\-\-list\-highlight\-languages\f[R] List supported languages for syntax highlighting, one per line. .TP \f[CR]\-\-list\-highlight\-styles\f[R] List supported styles for syntax highlighting, one per line. See \f[CR]\-\-syntax\-highlighting\f[R]. .TP \f[CR]\-v\f[R], \f[CR]\-\-version\f[R] Print version. .TP \f[CR]\-h\f[R], \f[CR]\-\-help\f[R] Show usage message. .SS Reader options .TP \f[CR]\-\-shift\-heading\-level\-by=\f[R]\f[I]NUMBER\f[R] Shift heading levels by a positive or negative integer. For example, with \f[CR]\-\-shift\-heading\-level\-by=\-1\f[R], level 2 headings become level 1 headings, and level 3 headings become level 2 headings. Headings cannot have a level less than 1, so a heading that would be shifted below level 1 becomes a regular paragraph. Exception: with a shift of \-N, a level\-N heading at the beginning of the document replaces the metadata title. \f[CR]\-\-shift\-heading\-level\-by=\-1\f[R] is a good choice when converting HTML or Markdown documents that use an initial level\-1 heading for the document title and level\-2+ headings for sections. \f[CR]\-\-shift\-heading\-level\-by=1\f[R] may be a good choice for converting Markdown documents that use level\-1 headings for sections to HTML, since pandoc uses a level\-1 heading to render the document title. .TP \f[CR]\-\-base\-header\-level=\f[R]\f[I]NUMBER\f[R] \f[I]Deprecated. Use \f[CI]\-\-shift\-heading\-level\-by\f[I]=X instead, where X = NUMBER \- 1.\f[R] Specify the base level for headings (defaults to 1). .TP \f[CR]\-\-indented\-code\-classes=\f[R]\f[I]CLASSES\f[R] Specify classes to use for indented code blocks\(emfor example, \f[CR]perl,numberLines\f[R] or \f[CR]haskell\f[R]. Multiple classes may be separated by spaces or commas. .TP \f[CR]\-\-default\-image\-extension=\f[R]\f[I]EXTENSION\f[R] Specify a default extension to use when image paths/URLs have no extension. This allows you to use the same source for formats that require different kinds of images. Currently this option only affects the Markdown and LaTeX readers. .TP \f[CR]\-\-file\-scope[=true|false]\f[R] Parse each file individually before combining for multifile documents. This will allow footnotes in different files with the same identifiers to work as expected. If this option is set, footnotes and links will not work across files. Reading binary files (docx, odt, epub) implies \f[CR]\-\-file\-scope\f[R]. .RS .PP If two or more files are processed using \f[CR]\-\-file\-scope\f[R], prefixes based on the filenames will be added to identifiers in order to disambiguate them, and internal links will be adjusted accordingly. For example, a header with identifier \f[CR]foo\f[R] in \f[CR]subdir/file1.txt\f[R] will have its identifier changed to \f[CR]subdir__file1.txt__foo\f[R]. .RE .TP \f[CR]\-F\f[R] \f[I]PROGRAM\f[R], \f[CR]\-\-filter=\f[R]\f[I]PROGRAM\f[R] Specify an executable to be used as a filter transforming the pandoc AST after the input is parsed and before the output is written. The executable should read JSON from stdin and write JSON to stdout. The JSON must be formatted like pandoc\(cqs own JSON input and output. The name of the output format will be passed to the filter as the first argument. Hence, .RS .IP .EX pandoc \-\-filter ./caps.py \-t latex .EE .PP is equivalent to .IP .EX pandoc \-t json | ./caps.py latex | pandoc \-f json \-t latex .EE .PP The latter form may be useful for debugging filters. .PP Filters may be written in any language. \f[CR]Text.Pandoc.JSON\f[R] exports \f[CR]toJSONFilter\f[R] to facilitate writing filters in Haskell. Those who would prefer to write filters in python can use the module \f[CR]pandocfilters\f[R], installable from PyPI. There are also pandoc filter libraries in PHP, perl, and JavaScript/node.js. .PP In order of preference, pandoc will look for filters in .IP "1." 3 a specified full or relative path (executable or non\-executable), .IP "2." 3 \f[CR]$DATADIR/filters\f[R] (executable or non\-executable) where \f[CR]$DATADIR\f[R] is the user data directory (see \f[CR]\-\-data\-dir\f[R], above), .IP "3." 3 \f[CR]$PATH\f[R] (executable only). .PP Filters, Lua\-filters, and citeproc processing are applied in the order specified on the command line. .RE .TP \f[CR]\-L\f[R] \f[I]SCRIPT\f[R], \f[CR]\-\-lua\-filter=\f[R]\f[I]SCRIPT\f[R] Transform the document in a similar fashion as JSON filters (see \f[CR]\-\-filter\f[R]), but use pandoc\(cqs built\-in Lua filtering system. The given Lua script is expected to return a list of Lua filters which will be applied in order. Each Lua filter must contain element\-transforming functions indexed by the name of the AST element on which the filter function should be applied. .RS .PP The \f[CR]pandoc\f[R] Lua module provides helper functions for element creation. It is always loaded into the script\(cqs Lua environment. .PP See the Lua filters documentation for further details. .PP In order of preference, pandoc will look for Lua filters in .IP "1." 3 a specified full or relative path, .IP "2." 3 \f[CR]$DATADIR/filters\f[R] where \f[CR]$DATADIR\f[R] is the user data directory (see \f[CR]\-\-data\-dir\f[R], above). .PP Filters, Lua filters, and citeproc processing are applied in the order specified on the command line. .RE .TP \f[CR]\-M\f[R] \f[I]KEY\f[R][\f[CR]=\f[R]\f[I]VAL\f[R]], \f[CR]\-\-metadata=\f[R]\f[I]KEY\f[R][\f[CR]:\f[R]\f[I]VAL\f[R]] Set the metadata field \f[I]KEY\f[R] to the value \f[I]VAL\f[R]. A value specified on the command line overrides a value specified in the document using YAML metadata blocks. Values will be parsed as YAML boolean or string values. If no value is specified, the value will be treated as Boolean true. Like \f[CR]\-\-variable\f[R], \f[CR]\-\-metadata\f[R] causes template variables to be set. But unlike \f[CR]\-\-variable\f[R], \f[CR]\-\-metadata\f[R] affects the metadata of the underlying document (which is accessible from filters and may be printed in some output formats) and metadata values will be escaped when inserted into the template. .TP \f[CR]\-\-metadata\-file=\f[R]\f[I]FILE\f[R] Read metadata from the supplied YAML (or JSON) file. This option can be used with every input format, but string scalars in the metadata file will always be parsed as Markdown. (If the input format is Markdown or a Markdown variant, then the same variant will be used to parse the metadata file; if it is a non\-Markdown format, pandoc\(cqs default Markdown extensions will be used.) This option can be used repeatedly to include multiple metadata files; values in files specified later on the command line will be preferred over those specified in earlier files. Metadata values specified inside the document, or by using \f[CR]\-M\f[R], overwrite values specified with this option. The file will be searched for first in the working directory, and then in the \f[CR]metadata\f[R] subdirectory of the user data directory (see \f[CR]\-\-data\-dir\f[R]). .TP \f[CR]\-p\f[R], \f[CR]\-\-preserve\-tabs[=true|false]\f[R] Preserve tabs instead of converting them to spaces. (By default, pandoc converts tabs to spaces before parsing its input.) Note that this will only affect tabs in literal code spans and code blocks. Tabs in regular text are always treated as spaces. .TP \f[CR]\-\-tab\-stop=\f[R]\f[I]NUMBER\f[R] Specify the number of spaces per tab (default is 4). .TP \f[CR]\-\-track\-changes=accept\f[R]|\f[CR]reject\f[R]|\f[CR]all\f[R] Specifies what to do with insertions, deletions, and comments produced by the MS Word \(lqTrack Changes\(rq feature. \f[CR]accept\f[R] (the default) processes all the insertions and deletions. \f[CR]reject\f[R] ignores them. Both \f[CR]accept\f[R] and \f[CR]reject\f[R] ignore comments. \f[CR]all\f[R] includes all insertions, deletions, and comments, wrapped in spans with \f[CR]insertion\f[R], \f[CR]deletion\f[R], \f[CR]comment\-start\f[R], and \f[CR]comment\-end\f[R] classes, respectively. The author and time of change is included. \f[CR]all\f[R] is useful for scripting: only accepting changes from a certain reviewer, say, or before a certain date. If a paragraph is inserted or deleted, \f[CR]track\-changes=all\f[R] produces a span with the class \f[CR]paragraph\-insertion\f[R]/\f[CR]paragraph\-deletion\f[R] before the affected paragraph break. This option only affects the docx reader. .TP \f[CR]\-\-extract\-media=\f[R]\f[I]DIR\f[R]|\f[I]FILE\f[R]\f[CR].zip\f[R] Extract images and other media contained in or linked from the source document to the path \f[I]DIR\f[R], creating it if necessary, and adjust the images references in the document so they point to the extracted files. Media are downloaded, read from the file system, or extracted from a binary container (e.g.\ docx), as needed. The original file paths are used if they are relative paths not containing \f[CR]..\f[R]. Otherwise filenames are constructed from the SHA1 hash of the contents. .RS .PP If the path given ends in \f[CR].zip\f[R], then instead of creating a directory, pandoc will create a zip archive containing the media files. .RE .TP \f[CR]\-\-abbreviations=\f[R]\f[I]FILE\f[R] Specifies a custom abbreviations file, with abbreviations one to a line. If this option is not specified, pandoc will read the data file \f[CR]abbreviations\f[R] from the user data directory or fall back on a system default. To see the system default, use \f[CR]pandoc \-\-print\-default\-data\-file=abbreviations\f[R]. The only use pandoc makes of this list is in the Markdown reader. Strings found in this list will be followed by a nonbreaking space, and the period will not produce sentence\-ending space in formats like LaTeX. The strings may not contain spaces. .TP \f[CR]\-\-trace[=true|false]\f[R] Print diagnostic output tracing parser progress to stderr. This option is intended for use by developers in diagnosing performance issues. .SS General writer options .TP \f[CR]\-s\f[R], \f[CR]\-\-standalone\f[R] Produce output with an appropriate header and footer (e.g.\ a standalone HTML, LaTeX, TEI, or RTF file, not a fragment). This option is set automatically for \f[CR]pdf\f[R], \f[CR]epub\f[R], \f[CR]epub3\f[R], \f[CR]fb2\f[R], \f[CR]docx\f[R], and \f[CR]odt\f[R] output. For \f[CR]native\f[R] output, this option causes metadata to be included; otherwise, metadata is suppressed. .TP \f[CR]\-\-template=\f[R]\f[I]FILE\f[R]|\f[I]URL\f[R] Use the specified file as a custom template for the generated document. Implies \f[CR]\-\-standalone\f[R]. See Templates, below, for a description of template syntax. If the template is not found, pandoc will search for it in the \f[CR]templates\f[R] subdirectory of the user data directory (see \f[CR]\-\-data\-dir\f[R]). If no extension is specified and an extensionless template is not found, pandoc will look for a template with an extension corresponding to the writer, so that \f[CR]\-\-template=special\f[R] looks for \f[CR]special.html\f[R] for HTML output. If this option is not used, a default template appropriate for the output format will be used (see \f[CR]\-D/\-\-print\-default\-template\f[R]). .TP \f[CR]\-V\f[R] \f[I]KEY\f[R][\f[CR]=\f[R]\f[I]VAL\f[R]], \f[CR]\-\-variable=\f[R]\f[I]KEY\f[R][\f[CR]=\f[R]\f[I]VAL\f[R]] Set the template variable \f[I]KEY\f[R] to the string value \f[I]VAL\f[R] when rendering the document in standalone mode. Either \f[CR]:\f[R] or \f[CR]=\f[R] may be used to separate \f[I]KEY\f[R] from \f[I]VAL\f[R]. If no \f[I]VAL\f[R] is specified, the key will be given the value \f[CR]true\f[R]. Structured values (lists, maps) cannot be assigned using this option, but they can be assigned in the \f[CR]variables\f[R] section of a defaults file or using the \f[CR]\-\-variable\-json\f[R] option. If the variable already has a \f[I]list\f[R] value, the value will be added to the list. If it already has another kind of value, it will be made into a list containing the previous and the new value. For example, \f[CR]\-V keyword=Joe \-V author=Sue\f[R] makes \f[CR]author\f[R] contain a list of strings: \f[CR]Joe\f[R] and \f[CR]Sue\f[R]. .TP \f[CR]\-\-variable\-json=\f[R]\f[I]KEY\f[R][\f[CR]=\f[R]:\f[I]JSON\f[R]] Set the template variable \f[I]KEY\f[R] to the value specified by a JSON string (this may be a boolean, a string, a list, or a mapping; a number will be treated as a string). For example, \f[CR]\-\-variable\-json foo=false\f[R] will give \f[CR]foo\f[R] the boolean false value, while \f[CR]\-\-variable\-json foo=\(aq\(dqfalse\(dq\(aq\f[R] will give it the string value \f[CR]\(dqfalse\(dq\f[R]. Either \f[CR]:\f[R] or \f[CR]=\f[R] may be used to separate \f[I]KEY\f[R] from \f[I]VAL\f[R]. If the variable already has a value, this value will be replaced. .TP \f[CR]\-\-sandbox[=true|false]\f[R] Run pandoc in a sandbox, limiting IO operations in readers and writers to reading the files specified on the command line. Note that this option does not limit IO operations by filters or in the production of PDF documents. But it does offer security against, for example, disclosure of files through the use of \f[CR]include\f[R] directives. Anyone using pandoc on untrusted user input should use this option. .RS .PP Note: some readers and writers (e.g., \f[CR]docx\f[R]) need access to data files. If these are stored on the file system, then pandoc will not be able to find them when run in \f[CR]\-\-sandbox\f[R] mode and will raise an error. For these applications, we recommend using a pandoc binary compiled with the \f[CR]embed_data_files\f[R] option, which causes the data files to be baked into the binary instead of being stored on the file system. .RE .TP \f[CR]\-D\f[R] \f[I]FORMAT\f[R], \f[CR]\-\-print\-default\-template=\f[R]\f[I]FORMAT\f[R] Print the system default template for an output \f[I]FORMAT\f[R]. (See \f[CR]\-t\f[R] for a list of possible \f[I]FORMAT\f[R]s.) Templates in the user data directory are ignored. This option may be used with \f[CR]\-o\f[R]/\f[CR]\-\-output\f[R] to redirect output to a file, but \f[CR]\-o\f[R]/\f[CR]\-\-output\f[R] must come before \f[CR]\-\-print\-default\-template\f[R] on the command line. .RS .PP Note that some of the default templates use partials, for example \f[CR]styles.html\f[R]. To print the partials, use \f[CR]\-\-print\-default\-data\-file\f[R]: for example, \f[CR]\-\-print\-default\-data\-file=templates/styles.html\f[R]. .RE .TP \f[CR]\-\-print\-default\-data\-file=\f[R]\f[I]FILE\f[R] Print a system default data file. Files in the user data directory are ignored. This option may be used with \f[CR]\-o\f[R]/\f[CR]\-\-output\f[R] to redirect output to a file, but \f[CR]\-o\f[R]/\f[CR]\-\-output\f[R] must come before \f[CR]\-\-print\-default\-data\-file\f[R] on the command line. .TP \f[CR]\-\-eol=crlf\f[R]|\f[CR]lf\f[R]|\f[CR]native\f[R] Manually specify line endings: \f[CR]crlf\f[R] (Windows), \f[CR]lf\f[R] (macOS/Linux/UNIX), or \f[CR]native\f[R] (line endings appropriate to the OS on which pandoc is being run). The default is \f[CR]native\f[R]. .TP \f[CR]\-\-dpi\f[R]=\f[I]NUMBER\f[R] Specify the default dpi (dots per inch) value for conversion from pixels to inch/centimeters and vice versa. (Technically, the correct term would be ppi: pixels per inch.) The default is 96dpi. When images contain information about dpi internally, the encoded value is used instead of the default specified by this option. .TP \f[CR]\-\-wrap=auto\f[R]|\f[CR]none\f[R]|\f[CR]preserve\f[R] Determine how text is wrapped in the output (the source code, not the rendered version). With \f[CR]auto\f[R] (the default), pandoc will attempt to wrap lines to the column width specified by \f[CR]\-\-columns\f[R] (default 72). With \f[CR]none\f[R], pandoc will not wrap lines at all. With \f[CR]preserve\f[R], pandoc will attempt to preserve the wrapping from the source document (that is, where there are nonsemantic newlines in the source, there will be nonsemantic newlines in the output as well). In \f[CR]ipynb\f[R] output, this option affects wrapping of the contents of Markdown cells. .TP \f[CR]\-\-columns=\f[R]\f[I]NUMBER\f[R] Specify length of lines in characters. This affects text wrapping in the generated source code (see \f[CR]\-\-wrap\f[R]). It also affects calculation of column widths for plain text tables (see Tables below). .TP \f[CR]\-\-toc[=true|false]\f[R], \f[CR]\-\-table\-of\-contents[=true|false]\f[R] Include an automatically generated table of contents (or, in the case of \f[CR]latex\f[R], \f[CR]context\f[R], \f[CR]docx\f[R], \f[CR]odt\f[R], \f[CR]opendocument\f[R], \f[CR]rst\f[R], or \f[CR]ms\f[R], an instruction to create one) in the output document. This option has no effect unless \f[CR]\-s/\-\-standalone\f[R] is used, and it has no effect on \f[CR]man\f[R], \f[CR]docbook4\f[R], \f[CR]docbook5\f[R], or \f[CR]jats\f[R] output. .RS .PP Note that if you are producing a PDF via \f[CR]ms\f[R] and using (the default) \f[CR]pdfroff\f[R] as a \f[CR]\-\-pdf\-engine\f[R], the table of contents will appear at the beginning of the document, before the title. If you would prefer it to be at the end of the document, use the option \f[CR]\-\-pdf\-engine\-opt=\-\-no\-toc\-relocation\f[R]. If \f[CR]groff\f[R] is used as the \f[CR]\-\-pdf\-engine\f[R], the table of contents will always appear at the end of the document. .RE .TP \f[CR]\-\-toc\-depth=\f[R]\f[I]NUMBER\f[R] Specify the number of section levels to include in the table of contents. The default is 3 (which means that level\-1, 2, and 3 headings will be listed in the contents). .TP \f[CR]\-\-lof[=true|false]\f[R], \f[CR]\-\-list\-of\-figures[=true|false]\f[R] Include an automatically generated list of figures (or, in some formats, an instruction to create one) in the output document. This option has no effect unless \f[CR]\-s/\-\-standalone\f[R] is used, and it only has an effect on \f[CR]latex\f[R], \f[CR]context\f[R], and \f[CR]docx\f[R] output. .TP \f[CR]\-\-lot[=true|false]\f[R], \f[CR]\-\-list\-of\-tables[=true|false]\f[R] Include an automatically generated list of tables (or, in some formats, an instruction to create one) in the output document. This option has no effect unless \f[CR]\-s/\-\-standalone\f[R] is used, and it only has an effect on \f[CR]latex\f[R], \f[CR]context\f[R], and \f[CR]docx\f[R] output. .TP \f[CR]\-\-strip\-comments[=true|false]\f[R] Strip out HTML comments in the Markdown or Textile source, rather than passing them on to Markdown, Textile or HTML output as raw HTML. This does not apply to HTML comments inside raw HTML blocks when the \f[CR]markdown_in_html_blocks\f[R] extension is not set. .TP \f[CR]\-\-syntax\-highlighting=default|none|idiomatic|\f[R]\f[I]STYLE\f[R]\f[CR]|\f[R]\f[I]FILE\f[R] The method to use for code syntax highlighting. Setting a specific \f[I]STYLE\f[R] causes highlighting to be performed with the internal highlighting engine, using KDE syntax definitions and styles. The \f[CR]idiomatic\f[R] 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 \f[CR]none\f[R] disables all syntax highlighting. The \f[CR]default\f[R] method uses a format\-specific default. .RS .PP The default for HTML, EPUB, Docx, Ms, Man, and LaTeX output is to use the internal highlighter with the default style; for Typst it is to use Typst\(cqs own syntax highlighting system. .PP Style options are \f[CR]pygments\f[R] (the default), \f[CR]kate\f[R], \f[CR]monochrome\f[R], \f[CR]breezeDark\f[R], \f[CR]espresso\f[R], \f[CR]zenburn\f[R], \f[CR]haddock\f[R], and \f[CR]tango\f[R]. For more information on syntax highlighting in pandoc, see Syntax highlighting, below. See also \f[CR]\-\-list\-highlight\-styles\f[R]. .PP Instead of a \f[I]STYLE\f[R] name, a JSON file with extension \f[CR].theme\f[R] may be supplied. This will be parsed as a KDE syntax highlighting theme and (if valid) used as the highlighting style. .PP To generate the JSON version of an existing style, use \f[CR]\-\-print\-highlight\-style\f[R]. .RE .TP \f[CR]\-\-no\-highlight\f[R] \f[I]Deprecated, use \f[CI]\-\-syntax\-highlighting=none\f[I] instead.\f[R] .RS .PP Disables syntax highlighting for code blocks and inlines, even when a language attribute is given. .RE .TP \f[CR]\-\-highlight\-style=\f[R]\f[I]STYLE\f[R]|\f[I]FILE\f[R] \f[I]Deprecated, use \f[CI]\-\-syntax\-highlighting=\f[I]\f[R]STYLE\f[I]|\f[R]FILE\f[I] instead.\f[R] .RS .PP Specifies the coloring style to be used in highlighted source code. .RE .TP \f[CR]\-\-print\-highlight\-style=\f[R]\f[I]STYLE\f[R]|\f[I]FILE\f[R] Prints a JSON version of a highlighting style, which can be modified, saved with a \f[CR].theme\f[R] extension, and used with \f[CR]\-\-syntax\-highlighting\f[R]. This option may be used with \f[CR]\-o\f[R]/\f[CR]\-\-output\f[R] to redirect output to a file, but \f[CR]\-o\f[R]/\f[CR]\-\-output\f[R] must come before \f[CR]\-\-print\-highlight\-style\f[R] on the command line. .TP \f[CR]\-\-syntax\-definition=\f[R]\f[I]FILE\f[R] Instructs pandoc to load a KDE XML syntax definition file, which will be used for syntax highlighting of appropriately marked code blocks. This can be used to add support for new languages or to use altered syntax definitions for existing languages. This option may be repeated to add multiple syntax definitions. .TP \f[CR]\-H\f[R] \f[I]FILE\f[R], \f[CR]\-\-include\-in\-header=\f[R]\f[I]FILE\f[R]|\f[I]URL\f[R] Include contents of \f[I]FILE\f[R], verbatim, at the end of the header. This can be used, for example, to include special CSS or JavaScript in HTML documents. This option can be used repeatedly to include multiple files in the header. They will be included in the order specified. Implies \f[CR]\-\-standalone\f[R]. .TP \f[CR]\-B\f[R] \f[I]FILE\f[R], \f[CR]\-\-include\-before\-body=\f[R]\f[I]FILE\f[R]|\f[I]URL\f[R] Include contents of \f[I]FILE\f[R], verbatim, at the beginning of the document body (e.g.\ after the \f[CR]\f[R] tag in HTML, or the \f[CR]\(rsbegin{document}\f[R] command in LaTeX). This can be used to include navigation bars or banners in HTML documents. This option can be used repeatedly to include multiple files. They will be included in the order specified. Implies \f[CR]\-\-standalone\f[R]. Note that if the output format is \f[CR]odt\f[R], this file must be in OpenDocument XML format suitable for insertion into the body of the document, and if the output is \f[CR]docx\f[R], this file must be in appropriate OpenXML format. .TP \f[CR]\-A\f[R] \f[I]FILE\f[R], \f[CR]\-\-include\-after\-body=\f[R]\f[I]FILE\f[R]|\f[I]URL\f[R] Include contents of \f[I]FILE\f[R], verbatim, at the end of the document body (before the \f[CR]\f[R] tag in HTML, or the \f[CR]\(rsend{document}\f[R] command in LaTeX). This option can be used repeatedly to include multiple files. They will be included in the order specified. Implies \f[CR]\-\-standalone\f[R]. Note that if the output format is \f[CR]odt\f[R], this file must be in OpenDocument XML format suitable for insertion into the body of the document, and if the output is \f[CR]docx\f[R], this file must be in appropriate OpenXML format. .TP \f[CR]\-\-resource\-path=\f[R]\f[I]SEARCHPATH\f[R] List of paths to search for images and other resources. The paths should be separated by \f[CR]:\f[R] on Linux, UNIX, and macOS systems, and by \f[CR];\f[R] on Windows. If \f[CR]\-\-resource\-path\f[R] is not specified, the default resource path is the working directory. Note that, if \f[CR]\-\-resource\-path\f[R] is specified, the working directory must be explicitly listed or it will not be searched. For example: \f[CR]\-\-resource\-path=.:test\f[R] will search the working directory and the \f[CR]test\f[R] subdirectory, in that order. This option can be used repeatedly. Search path components that come later on the command line will be searched before those that come earlier, so \f[CR]\-\-resource\-path foo:bar \-\-resource\-path baz:bim\f[R] is equivalent to \f[CR]\-\-resource\-path baz:bim:foo:bar\f[R]. Note that this option only has an effect when pandoc itself needs to find an image (e.g., in producing a PDF or docx, or when \f[CR]\-\-embed\-resources\f[R] is used.) It will not cause image paths to be rewritten in other cases (e.g., when pandoc is generating LaTeX or HTML). .TP \f[CR]\-\-request\-header=\f[R]\f[I]NAME\f[R]\f[CR]:\f[R]\f[I]VAL\f[R] Set the request header \f[I]NAME\f[R] to the value \f[I]VAL\f[R] when making HTTP requests (for example, when a URL is given on the command line, or when resources used in a document must be downloaded). If you\(cqre behind a proxy, you also need to set the environment variable \f[CR]http_proxy\f[R] to \f[CR]http://...\f[R]. .TP \f[CR]\-\-no\-check\-certificate[=true|false]\f[R] Disable the certificate verification to allow access to unsecure HTTP resources (for example when the certificate is no longer valid or self signed). .SS Options affecting specific writers .TP \f[CR]\-\-self\-contained[=true|false]\f[R] \f[I]Deprecated synonym for \f[CI]\-\-embed\-resources \-\-standalone\f[I].\f[R] .TP \f[CR]\-\-embed\-resources[=true|false]\f[R] Produce a standalone HTML file with no external dependencies, using \f[CR]data:\f[R] URIs to incorporate the contents of linked scripts, stylesheets, images, and videos. The resulting file should be \(lqself\-contained,\(rq in the sense that it needs no external files and no net access to be displayed properly by a browser. This option works only with HTML output formats, including \f[CR]html4\f[R], \f[CR]html5\f[R], \f[CR]html+lhs\f[R], \f[CR]html5+lhs\f[R], \f[CR]s5\f[R], \f[CR]slidy\f[R], \f[CR]slideous\f[R], \f[CR]dzslides\f[R], and \f[CR]revealjs\f[R]. Scripts, images, and stylesheets at absolute URLs will be downloaded; those at relative URLs will be sought relative to the working directory (if the first source file is local) or relative to the base URL (if the first source file is remote). Elements with the attribute \f[CR]data\-external=\(dq1\(dq\f[R] will be left alone; the documents they link to will not be incorporated in the document. Limitation: resources that are loaded dynamically through JavaScript cannot be incorporated; as a result, fonts may be missing when \f[CR]\-\-mathjax\f[R] is used, and some advanced features (e.g.\ zoom or speaker notes) may not work in an offline \(lqself\-contained\(rq \f[CR]reveal.js\f[R] slide show. .RS .PP For SVG images, \f[CR]img\f[R] tags with \f[CR]data:\f[R] URIs are used, unless the image has the class \f[CR]inline\-svg\f[R], in which case an inline SVG element is inserted. This approach is recommended when there are many occurrences of the same SVG in a document, as \f[CR]\f[R] elements will be used to reduce duplication. .RE .TP \f[CR]\-\-link\-images[=true|false]\f[R] Include links to images instead of embedding the images in ODT. (This option currently only affects ODT output.) .TP \f[CR]\-\-html\-q\-tags[=true|false]\f[R] Use \f[CR]\f[R] tags for quotes in HTML. (This option only has an effect if the \f[CR]smart\f[R] extension is enabled for the input format used.) .TP \f[CR]\-\-ascii[=true|false]\f[R] Use only ASCII characters in output. Currently supported for XML and HTML formats (which use entities instead of UTF\-8 when this option is selected), CommonMark, gfm, and Markdown (which use entities), roff man and ms (which use hexadecimal escapes), and to a limited degree LaTeX (which uses standard commands for accented characters when possible). .TP \f[CR]\-\-reference\-links[=true|false]\f[R] Use reference\-style links, rather than inline links, in writing Markdown or reStructuredText. By default inline links are used. The placement of link references is affected by the \f[CR]\-\-reference\-location\f[R] option. .TP \f[CR]\-\-reference\-location=block\f[R]|\f[CR]section\f[R]|\f[CR]document\f[R] Specify whether footnotes (and references, if \f[CR]reference\-links\f[R] is set) are placed at the end of the current (top\-level) block, the current section, or the document. The default is \f[CR]document\f[R]. Currently this option only affects the \f[CR]markdown\f[R], \f[CR]muse\f[R], \f[CR]html\f[R], \f[CR]epub\f[R], \f[CR]slidy\f[R], \f[CR]s5\f[R], \f[CR]slideous\f[R], \f[CR]dzslides\f[R], and \f[CR]revealjs\f[R] writers. In slide formats, specifying \f[CR]\-\-reference\-location=section\f[R] will cause notes to be rendered at the bottom of a slide. .TP \f[CR]\-\-figure\-caption\-position=above\f[R]|\f[CR]below\f[R] Specify whether figure captions go above or below figures (default is \f[CR]below\f[R]). This option only affects HTML, LaTeX, Docx, ODT, and Typst output. .TP \f[CR]\-\-table\-caption\-position=above\f[R]|\f[CR]below\f[R] Specify whether table captions go above or below tables (default is \f[CR]above\f[R]). This option only affects HTML, LaTeX, Docx, ODT, and Typst output. .TP \f[CR]\-\-markdown\-headings=setext\f[R]|\f[CR]atx\f[R] Specify whether to use ATX\-style (\f[CR]#\f[R]\-prefixed) or Setext\-style (underlined) headings for level 1 and 2 headings in Markdown output. (The default is \f[CR]atx\f[R].) ATX\-style headings are always used for levels 3+. This option also affects Markdown cells in \f[CR]ipynb\f[R] output. .TP \f[CR]\-\-list\-tables[=true|false]\f[R] Render tables as list tables in RST output. .TP \f[CR]\-\-top\-level\-division=default\f[R]|\f[CR]section\f[R]|\f[CR]chapter\f[R]|\f[CR]part\f[R] Treat top\-level headings as the given division type in LaTeX, ConTeXt, DocBook, and TEI output. The hierarchy order is part, chapter, then section; all headings are shifted such that the top\-level heading becomes the specified type. The default behavior is to determine the best division type via heuristics: unless other conditions apply, \f[CR]section\f[R] is chosen. When the \f[CR]documentclass\f[R] variable is set to \f[CR]report\f[R], \f[CR]book\f[R], or \f[CR]memoir\f[R] (unless the \f[CR]article\f[R] option is specified), \f[CR]chapter\f[R] is implied as the setting for this option. If \f[CR]beamer\f[R] is the output format, specifying either \f[CR]chapter\f[R] or \f[CR]part\f[R] will cause top\-level headings to become \f[CR]\(rspart{..}\f[R], while second\-level headings remain as their default type. .RS .PP In Docx output, this option adds section breaks before first\-level headings if \f[CR]chapter\f[R] is selected, and before first\- and second\-level headings if \f[CR]part\f[R] is selected. Footnote numbers will restart with each section break unless the reference doc modifies this. .RE .TP \f[CR]\-N\f[R], \f[CR]\-\-number\-sections=[true|false]\f[R] Number section headings in LaTeX, ConTeXt, HTML, Docx, ms, or EPUB output. By default, sections are not numbered. Sections with class \f[CR]unnumbered\f[R] will never be numbered, even if \f[CR]\-\-number\-sections\f[R] is specified. .TP \f[CR]\-\-number\-offset=\f[R]\f[I]NUMBER\f[R][\f[CR],\f[R]\f[I]NUMBER\f[R]\f[CR],\f[R]\f[I]\&...\f[R]] Offsets for section heading numbers. The first number is added to the section number for level\-1 headings, the second for level\-2 headings, and so on. So, for example, if you want the first level\-1 heading in your document to be numbered \(lq6\(rq instead of \(lq1\(rq, specify \f[CR]\-\-number\-offset=5\f[R]. If your document starts with a level\-2 heading which you want to be numbered \(lq1.5\(rq, specify \f[CR]\-\-number\-offset=1,4\f[R]. \f[CR]\-\-number\-offset\f[R] only directly affects the number of the first section heading in a document; subsequent numbers increment in the normal way. Implies \f[CR]\-\-number\-sections\f[R]. Currently this feature only affects HTML and Docx output. .TP \f[CR]\-\-listings[=true|false]\f[R] *Deprecated, use \f[CR]\-\-syntax\-highlighting=idiomatic\f[R] or \f[CR]\-\-syntax\-highlighting=default\f[R] instead. .RS .PP Use the \f[CR]listings\f[R] package for LaTeX code blocks. The package does not support multi\-byte encoding for source code. To handle UTF\-8 you would need to use a custom template. This issue is fully documented here: Encoding issue with the listings package. .RE .TP \f[CR]\-i\f[R], \f[CR]\-\-incremental[=true|false]\f[R] Make list items in slide shows display incrementally (one by one). The default is for lists to be displayed all at once. .TP \f[CR]\-\-slide\-level=\f[R]\f[I]NUMBER\f[R] Specifies that headings with the specified level create slides (for \f[CR]beamer\f[R], \f[CR]revealjs\f[R], \f[CR]pptx\f[R], \f[CR]s5\f[R], \f[CR]slidy\f[R], \f[CR]slideous\f[R], \f[CR]dzslides\f[R]). Headings above this level in the hierarchy are used to divide the slide show into sections; headings below this level create subheads within a slide. Valid values are 0\-6. If a slide level of 0 is specified, slides will not be split automatically on headings, and horizontal rules must be used to indicate slide boundaries. If a slide level is not specified explicitly, the slide level will be set automatically based on the contents of the document; see Structuring the slide show. .TP \f[CR]\-\-section\-divs[=true|false]\f[R] Wrap sections in \f[CR]
\f[R] tags (or \f[CR]
\f[R] tags for \f[CR]html4\f[R]), and attach identifiers to the enclosing \f[CR]
\f[R] (or \f[CR]
\f[R]) rather than the heading itself (see Heading identifiers, below). This option only affects HTML output (and does not affect HTML slide formats). .TP \f[CR]\-\-email\-obfuscation=none\f[R]|\f[CR]javascript\f[R]|\f[CR]references\f[R] Specify a method for obfuscating \f[CR]mailto:\f[R] links in HTML documents. \f[CR]none\f[R] leaves \f[CR]mailto:\f[R] links as they are. \f[CR]javascript\f[R] obfuscates them using JavaScript. \f[CR]references\f[R] obfuscates them by printing their letters as decimal or hexadecimal character references. The default is \f[CR]none\f[R]. .TP \f[CR]\-\-id\-prefix=\f[R]\f[I]STRING\f[R] Specify a prefix to be added to all identifiers and internal links in HTML and DocBook output, and to footnote numbers in Markdown and Haddock output. This is useful for preventing duplicate identifiers when generating fragments to be included in other pages. .TP \f[CR]\-T\f[R] \f[I]STRING\f[R], \f[CR]\-\-title\-prefix=\f[R]\f[I]STRING\f[R] Specify \f[I]STRING\f[R] as a prefix at the beginning of the title that appears in the HTML header (but not in the title as it appears at the beginning of the HTML body). Implies \f[CR]\-\-standalone\f[R]. .TP \f[CR]\-c\f[R] \f[I]URL\f[R], \f[CR]\-\-css=\f[R]\f[I]URL\f[R] Link to a CSS style sheet. This option can be used repeatedly to include multiple files. They will be included in the order specified. This option only affects HTML (including HTML slide shows) and EPUB output. It should be used together with \f[CR]\-s/\-\-standalone\f[R], because the link to the stylesheet goes in the document header. .RS .PP A stylesheet is required for generating EPUB. If none is provided using this option (or the \f[CR]css\f[R] or \f[CR]stylesheet\f[R] metadata fields), pandoc will look for a file \f[CR]epub.css\f[R] in the user data directory (see \f[CR]\-\-data\-dir\f[R]). If it is not found there, sensible defaults will be used. .RE .TP \f[CR]\-\-reference\-doc=\f[R]\f[I]FILE\f[R]|\f[I]URL\f[R] Use the specified file as a style reference in producing a docx or ODT file. .RS .TP Docx For best results, the reference docx should be a modified version of a docx file produced using pandoc. The contents of the reference docx are ignored, but its stylesheets and document properties (including margins, page size, header, and footer) are used in the new docx. If no reference docx is specified on the command line, pandoc will look for a file \f[CR]reference.docx\f[R] in the user data directory (see \f[CR]\-\-data\-dir\f[R]). If this is not found either, sensible defaults will be used. .RS .PP To produce a custom \f[CR]reference.docx\f[R], first get a copy of the default \f[CR]reference.docx\f[R]: \f[CR]pandoc \-o custom\-reference.docx \-\-print\-default\-data\-file reference.docx\f[R]. Then open \f[CR]custom\-reference.docx\f[R] in Word, modify the styles as you wish, and save the file. For best results, do not make changes to this file other than modifying the styles used by pandoc: .PP Paragraph styles: .IP \(bu 2 Normal .IP \(bu 2 Body Text .IP \(bu 2 First Paragraph .IP \(bu 2 Compact .IP \(bu 2 Title .IP \(bu 2 Subtitle .IP \(bu 2 Author .IP \(bu 2 Date .IP \(bu 2 Abstract .IP \(bu 2 AbstractTitle .IP \(bu 2 Bibliography .IP \(bu 2 Heading 1 .IP \(bu 2 Heading 2 .IP \(bu 2 Heading 3 .IP \(bu 2 Heading 4 .IP \(bu 2 Heading 5 .IP \(bu 2 Heading 6 .IP \(bu 2 Heading 7 .IP \(bu 2 Heading 8 .IP \(bu 2 Heading 9 .IP \(bu 2 Block Text [for block quotes] .IP \(bu 2 Footnote Block Text [for block quotes in footnotes] .IP \(bu 2 Source Code .IP \(bu 2 Footnote Text .IP \(bu 2 Definition Term .IP \(bu 2 Definition .IP \(bu 2 Caption .IP \(bu 2 Table Caption .IP \(bu 2 Image Caption .IP \(bu 2 Figure .IP \(bu 2 Captioned Figure .IP \(bu 2 TOC Heading .PP Character styles: .IP \(bu 2 Default Paragraph Font .IP \(bu 2 Verbatim Char .IP \(bu 2 Footnote Reference .IP \(bu 2 Hyperlink .IP \(bu 2 Section Number .PP Table style: .IP \(bu 2 Table .RE .TP ODT For best results, the reference ODT should be a modified version of an ODT produced using pandoc. The contents of the reference ODT are ignored, but its stylesheets are used in the new ODT. If no reference ODT is specified on the command line, pandoc will look for a file \f[CR]reference.odt\f[R] in the user data directory (see \f[CR]\-\-data\-dir\f[R]). If this is not found either, sensible defaults will be used. .RS .PP To produce a custom \f[CR]reference.odt\f[R], first get a copy of the default \f[CR]reference.odt\f[R]: \f[CR]pandoc \-o custom\-reference.odt \-\-print\-default\-data\-file reference.odt\f[R]. Then open \f[CR]custom\-reference.odt\f[R] in LibreOffice, modify the styles as you wish, and save the file. .RE .TP PowerPoint Templates included with Microsoft PowerPoint 2013 (either with \f[CR].pptx\f[R] or \f[CR].potx\f[R] extension) are known to work, as are most templates derived from these. .RS .PP The specific requirement is that the template should contain layouts with the following names (as seen within PowerPoint): .IP \(bu 2 Title Slide .IP \(bu 2 Title and Content .IP \(bu 2 Section Header .IP \(bu 2 Two Content .IP \(bu 2 Comparison .IP \(bu 2 Content with Caption .IP \(bu 2 Blank .PP For each name, the first layout found with that name will be used. If no layout is found with one of the names, pandoc will output a warning and use the layout with that name from the default reference doc instead. (How these layouts are used is described in PowerPoint layout choice.) .PP All templates included with a recent version of MS PowerPoint will fit these criteria. (You can click on \f[CR]Layout\f[R] under the \f[CR]Home\f[R] menu to check.) .PP You can also modify the default \f[CR]reference.pptx\f[R]: first run \f[CR]pandoc \-o custom\-reference.pptx \-\-print\-default\-data\-file reference.pptx\f[R], and then modify \f[CR]custom\-reference.pptx\f[R] in MS PowerPoint (pandoc will use the layouts with the names listed above). .RE .RE .TP \f[CR]\-\-split\-level=\f[R]\f[I]NUMBER\f[R] Specify the heading level at which to split an EPUB or chunked HTML document into separate files. The default is to split into chapters at level\-1 headings. In the case of EPUB, this option only affects the internal composition of the EPUB, not the way chapters and sections are displayed to users. Some readers may be slow if the chapter files are too large, so for large documents with few level\-1 headings, one might want to use a chapter level of 2 or 3. For chunked HTML, this option determines how much content goes in each \(lqchunk.\(rq .TP \f[CR]\-\-chunk\-template=\f[R]\f[I]PATHTEMPLATE\f[R] Specify a template for the filenames in a \f[CR]chunkedhtml\f[R] document. In the template, \f[CR]%n\f[R] will be replaced by the chunk number (padded with leading 0s to 3 digits), \f[CR]%s\f[R] with the section number of the chunk, \f[CR]%h\f[R] with the heading text (with formatting removed), \f[CR]%i\f[R] with the section identifier. For example, \f[CR]section\-%s\-%i.html\f[R] might be resolved to \f[CR]section\-1.1\-introduction.html\f[R]. The characters \f[CR]/\f[R] and \f[CR]\(rs\f[R] are not allowed in chunk templates and will be ignored. The default is \f[CR]%s\-%i.html\f[R]. .TP \f[CR]\-\-epub\-chapter\-level=\f[R]\f[I]NUMBER\f[R] \f[I]Deprecated synonym for \f[CI]\-\-split\-level\f[I].\f[R] .TP \f[CR]\-\-epub\-cover\-image=\f[R]\f[I]FILE\f[R] Use the specified image as the EPUB cover. It is recommended that the image be less than 1000px in width and height. Note that in a Markdown source document you can also specify \f[CR]cover\-image\f[R] in a YAML metadata block (see EPUB Metadata, below). .TP \f[CR]\-\-epub\-title\-page=true\f[R]|\f[CR]false\f[R] Determines whether a the title page is included in the EPUB (default is \f[CR]true\f[R]). .TP \f[CR]\-\-epub\-metadata=\f[R]\f[I]FILE\f[R] Look in the specified XML file for metadata for the EPUB. The file should contain a series of Dublin Core elements. For example: .RS .IP .EX Creative Commons es\-AR .EE .PP By default, pandoc will include the following metadata elements: \f[CR]\f[R] (from the document title), \f[CR]\f[R] (from the document authors), \f[CR]\f[R] (from the document date, which should be in ISO 8601 format), \f[CR]\f[R] (from the \f[CR]lang\f[R] variable, or, if is not set, the locale), and \f[CR]\f[R] (a randomly generated UUID). Any of these may be overridden by elements in the metadata file. .PP Note: if the source document is Markdown, a YAML metadata block in the document can be used instead. See below under EPUB Metadata. .RE .TP \f[CR]\-\-epub\-embed\-font=\f[R]\f[I]FILE\f[R] Embed the specified font in the EPUB. This option can be repeated to embed multiple fonts. Wildcards can also be used: for example, \f[CR]DejaVuSans\-*.ttf\f[R]. However, if you use wildcards on the command line, be sure to escape them or put the whole filename in single quotes, to prevent them from being interpreted by the shell. To use the embedded fonts, you will need to add declarations like the following to your CSS (see \f[CR]\-\-css\f[R]): .RS .IP .EX \(atfont\-face { font\-family: DejaVuSans; font\-style: normal; font\-weight: normal; src:url(\(dq../fonts/DejaVuSans\-Regular.ttf\(dq); } \(atfont\-face { font\-family: DejaVuSans; font\-style: normal; font\-weight: bold; src:url(\(dq../fonts/DejaVuSans\-Bold.ttf\(dq); } \(atfont\-face { font\-family: DejaVuSans; font\-style: italic; font\-weight: normal; src:url(\(dq../fonts/DejaVuSans\-Oblique.ttf\(dq); } \(atfont\-face { font\-family: DejaVuSans; font\-style: italic; font\-weight: bold; src:url(\(dq../fonts/DejaVuSans\-BoldOblique.ttf\(dq); } body { font\-family: \(dqDejaVuSans\(dq; } .EE .RE .TP \f[CR]\-\-epub\-subdirectory=\f[R]\f[I]DIRNAME\f[R] Specify the subdirectory in the OCF container that is to hold the EPUB\-specific contents. The default is \f[CR]EPUB\f[R]. To put the EPUB contents in the top level, use an empty string. .TP \f[CR]\-\-ipynb\-output=all|none|best\f[R] Determines how ipynb output cells are treated. \f[CR]all\f[R] means that all of the data formats included in the original are preserved. \f[CR]none\f[R] means that the contents of data cells are omitted. \f[CR]best\f[R] causes pandoc to try to pick the richest data block in each output cell that is compatible with the output format. The default is \f[CR]best\f[R]. .TP \f[CR]\-\-pdf\-engine=\f[R]\f[I]PROGRAM\f[R] Use the specified engine when producing PDF output. Valid values are \f[CR]pdflatex\f[R], \f[CR]lualatex\f[R], \f[CR]xelatex\f[R], \f[CR]latexmk\f[R], \f[CR]tectonic\f[R], \f[CR]wkhtmltopdf\f[R], \f[CR]weasyprint\f[R], \f[CR]pagedjs\-cli\f[R], \f[CR]prince\f[R], \f[CR]context\f[R], \f[CR]groff\f[R], \f[CR]pdfroff\f[R], and \f[CR]typst\f[R]. If the engine is not in your PATH, the full path of the engine may be specified here. If this option is not specified, pandoc uses the following defaults depending on the output format specified using \f[CR]\-t/\-\-to\f[R]: .RS .IP \(bu 2 \f[CR]\-t latex\f[R] or none: \f[CR]pdflatex\f[R] (other options: \f[CR]xelatex\f[R], \f[CR]lualatex\f[R], \f[CR]tectonic\f[R], \f[CR]latexmk\f[R]) .IP \(bu 2 \f[CR]\-t context\f[R]: \f[CR]context\f[R] .IP \(bu 2 \f[CR]\-t html\f[R]: \f[CR]weasyprint\f[R] (other options: \f[CR]prince\f[R], \f[CR]wkhtmltopdf\f[R], \f[CR]pagedjs\-cli\f[R]; see print\-css.rocks for a good introduction to PDF generation from HTML/CSS) .IP \(bu 2 \f[CR]\-t ms\f[R]: \f[CR]pdfroff\f[R] .IP \(bu 2 \f[CR]\-t typst\f[R]: \f[CR]typst\f[R] .PP This option is normally intended to be used when a PDF file is specified as \f[CR]\-o/\-\-output\f[R]. However, it may still have an effect when other output formats are requested. For example, \f[CR]ms\f[R] output will include \f[CR].pdfhref\f[R] macros only if a \f[CR]\-\-pdf\-engine\f[R] is selected, and the macros will be differently encoded depending on whether \f[CR]groff\f[R] or \f[CR]pdfroff\f[R] is specified. .RE .TP \f[CR]\-\-pdf\-engine\-opt=\f[R]\f[I]STRING\f[R] Use the given string as a command\-line argument to the \f[CR]pdf\-engine\f[R]. For example, to use a persistent directory \f[CR]foo\f[R] for \f[CR]latexmk\f[R]\(cqs auxiliary files, use \f[CR]\-\-pdf\-engine\-opt=\-outdir=foo\f[R]. Note that no check for duplicate options is done. .SS Citation rendering .TP \f[CR]\-C\f[R], \f[CR]\-\-citeproc\f[R] Process the citations in the file, replacing them with rendered citations and adding a bibliography. Citation processing will not take place unless bibliographic data is supplied, either through an external file specified using the \f[CR]\-\-bibliography\f[R] option or the \f[CR]bibliography\f[R] field in metadata, or via a \f[CR]references\f[R] section in metadata containing a list of citations in CSL YAML format with Markdown formatting. The style is controlled by a CSL stylesheet specified using the \f[CR]\-\-csl\f[R] option or the \f[CR]csl\f[R] field in metadata. (If no stylesheet is specified, the \f[CR]chicago\-author\-date\f[R] style will be used by default.) The citation processing transformation may be applied before or after filters or Lua filters (see \f[CR]\-\-filter\f[R], \f[CR]\-\-lua\-filter\f[R]): these transformations are applied in the order they appear on the command line. For more information, see the section on Citations. .RS .PP Note: if this option is specified, the \f[CR]citations\f[R] extension will be disabled automatically in the writer, to ensure that the citeproc\-generated citations will be rendered instead of the format\(cqs own citation syntax. .RE .TP \f[CR]\-\-bibliography=\f[R]\f[I]FILE\f[R] Set the \f[CR]bibliography\f[R] field in the document\(cqs metadata to \f[I]FILE\f[R], overriding any value set in the metadata. If you supply this argument multiple times, each \f[I]FILE\f[R] will be added to bibliography. If \f[I]FILE\f[R] is a URL, it will be fetched via HTTP. If \f[I]FILE\f[R] is not found relative to the working directory, it will be sought in the resource path (see \f[CR]\-\-resource\-path\f[R]). .TP \f[CR]\-\-csl=\f[R]\f[I]FILE\f[R] Set the \f[CR]csl\f[R] field in the document\(cqs metadata to \f[I]FILE\f[R], overriding any value set in the metadata. (This is equivalent to \f[CR]\-\-metadata csl=FILE\f[R].) If \f[I]FILE\f[R] is a URL, it will be fetched via HTTP. If \f[I]FILE\f[R] is not found relative to the working directory, it will be sought in the resource path (see \f[CR]\-\-resource\-path\f[R]) and finally in the \f[CR]csl\f[R] subdirectory of the pandoc user data directory. .TP \f[CR]\-\-citation\-abbreviations=\f[R]\f[I]FILE\f[R] Set the \f[CR]citation\-abbreviations\f[R] field in the document\(cqs metadata to \f[I]FILE\f[R], overriding any value set in the metadata. (This is equivalent to \f[CR]\-\-metadata citation\-abbreviations=FILE\f[R].) If \f[I]FILE\f[R] is a URL, it will be fetched via HTTP. If \f[I]FILE\f[R] is not found relative to the working directory, it will be sought in the resource path (see \f[CR]\-\-resource\-path\f[R]) and finally in the \f[CR]csl\f[R] subdirectory of the pandoc user data directory. .TP \f[CR]\-\-natbib\f[R] Use \f[CR]natbib\f[R] for citations in LaTeX output. This option is not for use with the \f[CR]\-\-citeproc\f[R] option or with PDF output. It is intended for use in producing a LaTeX file that can be processed with \f[CR]bibtex\f[R]. .TP \f[CR]\-\-biblatex\f[R] Use \f[CR]biblatex\f[R] for citations in LaTeX output. This option is not for use with the \f[CR]\-\-citeproc\f[R] option or with PDF output. It is intended for use in producing a LaTeX file that can be processed with \f[CR]bibtex\f[R] or \f[CR]biber\f[R]. .SS Math rendering in HTML The default is to render TeX math as far as possible using Unicode characters. Formulas are put inside a \f[CR]span\f[R] with \f[CR]class=\(dqmath\(dq\f[R], so that they may be styled differently from the surrounding text if needed. However, this gives acceptable results only for basic math, usually you will want to use \f[CR]\-\-mathjax\f[R] or another of the following options. .TP \f[CR]\-\-mathjax\f[R][\f[CR]=\f[R]\f[I]URL\f[R]] Use MathJax to display embedded TeX math in HTML output. TeX math will be put between \f[CR]\(rs(...\(rs)\f[R] (for inline math) or \f[CR]\(rs[...\(rs]\f[R] (for display math) and wrapped in \f[CR]\f[R] tags with class \f[CR]math\f[R]. Then the MathJax JavaScript will render it. The \f[I]URL\f[R] should point to the \f[CR]MathJax.js\f[R] load script. If a \f[I]URL\f[R] is not provided, a link to the Cloudflare CDN will be inserted. .TP \f[CR]\-\-mathml\f[R] Convert TeX math to MathML (in \f[CR]epub3\f[R], \f[CR]docbook4\f[R], \f[CR]docbook5\f[R], \f[CR]jats\f[R], \f[CR]html4\f[R] and \f[CR]html5\f[R]). This is the default in \f[CR]odt\f[R] output. MathML is supported natively by the main web browsers and select e\-book readers. .TP \f[CR]\-\-webtex\f[R][\f[CR]=\f[R]\f[I]URL\f[R]] Convert TeX formulas to \f[CR]\f[R] tags that link to an external script that converts formulas to images. The formula will be URL\-encoded and concatenated with the URL provided. For SVG images you can for example use \f[CR]\-\-webtex https://latex.codecogs.com/svg.latex?\f[R]. If no URL is specified, the CodeCogs URL generating PNGs will be used (\f[CR]https://latex.codecogs.com/png.latex?\f[R]). Note: the \f[CR]\-\-webtex\f[R] option will affect Markdown output as well as HTML, which is useful if you\(cqre targeting a version of Markdown without native math support. .TP \f[CR]\-\-katex\f[R][\f[CR]=\f[R]\f[I]URL\f[R]] Use KaTeX to display embedded TeX math in HTML output. The \f[I]URL\f[R] is the base URL for the KaTeX library. That directory should contain a \f[CR]katex.min.js\f[R] and a \f[CR]katex.min.css\f[R] file. If a \f[I]URL\f[R] is not provided, a link to the KaTeX CDN will be inserted. .TP \f[CR]\-\-gladtex\f[R] Enclose TeX math in \f[CR]\f[R] tags in HTML output. The resulting HTML can then be processed by GladTeX to produce SVG images of the typeset formulas and an HTML file with these images embedded. .RS .IP .EX pandoc \-s \-\-gladtex input.md \-o myfile.htex gladtex \-d image_dir myfile.htex # produces myfile.html and images in image_dir .EE .RE .SS Options for wrapper scripts .TP \f[CR]\-\-dump\-args[=true|false]\f[R] Print information about command\-line arguments to \f[I]stdout\f[R], then exit. This option is intended primarily for use in wrapper scripts. The first line of output contains the name of the output file specified with the \f[CR]\-o\f[R] option, or \f[CR]\-\f[R] (for \f[I]stdout\f[R]) if no output file was specified. The remaining lines contain the command\-line arguments, one per line, in the order they appear. These do not include regular pandoc options and their arguments, but do include any options appearing after a \f[CR]\-\-\f[R] separator at the end of the line. .TP \f[CR]\-\-ignore\-args[=true|false]\f[R] Ignore command\-line arguments (for use in wrapper scripts). Regular pandoc options are not ignored. Thus, for example, .RS .IP .EX pandoc \-\-ignore\-args \-o foo.html \-s foo.txt \-\- \-e latin1 .EE .PP is equivalent to .IP .EX pandoc \-o foo.html \-s .EE .RE .SH EXIT CODES If pandoc completes successfully, it will return exit code 0. Nonzero exit codes have the following meanings: .RS -14n .IP .EX Code Error \-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- 1 PandocIOError 3 PandocFailOnWarningError 4 PandocAppError 5 PandocTemplateError 6 PandocOptionError 21 PandocUnknownReaderError 22 PandocUnknownWriterError 23 PandocUnsupportedExtensionError 24 PandocCiteprocError 25 PandocBibliographyError 31 PandocEpubSubdirectoryError 43 PandocPDFError 44 PandocXMLError 47 PandocPDFProgramNotFoundError 61 PandocHttpError 62 PandocShouldNeverHappenError 63 PandocSomeError 64 PandocParseError 66 PandocMakePDFError 67 PandocSyntaxMapError 83 PandocFilterError 84 PandocLuaError 89 PandocNoScriptingEngine 91 PandocMacroLoop 92 PandocUTF8DecodingError 93 PandocIpynbDecodingError 94 PandocUnsupportedCharsetError 95 PandocInputNotTextError 97 PandocCouldNotFindDataFileError 98 PandocCouldNotFindMetadataFileError 99 PandocResourceNotFound .EE .RE .SH DEFAULTS FILES The \f[CR]\-\-defaults\f[R] option may be used to specify a package 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. .PP Fields that are omitted will just have their regular default values. So a defaults file can be as simple as one line: .IP .EX verbosity\f[B]:\f[R] INFO .EE .PP or in JSON: .IP .EX { \(dqverbosity\(dq: \(dqINFO\(dq } .EE .PP In fields that expect a file path (or list of file paths), the following syntax may be used to interpolate environment variables: .IP .EX csl\f[B]:\f[R] ${HOME}/mycsldir/special.csl .EE .PP \f[CR]${USERDATA}\f[R] may also be used; this will always resolve to the user data directory that is current when the defaults file is parsed, regardless of the setting of the environment variable \f[CR]USERDATA\f[R]. .PP \f[CR]${.}\f[R] will resolve to the directory containing the defaults file itself. This allows you to refer to resources contained in that directory: .IP .EX epub\-cover\-image\f[B]:\f[R] ${.}/cover.jpg epub\-metadata\f[B]:\f[R] ${.}/meta.xml resource\-path\f[B]:\f[R] \f[B]\-\f[R] .\f[I] # the working directory from which pandoc is run\f[R] \f[B]\-\f[R] ${.}/images\f[I] # the images subdirectory of the directory\f[R] \f[I] # containing this defaults file\f[R] .EE .PP This environment variable interpolation syntax \f[I]only\f[R] works in fields that expect file paths. .PP Defaults files can be placed in the \f[CR]defaults\f[R] subdirectory of the user data directory and used from any directory. For example, one could create a file specifying defaults for writing letters, save it as \f[CR]letter.yaml\f[R] in the \f[CR]defaults\f[R] subdirectory of the user data directory, and then invoke these defaults from any directory using \f[CR]pandoc \-\-defaults letter\f[R] or \f[CR]pandoc \-dletter\f[R]. .PP When multiple defaults are used, their contents will be combined. .PP Note that, where command\-line arguments may be repeated (\f[CR]\-\-metadata\-file\f[R], \f[CR]\-\-css\f[R], \f[CR]\-\-include\-in\-header\f[R], \f[CR]\-\-include\-before\-body\f[R], \f[CR]\-\-include\-after\-body\f[R], \f[CR]\-\-variable\f[R], \f[CR]\-\-metadata\f[R], \f[CR]\-\-syntax\-definition\f[R]), the values specified on the command line will combine with values specified in the defaults file, rather than replacing them. .PP The following tables show the mapping between the command line and defaults file entries. .RS -14n .IP .EX command line defaults file \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- foo.md input\-file: foo.md foo.md bar.md input\-files: \- foo.md \- bar.md .EE .RE .PP The value of \f[CR]input\-files\f[R] may be left empty to indicate input from stdin, and it can be an empty sequence \f[CR][]\f[R] for no input. .SS General options .RS -14n .IP .EX command line defaults file \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-from markdown+emoji from: markdown+emoji reader: markdown+emoji \-\-to markdown+hard_line_breaks to: markdown+hard_line_breaks writer: markdown+hard_line_breaks \-\-output foo.pdf output\-file: foo.pdf \-\-output \- output\-file: \-\-data\-dir dir data\-dir: dir \-\-defaults file defaults: \- file \-\-verbose verbosity: INFO \-\-quiet verbosity: ERROR \-\-fail\-if\-warnings fail\-if\-warnings: true \-\-sandbox sandbox: true \-\-log=FILE log\-file: FILE .EE .RE .PP Options specified in a defaults file itself always have priority over those in another file included with a \f[CR]defaults:\f[R] entry. .PP \f[CR]verbosity\f[R] can have the values \f[CR]ERROR\f[R], \f[CR]WARNING\f[R], or \f[CR]INFO\f[R]. .SS Reader options .RS -14n .IP .EX command line defaults file \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-shift\-heading\-level\-by \-1 shift\-heading\-level\-by: \-1 \-\-indented\-code\-classes python indented\-code\-classes: \- python \-\-default\-image\-extension \(dq.jpg\(dq default\-image\-extension: \(aq.jpg\(aq \-\-file\-scope file\-scope: true \-\-citeproc \(rs filters: \-\-lua\-filter count\-words.lua \(rs \- citeproc \-\-filter special.lua \- count\-words.lua \- type: json path: special.lua \-\-metadata key=value \(rs metadata: \-\-metadata key2 key: value key2: true \-\-metadata\-file meta.yaml metadata\-files: \- meta.yaml metadata\-file: meta.yaml \-\-preserve\-tabs preserve\-tabs: true \-\-tab\-stop 8 tab\-stop: 8 \-\-track\-changes accept track\-changes: accept \-\-extract\-media dir extract\-media: dir \-\-abbreviations abbrevs.txt abbreviations: abbrevs.txt \-\-trace trace: true .EE .RE .PP Metadata values specified in a defaults file are parsed as literal string text, not Markdown. .PP Filters will be assumed to be Lua filters if they have the \f[CR].lua\f[R] extension, and JSON filters otherwise. But the filter type can also be specified explicitly, as shown. Filters are run in the order specified. To include the built\-in citeproc filter, use either \f[CR]citeproc\f[R] or \f[CR]{type: citeproc}\f[R]. .SS General writer options .RS -14n .IP .EX command line defaults file \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-standalone standalone: true \-\-template letter template: letter \-\-variable key=val \(rs variables: \-\-variable key2 key: val key2: true \-\-eol nl eol: nl \-\-dpi 300 dpi: 300 \-\-wrap preserve wrap: \(dqpreserve\(dq \-\-columns 72 columns: 72 \-\-table\-of\-contents table\-of\-contents: true \-\-toc toc: true \-\-toc\-depth 3 toc\-depth: 3 \-\-strip\-comments strip\-comments: true \-\-no\-highlight syntax\-highlighting: \(aqnone\(aq \-\-syntax\-highlighting kate syntax\-highlighting: kate \-\-syntax\-definition mylang.xml syntax\-definitions: \- mylang.xml syntax\-definition: mylang.xml \-\-include\-in\-header inc.tex include\-in\-header: \- inc.tex \-\-include\-before\-body inc.tex include\-before\-body: \- inc.tex \-\-include\-after\-body inc.tex include\-after\-body: \- inc.tex \-\-resource\-path .:foo resource\-path: [\(aq.\(aq,\(aqfoo\(aq] \-\-request\-header foo:bar request\-headers: \- [\(dqUser\-Agent\(dq, \(dqMozilla/5.0\(dq] \-\-no\-check\-certificate no\-check\-certificate: true .EE .RE .SS Options affecting specific writers .RS -14n .IP .EX command line defaults file \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-self\-contained self\-contained: true \-\-link\-images link\-images: true \-\-html\-q\-tags html\-q\-tags: true \-\-ascii ascii: true \-\-reference\-links reference\-links: true \-\-reference\-location block reference\-location: block \-\-figure\-caption\-position=above figure\-caption\-position: above \-\-table\-caption\-position=below table\-caption\-position: below \-\-markdown\-headings atx markdown\-headings: atx \-\-list\-tables list\-tables: true \-\-top\-level\-division chapter top\-level\-division: chapter \-\-number\-sections number\-sections: true \-\-number\-offset=1,4 number\-offset: \(rs[1,4\(rs] \-\-listings listings: true \-\-list\-of\-figures list\-of\-figures: true \-\-lof lof: true \-\-list\-of\-tables list\-of\-tables: true \-\-lot lot: true \-\-incremental incremental: true \-\-slide\-level 2 slide\-level: 2 \-\-section\-divs section\-divs: true \-\-email\-obfuscation references email\-obfuscation: references \-\-id\-prefix ch1 identifier\-prefix: ch1 \-\-title\-prefix MySite title\-prefix: MySite \-\-css styles/screen.css \(rs css: \-\-css styles/special.css \- styles/screen.css \- styles/special.css \-\-reference\-doc my.docx reference\-doc: my.docx \-\-epub\-cover\-image cover.jpg epub\-cover\-image: cover.jpg \-\-epub\-title\-page=false epub\-title\-page: false \-\-epub\-metadata meta.xml epub\-metadata: meta.xml \-\-epub\-embed\-font special.otf \(rs epub\-fonts: \-\-epub\-embed\-font headline.otf \- special.otf \- headline.otf \-\-split\-level 2 split\-level: 2 \-\-chunk\-template=\(dq%i.html\(dq chunk\-template: \(dq%i.html\(dq \-\-epub\-subdirectory=\(dq\(dq epub\-subdirectory: \(aq\(aq \-\-ipynb\-output best ipynb\-output: best \-\-pdf\-engine xelatex pdf\-engine: xelatex \-\-pdf\-engine\-opt=\-\-shell\-escape pdf\-engine\-opts: \- \(aq\-shell\-escape\(aq pdf\-engine\-opt: \(aq\-shell\-escape\(aq .EE .RE .SS Citation rendering .RS -14n .IP .EX command line defaults file \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-citeproc citeproc: true \-\-bibliography logic.bib bibliography: logic.bib \-\-csl ieee.csl csl: ieee.csl \-\-citation\-abbreviations ab.json citation\-abbreviations: ab.json \-\-natbib cite\-method: natbib \-\-biblatex cite\-method: biblatex .EE .RE .PP \f[CR]cite\-method\f[R] can be \f[CR]citeproc\f[R], \f[CR]natbib\f[R], or \f[CR]biblatex\f[R]. This only affects LaTeX output. If you want to use citeproc to format citations, you should also set `citeproc: true'. .PP If you need control over when the citeproc processing is done relative to other filters, you should instead use \f[CR]citeproc\f[R] in the list of \f[CR]filters\f[R] (see Reader options). .SS Math rendering in HTML .RS -14n .IP .EX command line defaults file \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-mathjax html\-math\-method: method: mathjax \-\-mathml html\-math\-method: method: mathml \-\-webtex html\-math\-method: method: webtex \-\-katex html\-math\-method: method: katex \-\-gladtex html\-math\-method: method: gladtex .EE .RE .PP In addition to the values listed above, \f[CR]method\f[R] can have the value \f[CR]plain\f[R]. .PP If the command line option accepts a URL argument, an \f[CR]url:\f[R] field can be added to \f[CR]html\-math\-method:\f[R]. .SS Options for wrapper scripts .RS -14n .IP .EX command line defaults file \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-dump\-args dump\-args: true \-\-ignore\-args ignore\-args: true .EE .RE .SH TEMPLATES When the \f[CR]\-s/\-\-standalone\f[R] option is used, pandoc uses a template to add header and footer material that is needed for a self\-standing document. To see the default template that is used, just type .IP .EX pandoc \-D *FORMAT* .EE .PP where \f[I]FORMAT\f[R] is the name of the output format. A custom template can be specified using the \f[CR]\-\-template\f[R] option. You can also override the system default templates for a given output format \f[I]FORMAT\f[R] by putting a file \f[CR]templates/default.*FORMAT*\f[R] in the user data directory (see \f[CR]\-\-data\-dir\f[R], above). \f[I]Exceptions:\f[R] .IP \(bu 2 For \f[CR]odt\f[R] output, customize the \f[CR]default.opendocument\f[R] template. .IP \(bu 2 For \f[CR]docx\f[R] output, customize the \f[CR]default.openxml\f[R] template. .IP \(bu 2 For \f[CR]pdf\f[R] output, customize the \f[CR]default.latex\f[R] template (or the \f[CR]default.context\f[R] template, if you use \f[CR]\-t context\f[R], or the \f[CR]default.ms\f[R] template, if you use \f[CR]\-t ms\f[R], or the \f[CR]default.html\f[R] template, if you use \f[CR]\-t html\f[R]). .IP \(bu 2 \f[CR]pptx\f[R] has no template. .PP Note that \f[CR]docx\f[R], \f[CR]odt\f[R], and \f[CR]pptx\f[R] output can also be customized using \f[CR]\-\-reference\-doc\f[R]. Use a reference doc to adjust the styles in your document; use a template to handle variable interpolation and customize the presentation of metadata, the position of the table of contents, boilerplate text, etc. .PP Templates contain \f[I]variables\f[R], which allow for the inclusion of arbitrary information at any point in the file. They may be set at the command line using the \f[CR]\-V/\-\-variable\f[R] option. If a variable is not set, pandoc will look for the key in the document\(cqs metadata, which can be set using either YAML metadata blocks or with the \f[CR]\-M/\-\-metadata\f[R] option. In addition, some variables are given default values by pandoc. See Variables below for a list of variables used in pandoc\(cqs default templates. .PP If you use custom templates, you may need to revise them as pandoc changes. We recommend tracking the changes in the default templates, and modifying your custom templates accordingly. An easy way to do this is to fork the pandoc\-templates repository and merge in changes after each pandoc release. .SS Template syntax .SS Comments Anything between the sequence \f[CR]$\-\-\f[R] and the end of the line will be treated as a comment and omitted from the output. .SS Delimiters To mark variables and control structures in the template, either \f[CR]$\f[R]\&...\f[CR]$\f[R] or \f[CR]${\f[R]\&...\f[CR]}\f[R] may be used as delimiters. The styles may also be mixed in the same template, but the opening and closing delimiter must match in each case. The opening delimiter may be followed by one or more spaces or tabs, which will be ignored. The closing delimiter may be preceded by one or more spaces or tabs, which will be ignored. .PP To include a literal \f[CR]$\f[R] in the document, use \f[CR]$$\f[R]. .SS Interpolated variables A slot for an interpolated variable is a variable name surrounded by matched delimiters. Variable names must begin with a letter and can contain letters, numbers, \f[CR]_\f[R], \f[CR]\-\f[R], and \f[CR].\f[R]. The keywords \f[CR]it\f[R], \f[CR]if\f[R], \f[CR]else\f[R], \f[CR]endif\f[R], \f[CR]for\f[R], \f[CR]sep\f[R], and \f[CR]endfor\f[R] may not be used as variable names. Examples: .IP .EX $foo$ $foo.bar.baz$ $foo_bar.baz\-bim$ $ foo $ ${foo} ${foo.bar.baz} ${foo_bar.baz\-bim} ${ foo } .EE .PP Variable names with periods are used to get at structured variable values. So, for example, \f[CR]employee.salary\f[R] will return the value of the \f[CR]salary\f[R] field of the object that is the value of the \f[CR]employee\f[R] field. .IP \(bu 2 If the value of the variable is a simple value, it will be rendered verbatim. (Note that no escaping is done; the assumption is that the calling program will escape the strings appropriately for the output format.) .IP \(bu 2 If the value is a list, the values will be concatenated. .IP \(bu 2 If the value is a map, the string \f[CR]true\f[R] will be rendered. .IP \(bu 2 Every other value will be rendered as the empty string. .SS Conditionals A conditional begins with \f[CR]if(variable)\f[R] (enclosed in matched delimiters) and ends with \f[CR]endif\f[R] (enclosed in matched delimiters). It may optionally contain an \f[CR]else\f[R] (enclosed in matched delimiters). The \f[CR]if\f[R] section is used if \f[CR]variable\f[R] has a true value, otherwise the \f[CR]else\f[R] section is used (if present). The following values count as true: .IP \(bu 2 any map .IP \(bu 2 any array containing at least one true value .IP \(bu 2 any nonempty string .IP \(bu 2 boolean True .PP Note that in YAML metadata (and metadata specified on the command line using \f[CR]\-M/\-\-metadata\f[R]), unquoted \f[CR]true\f[R] and \f[CR]false\f[R] will be interpreted as Boolean values. But a variable specified on the command line using \f[CR]\-V/\-\-variable\f[R] will always be given a string value. Hence a conditional \f[CR]if(foo)\f[R] will be triggered if you use \f[CR]\-V foo=false\f[R], but not if you use \f[CR]\-M foo=false\f[R]. .PP Examples: .IP .EX $if(foo)$bar$endif$ $if(foo)$ $foo$ $endif$ $if(foo)$ part one $else$ part two $endif$ ${if(foo)}bar${endif} ${if(foo)} ${foo} ${endif} ${if(foo)} ${ foo.bar } ${else} no foo! ${endif} .EE .PP The keyword \f[CR]elseif\f[R] may be used to simplify complex nested conditionals: .IP .EX $if(foo)$ XXX $elseif(bar)$ YYY $else$ ZZZ $endif$ .EE .SS For loops A for loop begins with \f[CR]for(variable)\f[R] (enclosed in matched delimiters) and ends with \f[CR]endfor\f[R] (enclosed in matched delimiters). .IP \(bu 2 If \f[CR]variable\f[R] is an array, the material inside the loop will be evaluated repeatedly, with \f[CR]variable\f[R] being set to each value of the array in turn, and concatenated. .IP \(bu 2 If \f[CR]variable\f[R] is a map, the material inside will be set to the map. .IP \(bu 2 If the value of the associated variable is not an array or a map, a single iteration will be performed on its value. .PP Examples: .IP .EX $for(foo)$$foo$$sep$, $endfor$ $for(foo)$ \- $foo.last$, $foo.first$ $endfor$ ${ for(foo.bar) } \- ${ foo.bar.last }, ${ foo.bar.first } ${ endfor } $for(mymap)$ $it.name$: $it.office$ $endfor$ .EE .PP You may optionally specify a separator between consecutive values using \f[CR]sep\f[R] (enclosed in matched delimiters). The material between \f[CR]sep\f[R] and the \f[CR]endfor\f[R] is the separator. .IP .EX ${ for(foo) }${ foo }${ sep }, ${ endfor } .EE .PP Instead of using \f[CR]variable\f[R] inside the loop, the special anaphoric keyword \f[CR]it\f[R] may be used. .IP .EX ${ for(foo.bar) } \- ${ it.last }, ${ it.first } ${ endfor } .EE .SS Partials Partials (subtemplates stored in different files) may be included by using the name of the partial, followed by \f[CR]()\f[R], for example: .IP .EX ${ styles() } .EE .PP Partials will be sought in the directory containing the main template. The file name will be assumed to have the same extension as the main template if it lacks an extension. When calling the partial, the full name including file extension can also be used: .IP .EX ${ styles.html() } .EE .PP (If a partial is not found in the directory of the template and the template path is given as a relative path, it will also be sought in the \f[CR]templates\f[R] subdirectory of the user data directory.) .PP Partials may optionally be applied to variables using a colon: .IP .EX ${ date:fancy() } ${ articles:bibentry() } .EE .PP If \f[CR]articles\f[R] is an array, this will iterate over its values, applying the partial \f[CR]bibentry()\f[R] to each one. So the second example above is equivalent to .IP .EX ${ for(articles) } ${ it:bibentry() } ${ endfor } .EE .PP Note that the anaphoric keyword \f[CR]it\f[R] must be used when iterating over partials. In the above examples, the \f[CR]bibentry\f[R] partial should contain \f[CR]it.title\f[R] (and so on) instead of \f[CR]articles.title\f[R]. .PP Final newlines are omitted from included partials. .PP Partials may include other partials. .PP A separator between values of an array may be specified in square brackets, immediately after the variable name or partial: .IP .EX ${months[, ]} ${articles:bibentry()[; ]} .EE .PP The separator in this case is literal and (unlike with \f[CR]sep\f[R] in an explicit \f[CR]for\f[R] loop) cannot contain interpolated variables or other template directives. .SS Nesting To ensure that content is \(lqnested,\(rq that is, subsequent lines indented, use the \f[CR]\(ha\f[R] directive: .IP .EX $item.number$ $\(ha$$item.description$ ($item.price$) .EE .PP In this example, if \f[CR]item.description\f[R] has multiple lines, they will all be indented to line up with the first line: .IP .EX 00123 A fine bottle of 18\-year old Oban whiskey. ($148) .EE .PP To nest multiple lines to the same level, align them with the \f[CR]\(ha\f[R] directive in the template. For example: .IP .EX $item.number$ $\(ha$$item.description$ ($item.price$) (Available til $item.sellby$.) .EE .PP will produce .IP .EX 00123 A fine bottle of 18\-year old Oban whiskey. ($148) (Available til March 30, 2020.) .EE .PP If a variable occurs by itself on a line, preceded by whitespace and not followed by further text or directives on the same line, and the variable\(cqs value contains multiple lines, it will be nested automatically. .SS Breakable spaces Normally, spaces in the template itself (as opposed to values of the interpolated variables) are not breakable, but they can be made breakable in part of the template by using the \f[CR]\(ti\f[R] keyword (ended with another \f[CR]\(ti\f[R]). .IP .EX $\(ti$This long line may break if the document is rendered with a short line length.$\(ti$ .EE .SS Pipes A pipe transforms the value of a variable or partial. Pipes are specified using a slash (\f[CR]/\f[R]) between the variable name (or partial) and the pipe name. Example: .IP .EX $for(name)$ $name/uppercase$ $endfor$ $for(metadata/pairs)$ \- $it.key$: $it.value$ $endfor$ $employee:name()/uppercase$ .EE .PP Pipes may be chained: .IP .EX $for(employees/pairs)$ $it.key/alpha/uppercase$. $it.name$ $endfor$ .EE .PP Some pipes take parameters: .IP .EX |\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-|\-\-\-\-\-\-\-\-\-\-\-\-| $for(employee)$ $it.name.first/uppercase/left 20 \(dq| \(dq$$it.name.salary/right 10 \(dq | \(dq \(dq |\(dq$ $endfor$ |\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-|\-\-\-\-\-\-\-\-\-\-\-\-| .EE .PP Currently the following pipes are predefined: .IP \(bu 2 \f[CR]pairs\f[R]: Converts a map or array to an array of maps, each with \f[CR]key\f[R] and \f[CR]value\f[R] fields. If the original value was an array, the \f[CR]key\f[R] will be the array index, starting with 1. .IP \(bu 2 \f[CR]uppercase\f[R]: Converts text to uppercase. .IP \(bu 2 \f[CR]lowercase\f[R]: Converts text to lowercase. .IP \(bu 2 \f[CR]length\f[R]: Returns the length of the value: number of characters for a textual value, number of elements for a map or array. .IP \(bu 2 \f[CR]reverse\f[R]: Reverses a textual value or array, and has no effect on other values. .IP \(bu 2 \f[CR]first\f[R]: Returns the first value of an array, if applied to a non\-empty array; otherwise returns the original value. .IP \(bu 2 \f[CR]last\f[R]: Returns the last value of an array, if applied to a non\-empty array; otherwise returns the original value. .IP \(bu 2 \f[CR]rest\f[R]: Returns all but the first value of an array, if applied to a non\-empty array; otherwise returns the original value. .IP \(bu 2 \f[CR]allbutlast\f[R]: Returns all but the last value of an array, if applied to a non\-empty array; otherwise returns the original value. .IP \(bu 2 \f[CR]chomp\f[R]: Removes trailing newlines (and breakable space). .IP \(bu 2 \f[CR]nowrap\f[R]: Disables line wrapping on breakable spaces. .IP \(bu 2 \f[CR]alpha\f[R]: Converts textual values that can be read as an integer into lowercase alphabetic characters \f[CR]a..z\f[R] (mod 26). This can be used to get lettered enumeration from array indices. To get uppercase letters, chain with \f[CR]uppercase\f[R]. .IP \(bu 2 \f[CR]roman\f[R]: Converts textual values that can be read as an integer into lowercase roman numerals. This can be used to get lettered enumeration from array indices. To get uppercase roman, chain with \f[CR]uppercase\f[R]. .IP \(bu 2 \f[CR]left n \(dqleftborder\(dq \(dqrightborder\(dq\f[R]: Renders a textual value in a block of width \f[CR]n\f[R], aligned to the left, with an optional left and right border. Has no effect on other values. This can be used to align material in tables. Widths are positive integers indicating the number of characters. Borders are strings inside double quotes; literal \f[CR]\(dq\f[R] and \f[CR]\(rs\f[R] characters must be backslash\-escaped. .IP \(bu 2 \f[CR]right n \(dqleftborder\(dq \(dqrightborder\(dq\f[R]: Renders a textual value in a block of width \f[CR]n\f[R], aligned to the right, and has no effect on other values. .IP \(bu 2 \f[CR]center n \(dqleftborder\(dq \(dqrightborder\(dq\f[R]: Renders a textual value in a block of width \f[CR]n\f[R], aligned to the center, and has no effect on other values. .SS Variables .SS Metadata variables .TP \f[CR]title\f[R], \f[CR]author\f[R], \f[CR]date\f[R] allow identification of basic aspects of the document. Included in PDF metadata through LaTeX and ConTeXt. These can be set through a pandoc title block, which allows for multiple authors, or through a YAML metadata block: .RS .IP .EX \-\-\- author: \- Aristotle \- Peter Abelard \&... .EE Note that if you just want to set PDF or HTML metadata, without including a title block in the document itself, you can set the \f[CR]title\-meta\f[R], \f[CR]author\-meta\f[R], and \f[CR]date\-meta\f[R] variables. (By default these are set automatically, based on \f[CR]title\f[R], \f[CR]author\f[R], and \f[CR]date\f[R].) The page title in HTML is set by \f[CR]pagetitle\f[R], which is equal to \f[CR]title\f[R] by default. .RE .TP \f[CR]subtitle\f[R] document subtitle, included in HTML, EPUB, LaTeX, ConTeXt, and docx documents .TP \f[CR]abstract\f[R] document summary, included in HTML, LaTeX, ConTeXt, AsciiDoc, and docx documents .TP \f[CR]abstract\-title\f[R] title of abstract, currently used only in HTML, EPUB, docx, and Typst. This will be set automatically to a localized value, depending on \f[CR]lang\f[R], but can be manually overridden. .TP \f[CR]keywords\f[R] list of keywords to be included in HTML, PDF, ODT, pptx, docx and AsciiDoc metadata; repeat as for \f[CR]author\f[R], above .TP \f[CR]subject\f[R] document subject, included in ODT, PDF, docx, EPUB, and pptx metadata .TP \f[CR]description\f[R] document description, included in ODT, docx and pptx metadata. Some applications show this as \f[CR]Comments\f[R] metadata. .TP \f[CR]category\f[R] document category, included in docx and pptx metadata .PP Additionally, any root\-level string metadata, not included in ODT, docx or pptx metadata is added as a \f[I]custom property\f[R]. The following YAML metadata block for instance: .IP .EX \-\-\- title: \(aqThis is the title\(aq subtitle: \(dqThis is the subtitle\(dq author: \- Author One \- Author Two description: | This is a long description. It consists of two paragraphs \&... .EE .PP will include \f[CR]title\f[R], \f[CR]author\f[R] and \f[CR]description\f[R] as standard document properties and \f[CR]subtitle\f[R] as a custom property when converting to docx, ODT or pptx. .SS Language variables .TP \f[CR]lang\f[R] identifies the main language of the document using IETF language tags (following the BCP 47 standard), such as \f[CR]en\f[R] or \f[CR]en\-GB\f[R]. The Language subtag lookup tool can look up or verify these tags. This affects most formats, and controls hyphenation in PDF output when using LaTeX (through \f[CR]babel\f[R] and \f[CR]polyglossia\f[R]) or ConTeXt. .RS Use native pandoc Divs and Spans with the \f[CR]lang\f[R] attribute to switch the language: .IP .EX \-\-\- lang: en\-GB \&... Text in the main document language (British English). ::: {lang=fr\-CA} > Cette citation est écrite en français canadien. ::: More text in English. [\(aqZitat auf Deutsch.\(aq]{lang=de} .EE .RE .TP \f[CR]dir\f[R] the base script direction, either \f[CR]rtl\f[R] (right\-to\-left) or \f[CR]ltr\f[R] (left\-to\-right). .RS For bidirectional documents, native pandoc \f[CR]span\f[R]s and \f[CR]div\f[R]s with the \f[CR]dir\f[R] attribute (value \f[CR]rtl\f[R] or \f[CR]ltr\f[R]) can be used to override the base direction in some output formats. This may not always be necessary if the final renderer (e.g.\ the browser, when generating HTML) supports the Unicode Bidirectional Algorithm. When using LaTeX for bidirectional documents, only the \f[CR]xelatex\f[R] engine is fully supported (use \f[CR]\-\-pdf\-engine=xelatex\f[R]). .RE .SS Variables for HTML .TP \f[CR]document\-css\f[R] Enables inclusion of most of the CSS in the \f[CR]styles.html\f[R] partial (have a look with \f[CR]pandoc \-\-print\-default\-data\-file=templates/styles.html\f[R]). Unless you use \f[CR]\-\-css\f[R], this variable is set to \f[CR]true\f[R] by default. You can disable it with e.g.\ \f[CR]pandoc \-M document\-css=false\f[R]. .TP \f[CR]mainfont\f[R] sets the CSS \f[CR]font\-family\f[R] property on the \f[CR]html\f[R] element. .TP \f[CR]fontsize\f[R] sets the base CSS \f[CR]font\-size\f[R], which you\(cqd usually set to e.g.\ \f[CR]20px\f[R], but it also accepts \f[CR]pt\f[R] (12pt = 16px in most browsers). .TP \f[CR]fontcolor\f[R] sets the CSS \f[CR]color\f[R] property on the \f[CR]html\f[R] element. .TP \f[CR]linkcolor\f[R] sets the CSS \f[CR]color\f[R] property on all links. .TP \f[CR]monofont\f[R] sets the CSS \f[CR]font\-family\f[R] property on \f[CR]code\f[R] elements. .TP \f[CR]monobackgroundcolor\f[R] sets the CSS \f[CR]background\-color\f[R] property on \f[CR]code\f[R] elements and adds extra padding. .TP \f[CR]linestretch\f[R] sets the CSS \f[CR]line\-height\f[R] property on the \f[CR]html\f[R] element, which is preferred to be unitless. .TP \f[CR]maxwidth\f[R] sets the CSS \f[CR]max\-width\f[R] property (default is 36em). .TP \f[CR]backgroundcolor\f[R] sets the CSS \f[CR]background\-color\f[R] property on the \f[CR]html\f[R] element. .TP \f[CR]margin\-left\f[R], \f[CR]margin\-right\f[R], \f[CR]margin\-top\f[R], \f[CR]margin\-bottom\f[R] sets the corresponding CSS \f[CR]padding\f[R] properties on the \f[CR]body\f[R] element. .PP To override or extend some CSS for just one document, include for example: .IP .EX \-\-\- header\-includes: | \-\-\- .EE .SS Variables for HTML math .TP \f[CR]classoption\f[R] when using \f[CR]\-\-katex\f[R], you can render display math equations flush left using YAML metadata or with \f[CR]\-M classoption=fleqn\f[R]. .SS Variables for HTML slides These affect HTML output when producing slide shows with pandoc. .TP \f[CR]institute\f[R] author affiliations: can be a list when there are multiple authors .TP \f[CR]revealjs\-url\f[R] base URL for reveal.js documents (defaults to \f[CR]https://unpkg.com/reveal.js\(at\(ha5\f[R]) .TP \f[CR]s5\-url\f[R] base URL for S5 documents (defaults to \f[CR]s5/default\f[R]) .TP \f[CR]slidy\-url\f[R] base URL for Slidy documents (defaults to \f[CR]https://www.w3.org/Talks/Tools/Slidy2\f[R]) .TP \f[CR]slideous\-url\f[R] base URL for Slideous documents (defaults to \f[CR]slideous\f[R]) .TP \f[CR]title\-slide\-attributes\f[R] additional attributes for the title slide of reveal.js slide shows. See background in reveal.js, beamer, and pptx for an example. .TP \f[CR]highlightjs\-theme\f[R] highlight.js theme for code highlighting when using \f[CR]\-\-syntax\-highlighting=idiomatic\f[R] with reveal.js (defaults to \f[CR]monokai\f[R]). See the highlight.js demo page for available themes. .PP All reveal.js configuration options are available as variables. To turn off boolean flags that default to true in reveal.js, use \f[CR]0\f[R]. .SS Variables for Beamer slides These variables change the appearance of PDF slides using \f[CR]beamer\f[R]. .TP \f[CR]aspectratio\f[R] slide aspect ratio (\f[CR]43\f[R] for 4:3 [default], \f[CR]169\f[R] for 16:9, \f[CR]1610\f[R] for 16:10, \f[CR]149\f[R] for 14:9, \f[CR]141\f[R] for 1.41:1, \f[CR]54\f[R] for 5:4, \f[CR]32\f[R] for 3:2) .TP \f[CR]beameroption\f[R] add extra beamer option with \f[CR]\(rssetbeameroption{}\f[R] .TP \f[CR]institute\f[R] author affiliations: can be a list when there are multiple authors .TP \f[CR]logo\f[R] logo image for slides .TP \f[CR]logooptions\f[R] options for logo image (e.g., \f[CR]width\f[R], \f[CR]height\f[R]) .TP \f[CR]navigation\f[R] controls navigation symbols (default is \f[CR]empty\f[R] for no navigation symbols; other valid values are \f[CR]frame\f[R], \f[CR]vertical\f[R], and \f[CR]horizontal\f[R]) .TP \f[CR]section\-titles\f[R] enables \(lqtitle pages\(rq for new sections (default is true) .TP \f[CR]theme\f[R], \f[CR]colortheme\f[R], \f[CR]fonttheme\f[R], \f[CR]innertheme\f[R], \f[CR]outertheme\f[R] beamer themes .TP \f[CR]themeoptions\f[R], \f[CR]colorthemeoptions\f[R], \f[CR]fontthemeoptions\f[R], \f[CR]innerthemeoptions\f[R], \f[CR]outerthemeoptions\f[R] options for LaTeX beamer themes (lists) .TP \f[CR]titlegraphic\f[R] image for title slide: can be a list .TP \f[CR]titlegraphicoptions\f[R] options for title slide image (e.g., \f[CR]width\f[R], \f[CR]height\f[R]) .TP \f[CR]shorttitle\f[R], \f[CR]shortsubtitle\f[R], \f[CR]shortauthor\f[R], \f[CR]shortinstitute\f[R], \f[CR]shortdate\f[R] some beamer themes use short versions of the title, subtitle, author, institute, date .SS Variables for PowerPoint These variables control the visual aspects of a slide show that are not easily controlled via templates. .TP \f[CR]monofont\f[R] font to use for code. .SS Variables for LaTeX Pandoc uses these variables when creating a PDF with a LaTeX engine. .SS Layout .TP \f[CR]block\-headings\f[R] make \f[CR]\(rsparagraph\f[R] and \f[CR]\(rssubparagraph\f[R] (fourth\- and fifth\-level headings, or fifth\- and sixth\-level with book classes) free\-standing rather than run\-in; requires further formatting to distinguish from \f[CR]\(rssubsubsection\f[R] (third\- or fourth\-level headings). Instead of using this option, KOMA\-Script can adjust headings more extensively: .RS .IP .EX \-\-\- documentclass: scrartcl header\-includes: | \(rsRedeclareSectionCommand[ beforeskip=\-10pt plus \-2pt minus \-1pt, afterskip=1sp plus \-1sp minus 1sp, font=\(rsnormalfont\(rsitshape]{paragraph} \(rsRedeclareSectionCommand[ beforeskip=\-10pt plus \-2pt minus \-1pt, afterskip=1sp plus \-1sp minus 1sp, font=\(rsnormalfont\(rsscshape, indent=0pt]{subparagraph} \&... .EE .RE .TP \f[CR]classoption\f[R] option for document class, e.g.\ \f[CR]oneside\f[R]; repeat for multiple options: .RS .IP .EX \-\-\- classoption: \- twocolumn \- landscape \&... .EE .RE .TP \f[CR]documentclass\f[R] document class: usually one of the standard classes, \f[CR]article\f[R], \f[CR]book\f[R], and \f[CR]report\f[R]; the KOMA\-Script equivalents, \f[CR]scrartcl\f[R], \f[CR]scrbook\f[R], and \f[CR]scrreprt\f[R], which default to smaller margins; or \f[CR]memoir\f[R] .TP \f[CR]geometry\f[R] option for \f[CR]geometry\f[R] package, e.g.\ \f[CR]margin=1in\f[R]; repeat for multiple options: .RS .IP .EX \-\-\- geometry: \- top=30mm \- left=20mm \- heightrounded \&... .EE .RE .TP \f[CR]shorthands\f[R] Enable language\-specific shorthands when loading \f[CR]babel\f[R]. (By default, pandoc includes \f[CR]shorthands=off\f[R] when loading \f[CR]babel\f[R], disabling language\-specific shorthands.) .TP \f[CR]hyperrefoptions\f[R] option for \f[CR]hyperref\f[R] package, e.g.\ \f[CR]linktoc=all\f[R]; repeat for multiple options: .RS .IP .EX \-\-\- hyperrefoptions: \- linktoc=all \- pdfwindowui \- pdfpagemode=FullScreen \&... .EE .RE .TP \f[CR]indent\f[R] if true, pandoc will use document class settings for indentation (the default LaTeX template otherwise removes indentation and adds space between paragraphs) .TP \f[CR]linestretch\f[R] adjusts line spacing using the \f[CR]setspace\f[R] package, e.g.\ \f[CR]1.25\f[R], \f[CR]1.5\f[R] .TP \f[CR]margin\-left\f[R], \f[CR]margin\-right\f[R], \f[CR]margin\-top\f[R], \f[CR]margin\-bottom\f[R] sets margins if \f[CR]geometry\f[R] is not used (otherwise \f[CR]geometry\f[R] overrides these) .TP \f[CR]pagestyle\f[R] control \f[CR]\(rspagestyle{}\f[R]: the default article class supports \f[CR]plain\f[R] (default), \f[CR]empty\f[R] (no running heads or page numbers), and \f[CR]headings\f[R] (section titles in running heads) .TP \f[CR]papersize\f[R] paper size, e.g.\ \f[CR]letter\f[R], \f[CR]a4\f[R] .TP \f[CR]secnumdepth\f[R] numbering depth for sections (with \f[CR]\-\-number\-sections\f[R] option or \f[CR]numbersections\f[R] variable) .TP \f[CR]beamerarticle\f[R] produce an article from Beamer slides. Note: if you set this variable, you must specify the beamer writer but use the default \f[I]LaTeX\f[R] template: for example, \f[CR]pandoc \-Vbeamerarticle \-t beamer \-\-template default.latex\f[R]. .TP \f[CR]handout\f[R] produce a handout version of Beamer slides (with overlays condensed into single slides) .TP \f[CR]csquotes\f[R] load \f[CR]csquotes\f[R] package and use \f[CR]\(rsenquote\f[R] or \f[CR]\(rsenquote*\f[R] for quoted text. .TP \f[CR]csquotesoptions\f[R] options to use for \f[CR]csquotes\f[R] package (repeat for multiple options). .TP \f[CR]babeloptions\f[R] options to pass to the babel package (may be repeated for multiple options). This defaults to \f[CR]provide=*\f[R] if the main language isn\(cqt a European language written with Latin or Cyrillic script or Vietnamese. Most users will not need to adjust the default setting. .SS Fonts .TP \f[CR]fontenc\f[R] allows font encoding to be specified through \f[CR]fontenc\f[R] package (with \f[CR]pdflatex\f[R]); default is \f[CR]T1\f[R] (see LaTeX font encodings guide) .TP \f[CR]fontfamily\f[R] font package for use with \f[CR]pdflatex\f[R]: TeX Live includes many options, documented in the LaTeX Font Catalogue. The default is Latin Modern. .TP \f[CR]fontfamilyoptions\f[R] options for package used as \f[CR]fontfamily\f[R]; repeat for multiple options. For example, to use the Libertine font with proportional lowercase (old\-style) figures through the \f[CR]libertinus\f[R] package: .RS .IP .EX \-\-\- fontfamily: libertinus fontfamilyoptions: \- osf \- p \&... .EE .RE .TP \f[CR]fontsize\f[R] font size for body text. The standard classes allow 10pt, 11pt, and 12pt. To use another size, set \f[CR]documentclass\f[R] to one of the KOMA\-Script classes, such as \f[CR]scrartcl\f[R] or \f[CR]scrbook\f[R]. .TP \f[CR]mainfont\f[R], \f[CR]sansfont\f[R], \f[CR]monofont\f[R], \f[CR]mathfont\f[R], \f[CR]CJKmainfont\f[R], \f[CR]CJKsansfont\f[R], \f[CR]CJKmonofont\f[R] font families for use with \f[CR]xelatex\f[R] or \f[CR]lualatex\f[R]: take the name of any system font, using the \f[CR]fontspec\f[R] package. \f[CR]CJKmainfont\f[R] uses the \f[CR]xecjk\f[R] package if \f[CR]xelatex\f[R] is used, or the \f[CR]luatexja\f[R] package if \f[CR]lualatex\f[R] is used. .TP \f[CR]mainfontoptions\f[R], \f[CR]sansfontoptions\f[R], \f[CR]monofontoptions\f[R], \f[CR]mathfontoptions\f[R], \f[CR]CJKoptions\f[R], \f[CR]luatexjapresetoptions\f[R] options to use with \f[CR]mainfont\f[R], \f[CR]sansfont\f[R], \f[CR]monofont\f[R], \f[CR]mathfont\f[R], \f[CR]CJKmainfont\f[R] in \f[CR]xelatex\f[R] and \f[CR]lualatex\f[R]. Allow for any choices available through \f[CR]fontspec\f[R]; repeat for multiple options. For example, to use the TeX Gyre version of Palatino with lowercase figures: .RS .IP .EX \-\-\- mainfont: TeX Gyre Pagella mainfontoptions: \- Numbers=Lowercase \- Numbers=Proportional \&... .EE .RE .TP \f[CR]mainfontfallback\f[R], \f[CR]sansfontfallback\f[R], \f[CR]monofontfallback\f[R] fonts to try if a glyph isn\(cqt found in \f[CR]mainfont\f[R], \f[CR]sansfont\f[R], or \f[CR]monofont\f[R] respectively. These are lists. The font name must be followed by a colon and optionally a set of options, for example: .RS .IP .EX \-\-\- mainfontfallback: \- \(dqFreeSans:\(dq \- \(dqNotoColorEmoji:mode=harf\(dq \&... .EE Font fallbacks currently only work with \f[CR]lualatex\f[R]. .RE .TP \f[CR]babelfonts\f[R] a map of Babel language names (e.g.\ \f[CR]chinese\f[R]) to the font to be used with the language: .RS .IP .EX \-\-\- babelfonts: chinese\-hant: \(dqNoto Serif CJK TC\(dq russian: \(dqNoto Serif\(dq \&... .EE .RE .TP \f[CR]microtypeoptions\f[R] options to pass to the microtype package .SS Links .TP \f[CR]colorlinks\f[R] add color to link text; automatically enabled if any of \f[CR]linkcolor\f[R], \f[CR]filecolor\f[R], \f[CR]citecolor\f[R], \f[CR]urlcolor\f[R], or \f[CR]toccolor\f[R] are set .TP \f[CR]boxlinks\f[R] add visible box around links (has no effect if \f[CR]colorlinks\f[R] is set) .TP \f[CR]linkcolor\f[R], \f[CR]filecolor\f[R], \f[CR]citecolor\f[R], \f[CR]urlcolor\f[R], \f[CR]toccolor\f[R] color for internal links, external links, citation links, linked URLs, and links in table of contents, respectively: uses options allowed by \f[CR]xcolor\f[R], including the \f[CR]dvipsnames\f[R], \f[CR]svgnames\f[R], and \f[CR]x11names\f[R] lists .TP \f[CR]links\-as\-notes\f[R] causes links to be printed as footnotes .TP \f[CR]urlstyle\f[R] style for URLs (e.g., \f[CR]tt\f[R], \f[CR]rm\f[R], \f[CR]sf\f[R], and, the default, \f[CR]same\f[R]) .SS Front matter .TP \f[CR]lof\f[R], \f[CR]lot\f[R] include list of figures, list of tables (can also be set using \f[CR]\-\-lof/\-\-list\-of\-figures\f[R], \f[CR]\-\-lot/\-\-list\-of\-tables\f[R]) .TP \f[CR]thanks\f[R] contents of acknowledgments footnote after document title .TP \f[CR]toc\f[R] include table of contents (can also be set using \f[CR]\-\-toc/\-\-table\-of\-contents\f[R]) .TP \f[CR]toc\-depth\f[R] level of section to include in table of contents .SS BibLaTeX Bibliographies These variables function when using BibLaTeX for citation rendering. .TP \f[CR]biblatexoptions\f[R] list of options for biblatex .TP \f[CR]biblio\-style\f[R] bibliography style, when used with \f[CR]\-\-natbib\f[R] and \f[CR]\-\-biblatex\f[R] .TP \f[CR]biblio\-title\f[R] bibliography title, when used with \f[CR]\-\-natbib\f[R] and \f[CR]\-\-biblatex\f[R] .TP \f[CR]bibliography\f[R] bibliography to use for resolving references .TP \f[CR]natbiboptions\f[R] list of options for natbib .SS Other .TP \f[CR]pdf\-trailer\-id\f[R] the PDF trailer ID; must be two PDF byte strings if set, conventionally with 16 bytes each. E.g., \f[CR]<00112233445566778899aabbccddeeff> <00112233445566778899aabbccddeeff>\f[R]. .RS See the section on reproducible builds. .RE .TP \f[CR]pdfstandard\f[R] PDF standard(s) for the document, e.g.\ \f[CR]ua\-2\f[R], \f[CR]a\-4f\f[R]. Supports PDF/A, PDF/X, and PDF/UA variants. Requires LuaLaTeX and LaTeX 2023+. Repeat for multiple standards: .RS .IP .EX \-\-\- pdfstandard: \- ua\-2 \- a\-4f \&... .EE .RE .SS Variables for ConTeXt Pandoc uses these variables when creating a PDF with ConTeXt. .TP \f[CR]fontsize\f[R] font size for body text (e.g.\ \f[CR]10pt\f[R], \f[CR]12pt\f[R]) .TP \f[CR]headertext\f[R], \f[CR]footertext\f[R] text to be placed in running header or footer (see ConTeXt Headers and Footers); repeat up to four times for different placement .TP \f[CR]indenting\f[R] controls indentation of paragraphs, e.g.\ \f[CR]yes,small,next\f[R] (see ConTeXt Indentation); repeat for multiple options .TP \f[CR]interlinespace\f[R] adjusts line spacing, e.g.\ \f[CR]4ex\f[R] (using \f[CR]setupinterlinespace\f[R]); repeat for multiple options .TP \f[CR]layout\f[R] options for page margins and text arrangement (see ConTeXt Layout); repeat for multiple options .TP \f[CR]linkcolor\f[R], \f[CR]contrastcolor\f[R] color for links outside and inside a page, e.g.\ \f[CR]red\f[R], \f[CR]blue\f[R] (see ConTeXt Color) .TP \f[CR]linkstyle\f[R] typeface style for links, e.g.\ \f[CR]normal\f[R], \f[CR]bold\f[R], \f[CR]slanted\f[R], \f[CR]boldslanted\f[R], \f[CR]type\f[R], \f[CR]cap\f[R], \f[CR]small\f[R] .TP \f[CR]lof\f[R], \f[CR]lot\f[R] include list of figures, list of tables .TP \f[CR]mainfont\f[R], \f[CR]sansfont\f[R], \f[CR]monofont\f[R], \f[CR]mathfont\f[R] font families: take the name of any system font (see ConTeXt Font Switching) .TP \f[CR]mainfontfallback\f[R], \f[CR]sansfontfallback\f[R], \f[CR]monofontfallback\f[R] list of fonts to try, in order, if a glyph is not found in the main font. Use \f[CR]\(rsdefinefallbackfamily\f[R]\-compatible font name syntax. Emoji fonts are unsupported. .TP \f[CR]margin\-left\f[R], \f[CR]margin\-right\f[R], \f[CR]margin\-top\f[R], \f[CR]margin\-bottom\f[R] sets margins, if \f[CR]layout\f[R] is not used (otherwise \f[CR]layout\f[R] overrides these) .TP \f[CR]pagenumbering\f[R] page number style and location (using \f[CR]setuppagenumbering\f[R]); repeat for multiple options .TP \f[CR]papersize\f[R] paper size, e.g.\ \f[CR]letter\f[R], \f[CR]A4\f[R], \f[CR]landscape\f[R] (see ConTeXt Paper Setup); repeat for multiple options .TP \f[CR]pdfa\f[R] adds to the preamble the setup necessary to generate PDF/A of the type specified, e.g.\ \f[CR]1a:2005\f[R], \f[CR]2a\f[R]. If no type is specified (i.e.\ the value is set to True, by e.g. \f[CR]\-\-metadata=pdfa\f[R] or \f[CR]pdfa: true\f[R] in a YAML metadata block), \f[CR]1b:2005\f[R] will be used as default, for reasons of backwards compatibility. Using \f[CR]\-\-variable=pdfa\f[R] without specified value is not supported. To successfully generate PDF/A the required ICC color profiles have to be available and the content and all included files (such as images) have to be standard\-conforming. The ICC profiles and output intent may be specified using the variables \f[CR]pdfaiccprofile\f[R] and \f[CR]pdfaintent\f[R]. See also ConTeXt PDFA for more details. .TP \f[CR]pdfaiccprofile\f[R] when used in conjunction with \f[CR]pdfa\f[R], specifies the ICC profile to use in the PDF, e.g.\ \f[CR]default.cmyk\f[R]. If left unspecified, \f[CR]sRGB.icc\f[R] is used as default. May be repeated to include multiple profiles. Note that the profiles have to be available on the system. They can be obtained from ConTeXt ICC Profiles. .TP \f[CR]pdfaintent\f[R] when used in conjunction with \f[CR]pdfa\f[R], specifies the output intent for the colors, e.g.\ \f[CR]ISO coated v2 300\(rsletterpercent\(rsspace (ECI)\f[R] If left unspecified, \f[CR]sRGB IEC61966\-2.1\f[R] is used as default. .TP \f[CR]toc\f[R] include table of contents (can also be set using \f[CR]\-\-toc/\-\-table\-of\-contents\f[R]) .TP \f[CR]urlstyle\f[R] typeface style for links without link text, e.g.\ \f[CR]normal\f[R], \f[CR]bold\f[R], \f[CR]slanted\f[R], \f[CR]boldslanted\f[R], \f[CR]type\f[R], \f[CR]cap\f[R], \f[CR]small\f[R] .TP \f[CR]whitespace\f[R] spacing between paragraphs, e.g.\ \f[CR]none\f[R], \f[CR]small\f[R] (using \f[CR]setupwhitespace\f[R]) .TP \f[CR]includesource\f[R] include all source documents as file attachments in the PDF file .SS Variables for \f[CR]wkhtmltopdf\f[R] Pandoc uses these variables when creating a PDF with \f[CR]wkhtmltopdf\f[R]. The \f[CR]\-\-css\f[R] option also affects the output. .TP \f[CR]footer\-html\f[R], \f[CR]header\-html\f[R] add information to the header and footer .TP \f[CR]margin\-left\f[R], \f[CR]margin\-right\f[R], \f[CR]margin\-top\f[R], \f[CR]margin\-bottom\f[R] set the page margins .TP \f[CR]papersize\f[R] sets the PDF paper size .SS Variables for man pages .TP \f[CR]adjusting\f[R] adjusts text to left (\f[CR]l\f[R]), right (\f[CR]r\f[R]), center (\f[CR]c\f[R]), or both (\f[CR]b\f[R]) margins .TP \f[CR]footer\f[R] footer in man pages .TP \f[CR]header\f[R] header in man pages .TP \f[CR]section\f[R] section number in man pages .SS Variables for Texinfo .TP \f[CR]version\f[R] version of software (used in title and title page) .TP \f[CR]filename\f[R] name of info file to be generated (defaults to a name based on the texi filename) .SS Variables for Typst .TP \f[CR]template\f[R] Typst template to use (relative path only). .TP \f[CR]margin\f[R] A dictionary with the fields defined in the Typst documentation: \f[CR]x\f[R], \f[CR]y\f[R], \f[CR]top\f[R], \f[CR]bottom\f[R], \f[CR]left\f[R], \f[CR]right\f[R]. .TP \f[CR]papersize\f[R] Paper size: \f[CR]a4\f[R], \f[CR]us\-letter\f[R], etc. .TP \f[CR]mainfont\f[R] Name of system font to use for the main font. .TP \f[CR]fontsize\f[R] Font size (e.g., \f[CR]12pt\f[R]). .TP \f[CR]section\-numbering\f[R] Schema to use for numbering sections, e.g.\ \f[CR]1.A.1\f[R]. .TP \f[CR]page\-numbering\f[R] Schema to use for numbering pages, e.g.\ \f[CR]1\f[R] or \f[CR]i\f[R], or an empty string to omit page numbering. .TP \f[CR]columns\f[R] Number of columns for body text. .TP \f[CR]thanks\f[R] contents of acknowledgments footnote after document title .TP \f[CR]mathfont\f[R], \f[CR]codefont\f[R] Name of system font to use for math and code, respectively. .TP \f[CR]linestretch\f[R] adjusts line spacing, e.g.\ \f[CR]1.25\f[R], \f[CR]1.5\f[R] .TP \f[CR]linkcolor\f[R], \f[CR]filecolor\f[R], \f[CR]citecolor\f[R] color for external links, internal links, and citation links, respectively: expects a hexadecimal color code .SS Variables for ms .TP \f[CR]fontfamily\f[R] \f[CR]A\f[R] (Avant Garde), \f[CR]B\f[R] (Bookman), \f[CR]C\f[R] (Helvetica), \f[CR]HN\f[R] (Helvetica Narrow), \f[CR]P\f[R] (Palatino), or \f[CR]T\f[R] (Times New Roman). This setting does not affect source code, which is always displayed using monospace Courier. These built\-in fonts are limited in their coverage of characters. Additional fonts may be installed using the script \f[CR]install\-font.sh\f[R] provided by Peter Schaffter and documented in detail on his web site. .TP \f[CR]indent\f[R] paragraph indent (e.g.\ \f[CR]2m\f[R]) .TP \f[CR]lineheight\f[R] line height (e.g.\ \f[CR]12p\f[R]) .TP \f[CR]pointsize\f[R] point size (e.g.\ \f[CR]10p\f[R]) .SS Variables set automatically Pandoc sets these variables automatically in response to options or document contents; users can also modify them. These vary depending on the output format, and include the following: .TP \f[CR]body\f[R] body of document .TP \f[CR]date\-meta\f[R] the \f[CR]date\f[R] variable converted to ISO 8601 YYYY\-MM\-DD, included in all HTML based formats (dzslides, epub, html, html4, html5, revealjs, s5, slideous, slidy). The recognized formats for \f[CR]date\f[R] are: \f[CR]mm/dd/yyyy\f[R], \f[CR]mm/dd/yy\f[R], \f[CR]yyyy\-mm\-dd\f[R] (ISO 8601), \f[CR]dd MM yyyy\f[R] (e.g.\ either \f[CR]02 Apr 2018\f[R] or \f[CR]02 April 2018\f[R]), \f[CR]MM dd, yyyy\f[R] (e.g.\ \f[CR]Apr. 02, 2018\f[R] or \f[CR]April 02, 2018),\f[R]yyyy[mm[dd]]\f[CR](e.g.\f[R]20180402, \f[CR]201804\f[R] or \f[CR]2018\f[R]). .TP \f[CR]header\-includes\f[R] contents specified by \f[CR]\-H/\-\-include\-in\-header\f[R] (may have multiple values) .TP \f[CR]include\-before\f[R] contents specified by \f[CR]\-B/\-\-include\-before\-body\f[R] (may have multiple values) .TP \f[CR]include\-after\f[R] contents specified by \f[CR]\-A/\-\-include\-after\-body\f[R] (may have multiple values) .TP \f[CR]meta\-json\f[R] JSON representation of all of the document\(cqs metadata. Field values are transformed to the selected output format. .TP \f[CR]numbersections\f[R] non\-null value if \f[CR]\-N/\-\-number\-sections\f[R] was specified .TP \f[CR]sourcefile\f[R], \f[CR]outputfile\f[R] source and destination filenames, as given on the command line. \f[CR]sourcefile\f[R] can also be a list if input comes from multiple files, or empty if input is from stdin. You can use the following snippet in your template to distinguish them: .RS .IP .EX $if(sourcefile)$ $for(sourcefile)$ $sourcefile$ $endfor$ $else$ (stdin) $endif$ .EE Similarly, \f[CR]outputfile\f[R] can be \f[CR]\-\f[R] if output goes to the terminal. If you need absolute paths, use e.g.\ \f[CR]$curdir$/$sourcefile$\f[R]. .RE .TP \f[CR]pdf\-engine\f[R] name of PDF engine if provided using \f[CR]\-\-pdf\-engine\f[R], or the default engine for the format if PDF output is requested. .TP \f[CR]curdir\f[R] working directory from which pandoc is run. .TP \f[CR]pandoc\-version\f[R] pandoc version. .TP \f[CR]toc\f[R] non\-null value if \f[CR]\-\-toc/\-\-table\-of\-contents\f[R] was specified .TP \f[CR]toc\-title\f[R] title of table of contents (works only with EPUB, HTML, revealjs, opendocument, odt, docx, pptx, beamer, LaTeX). Note that in docx and pptx a custom \f[CR]toc\-title\f[R] will be picked up from metadata, but cannot be set as a variable. .SH EXTENSIONS The behavior of some of the readers and writers can be adjusted by enabling or disabling various extensions. .PP An extension can be enabled by adding \f[CR]+EXTENSION\f[R] to the format name and disabled by adding \f[CR]\-EXTENSION\f[R]. For example, \f[CR]\-\-from markdown_strict+footnotes\f[R] is strict Markdown with footnotes enabled, while \f[CR]\-\-from markdown\-footnotes\-pipe_tables\f[R] is pandoc\(cqs Markdown without footnotes or pipe tables. .PP The Markdown reader and writer make by far the most use of extensions. Extensions only used by them are therefore covered in the section Pandoc\(cqs Markdown below (see Markdown variants for \f[CR]commonmark\f[R] and \f[CR]gfm\f[R]). In the following, extensions that also work for other formats are covered. .PP Note that Markdown extensions added to the \f[CR]ipynb\f[R] format affect Markdown cells in Jupyter notebooks (as do command\-line options like \f[CR]\-\-markdown\-headings\f[R]). .SS Typography .SS Extension: \f[CR]smart\f[R] Interpret straight quotes as curly quotes, \f[CR]\-\-\-\f[R] as em\-dashes, \f[CR]\-\-\f[R] as en\-dashes, and \f[CR]...\f[R] as ellipses. Nonbreaking spaces are inserted after certain abbreviations, such as \(lqMr.\(rq .PP This extension can be enabled/disabled for the following formats: .TP input formats \f[CR]markdown\f[R], \f[CR]commonmark\f[R], \f[CR]latex\f[R], \f[CR]mediawiki\f[R], \f[CR]org\f[R], \f[CR]rst\f[R], \f[CR]twiki\f[R], \f[CR]html\f[R] .TP output formats \f[CR]markdown\f[R], \f[CR]latex\f[R], \f[CR]context\f[R], \f[CR]org\f[R], \f[CR]rst\f[R] .TP enabled by default in \f[CR]markdown\f[R], \f[CR]latex\f[R], \f[CR]context\f[R] (both input and output) .PP Note: If you are \f[I]writing\f[R] Markdown, then the \f[CR]smart\f[R] extension has the reverse effect: what would have been curly quotes comes out straight. .PP In LaTeX, \f[CR]smart\f[R] means to use the standard TeX ligatures for quotation marks (\f[CR]\(ga\(ga\f[R] and \f[CR]\(aq\(aq\f[R] for double quotes, \f[CR]\(ga\f[R] and \f[CR]\(aq\f[R] for single quotes) and dashes (\f[CR]\-\-\f[R] for en\-dash and \f[CR]\-\-\-\f[R] for em\-dash). If \f[CR]smart\f[R] is disabled, then in reading LaTeX pandoc will parse these characters literally. In writing LaTeX, enabling \f[CR]smart\f[R] tells pandoc to use the ligatures when possible; if \f[CR]smart\f[R] is disabled pandoc will use unicode quotation mark and dash characters. .SS Headings and sections .SS Extension: \f[CR]auto_identifiers\f[R] A heading without an explicitly specified identifier will be automatically assigned a unique identifier based on the heading text. .PP This extension can be enabled/disabled for the following formats: .TP input formats \f[CR]markdown\f[R], \f[CR]latex\f[R], \f[CR]rst\f[R], \f[CR]mediawiki\f[R], \f[CR]textile\f[R] .TP output formats \f[CR]markdown\f[R], \f[CR]muse\f[R] .TP enabled by default in \f[CR]markdown\f[R], \f[CR]muse\f[R] .PP The default algorithm used to derive the identifier from the heading text is: .IP \(bu 2 Remove all formatting, links, etc. .IP \(bu 2 Remove all footnotes. .IP \(bu 2 Remove all non\-alphanumeric characters, except underscores, hyphens, and periods. .IP \(bu 2 Replace all spaces and newlines with hyphens. .IP \(bu 2 Convert all alphabetic characters to lowercase. .IP \(bu 2 Remove everything up to the first letter (identifiers may not begin with a number or punctuation mark). .IP \(bu 2 If nothing is left after this, use the identifier \f[CR]section\f[R]. .PP Thus, for example, .RS -14n .IP .EX Heading Identifier \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Heading identifiers in HTML heading\-identifiers\-in\-html Maître d\(aqhôtel maître\-dhôtel *Dogs*?\-\-in *my* house? dogs\-\-in\-my\-house [HTML], [S5], or [RTF]? html\-s5\-or\-rtf 3. Applications applications 33 section .EE .RE .PP These rules should, in most cases, allow one to determine the identifier from the heading text. The exception is when several headings have the same text; in this case, the first will get an identifier as described above; the second will get the same identifier with \f[CR]\-1\f[R] appended; the third with \f[CR]\-2\f[R]; and so on. .PP (However, a different algorithm is used if \f[CR]gfm_auto_identifiers\f[R] is enabled; see below.) .PP These identifiers are used to provide link targets in the table of contents generated by the \f[CR]\-\-toc|\-\-table\-of\-contents\f[R] option. They also make it easy to provide links from one section of a document to another. A link to this section, for example, might look like this: .IP .EX See the section on [heading identifiers](#heading\-identifiers\-in\-html\-latex\-and\-context). .EE .PP Note, however, that this method of providing links to sections works only in HTML, LaTeX, and ConTeXt formats. .PP If the \f[CR]\-\-section\-divs\f[R] option is specified, then each section will be wrapped in a \f[CR]section\f[R] (or a \f[CR]div\f[R], if \f[CR]html4\f[R] was specified), and the identifier will be attached to the enclosing \f[CR]
\f[R] (or \f[CR]
\f[R]) tag rather than the heading itself. This allows entire sections to be manipulated using JavaScript or treated differently in CSS. .SS Extension: \f[CR]ascii_identifiers\f[R] Causes the identifiers produced by \f[CR]auto_identifiers\f[R] to be pure ASCII. Accents are stripped off of accented Latin letters, and non\-Latin letters are omitted. .SS Extension: \f[CR]gfm_auto_identifiers\f[R] Changes the algorithm used by \f[CR]auto_identifiers\f[R] to conform to GitHub\(cqs method. Spaces are converted to dashes (\f[CR]\-\f[R]), uppercase characters to lowercase characters, and punctuation characters other than \f[CR]\-\f[R] and \f[CR]_\f[R] are removed. Emojis are replaced by their names. .SS Math Input The extensions \f[CR]tex_math_dollars\f[R], \f[CR]tex_math_gfm\f[R], \f[CR]tex_math_single_backslash\f[R], and \f[CR]tex_math_double_backslash\f[R] are described in the section about Pandoc\(cqs Markdown. .PP However, they can also be used with HTML input. This is handy for reading web pages formatted using MathJax, for example. .SS Raw HTML/TeX The following extensions are described in more detail in their respective sections of Pandoc\(cqs Markdown: .IP \(bu 2 \f[CR]raw_html\f[R] allows HTML elements which are not representable in pandoc\(cqs AST to be parsed as raw HTML. By default, this is disabled for HTML input. .IP \(bu 2 \f[CR]raw_tex\f[R] allows raw LaTeX, TeX, and ConTeXt to be included in a document. This extension can be enabled/disabled for the following formats (in addition to \f[CR]markdown\f[R]): .RS 2 .TP input formats \f[CR]latex\f[R], \f[CR]textile\f[R], \f[CR]html\f[R] (environments, \f[CR]\(rsref\f[R], and \f[CR]\(rseqref\f[R] only), \f[CR]ipynb\f[R] .TP output formats \f[CR]textile\f[R], \f[CR]commonmark\f[R] .PP Note: as applied to \f[CR]ipynb\f[R], \f[CR]raw_html\f[R] and \f[CR]raw_tex\f[R] affect not only raw TeX in Markdown cells, but data with mime type \f[CR]text/html\f[R] in output cells. Since the \f[CR]ipynb\f[R] reader attempts to preserve the richest possible outputs when several options are given, you will get best results if you disable \f[CR]raw_html\f[R] and \f[CR]raw_tex\f[R] when converting to formats like \f[CR]docx\f[R] which don\(cqt allow raw \f[CR]html\f[R] or \f[CR]tex\f[R]. .RE .IP \(bu 2 \f[CR]native_divs\f[R] causes HTML \f[CR]div\f[R] elements to be parsed as native pandoc Div blocks. If you want them to be parsed as raw HTML, use \f[CR]\-f html\-native_divs+raw_html\f[R]. .IP \(bu 2 \f[CR]native_spans\f[R] causes HTML \f[CR]span\f[R] elements to be parsed as native pandoc Span inlines. If you want them to be parsed as raw HTML, use \f[CR]\-f html\-native_spans+raw_html\f[R]. If you want to drop all \f[CR]div\f[R]s and \f[CR]span\f[R]s when converting HTML to Markdown, you can use \f[CR]pandoc \-f html\-native_divs\-native_spans \-t markdown\f[R]. .SS Literate Haskell support .SS Extension: \f[CR]literate_haskell\f[R] Treat the document as literate Haskell source. .PP This extension can be enabled/disabled for the following formats: .TP input formats \f[CR]markdown\f[R], \f[CR]rst\f[R], \f[CR]latex\f[R] .TP output formats \f[CR]markdown\f[R], \f[CR]rst\f[R], \f[CR]latex\f[R], \f[CR]html\f[R] .PP If you append \f[CR]+lhs\f[R] (or \f[CR]+literate_haskell\f[R]) to one of the formats above, pandoc will treat the document as literate Haskell source. This means that .IP \(bu 2 In Markdown input, \(lqbird track\(rq sections will be parsed as Haskell code rather than block quotations. Text between \f[CR]\(rsbegin{code}\f[R] and \f[CR]\(rsend{code}\f[R] will also be treated as Haskell code. For ATX\-style headings the character `=' will be used instead of `#'. .IP \(bu 2 In Markdown output, code blocks with classes \f[CR]haskell\f[R] and \f[CR]literate\f[R] will be rendered using bird tracks, and block quotations will be indented one space, so they will not be treated as Haskell code. In addition, headings will be rendered setext\-style (with underlines) rather than ATX\-style (with `#' characters). (This is because ghc treats `#' characters in column 1 as introducing line numbers.) .IP \(bu 2 In restructured text input, \(lqbird track\(rq sections will be parsed as Haskell code. .IP \(bu 2 In restructured text output, code blocks with class \f[CR]haskell\f[R] will be rendered using bird tracks. .IP \(bu 2 In LaTeX input, text in \f[CR]code\f[R] environments will be parsed as Haskell code. .IP \(bu 2 In LaTeX output, code blocks with class \f[CR]haskell\f[R] will be rendered inside \f[CR]code\f[R] environments. .IP \(bu 2 In HTML output, code blocks with class \f[CR]haskell\f[R] will be rendered with class \f[CR]literatehaskell\f[R] and bird tracks. .PP Examples: .IP .EX pandoc \-f markdown+lhs \-t html .EE .PP reads literate Haskell source formatted with Markdown conventions and writes ordinary HTML (without bird tracks). .IP .EX pandoc \-f markdown+lhs \-t html+lhs .EE .PP writes HTML with the Haskell code in bird tracks, so it can be copied and pasted as literate Haskell source. .PP Note that GHC expects the bird tracks in the first column, so indented literate code blocks (e.g.\ inside an itemized environment) will not be picked up by the Haskell compiler. .SS Other extensions .SS Extension: \f[CR]empty_paragraphs\f[R] Allows empty paragraphs. By default empty paragraphs are omitted. .PP This extension can be enabled/disabled for the following formats: .TP input formats \f[CR]docx\f[R], \f[CR]html\f[R] .TP output formats \f[CR]docx\f[R], \f[CR]odt\f[R], \f[CR]opendocument\f[R], \f[CR]html\f[R], \f[CR]latex\f[R] .SS Extension: \f[CR]native_numbering\f[R] Enables native numbering of figures and tables. Enumeration starts at 1. .PP This extension can be enabled/disabled for the following formats: .TP output formats \f[CR]odt\f[R], \f[CR]opendocument\f[R], \f[CR]docx\f[R] .SS Extension: \f[CR]xrefs_name\f[R] Links to headings, figures and tables inside the document are substituted with cross\-references that will use the name or caption of the referenced item. The original link text is replaced once the generated document is refreshed. This extension can be combined with \f[CR]xrefs_number\f[R] in which case numbers will appear before the name. .PP Text in cross\-references is only made consistent with the referenced item once the document has been refreshed. .PP This extension can be enabled/disabled for the following formats: .TP output formats \f[CR]odt\f[R], \f[CR]opendocument\f[R] .SS Extension: \f[CR]xrefs_number\f[R] Links to headings, figures and tables inside the document are substituted with cross\-references that will use the number of the referenced item. The original link text is discarded. This extension can be combined with \f[CR]xrefs_name\f[R] in which case the name or caption numbers will appear after the number. .PP For the \f[CR]xrefs_number\f[R] to be useful heading numbers must be enabled in the generated document, also table and figure captions must be enabled using for example the \f[CR]native_numbering\f[R] extension. .PP Numbers in cross\-references are only visible in the final document once it has been refreshed. .PP This extension can be enabled/disabled for the following formats: .TP output formats \f[CR]odt\f[R], \f[CR]opendocument\f[R] .SS Extension: \f[CR]styles\f[R] When converting from docx, add \f[CR]custom\-styles\f[R] attributes for all docx styles, regardless of whether pandoc understands the meanings of these styles. Because attributes cannot be added directly to paragraphs or text in the pandoc AST, paragraph styles will cause Divs to be created and character styles will cause Spans to be created to hold the attributes. (Table styles will be added to the Table elements directly.) This extension can be used with docx custom styles. .TP input formats \f[CR]docx\f[R] .SS Extension: \f[CR]amuse\f[R] In the \f[CR]muse\f[R] input format, this enables Text::Amuse extensions to Emacs Muse markup. .SS Extension: \f[CR]raw_markdown\f[R] In the \f[CR]ipynb\f[R] input format, this causes Markdown cells to be included as raw Markdown blocks (allowing lossless round\-tripping) rather than being parsed. Use this only when you are targeting \f[CR]ipynb\f[R] or a Markdown\-based output format. .SS Extension: \f[CR]citations\f[R] (typst) When the \f[CR]citations\f[R] extension is enabled in \f[CR]typst\f[R] (as it is by default), \f[CR]typst\f[R] citations will be parsed as native pandoc citations, and native pandoc citations will be rendered as \f[CR]typst\f[R] citations. .SS Extension: \f[CR]citations\f[R] (org) When the \f[CR]citations\f[R] extension is enabled in \f[CR]org\f[R], org\-cite and org\-ref style citations will be parsed as native pandoc citations, and org\-cite citations will be used to render native pandoc citations. .SS Extension: \f[CR]citations\f[R] (docx) When \f[CR]citations\f[R] is enabled in \f[CR]docx\f[R], citations inserted by Zotero or Mendeley or EndNote plugins will be parsed as native pandoc citations. (Otherwise, the formatted citations generated by the bibliographic software will be parsed as regular text.) .SS Extension: \f[CR]fancy_lists\f[R] (org) Some aspects of Pandoc\(cqs Markdown fancy lists are also accepted in \f[CR]org\f[R] input, mimicking the option \f[CR]org\-list\-allow\-alphabetical\f[R] in Emacs. As in Org Mode, enabling this extension allows lowercase and uppercase alphabetical markers for ordered lists to be parsed in addition to arabic ones. Note that for Org, this does not include roman numerals or the \f[CR]#\f[R] placeholder that are enabled by the extension in Pandoc\(cqs Markdown. .SS Extension: \f[CR]element_citations\f[R] In the \f[CR]jats\f[R] output formats, this causes reference items to be replaced with \f[CR]\f[R] elements. These elements are not influenced by CSL styles, but all information on the item is included in tags. .SS Extension: \f[CR]ntb\f[R] In the \f[CR]context\f[R] output format this enables the use of Natural Tables (TABLE) instead of the default Extreme Tables (xtables). Natural tables allow more fine\-grained global customization but come at a performance penalty compared to extreme tables. .SS Extension: \f[CR]smart_quotes\f[R] (org) Interpret straight quotes as curly quotes during parsing. When \f[I]writing\f[R] Org, then the \f[CR]smart_quotes\f[R] extension has the reverse effect: what would have been curly quotes comes out straight. .PP This extension is implied if \f[CR]smart\f[R] is enabled. .SS Extension: \f[CR]special_strings\f[R] (org) Interpret \f[CR]\-\-\-\f[R] as em\-dashes, \f[CR]\-\-\f[R] as en\-dashes, \f[CR]\(rs\-\f[R] as shy hyphen, and \f[CR]...\f[R] as ellipses. .PP This extension is implied if \f[CR]smart\f[R] is enabled. .SS Extension: \f[CR]tagging\f[R] Enabling this extension with \f[CR]context\f[R] output will produce markup suitable for the production of tagged PDFs. This includes additional markers for paragraphs and alternative markup for emphasized text. The \f[CR]emphasis\-command\f[R] template variable is set if the extension is enabled. .SH PANDOC\(cqS MARKDOWN Pandoc understands an extended and slightly revised version of John Gruber\(cqs Markdown syntax. This document explains the syntax, noting differences from original Markdown. Except where noted, these differences can be suppressed by using the \f[CR]markdown_strict\f[R] format instead of \f[CR]markdown\f[R]. Extensions can be enabled or disabled to specify the behavior more granularly. They are described in the following. See also Extensions above, for extensions that work also on other formats. .SS Philosophy Markdown is designed to be easy to write, and, even more importantly, easy to read: .RS .PP A Markdown\-formatted document should be publishable as\-is, as plain text, without looking like it\(cqs been marked up with tags or formatting instructions. .PD 0 .P .PD \(en John Gruber .RE .PP This principle has guided pandoc\(cqs decisions in finding syntax for tables, footnotes, and other extensions. .PP There is, however, one respect in which pandoc\(cqs aims are different from the original aims of Markdown. Whereas Markdown was originally designed with HTML generation in mind, pandoc is designed for multiple output formats. Thus, while pandoc allows the embedding of raw HTML, it discourages it, and provides other, non\-HTMLish ways of representing important document elements like definition lists, tables, mathematics, and footnotes. .SS Paragraphs A paragraph is one or more lines of text followed by one or more blank lines. Newlines are treated as spaces, so you can reflow your paragraphs as you like. If you need a hard line break, put two or more spaces at the end of a line. .SS Extension: \f[CR]escaped_line_breaks\f[R] A backslash followed by a newline is also a hard line break. Note: in multiline and grid table cells, this is the only way to create a hard line break, since trailing spaces in the cells are ignored. .SS Headings There are two kinds of headings: Setext and ATX. .SS Setext\-style headings A setext\-style heading is a line of text \(lqunderlined\(rq with a row of \f[CR]=\f[R] signs (for a level\-one heading) or \f[CR]\-\f[R] signs (for a level\-two heading): .IP .EX A level\-one heading =================== A level\-two heading \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- .EE .PP The heading text can contain inline formatting, such as emphasis (see Inline formatting, below). .SS ATX\-style headings An ATX\-style heading consists of one to six \f[CR]#\f[R] signs and a line of text, optionally followed by any number of \f[CR]#\f[R] signs. The number of \f[CR]#\f[R] signs at the beginning of the line is the heading level: .IP .EX ## A level\-two heading ### A level\-three heading ### .EE .PP As with setext\-style headings, the heading text can contain formatting: .IP .EX # A level\-one heading with a [link](/url) and *emphasis* .EE .SS Extension: \f[CR]blank_before_header\f[R] Original Markdown syntax does not require a blank line before a heading. Pandoc does require this (except, of course, at the beginning of the document). The reason for the requirement is that it is all too easy for a \f[CR]#\f[R] to end up at the beginning of a line by accident (perhaps through line wrapping). Consider, for example: .IP .EX I like several of their flavors of ice cream: #22, for example, and #5. .EE .SS Extension: \f[CR]space_in_atx_header\f[R] Many Markdown implementations do not require a space between the opening \f[CR]#\f[R]s of an ATX heading and the heading text, so that \f[CR]#5 bolt\f[R] and \f[CR]#hashtag\f[R] count as headings. With this extension, pandoc does require the space. .SS Heading identifiers See also the \f[CR]auto_identifiers\f[R] extension above. .SS Extension: \f[CR]header_attributes\f[R] Headings can be assigned attributes using this syntax at the end of the line containing the heading text: .IP .EX {#identifier .class .class key=value key=value} .EE .PP Thus, for example, the following headings will all be assigned the identifier \f[CR]foo\f[R]: .IP .EX # My heading {#foo} ## My heading ## {#foo} My other heading {#foo} \-\-\-\-\-\-\-\-\-\-\-\-\-\-\- .EE .PP (This syntax is compatible with PHP Markdown Extra.) .PP Note that although this syntax allows assignment of classes and key/value attributes, writers generally don\(cqt use all of this information. Identifiers, classes, and key/value attributes are used in HTML and HTML\-based formats such as EPUB and slidy. Identifiers are used for labels and link anchors in the LaTeX, ConTeXt, Textile, Jira markup, and AsciiDoc writers. .PP Headings with the class \f[CR]unnumbered\f[R] will not be numbered, even if \f[CR]\-\-number\-sections\f[R] is specified. A single hyphen (\f[CR]\-\f[R]) in an attribute context is equivalent to \f[CR].unnumbered\f[R], and preferable in non\-English documents. So, .IP .EX # My heading {\-} .EE .PP is just the same as .IP .EX # My heading {.unnumbered} .EE .PP If the \f[CR]unlisted\f[R] class is present in addition to \f[CR]unnumbered\f[R], the heading will not be included in a table of contents. (Currently this feature is only implemented for certain formats: those based on LaTeX and HTML, PowerPoint, and RTF.) .SS Extension: \f[CR]implicit_header_references\f[R] Pandoc behaves as if reference links have been defined for each heading. So, to link to a heading .IP .EX # Heading identifiers in HTML .EE .PP you can simply write .IP .EX [Heading identifiers in HTML] .EE .PP or .IP .EX [Heading identifiers in HTML][] .EE .PP or .IP .EX [the section on heading identifiers][heading identifiers in HTML] .EE .PP instead of giving the identifier explicitly: .IP .EX [Heading identifiers in HTML](#heading\-identifiers\-in\-html) .EE .PP If there are multiple headings with identical text, the corresponding reference will link to the first one only, and you will need to use explicit links to link to the others, as described above. .PP Like regular reference links, these references are case\-insensitive. .PP Explicit link reference definitions always take priority over implicit heading references. So, in the following example, the link will point to \f[CR]bar\f[R], not to \f[CR]#foo\f[R]: .IP .EX # Foo [foo]: bar See [foo] .EE .SS Block quotations Markdown uses email conventions for quoting blocks of text. A block quotation is one or more paragraphs or other block elements (such as lists or headings), with each line preceded by a \f[CR]>\f[R] character and an optional space. (The \f[CR]>\f[R] need not start at the left margin, but it should not be indented more than three spaces.) .IP .EX > This is a block quote. This > paragraph has two lines. > > 1. This is a list inside a block quote. > 2. Second item. .EE .PP A \(lqlazy\(rq form, which requires the \f[CR]>\f[R] character only on the first line of each block, is also allowed: .IP .EX > This is a block quote. This paragraph has two lines. > 1. This is a list inside a block quote. 2. Second item. .EE .PP Among the block elements that can be contained in a block quote are other block quotes. That is, block quotes can be nested: .IP .EX > This is a block quote. > > > A block quote within a block quote. .EE .PP If the \f[CR]>\f[R] character is followed by an optional space, that space will be considered part of the block quote marker and not part of the indentation of the contents. Thus, to put an indented code block in a block quote, you need five spaces after the \f[CR]>\f[R]: .IP .EX > code .EE .SS Extension: \f[CR]blank_before_blockquote\f[R] Original Markdown syntax does not require a blank line before a block quote. Pandoc does require this (except, of course, at the beginning of the document). The reason for the requirement is that it is all too easy for a \f[CR]>\f[R] to end up at the beginning of a line by accident (perhaps through line wrapping). So, unless the \f[CR]markdown_strict\f[R] format is used, the following does not produce a nested block quote in pandoc: .IP .EX > This is a block quote. >> Not nested, since \(gablank_before_blockquote\(ga is enabled by default .EE .SS Verbatim (code) blocks .SS Indented code blocks A block of text indented four spaces (or one tab) is treated as verbatim text: that is, special characters do not trigger special formatting, and all spaces and line breaks are preserved. For example, .IP .EX if (a > 3) { moveShip(5 * gravity, DOWN); } .EE .PP The initial (four space or one tab) indentation is not considered part of the verbatim text, and is removed in the output. .PP Note: blank lines in the verbatim text need not begin with four spaces. .SS Fenced code blocks .SS Extension: \f[CR]fenced_code_blocks\f[R] In addition to standard indented code blocks, pandoc supports \f[I]fenced\f[R] code blocks. These begin with a row of three or more tildes (\f[CR]\(ti\f[R]) and end with a row of tildes that must be at least as long as the starting row. Everything between these lines is treated as code. No indentation is necessary: .IP .EX \(ti\(ti\(ti\(ti\(ti\(ti\(ti if (a > 3) { moveShip(5 * gravity, DOWN); } \(ti\(ti\(ti\(ti\(ti\(ti\(ti .EE .PP Like regular code blocks, fenced code blocks must be separated from surrounding text by blank lines. .PP If the code itself contains a row of tildes or backticks, just use a longer row of tildes or backticks at the start and end: .IP .EX \(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti \(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti code including tildes \(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti \(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti .EE .SS Extension: \f[CR]backtick_code_blocks\f[R] Same as \f[CR]fenced_code_blocks\f[R], but uses backticks (\f[CR]\(ga\f[R]) instead of tildes (\f[CR]\(ti\f[R]). .SS Extension: \f[CR]fenced_code_attributes\f[R] Optionally, you may attach attributes to fenced or backtick code block using this syntax: .IP .EX \(ti\(ti\(ti\(ti {#mycode .haskell .numberLines startFrom=\(dq100\(dq} qsort [] = [] qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs) \(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti\(ti .EE .PP Here \f[CR]mycode\f[R] is an identifier, \f[CR]haskell\f[R] and \f[CR]numberLines\f[R] are classes, and \f[CR]startFrom\f[R] is an attribute with value \f[CR]100\f[R]. Some output formats can use this information to do syntax highlighting. Currently, the only output formats that use this information are HTML, LaTeX, Docx, Ms, and PowerPoint. If highlighting is supported for your output format and language, then the code block above will appear highlighted, with numbered lines. (To see which languages are supported, type \f[CR]pandoc \-\-list\-highlight\-languages\f[R].) Otherwise, the code block above will appear as follows: .IP .EX
  
  ...
  
.EE .PP The \f[CR]numberLines\f[R] (or \f[CR]number\-lines\f[R]) class will cause the lines of the code block to be numbered, starting with \f[CR]1\f[R] or the value of the \f[CR]startFrom\f[R] attribute. The \f[CR]lineAnchors\f[R] (or \f[CR]line\-anchors\f[R]) class will cause the lines to be clickable anchors in HTML output. .PP A shortcut form can also be used for specifying the language of the code block: .IP .EX \(ga\(ga\(gahaskell qsort [] = [] \(ga\(ga\(ga .EE .PP This is equivalent to: .IP .EX \(ga\(ga\(ga {.haskell} qsort [] = [] \(ga\(ga\(ga .EE .PP This shortcut form may be combined with attributes: .IP .EX \(ga\(ga\(gahaskell {.numberLines} qsort [] = [] \(ga\(ga\(ga .EE .PP Which is equivalent to: .IP .EX \(ga\(ga\(ga {.haskell .numberLines} qsort [] = [] \(ga\(ga\(ga .EE .PP If the \f[CR]fenced_code_attributes\f[R] extension is disabled, but input contains class attribute(s) for the code block, the first class attribute will be printed after the opening fence as a bare word. .PP To prevent all highlighting, use the \f[CR]\-\-syntax\-highlighting=none\f[R] option. To set the highlighting style or method, use \f[CR]\-\-syntax\-highlighting\f[R]. For more information on highlighting, see Syntax highlighting, below. .SS Line blocks .SS Extension: \f[CR]line_blocks\f[R] A line block is a sequence of lines beginning with a vertical bar (\f[CR]|\f[R]) followed by a space. The division into lines will be preserved in the output, as will any leading spaces; otherwise, the lines will be formatted as Markdown. This is useful for verse and addresses: .IP .EX | The limerick packs laughs anatomical | In space that is quite economical. | But the good ones I\(aqve seen | So seldom are clean | And the clean ones so seldom are comical | 200 Main St. | Berkeley, CA 94718 .EE .PP The lines can be hard\-wrapped if needed, but the continuation line must begin with a space. .IP .EX | The Right Honorable Most Venerable and Righteous Samuel L. Constable, Jr. | 200 Main St. | Berkeley, CA 94718 .EE .PP Inline formatting (such as emphasis) is allowed in the content (though it can\(cqt cross line boundaries). Block\-level formatting (such as block quotes or lists) is not recognized. .PP This syntax is borrowed from reStructuredText. .SS Lists .SS Bullet lists A bullet list is a list of bulleted list items. A bulleted list item begins with a bullet (\f[CR]*\f[R], \f[CR]+\f[R], or \f[CR]\-\f[R]). Here is a simple example: .IP .EX * one * two * three .EE .PP This will produce a \(lqcompact\(rq list. If you want a \(lqloose\(rq list, in which each item is formatted as a paragraph, put spaces between the items: .IP .EX * one * two * three .EE .PP The bullets need not be flush with the left margin; they may be indented one, two, or three spaces. The bullet must be followed by whitespace. .PP List items look best if subsequent lines are flush with the first line (after the bullet): .IP .EX * here is my first list item. * and my second. .EE .PP But Markdown also allows a \(lqlazy\(rq format: .IP .EX * here is my first list item. * and my second. .EE .SS Block content in list items A list item may contain multiple paragraphs and other block\-level content. However, subsequent paragraphs must be preceded by a blank line and indented to line up with the first non\-space content after the list marker. .IP .EX * First paragraph. Continued. * Second paragraph. With a code block, which must be indented eight spaces: { code } .EE .PP Exception: if the list marker is followed by an indented code block, which must begin 5 spaces after the list marker, then subsequent paragraphs must begin two columns after the last character of the list marker: .IP .EX * code continuation paragraph .EE .PP List items may include other lists. In this case the preceding blank line is optional. The nested list must be indented to line up with the first non\-space character after the list marker of the containing list item. .IP .EX * fruits + apples \- macintosh \- red delicious + pears + peaches * vegetables + broccoli + chard .EE .PP As noted above, Markdown allows you to write list items \(lqlazily,\(rq instead of indenting continuation lines. However, if there are multiple paragraphs or other blocks in a list item, the first line of each must be indented. .IP .EX + A lazy, lazy, list item. + Another one; this looks bad but is legal. Second paragraph of second list item. .EE .SS Ordered lists Ordered lists work just like bulleted lists, except that the items begin with enumerators rather than bullets. .PP In original Markdown, enumerators are decimal numbers followed by a period and a space. The numbers themselves are ignored, so there is no difference between this list: .IP .EX 1. one 2. two 3. three .EE .PP and this one: .IP .EX 5. one 7. two 1. three .EE .SS Extension: \f[CR]fancy_lists\f[R] Unlike original Markdown, pandoc allows ordered list items to be marked with uppercase and lowercase letters and roman numerals, in addition to Arabic numerals. List markers may be enclosed in parentheses or followed by a single right\-parenthesis or period. They must be separated from the text that follows by at least one space, and, if the list marker is a capital letter with a period, by at least two spaces. .PP The \f[CR]fancy_lists\f[R] extension also allows `\f[CR]#\f[R]' to be used as an ordered list marker in place of a numeral: .IP .EX #. one #. two .EE .PP Note: the `\f[CR]#\f[R]' ordered list marker doesn\(cqt work with \f[CR]commonmark\f[R]. .SS Extension: \f[CR]startnum\f[R] Pandoc also pays attention to the type of list marker used, and to the starting number, and both of these are preserved where possible in the output format. Thus, the following yields a list with numbers followed by a single parenthesis, starting with 9, and a sublist with lowercase roman numerals: .IP .EX 9) Ninth 10) Tenth 11) Eleventh i. subone ii. subtwo iii. subthree .EE .PP Pandoc will start a new list each time a different type of list marker is used. So, the following will create three lists: .IP .EX (2) Two (5) Three 1. Four * Five .EE .PP If default list markers are desired, use \f[CR]#.\f[R]: .IP .EX #. one #. two #. three .EE .SS Extension: \f[CR]task_lists\f[R] Pandoc supports task lists, using the syntax of GitHub\-Flavored Markdown. .IP .EX \- [ ] an unchecked task list item \- [x] checked item .EE .SS Definition lists .SS Extension: \f[CR]definition_lists\f[R] Pandoc supports definition lists, using the syntax of PHP Markdown Extra with some extensions. .IP .EX Term 1 : Definition 1 Term 2 with *inline markup* : Definition 2 { some code, part of Definition 2 } Third paragraph of definition 2. .EE .PP Each term must fit on one line, which may optionally be followed by a blank line, and must be followed by one or more definitions. A definition begins with a colon or tilde, which may be indented one or two spaces. .PP A term may have multiple definitions, and each definition may consist of one or more block elements (paragraph, code block, list, etc.), each indented four spaces or one tab stop. The body of the definition (not including the first line) should be indented four spaces. However, as with other Markdown lists, you can \(lqlazily\(rq omit indentation except at the beginning of a paragraph or other block element: .IP .EX Term 1 : Definition with lazy continuation. Second paragraph of the definition. .EE .PP If you leave space before the definition (as in the example above), the text of the definition will be treated as a paragraph. In some output formats, this will mean greater spacing between term/definition pairs. For a more compact definition list, omit the space before the definition: .IP .EX Term 1 \(ti Definition 1 Term 2 \(ti Definition 2a \(ti Definition 2b .EE .PP Note that space between items in a definition list is required. .SS Numbered example lists .SS Extension: \f[CR]example_lists\f[R] The special list marker \f[CR]\(at\f[R] can be used for sequentially numbered examples. The first list item with a \f[CR]\(at\f[R] marker will be numbered `1', the next `2', and so on, throughout the document. The numbered examples need not occur in a single list; each new list using \f[CR]\(at\f[R] will take up where the last stopped. So, for example: .IP .EX (\(at) My first example will be numbered (1). (\(at) My second example will be numbered (2). Explanation of examples. (\(at) My third example will be numbered (3). .EE .PP Numbered examples can be labeled and referred to elsewhere in the document: .IP .EX (\(atgood) This is a good example. As (\(atgood) illustrates, ... .EE .PP The label can be any string of alphanumeric characters, underscores, or hyphens. .PP Continuation paragraphs in example lists must always be indented four spaces, regardless of the length of the list marker. That is, example lists always behave as if the \f[CR]four_space_rule\f[R] extension is set. This is because example labels tend to be long, and indenting content to the first non\-space character after the label would be awkward. .PP You can repeat an earlier numbered example by re\-using its label: .IP .EX (\(atfoo) Sample sentence. Intervening text... This theory can explain the case we saw earlier (repeated): (\(atfoo) Sample sentence. .EE .PP This only works reliably, though, if the repeated item is in a list by itself, because each numbered example list will be numbered continuously from its starting number. .SS Ending a list What if you want to put an indented code block after a list? .IP .EX \- item one \- item two { my code block } .EE .PP Trouble! Here pandoc (like other Markdown implementations) will treat \f[CR]{ my code block }\f[R] as the second paragraph of item two, and not as a code block. .PP To \(lqcut off\(rq the list after item two, you can insert some non\-indented content, like an HTML comment, which won\(cqt produce visible output in any format: .IP .EX \- item one \- item two { my code block } .EE .PP You can use the same trick if you want two consecutive lists instead of one big list: .IP .EX 1. one 2. two 3. three 1. uno 2. dos 3. tres .EE .SS Horizontal rules A line containing a row of three or more \f[CR]*\f[R], \f[CR]\-\f[R], or \f[CR]_\f[R] characters (optionally separated by spaces) produces a horizontal rule: .IP .EX * * * * \-\-\-\-\-\-\-\-\-\-\-\-\-\-\- .EE .PP We strongly recommend that horizontal rules be separated from surrounding text by blank lines. If a horizontal rule is not followed by a blank line, pandoc may try to interpret the lines that follow as a YAML metadata block or a table. .SS Tables Four kinds of tables may be used. The first three kinds presuppose the use of a fixed\-width font, such as Courier. The fourth kind can be used with proportionally spaced fonts, as it does not require lining up columns. .SS Extension: \f[CR]table_captions\f[R] A caption may optionally be provided with all 4 kinds of tables (as illustrated in the examples below). A caption is a paragraph beginning with the string \f[CR]Table:\f[R] (or \f[CR]table:\f[R] or just \f[CR]:\f[R]), which will be stripped off. It may appear either before or after the table. .SS Extension: \f[CR]simple_tables\f[R] Simple tables look like this: .IP .EX Right Left Center Default \-\-\-\-\-\-\- \-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\- 12 12 12 12 123 123 123 123 1 1 1 1 Table: Demonstration of simple table syntax. .EE .PP The header and table rows must each fit on one line. Column alignments are determined by the position of the header text relative to the dashed line below it: .IP \(bu 2 If the dashed line is flush with the header text on the right side but extends beyond it on the left, the column is right\-aligned. .IP \(bu 2 If the dashed line is flush with the header text on the left side but extends beyond it on the right, the column is left\-aligned. .IP \(bu 2 If the dashed line extends beyond the header text on both sides, the column is centered. .IP \(bu 2 If the dashed line is flush with the header text on both sides, the default alignment is used (in most cases, this will be left). .PP The table must end with a blank line, or a line of dashes followed by a blank line. .PP The column header row may be omitted, provided a dashed line is used to end the table. For example: .IP .EX \-\-\-\-\-\-\- \-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\- 12 12 12 12 123 123 123 123 1 1 1 1 \-\-\-\-\-\-\- \-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\- .EE .PP When the header row is omitted, column alignments are determined on the basis of the first line of the table body. So, in the tables above, the columns would be right, left, center, and right aligned, respectively. .SS Extension: \f[CR]multiline_tables\f[R] Multiline tables allow header and table rows to span multiple lines of text (but cells that span multiple columns or rows of the table are not supported). Here is an example: .IP .EX \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Centered Default Right Left Header Aligned Aligned Aligned \-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here\(aqs another one. Note the blank line between rows. \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Table: Here\(aqs the caption. It, too, may span multiple lines. .EE .PP These work like simple tables, but with the following differences: .IP \(bu 2 They must begin with a row of dashes, before the header text (unless the header row is omitted). .IP \(bu 2 They must end with a row of dashes, then a blank line. .IP \(bu 2 The rows must be separated by blank lines. .PP In multiline tables, the table parser pays attention to the widths of the columns, and the writers try to reproduce these relative widths in the output. So, if you find that one of the columns is too narrow in the output, try widening it in the Markdown source. .PP The header may be omitted in multiline tables as well as simple tables: .IP .EX \-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here\(aqs another one. Note the blank line between rows. \-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- : Here\(aqs a multiline table without a header. .EE .PP It is possible for a multiline table to have just one row, but the row should be followed by a blank line (and then the row of dashes that ends the table), or the table may be interpreted as a simple table. .SS Extension: \f[CR]grid_tables\f[R] Grid tables look like this: .IP .EX : Sample grid table. +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ | Fruit | Price | Advantages | +===============+===============+====================+ | Bananas | $1.34 | \- built\-in wrapper | | | | \- bright color | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ | Oranges | $2.10 | \- cures scurvy | | | | \- tasty | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ .EE .PP The row of \f[CR]=\f[R]s separates the header from the table body, and can be omitted for a headerless table. The cells of grid tables may contain arbitrary block elements (multiple paragraphs, code blocks, lists, etc.). .PP Cells can span multiple columns or rows: .IP .EX +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-+ | Property | Earth | +=============+=======+==========+ | | min | \-89.2 °C | | Temperature +\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-+ | 1961\-1990 | mean | 14 °C | | +\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-+ | | max | 56.7 °C | +\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-+ .EE .PP A table header may contain more than one row: .IP .EX +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ | Location | Temperature 1961\-1990 | | | in degree Celsius | | +\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+ | | min | mean | max | +=====================+=======+=======+=======+ | Antarctica | \-89.2 | N/A | 19.8 | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+ | Earth | \-89.2 | 14 | 56.7 | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+\-\-\-\-\-\-\-+ .EE .PP Alignments can be specified as with pipe tables, by putting colons at the boundaries of the separator line after the header: .IP .EX +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ | Right | Left | Centered | +==============:+:==============+:==================:+ | Bananas | $1.34 | built\-in wrapper | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ .EE .PP For headerless tables, the colons go on the top line instead: .IP .EX +\-\-\-\-\-\-\-\-\-\-\-\-\-\-:+:\-\-\-\-\-\-\-\-\-\-\-\-\-\-+:\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-:+ | Right | Left | Centered | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ .EE .PP A table foot can be defined by enclosing it with separator lines that use \f[CR]=\f[R] instead of \f[CR]\-\f[R]: .IP .EX +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ | Fruit | Price | +===============+===============+ | Bananas | $1.34 | +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ | Oranges | $2.10 | +===============+===============+ | Sum | $3.44 | +===============+===============+ .EE .PP The foot must always be placed at the very bottom of the table. .PP Grid tables can be created easily using Emacs\(cq table\-mode (\f[CR]M\-x table\-insert\f[R]). .SS Extension: \f[CR]pipe_tables\f[R] Pipe tables look like this: .IP .EX | Right | Left | Default | Center | |\-\-\-\-\-\-:|:\-\-\-\-\-|\-\-\-\-\-\-\-\-\-|:\-\-\-\-\-\-:| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | : Demonstration of pipe table syntax. .EE .PP The syntax is identical to PHP Markdown Extra tables. The beginning and ending pipe characters are optional, but pipes are required between all columns. The colons indicate column alignment as shown. The header cannot be omitted. To simulate a headerless table, include a header with blank cells. .PP Since the pipes indicate column boundaries, columns need not be vertically aligned, as they are in the above example. So, this is a perfectly legal (though ugly) pipe table: .IP .EX fruit| price \-\-\-\-\-|\-\-\-\-\-: apple|2.05 pear|1.37 orange|3.09 .EE .PP The cells of pipe tables cannot contain block elements like paragraphs and lists, and cannot span multiple lines. If any line of the Markdown source is longer than the column width (see \f[CR]\-\-columns\f[R]), then the table will take up the full text width and the cell contents will wrap, with the relative cell widths determined by the number of dashes in the line separating the table header from the table body. (For example \f[CR]\-\-\-|\-\f[R] would make the first column 3/4 and the second column 1/4 of the full text width.) On the other hand, if no lines are wider than column width, then cell contents will not be wrapped, and the cells will be sized to their contents. .PP Note: pandoc also recognizes pipe tables of the following form, as can be produced by Emacs\(cq orgtbl\-mode: .IP .EX | One | Two | |\-\-\-\-\-+\-\-\-\-\-\-\-| | my | table | | is | nice | .EE .PP The difference is that \f[CR]+\f[R] is used instead of \f[CR]|\f[R]. Other orgtbl features are not supported. In particular, to get non\-default column alignment, you\(cqll need to add colons as above. .SS Extension: \f[CR]table_attributes\f[R] Attributes may be attached to tables by including them at the end of the caption. (For the syntax, see \f[CR]header_attributes\f[R].) .IP .EX : Here\(aqs the caption. {#ident .class key=\(dqvalue\(dq} .EE .SS Metadata blocks .SS Extension: \f[CR]pandoc_title_block\f[R] If the file begins with a title block .IP .EX % title % author(s) (separated by semicolons) % date .EE .PP it will be parsed as bibliographic information, not regular text. (It will be used, for example, in the title of standalone LaTeX or HTML output.) The block may contain just a title, a date and an author, or all three elements. If you want to include an author but no title, or a title and a date but no author, you need a blank line: .IP .EX % % Author .EE .IP .EX % My title % % June 15, 2006 .EE .PP The title may occupy multiple lines, but continuation lines must begin with leading space, thus: .IP .EX % My title on multiple lines .EE .PP If a document has multiple authors, the authors may be put on separate lines with leading space, or separated by semicolons, or both. So, all of the following are equivalent: .IP .EX % Author One Author Two .EE .IP .EX % Author One; Author Two .EE .IP .EX % Author One; Author Two .EE .PP The date must fit on one line. .PP All three metadata fields may contain standard inline formatting (italics, links, footnotes, etc.). .PP Title blocks will always be parsed, but they will affect the output only when the \f[CR]\-\-standalone\f[R] (\f[CR]\-s\f[R]) option is chosen. In HTML output, titles will appear twice: once in the document head\(emthis is the title that will appear at the top of the window in a browser\(emand once at the beginning of the document body. The title in the document head can have an optional prefix attached (\f[CR]\-\-title\-prefix\f[R] or \f[CR]\-T\f[R] option). The title in the body appears as an H1 element with class \(lqtitle\(rq, so it can be suppressed or reformatted with CSS. If a title prefix is specified with \f[CR]\-T\f[R] and no title block appears in the document, the title prefix will be used by itself as the HTML title. .PP The man page writer extracts a title, man page section number, and other header and footer information from the title line. The title is assumed to be the first word on the title line, which may optionally end with a (single\-digit) section number in parentheses. (There should be no space between the title and the parentheses.) Anything after this is assumed to be additional footer and header text. A single pipe character (\f[CR]|\f[R]) should be used to separate the footer text from the header text. Thus, .IP .EX % PANDOC(1) .EE .PP will yield a man page with the title \f[CR]PANDOC\f[R] and section 1. .IP .EX % PANDOC(1) Pandoc User Manuals .EE .PP will also have \(lqPandoc User Manuals\(rq in the footer. .IP .EX % PANDOC(1) Pandoc User Manuals | Version 4.0 .EE .PP will also have \(lqVersion 4.0\(rq in the header. .SS Extension: \f[CR]yaml_metadata_block\f[R] A YAML metadata block is a valid YAML object, delimited by a line of three hyphens (\f[CR]\-\-\-\f[R]) at the top and a line of three hyphens (\f[CR]\-\-\-\f[R]) or three dots (\f[CR]...\f[R]) at the bottom. The initial line \f[CR]\-\-\-\f[R] must not be followed by a blank line. A YAML metadata block may occur anywhere in the document, but if it is not at the beginning, it must be preceded by a blank line. (Note that JSON may be used as well, because JSON is a subset of YAML.) .PP Note that, because of the way pandoc concatenates input files when several are provided, you may also keep the metadata in a separate YAML file and pass it to pandoc as an argument, along with your Markdown files: .IP .EX pandoc chap1.md chap2.md chap3.md metadata.yaml \-s \-o book.html .EE .PP Just be sure that the YAML file begins with \f[CR]\-\-\-\f[R] and ends with \f[CR]\-\-\-\f[R] or \f[CR]...\f[R]. Alternatively, you can use the \f[CR]\-\-metadata\-file\f[R] option. Using that approach however, you cannot reference content (like footnotes) from the main Markdown input document. .PP Metadata will be taken from the fields of the YAML object and added to any existing document metadata. Metadata can contain lists and objects (nested arbitrarily), but all string scalars will be interpreted as Markdown. Fields with names ending in an underscore will be ignored by pandoc. (They may be given a role by external processors.) Field names must not be interpretable as YAML numbers or boolean values (so, for example, \f[CR]yes\f[R], \f[CR]True\f[R], and \f[CR]15\f[R] cannot be used as field names). .PP A document may contain multiple metadata blocks. If two metadata blocks attempt to set the same field, the value from the second block will be taken. .PP Each metadata block is handled internally as an independent YAML document. This means, for example, that any YAML anchors defined in a block cannot be referenced in another block. .PP When pandoc is used with \f[CR]\-t markdown\f[R] to create a Markdown document, a YAML metadata block will be produced only if the \f[CR]\-s/\-\-standalone\f[R] option is used. All of the metadata will appear in a single block at the beginning of the document. .PP Note that YAML escaping rules must be followed. Thus, for example, if a title contains a colon, it must be quoted, and if it contains a backslash escape, then it must be ensured that it is not treated as a YAML escape sequence. The pipe character (\f[CR]|\f[R]) can be used to begin an indented block that will be interpreted literally, without need for escaping. This form is necessary when the field contains blank lines or block\-level formatting: .IP .EX \-\-\- title: \(aqThis is the title: it contains a colon\(aq author: \- Author One \- Author Two keywords: [nothing, nothingness] abstract: | This is the abstract. It consists of two paragraphs. \&... .EE .PP The literal block after the \f[CR]|\f[R] must be indented relative to the line containing the \f[CR]|\f[R]. If it is not, the YAML will be invalid and pandoc will not interpret it as metadata. For an overview of the complex rules governing YAML, see the Wikipedia entry on YAML syntax. .PP Template variables will be set automatically from the metadata. Thus, for example, in writing HTML, the variable \f[CR]abstract\f[R] will be set to the HTML equivalent of the Markdown in the \f[CR]abstract\f[R] field: .IP .EX

This is the abstract.

It consists of two paragraphs.

.EE .PP Variables can contain arbitrary YAML structures, but the template must match this structure. The \f[CR]author\f[R] variable in the default templates expects a simple list or string, but can be changed to support more complicated structures. The following combination, for example, would add an affiliation to the author if one is given: .IP .EX \-\-\- title: The document title author: \- name: Author One affiliation: University of Somewhere \- name: Author Two affiliation: University of Nowhere \&... .EE .PP To use the structured authors in the example above, you would need a custom template: .IP .EX $for(author)$ $if(author.name)$ $author.name$$if(author.affiliation)$ ($author.affiliation$)$endif$ $else$ $author$ $endif$ $endfor$ .EE .PP Raw content to include in the document\(cqs header may be specified using \f[CR]header\-includes\f[R]; however, it is important to mark up this content as raw code for a particular output format, using the \f[CR]raw_attribute\f[R] extension, or it will be interpreted as Markdown. For example: .IP .EX header\-includes: \- | \(ga\(ga\(ga{=latex} \(rslet\(rsoldsection\(rssection \(rsrenewcommand{\(rssection}[1]{\(rsclearpage\(rsoldsection{#1}} \(ga\(ga\(ga .EE .PP Note: the \f[CR]yaml_metadata_block\f[R] extension works with \f[CR]commonmark\f[R] as well as \f[CR]markdown\f[R] (and it is enabled by default in \f[CR]gfm\f[R] and \f[CR]commonmark_x\f[R]). However, in these formats the following restrictions apply: .IP \(bu 2 The YAML metadata block must occur at the beginning of the document (and there can be only one). If multiple files are given as arguments to pandoc, only the first can be a YAML metadata block. .IP \(bu 2 The leaf nodes of the YAML structure are parsed in isolation from each other and from the rest of the document. So, for example, you can\(cqt use a reference link in these contexts if the link definition is somewhere else in the document. .SS Backslash escapes .SS Extension: \f[CR]all_symbols_escapable\f[R] Except inside a code block or inline code, any punctuation or space character preceded by a backslash will be treated literally, even if it would normally indicate formatting. Thus, for example, if one writes .IP .EX *\(rs*hello\(rs** .EE .PP one will get .IP .EX *hello* .EE .PP instead of .IP .EX hello .EE .PP This rule is easier to remember than original Markdown\(cqs rule, which allows only the following characters to be backslash\-escaped: .IP .EX \(rs\(ga*_{}[]()>#+\-.! .EE .PP (However, if the \f[CR]markdown_strict\f[R] format is used, the original Markdown rule will be used.) .PP A backslash\-escaped space is parsed as a nonbreaking space. In TeX output, it will appear as \f[CR]\(ti\f[R]. In HTML and XML output, it will appear as a literal unicode nonbreaking space character (note that it will thus actually look \(lqinvisible\(rq in the generated HTML source; you can still use the \f[CR]\-\-ascii\f[R] command\-line option to make it appear as an explicit entity). .PP A backslash\-escaped newline (i.e.\ a backslash occurring at the end of a line) is parsed as a hard line break. It will appear in TeX output as \f[CR]\(rs\(rs\f[R] and in HTML as \f[CR]
\f[R]. This is a nice alternative to Markdown\(cqs \(lqinvisible\(rq way of indicating hard line breaks using two trailing spaces on a line. .PP Backslash escapes do not work in verbatim contexts. .SS Inline formatting .SS Emphasis To \f[I]emphasize\f[R] some text, surround it with \f[CR]*\f[R]s or \f[CR]_\f[R], like this: .IP .EX This text is _emphasized with underscores_, and this is *emphasized with asterisks*. .EE .PP Double \f[CR]*\f[R] or \f[CR]_\f[R] produces \f[B]strong emphasis\f[R]: .IP .EX This is **strong emphasis** and __with underscores__. .EE .PP A \f[CR]*\f[R] or \f[CR]_\f[R] character surrounded by spaces, or backslash\-escaped, will not trigger emphasis: .IP .EX This is * not emphasized *, and \(rs*neither is this\(rs*. .EE .SS Extension: \f[CR]intraword_underscores\f[R] Because \f[CR]_\f[R] is sometimes used inside words and identifiers, pandoc does not interpret a \f[CR]_\f[R] surrounded by alphanumeric characters as an emphasis marker. If you want to emphasize just part of a word, use \f[CR]*\f[R]: .IP .EX feas*ible*, not feas*able*. .EE .SS Strikeout .SS Extension: \f[CR]strikeout\f[R] To strike out a section of text with a horizontal line, begin and end it with \f[CR]\(ti\(ti\f[R]. Thus, for example, .IP .EX This \(ti\(tiis deleted text.\(ti\(ti .EE .SS Superscripts and subscripts .SS Extension: \f[CR]superscript\f[R], \f[CR]subscript\f[R] Superscripts may be written by surrounding the superscripted text by \f[CR]\(ha\f[R] characters; subscripts may be written by surrounding the subscripted text by \f[CR]\(ti\f[R] characters. Thus, for example, .IP .EX H\(ti2\(tiO is a liquid. 2\(ha10\(ha is 1024. .EE .PP The text between \f[CR]\(ha...\(ha\f[R] or \f[CR]\(ti...\(ti\f[R] may not contain spaces or newlines. If the superscripted or subscripted text contains spaces, these spaces must be escaped with backslashes. (This is to prevent accidental superscripting and subscripting through the ordinary use of \f[CR]\(ti\f[R] and \f[CR]\(ha\f[R], and also bad interactions with footnotes.) Thus, if you want the letter P with `a cat' in subscripts, use \f[CR]P\(tia\(rs cat\(ti\f[R], not \f[CR]P\(tia cat\(ti\f[R]. .SS Verbatim To make a short span of text verbatim, put it inside backticks: .IP .EX What is the difference between \(ga>>=\(ga and \(ga>>\(ga? .EE .PP If the verbatim text includes a backtick, use double backticks: .IP .EX Here is a literal backtick \(ga\(ga \(ga \(ga\(ga. .EE .PP (The spaces after the opening backticks and before the closing backticks will be ignored.) .PP The general rule is that a verbatim span starts with a string of consecutive backticks (optionally followed by a space) and ends with a string of the same number of backticks (optionally preceded by a space). .PP Note that backslash\-escapes (and other Markdown constructs) do not work in verbatim contexts: .IP .EX This is a backslash followed by an asterisk: \(ga\(rs*\(ga. .EE .SS Extension: \f[CR]inline_code_attributes\f[R] Attributes can be attached to verbatim text, just as with fenced code blocks: .IP .EX \(ga<$>\(ga{.haskell} .EE .SS Underline To underline text, use the \f[CR]underline\f[R] class: .IP .EX [Underline]{.underline} .EE .PP Or, without the \f[CR]bracketed_spans\f[R] extension (but with \f[CR]native_spans\f[R]): .IP .EX Underline .EE .PP This will work in all output formats that support underline. .SS Small caps To write small caps, use the \f[CR]smallcaps\f[R] class: .IP .EX [Small caps]{.smallcaps} .EE .PP Or, without the \f[CR]bracketed_spans\f[R] extension: .IP .EX Small caps .EE .PP For compatibility with other Markdown flavors, CSS is also supported: .IP .EX Small caps .EE .PP This will work in all output formats that support small caps. .SS Highlighting To highlight text, use the \f[CR]mark\f[R] class: .IP .EX [Mark]{.mark} .EE .PP Or, without the \f[CR]bracketed_spans\f[R] extension (but with \f[CR]native_spans\f[R]): .IP .EX Mark .EE .PP This will work in all output formats that support highlighting. .SS Math .SS Extension: \f[CR]tex_math_dollars\f[R] Anything between two \f[CR]$\f[R] characters will be treated as TeX math. The opening \f[CR]$\f[R] must have a non\-space character immediately to its right, while the closing \f[CR]$\f[R] must have a non\-space character immediately to its left, and must not be followed immediately by a digit. Thus, \f[CR]$20,000 and $30,000\f[R] won\(cqt parse as math. If for some reason you need to enclose text in literal \f[CR]$\f[R] characters, backslash\-escape them and they won\(cqt be treated as math delimiters. .PP For display math, use \f[CR]$$\f[R] delimiters. (In this case, the delimiters may be separated from the formula by whitespace. However, there can be no blank lines between the opening and closing \f[CR]$$\f[R] delimiters.) .PP TeX math will be printed in all output formats. How it is rendered depends on the output format: .TP LaTeX It will appear verbatim surrounded by \f[CR]\(rs(...\(rs)\f[R] (for inline math) or \f[CR]\(rs[...\(rs]\f[R] (for display math). .TP Markdown, Emacs Org mode, ConTeXt, ZimWiki It will appear verbatim surrounded by \f[CR]$...$\f[R] (for inline math) or \f[CR]$$...$$\f[R] (for display math). .TP XWiki It will appear verbatim surrounded by \f[CR]{{formula}}..{{/formula}}\f[R]. .TP reStructuredText It will be rendered using an interpreted text role \f[CR]:math:\f[R]. .TP AsciiDoc For AsciiDoc output math will appear verbatim surrounded by \f[CR]latexmath:[...]\f[R]. For \f[CR]asciidoc_legacy\f[R] the bracketed material will also include inline or display math delimiters. .TP Texinfo It will be rendered inside a \f[CR]\(atmath\f[R] command. .TP roff man, Jira markup It will be rendered verbatim without \f[CR]$\f[R]\(cqs. .TP MediaWiki, DokuWiki It will be rendered inside \f[CR]\f[R] tags. .TP Textile It will be rendered inside \f[CR]\f[R] tags. .TP RTF, OpenDocument It will be rendered, if possible, using Unicode characters, and will otherwise appear verbatim. .TP ODT It will be rendered, if possible, using MathML. .TP DocBook If the \f[CR]\-\-mathml\f[R] flag is used, it will be rendered using MathML in an \f[CR]inlineequation\f[R] or \f[CR]informalequation\f[R] tag. Otherwise it will be rendered, if possible, using Unicode characters. .TP Docx and PowerPoint It will be rendered using OMML math markup. .TP FictionBook2 If the \f[CR]\-\-webtex\f[R] option is used, formulas are rendered as images using CodeCogs or other compatible web service, downloaded and embedded in the e\-book. Otherwise, they will appear verbatim. .TP HTML, Slidy, DZSlides, S5, EPUB The way math is rendered in HTML will depend on the command\-line options selected. Therefore see Math rendering in HTML above. .SS Raw HTML .SS Extension: \f[CR]raw_html\f[R] Markdown allows you to insert raw HTML (or DocBook) anywhere in a document (except verbatim contexts, where \f[CR]<\f[R], \f[CR]>\f[R], and \f[CR]&\f[R] are interpreted literally). (Technically this is not an extension, since standard Markdown allows it, but it has been made an extension so that it can be disabled if desired.) .PP The raw HTML is passed through unchanged in HTML, S5, Slidy, Slideous, DZSlides, EPUB, Markdown, CommonMark, Emacs Org mode, and Textile output, and suppressed in other formats. .PP For a more explicit way of including raw HTML in a Markdown document, see the \f[CR]raw_attribute\f[R] extension. .PP In the CommonMark format, if \f[CR]raw_html\f[R] is enabled, superscripts, subscripts, strikeouts and small capitals will be represented as HTML. Otherwise, plain\-text fallbacks will be used. Note that even if \f[CR]raw_html\f[R] is disabled, tables will be rendered with HTML syntax if they cannot use pipe syntax. .SS Extension: \f[CR]markdown_in_html_blocks\f[R] Original Markdown allows you to include HTML \(lqblocks\(rq: blocks of HTML between balanced tags that are separated from the surrounding text with blank lines, and start and end at the left margin. Within these blocks, everything is interpreted as HTML, not Markdown; so (for example), \f[CR]*\f[R] does not signify emphasis. .PP Pandoc behaves this way when the \f[CR]markdown_strict\f[R] format is used; but by default, pandoc interprets material between HTML block tags as Markdown. Thus, for example, pandoc will turn .IP .EX
*one* [a link](https://google.com)
.EE .PP into .IP .EX
one a link
.EE .PP whereas \f[CR]Markdown.pl\f[R] will preserve it as is. .PP There is one exception to this rule: text between \f[CR] HTML \(dq\(dq\(dq) \(ga\(ga\(ga ## Image This image ![image](myimage.png) will be included as a cell attachment. .EE .PP If you want to add cell attributes, group cells differently, or add output to code cells, then you need to include divs to indicate the structure. You can use either fenced divs or native divs for this. Here is an example: .IP .EX :::::: {.cell .markdown} # Lorem **Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus bibendum felis dictum sodales. :::::: :::::: {.cell .code execution_count=1} \(ga\(ga\(ga {.python} print(\(dqhello\(dq) \(ga\(ga\(ga ::: {.output .stream .stdout} \(ga\(ga\(ga hello \(ga\(ga\(ga ::: :::::: :::::: {.cell .code execution_count=2} \(ga\(ga\(ga {.python} from IPython.display import HTML HTML(\(dq\(dq\(dq HTML \(dq\(dq\(dq) \(ga\(ga\(ga ::: {.output .execute_result execution_count=2} \(ga\(ga\(ga{=html} HTML hello \(ga\(ga\(ga ::: :::::: .EE .PP If you include raw HTML or TeX in an output cell, use the raw attribute, as shown in the last cell of the example above. Although pandoc can process \(lqbare\(rq raw HTML and TeX, the result is often interspersed raw elements and normal textual elements, and in an output cell pandoc expects a single, connected raw block. To avoid using raw HTML or TeX except when marked explicitly using raw attributes, we recommend specifying the extensions \f[CR]\-raw_html\-raw_tex+raw_attribute\f[R] when translating between Markdown and ipynb notebooks. .PP Note that options and extensions that affect reading and writing of Markdown will also affect Markdown cells in ipynb notebooks. For example, \f[CR]\-\-wrap=preserve\f[R] will preserve soft line breaks in Markdown cells; \f[CR]\-\-markdown\-headings=setext\f[R] will cause Setext\-style headings to be used; and \f[CR]\-\-preserve\-tabs\f[R] will prevent tabs from being turned to spaces. .SH VIMDOC Vimdoc writer generates Vim help files and makes use of the following metadata variables: .IP .EX abstract\f[B]:\f[R] \(dqA short description\(dq author\f[B]:\f[R] Author title\f[B]:\f[R] Title \f[I]# Vimdoc\-specific\f[R] filename\f[B]:\f[R] \(dqdefinition\-lists.txt\(dq vimdoc\-prefix\f[B]:\f[R] pandoc .EE .PP Complete header requires \f[CR]abstract\f[R], \f[CR]author\f[R], \f[CR]title\f[R] and \f[CR]filename\f[R] to be set. Compiling file with such metadata produces the following file (assumes \f[CR]\-\-standalone\f[R], see Templates): .IP .EX *definition\-lists.txt* A short description Title by Author Type |gO| to see the table of contents. [...] vim:tw=72:sw=4:ts=4:ft=help:norl:et: .EE .PP If \f[CR]vimdoc\-prefix\f[R] is set, all non\-command tags are prefixed with its value, it is used to prevent tag collision: all headers have a tag (either inferred or explicit) and multiple help pages can have the same header names, therefore collision is to be expected. Let our input be the following markdown file: .IP .EX ## Header \f[BI]\(ga:[range]Fnl {expr}\(ga\f[R]{#:Fnl} : Evaluates {expr} or range \f[BI]\(gavim.b\(ga\f[R]{#vim.b} : Buffer\-scoped (\f[BI]\(ga:h b:\(ga\f[R]) variables for the current buffer. Invalid or unset key returns \f[BI]\(ganil\(ga\f[R]. Can be indexed with an integer to access variables for a specific buffer. \f[I][\f[R]Span\f[I]]\f[R]{#span} : generic inline container for phrasing content, which does not inherently represent anything. .EE .PP Convert it to vimdoc: .IP .EX \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Header *header* :[range]Fnl {expr} *:Fnl* Evaluates {expr} or range \(gavim.b\(ga *vim.b* Buffer\-scoped (|b:|) variables for the current buffer. Invalid or unset key returns \(ganil\(ga. Can be indexed with an integer to access variables for a specific buffer. Span *span* generic inline container for phrasing content, which does not inherently represent anything. .EE .PP Convert it to vimdoc with metadata variable set (e.g.\ with \f[CR]\-M vimdoc\-prefix=pandoc\f[R]) .IP .EX \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Header *pandoc\-header* :[range]Fnl {expr} *:Fnl* Evaluates {expr} or range \(gavim.b\(ga *pandoc\-vim.b* Buffer\-scoped (|b:|) variables for the current buffer. Invalid or unset key returns \(ganil\(ga. Can be indexed with an integer to access variables for a specific buffer. Span *pandoc\-span* generic inline container for phrasing content, which does not inherently represent anything. .EE .PP \f[CR]vim.b\f[R] and \f[CR]Span\f[R] got their prefixes but not \f[CR]:Fnl\f[R] because ex\-commands (those starting with \f[CR]:\f[R]) don\(cqt get a prefix, since they are considered unique across help pages. .PP In both cases \f[CR]:help b:\f[R] became reference \f[CR]|b:|\f[R] (also works with \f[CR]:h b:\f[R]). Links pointing to either https://vimhelp.org/ or https://neovim.io/doc/user become references too. .PP Vim traditionally wraps at 78, but Pandoc defaults to 72. Use \f[CR]\-\-columns 78\f[R] to match Vim. .SH SYNTAX HIGHLIGHTING Pandoc will automatically highlight syntax in fenced code blocks that are marked with a language name. The Haskell library skylighting is used for highlighting. Currently highlighting is supported only for HTML, EPUB, Docx, Ms, Man, Typst, and LaTeX/PDF output. To see a list of language names that pandoc will recognize, type \f[CR]pandoc \-\-list\-highlight\-languages\f[R]. .PP The color scheme can be selected using the \f[CR]\-\-syntax\-highlighting\f[R] option. The default color scheme is \f[CR]pygments\f[R], which imitates the default color scheme used by the Python library pygments (though pygments is not actually used to do the highlighting). To see a list of highlight styles, type \f[CR]pandoc \-\-list\-highlight\-styles\f[R]. .PP If you are not satisfied with the predefined styles, you can use \f[CR]\-\-print\-highlight\-style\f[R] to generate a JSON \f[CR].theme\f[R] file which can be modified and used as the argument to \f[CR]\-\-syntax\-highlighting\f[R]. To get a JSON version of the \f[CR]pygments\f[R] style, for example: .IP .EX pandoc \-o my.theme \-\-print\-highlight\-style pygments .EE .PP Then edit \f[CR]my.theme\f[R] and use it like this: .IP .EX pandoc \-\-syntax\-highlighting my.theme .EE .PP If you are not satisfied with the built\-in highlighting, or you want to highlight a language that isn\(cqt supported, you can use the \f[CR]\-\-syntax\-definition\f[R] option to load a KDE\-style XML syntax definition file. Before writing your own, have a look at KDE\(cqs repository of syntax definitions. .PP If you receive an error that pandoc \(lqCould not read highlighting theme\(rq, check that the JSON file is encoded with UTF\-8 and has no Byte\-Order Mark (BOM). .PP To disable highlighting, use \f[CR]\-\-syntax\-highlighting=none\f[R]. .PP To use a format\(cqs idiomatic syntax highlighting instead of pandoc\(cqs built\-in highlighting, use \f[CR]\-\-syntax\-highlighting=idiomatic\f[R]. Currently, \f[CR]idiomatic\f[R] only affects the following formats: .IP \(bu 2 In reveal.js, it causes reveal.js\(cqs highlighting plugin to be used for source code highlighting. The style may be customized by setting the \f[CR]highlightjs\-theme\f[R] variable. .IP \(bu 2 In Typst, it causes Typst\(cqs built\-in highlighting to be used. (This is also the default for Typst.) .IP \(bu 2 In LaTeX, it causes the \f[CR]listings\f[R] package to be used. Note that \f[CR]listings\f[R] 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. .IP \(bu 2 In other formats, \f[CR]idiomatic\f[R] will have the same result as \f[CR]default\f[R]. .SH CUSTOM STYLES Custom styles can be used in the docx, odt and ICML formats. .SS Output By default, pandoc\(cqs odt, docx and ICML output applies a predefined set of styles for blocks such as paragraphs and block quotes, and uses largely default formatting (italics, bold) for inlines. This will work for most purposes, especially alongside a reference doc file. However, if you need to apply your own styles to blocks, or match a preexisting set of styles, pandoc allows you to define custom styles for blocks and text using \f[CR]div\f[R]s and \f[CR]span\f[R]s, respectively. .PP If you define a Div, Span, or Table with the attribute \f[CR]custom\-style\f[R], pandoc will apply your specified style to the contained elements (with the exception of elements whose function depends on a style, like headings, code blocks, block quotes, or links). So, for example, using the \f[CR]bracketed_spans\f[R] syntax, .IP .EX [Get out]{custom\-style=\(dqEmphatically\(dq}, he said. .EE .PP would produce a file with \(lqGet out\(rq styled with character style \f[CR]Emphatically\f[R]. Similarly, using the \f[CR]fenced_divs\f[R] syntax, .IP .EX Dickinson starts the poem simply: ::: {custom\-style=\(dqPoetry\(dq} | A Bird came down the Walk\-\-\- | He did not know I saw\-\-\- ::: .EE .PP would style the two contained lines with the \f[CR]Poetry\f[R] paragraph style. .PP Styles will be defined in the output file as inheriting from normal text (docx) or Default Paragraph Style (odt), if the styles are not yet in your reference doc. If they are already defined, pandoc will not alter the definition. .PP This feature allows for greatest customization in conjunction with pandoc filters. If you want all paragraphs after block quotes to be indented, you can write a filter to apply the styles necessary. If you want all italics to be transformed to the \f[CR]Emphasis\f[R] character style (perhaps to change their color), you can write a filter which will transform all italicized inlines to inlines within an \f[CR]Emphasis\f[R] custom\-style \f[CR]span\f[R]. .PP For docx or odt output, you don\(cqt need to enable any extensions for custom styles to work. .PP For icml output, you can also set an \f[CR]object\-style\f[R] in images: .IP .EX ![Image with object style](myImage.jpg){object\-style=\(dqfixedSizeImage\(dq} .EE .PP In InDesign you\(cqll see that object style given to the image, and you\(cqll be able to customize it, or load its definition from a template of yours. .SS Input The docx reader, by default, only reads those styles that it can convert into pandoc elements, either by direct conversion or interpreting the derivation of the input document\(cqs styles. .PP By enabling the \f[CR]styles\f[R] extension in the docx reader (\f[CR]\-f docx+styles\f[R]), you can produce output that maintains the styles of the input document, using the \f[CR]custom\-style\f[R] class. A \f[CR]custom\-style\f[R] attribute will be added for each style. Divs will be created to hold the paragraph styles, and Spans to hold the character styles. Table styles will be applied directly to the Table. .PP For example, using the \f[CR]custom\-style\-reference.docx\f[R] file in the test directory, we have the following different outputs: .PP Without the \f[CR]+styles\f[R] extension: .IP .EX $ pandoc test/docx/custom\-style\-reference.docx \-f docx \-t markdown This is some text. This is text with an *emphasized* text style. And this is text with a **strengthened** text style. > Here is a styled paragraph that inherits from Block Text. .EE .PP And with the extension: .IP .EX $ pandoc test/docx/custom\-style\-reference.docx \-f docx+styles \-t markdown ::: {custom\-style=\(dqFirst Paragraph\(dq} This is some text. ::: ::: {custom\-style=\(dqBody Text\(dq} This is text with an [emphasized]{custom\-style=\(dqEmphatic\(dq} text style. And this is text with a [strengthened]{custom\-style=\(dqStrengthened\(dq} text style. ::: ::: {custom\-style=\(dqMy Block Style\(dq} > Here is a styled paragraph that inherits from Block Text. ::: .EE .PP With these custom styles, you can use your input document as a reference\-doc while creating docx output (see below), and maintain the same styles in your input and output files. .SH CUSTOM READERS AND WRITERS Pandoc can be extended with custom readers and writers written in Lua. (Pandoc includes a Lua interpreter, so Lua need not be installed separately.) .PP To use a custom reader or writer, simply specify the path to the Lua script in place of the input or output format. For example: .IP .EX pandoc \-t data/sample.lua pandoc \-f my_custom_markup_language.lua \-t latex \-s .EE .PP If the script is not found relative to the working directory, it will be sought in the \f[CR]custom\f[R] subdirectory of the user data directory (see \f[CR]\-\-data\-dir\f[R]). .PP A custom reader is a Lua script that defines one function, Reader, which takes a string as input and returns a Pandoc AST. See the Lua filters documentation for documentation of the functions that are available for creating pandoc AST elements. For parsing, the lpeg parsing library is available by default. To see a sample custom reader: .IP .EX pandoc \-\-print\-default\-data\-file creole.lua .EE .PP If you want your custom reader to have access to reader options (e.g.\ the tab stop setting), you give your Reader function a second \f[CR]options\f[R] parameter. .PP A custom writer is a Lua script that defines a function that specifies how to render each element in a Pandoc AST. See the djot\-writer.lua for a full\-featured example. .PP Note that custom writers have no default template. If you want to use \f[CR]\-\-standalone\f[R] with a custom writer, you will need to specify a template manually using \f[CR]\-\-template\f[R] or add a new default template with the name \f[CR]default.NAME_OF_CUSTOM_WRITER.lua\f[R] to the \f[CR]templates\f[R] subdirectory of your user data directory (see Templates). .SH REPRODUCIBLE BUILDS Some of the document formats pandoc targets (such as EPUB, docx, and ODT) include build timestamps in the generated document. That means that the files generated on successive builds will differ, even if the source does not. To avoid this, set the \f[CR]SOURCE_DATE_EPOCH\f[R] environment variable, and the timestamp will be taken from it instead of the current time. \f[CR]SOURCE_DATE_EPOCH\f[R] should contain an integer unix timestamp (specifying the number of seconds since midnight UTC January 1, 1970). .PP For reproducible builds with LaTeX, you can either specify the \f[CR]pdf\-trailer\-id\f[R] in the metadata or leave it undefined, in which case pandoc will create a trailer\-id based on a hash of the \f[CR]SOURCE_DATE_EPOCH\f[R] and the document\(cqs contents. .PP Some document formats also include a unique identifier. For EPUB, this can be set explicitly by setting the \f[CR]identifier\f[R] metadata field (see EPUB Metadata, above). .SH ACCESSIBLE PDFS AND PDF ARCHIVING STANDARDS PDF is a flexible format, and using PDF in certain contexts requires additional conventions. For example, PDFs are not accessible by default; they define how characters are placed on a page but do not contain semantic information on the content. However, it is possible to generate accessible PDFs, which use tagging to add semantic information to the document. .PP Pandoc defaults to LaTeX to generate PDF. LaTeX\(cqs \f[CR]\(rsDocumentMetadata\f[R] interface supports PDF standards and tagging when using LuaLaTeX; set the \f[CR]pdfstandard\f[R] variable to enable this (see below). For older LaTeX installations, alternative engines must be used. .PP The PDF standards PDF/A and PDF/UA define further restrictions intended to optimize PDFs for archiving and accessibility. Tagging is commonly used in combination with these standards to ensure best results. .PP Note, however, that standard compliance depends on many things, including the colorspace of embedded images. Pandoc cannot check this, and external programs must be used to ensure that generated PDFs are in compliance. .SS LaTeX Set the \f[CR]pdfstandard\f[R] variable to produce tagged PDFs conforming to PDF/A, PDF/X, or PDF/UA standards. For example: .IP .EX pandoc \-V pdfstandard=ua\-2 \-\-pdf\-engine=lualatex doc.md \-o doc.pdf .EE .PP Multiple standards can be combined: .IP .EX \-\-\- pdfstandard: \- ua\-2 \- a\-4f \-\-\- .EE .PP The required PDF version is inferred automatically. This feature requires LuaLaTeX in TeX Live 2025 with LaTeX kernel 2025\-06\-01 or newer. .SS ConTeXt ConTeXt always produces tagged PDFs, but the quality depends on the input. The default ConTeXt markup generated by pandoc is optimized for readability and reuse, not tagging. Enable the \f[CR]tagging\f[R] format extension to force markup that is optimized for tagging. For example: .IP .EX pandoc \-t context+tagging doc.md \-o doc.pdf .EE .PP A recent \f[CR]context\f[R] version should be used, as older versions contained a bug that lead to invalid PDF metadata. .SS WeasyPrint The HTML\-based engine WeasyPrint includes experimental support for PDF/A and PDF/UA since version 57. Tagged PDFs can created with .IP .EX pandoc \-\-pdf\-engine=weasyprint \(rs \-\-pdf\-engine\-opt=\-\-pdf\-variant=pdf/ua\-1 ... .EE .PP The feature is experimental and standard compliance should not be assumed. .SS Prince XML The non\-free HTML\-to\-PDF converter \f[CR]prince\f[R] has extensive support for various PDF standards as well as tagging. E.g.: .IP .EX pandoc \-\-pdf\-engine=prince \(rs \-\-pdf\-engine\-opt=\-\-tagged\-pdf ... .EE .PP See the prince documentation for more info. .SS Typst Typst 0.12 can produce PDF/A\-2b: .IP .EX pandoc \-\-pdf\-engine=typst \-\-pdf\-engine\-opt=\-\-pdf\-standard=a\-2b ... .EE .SS Word Processors Word processors like LibreOffice and MS Word can also be used to generate standardized and tagged PDF output. Pandoc does not support direct conversions via these tools. However, pandoc can convert a document to a \f[CR]docx\f[R] or \f[CR]odt\f[R] file, which can then be opened and converted to PDF with the respective word processor. See the documentation for Word and LibreOffice. .SH RUNNING PANDOC AS A WEB SERVER If you rename (or symlink) the pandoc executable to \f[CR]pandoc\-server\f[R], or if you call pandoc with \f[CR]server\f[R] as the first argument, it will start up a web server with a JSON API. This server exposes most of the conversion functionality of pandoc. For full documentation, see the pandoc\-server man page. .PP If you rename (or symlink) the pandoc executable to \f[CR]pandoc\-server.cgi\f[R], it will function as a CGI program exposing the same API as \f[CR]pandoc\-server\f[R]. .PP \f[CR]pandoc\-server\f[R] is designed to be maximally secure; it uses Haskell\(cqs type system to provide strong guarantees that no I/O will be performed on the server during pandoc conversions. .SH RUNNING PANDOC AS A LUA INTERPRETER Calling the pandoc executable under the name \f[CR]pandoc\-lua\f[R] or with \f[CR]lua\f[R] as the first argument will make it function as a standalone Lua interpreter. The behavior is mostly identical to that of the standalone \f[CR]lua\f[R] executable, version 5.4. All \f[CR]pandoc.*\f[R] packages, as well as the packages \f[CR]re\f[R] and \f[CR]lpeg\f[R], are available via global variables. Furthermore, the globals \f[CR]PANDOC_VERSION\f[R], \f[CR]PANDOC_STATE\f[R], and \f[CR]PANDOC_API_VERSION\f[R] are set at startup. For full documentation, see the pandoc\-lua man page. .SH A NOTE ON SECURITY .IP "1." 3 Although pandoc itself will not create or modify any files other than those you explicitly ask it create (with the exception of temporary files used in producing PDFs), a filter or custom writer could in principle do anything on your file system. Please audit filters and custom writers very carefully before using them. .IP "2." 3 Several input formats (including LaTeX, Org, RST, and Typst) support \f[CR]include\f[R] directives that allow the contents of a file to be included in the output. An untrusted attacker could use these to view the contents of files on the file system. (Using the \f[CR]\-\-sandbox\f[R] option can protect against this threat.) .IP "3." 3 Several output formats (including RTF, FB2, HTML with \f[CR]\-\-self\-contained\f[R], EPUB, Docx, and ODT) will embed encoded or raw images into the output file. An untrusted attacker could exploit this to view the contents of non\-image files on the file system. (Using the \f[CR]\-\-sandbox\f[R] option can protect against this threat, but will also prevent including images in these formats.) .IP "4." 3 In reading HTML files, pandoc will attempt to include the contents of \f[CR]iframe\f[R] elements by fetching content from the local file or URL specified by \f[CR]src\f[R]. If untrusted HTML is processed on a server, this has the potential to reveal anything readable by the process running the server. Using the \f[CR]\-f html+raw_html\f[R] will mitigate this threat by causing the whole \f[CR]iframe\f[R] to be parsed as a raw HTML block. Using \f[CR]\-\-sandbox\f[R] will also protect against the threat. .IP "5." 3 If your application uses pandoc as a Haskell library (rather than shelling out to the executable), it is possible to use it in a mode that fully isolates pandoc from your file system, by running the pandoc operations in the \f[CR]PandocPure\f[R] monad. See the document Using the pandoc API for more details. (This corresponds to the use of the \f[CR]\-\-sandbox\f[R] option on the command line.) .IP "6." 3 Pandoc\(cqs parsers can exhibit pathological performance on some corner cases. It is wise to put any pandoc operations under a timeout, to avoid DOS attacks that exploit these issues. If you are using the pandoc executable, you can add the command line options \f[CR]+RTS \-M512M \-RTS\f[R] (for example) to limit the heap size to 512MB. Note that the \f[CR]commonmark\f[R] parser (including \f[CR]commonmark_x\f[R] and \f[CR]gfm\f[R]) is much less vulnerable to pathological performance than the \f[CR]markdown\f[R] parser, so it is a better choice when processing untrusted input. .IP "7." 3 The HTML generated by pandoc is not guaranteed to be safe. If \f[CR]raw_html\f[R] is enabled for the Markdown input, users can inject arbitrary HTML. Even if \f[CR]raw_html\f[R] is disabled, users can include dangerous content in URLs and attributes. To be safe, you should run all HTML generated from untrusted user input through an HTML sanitizer. .SH AUTHORS Copyright 2006\(en2024 John MacFarlane (jgm\(atberkeley.edu). Released under the GPL, version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) For a full list of contributors, see the file AUTHORS.md in the pandoc source code. .PP The Pandoc source code may be downloaded from or . Further documentation is available at . ================================================ FILE: pandoc-cli/no-lua/PandocCLI/Lua.hs ================================================ {- | Module : PandocCLI.Lua Copyright : © 2022-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Placeholder values to be used when pandoc is compiled without support for the Lua scripting engine. -} module PandocCLI.Lua (runLuaInterpreter, getEngine) where import Control.Monad.IO.Class (MonadIO) import Text.Pandoc.Error (PandocError (PandocNoScriptingEngine), handleError) import Text.Pandoc.Scripting (ScriptingEngine, noEngine) -- | Raises an error, reporting that the scripting engine is unavailable. runLuaInterpreter :: String -- ^ Program name -> [String] -- ^ Command line arguments -> IO () runLuaInterpreter _progName _args = do handleError (Left PandocNoScriptingEngine) -- | Placeholder scripting engine. getEngine :: MonadIO m => m ScriptingEngine getEngine = pure noEngine ================================================ FILE: pandoc-cli/no-server/PandocCLI/Server.hs ================================================ {- | Module : PandocCLI.Server Copyright : © 2006-2024 John MacFarlane License : GPL-2.0-or-later Maintainer : John MacFarlane Placeholder module to be used when pandoc is compiled without server support. -} module PandocCLI.Server ( runCGI , runServer ) where import System.IO (hPutStrLn, stderr) import System.Exit (exitWith, ExitCode(ExitFailure)) -- | Placeholder function for the CGI server; prints an error message -- and exists with error code. runCGI :: IO () runCGI = serverUnsupported -- | Placeholder function for the HTTP server; prints an error message -- and exists with error code. runServer :: [String] -> IO () runServer _args = serverUnsupported serverUnsupported :: IO () serverUnsupported = do hPutStrLn stderr $ "Server mode unsupported.\n" <> "Pandoc was not compiled with the 'server' flag." exitWith $ ExitFailure 4 ================================================ FILE: pandoc-cli/pandoc-cli.cabal ================================================ cabal-version: 2.4 name: pandoc-cli version: 3.9.0.2 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md copyright: (c) 2006-2024 John MacFarlane author: John MacFarlane maintainer: John MacFarlane bug-reports: https://github.com/jgm/pandoc/issues stability: alpha homepage: https://pandoc.org category: Text synopsis: Conversion between documentation formats description: Pandoc-cli provides a command-line executable that uses the pandoc library to convert between markup formats. extra-source-files: man/pandoc.1 man/pandoc-lua.1 man/pandoc-server.1 source-repository head type: git location: https://github.com/jgm/pandoc.git flag lua description: Support custom modifications and conversions with the pandoc Lua scripting engine. default: True flag server Description: Include support for running pandoc as an HTTP server. Default: True flag repl Description: Include support for running a pandoc Lua repl. Default: True flag nightly Description: Add '-nightly-COMPILEDATE' to the output of '--version'. Default: False common common-options default-language: Haskell2010 other-extensions: OverloadedStrings build-depends: base >= 4.18 && < 5 ghc-options: -Wall -fno-warn-unused-do-bind -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wcpp-undef -Wincomplete-uni-patterns -Widentities -Wpartial-fields -Wmissing-signatures -fhide-source-paths -Wunused-packages -Winvalid-haddock if os(windows) cpp-options: -D_WINDOWS common common-executable import: common-options ghc-options: -rtsopts -with-rtsopts=-A8m executable pandoc import: common-executable main-is: pandoc.hs hs-source-dirs: src buildable: True -- Note: we always link to an exact version of pandoc, with the -- same version as this package: build-depends: pandoc == 3.9.0.2, text other-modules: PandocCLI.Lua , PandocCLI.Server if arch(wasm32) hs-source-dirs: wasm other-modules: PandocWasm cpp-options: -DINCLUDE_WASM build-depends: aeson, containers, bytestring, skylighting, filepath, pandoc-lua-engine ghc-options: -optl-Wl,--export=__wasm_call_ctors,--export=hs_init_with_rtsopts,--export=malloc,--export=convert,--export=query else ghc-options: -threaded if flag(nightly) cpp-options: -DNIGHTLY build-depends: template-haskell, time if flag(server) build-depends: pandoc-server >= 0.1.1 && < 0.2, wai-extra >= 3.0.24, warp, safe hs-source-dirs: server else hs-source-dirs: no-server if flag(lua) build-depends: pandoc-lua-engine >= 0.5.1 && < 0.6 hs-source-dirs: lua else hs-source-dirs: no-lua if flag(repl) build-depends: hslua-cli >= 1.4.1 && < 1.5, temporary >= 1.1 && < 1.4 cpp-options: -DREPL ================================================ FILE: pandoc-cli/server/PandocCLI/Server.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Main Copyright : © 2006-2024 John MacFarlane License : GPL-2.0-or-later Maintainer : John MacFarlane Functions for the pandoc server CLI. -} module PandocCLI.Server ( runCGI , runServer ) where import qualified Network.Wai.Handler.CGI as CGI import qualified Network.Wai.Handler.Warp as Warp import Network.Wai.Middleware.Timeout (timeout) import Safe (readDef) import System.Environment (lookupEnv) import Text.Pandoc.Server (ServerOpts(..), parseServerOptsFromArgs, app) import System.IO (stderr, hPutStrLn) -- | Runs the CGI server. runCGI :: IO () runCGI = do cgiTimeout <- maybe 2 (readDef 2) <$> lookupEnv "PANDOC_SERVER_TIMEOUT" CGI.run (timeout cgiTimeout app) -- | Runs the HTTP server. runServer :: [String] -> IO () runServer args = do sopts <- parseServerOptsFromArgs args hPutStrLn stderr $ "Starting server on port " <> show (serverPort sopts) <> "..." Warp.run (serverPort sopts) (timeout (serverTimeout sopts) app) ================================================ FILE: pandoc-cli/src/pandoc.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {- | Module : Main Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Parses command-line options and calls the appropriate readers and writers. -} module Main where import qualified Control.Exception as E import System.Environment (getArgs, getProgName) import Text.Pandoc.App ( convertWithOpts, defaultOpts, options , parseOptionsFromArgs, handleOptInfo, versionInfo ) import Text.Pandoc.Error (handleError) import Data.Monoid (Any(..)) import PandocCLI.Lua import PandocCLI.Server import Text.Pandoc.Scripting (ScriptingEngine(..)) import qualified Data.Text as T #ifdef NIGHTLY import qualified Language.Haskell.TH as TH import Data.Time #endif #ifdef INCLUDE_WASM import PandocWasm() #endif #ifdef NIGHTLY versionSuffix :: String versionSuffix = "-nightly-" ++ $(TH.stringE =<< TH.runIO (formatTime defaultTimeLocale "%F" <$> Data.Time.getCurrentTime)) #else versionSuffix :: String versionSuffix = "" #endif main :: IO () main = E.handle (handleError . Left) $ do prg <- getProgName rawArgs <- getArgs let hasVersion = getAny $ foldMap (\s -> Any (s == "-v" || s == "--version")) (takeWhile (/= "--") rawArgs) let versionOr action = if hasVersion then versionInfoCLI else action case prg of "pandoc-server.cgi" -> versionOr runCGI "pandoc-server" -> versionOr $ runServer rawArgs "pandoc-lua" -> runLuaInterpreter prg rawArgs _ -> case rawArgs of "lua" : args -> runLuaInterpreter "pandoc lua" args "server": args -> versionOr $ runServer args args -> versionOr $ do engine <- getEngine res <- parseOptionsFromArgs options defaultOpts prg args case res of Left e -> handleOptInfo engine e Right opts -> convertWithOpts engine opts getFeatures :: [String] getFeatures = [ #ifdef VERSION_pandoc_server "+server" #else "-server" #endif , #ifdef VERSION_hslua_cli "+lua" #else "-lua" #endif ] versionInfoCLI :: IO () versionInfoCLI = do scriptingEngine <- getEngine versionInfo getFeatures (Just $ T.unpack (engineName scriptingEngine)) versionSuffix ================================================ FILE: pandoc-cli/wasm/PandocWasm.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Main Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Exposes wasm functions to convert documents and get information from pandoc. -} module PandocWasm where import qualified Data.Map as M import qualified Control.Exception as E import Data.List (sort) import Data.Maybe (fromMaybe) import Text.Pandoc.App ( convertWithOpts, Opt(..), defaultOpts ) import Text.Pandoc (Verbosity(ERROR), pandocVersion, Reader, Writer, PandocIO, readers, writers, runIO, setUserDataDir) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Templates (getDefaultTemplate) import Skylighting (defaultSyntaxMap, Syntax(..)) import Text.Pandoc.Extensions (extensionsToList, extensionEnabled, getAllExtensions, getDefaultExtensions) import Text.Pandoc.Error import Text.Pandoc.Scripting (ScriptingEngine(..), customTemplate) import System.FilePath (splitExtension) import PandocCLI.Lua import Control.Exception import Foreign import Foreign.C import Data.Aeson as Aeson import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import Data.Version (showVersion) foreign export ccall "convert" convert :: Ptr CChar -> Int -> IO () -- | Do a pandoc conversion. The parameters are a pointer and length -- for a C string containing JSON-encoded pandoc options (isomorphic -- to a defaults file). The calling program should set up a virtual -- file system containing @/stdin@ (input), @/stdout@ (output), and -- @/warnings@ (output). @/stdin@ can be used to pass input to pandoc. convert :: Ptr CChar -> Int -> IO () convert ptr len = E.catch act (\(err :: SomeException) -> writeFile "/stderr" ("ERROR: " <> displayException err)) where act = do args <- getCString ptr len engine <- getEngine let aesonRes = Aeson.eitherDecode (UTF8.fromStringLazy args) case aesonRes of Left e -> error e Right (f :: Opt -> Opt) -> do let opts = f defaultOpts let opts' = opts{ optInputFiles = Just $ fromMaybe ["/stdin"] (optInputFiles opts) , optOutputFile = Just $ fromMaybe "/stdout" (optOutputFile opts) , optLogFile = Just $ fromMaybe "/warnings" (optLogFile opts) , optVerbosity = ERROR -- only show errors to stderr } convertWithOpts engine opts' foreign export ccall "query" query :: Ptr CChar -> Int -> IO () -- | Retrieve information from pandoc. The parameters are a pointer -- and length for a C string containing a JSON-encoded query. -- The calling program should set up a virtual file system containing -- @/stdout@, which will be used to hold the output, and @/stderr@, -- which will be used to hold any error messages. -- The following queries can be used: -- * @{"query":"version"}@ returns the pandoc version as a JSON-encoded -- string. -- * @{"query:"input-formats"}@ returns a JSON list of supported input -- formats. -- * @{"query:"output-formats"}@ returns a JSON list of supported output -- formats. -- * @{"query:"highlight-languages"}@ returns a JSON list of languages -- for which syntax highlighting is defined. -- * @{"query:"highlight-styles"}@ returns a JSON list of defined syntax -- highlighting styles. -- * @{"query":"default-template","format": FORMAT}@ returns -- the default template for the format as a JSON-encoded string. -- * @{"query":"extensions-for-format","format": FORMAT}@ returns -- an object mapping extension names to true or false for all the -- extensions relevant for the format. query :: Ptr CChar -> Int -> IO () query ptr len = E.catch act (\(err :: SomeException) -> writeFile "/stderr" ("ERROR: " <> displayException err)) where jsonOut :: forall a . ToJSON a => a -> IO () jsonOut = BL.writeFile "/stdout" . Aeson.encode act = do args <- getCString ptr len case Aeson.eitherDecode (UTF8.fromStringLazy args) of Left e -> error e Right PandocVersion -> jsonOut $ showVersion pandocVersion Right HighlightStyles -> jsonOut $ map fst highlightingStyles Right HighlightLanguages -> jsonOut $ sort $ [ T.toLower (sShortname s) | s <- M.elems defaultSyntaxMap , sShortname s `notElem` ["Alert", "Alert_indent"] ] Right (DefaultTemplate format) -> do templ <- runIO $ case splitExtension (T.unpack format) of (_, "") -> do -- built-in format setUserDataDir Nothing getDefaultTemplate format _ -> do -- format looks like a filepath => custom writer engine <- getEngine components <- engineLoadCustom engine (T.unpack format) case customTemplate components of Just t -> pure t Nothing -> E.throw $ PandocNoTemplateError format case templ of Right t | T.null t -> -- e.g. for docx, odt, json: E.throwIO $ PandocCouldNotFindDataFileError $ T.pack ("templates/default." ++ T.unpack format) | otherwise -> jsonOut t Left e -> E.throwIO e Right InputFormats -> jsonOut readersNames Right OutputFormats -> jsonOut writersNames Right (ExtensionsForFormat format) -> do let allExts = getAllExtensions format let defExts = getDefaultExtensions format let addExt x = M.insert (drop 4 (show x)) (extensionEnabled x defExts) jsonOut $ foldr addExt mempty (extensionsToList allExts) readersNames = sort (map fst (readers :: [(T.Text, Reader PandocIO)])) writersNames = sort ("pdf" : map fst (writers :: [(T.Text, Writer PandocIO)])) data Query = PandocVersion | InputFormats | OutputFormats | HighlightLanguages | HighlightStyles | ExtensionsForFormat T.Text | DefaultTemplate T.Text deriving (Show) instance FromJSON Query where parseJSON = withObject "Query" $ \o -> do queryType <- o .: "query" case queryType of "version" -> pure PandocVersion "input-formats" -> pure InputFormats "output-formats" -> pure OutputFormats "highlight-languages" -> pure HighlightLanguages "highlight-styles" -> pure HighlightStyles "default-template" -> DefaultTemplate <$> o .: "format" "extensions-for-format" -> ExtensionsForFormat <$> o .: "format" _ -> fail $ "Unknown query type " <> queryType getCString :: Ptr CChar -> Int -> IO String getCString ptr len = peekCStringLen (ptr, len) <* free ptr ================================================ FILE: pandoc-lua-engine/README.md ================================================ # pandoc-lua-engine This package provides a Lua pandoc scripting engine based. It allows to write filters, custom readers, and custom writers in Lua. ================================================ FILE: pandoc-lua-engine/pandoc-lua-engine.cabal ================================================ cabal-version: 2.4 name: pandoc-lua-engine version: 0.5.2 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md copyright: © 2006-2024 John MacFarlane, 2017-2024 Albert Krewinkel author: John MacFarlane, Albert Krewinkel maintainer: Albert Krewinkel bug-reports: https://github.com/jgm/pandoc/issues homepage: https://pandoc.org category: Text synopsis: Lua engine to power custom pandoc conversions description: This package provides a pandoc scripting engine based on Lua. extra-source-files: README.md , test/bytestring.bin , test/bytestring.lua , test/bytestring-reader.lua , test/extensions.lua , test/lua/*.lua , test/lua/module/*.lua , test/lua/module/include.tex , test/lua/module/partial.test , test/lua/module/sample.svg , test/lua/module/sample.epub , test/lua/module/tiny.epub , test/sample.lua , test/tables.custom , test/tables.native , test/testsuite.native , test/writer.custom , test/writer-template.lua , test/writer-template.out.txt source-repository head type: git location: https://github.com/jgm/pandoc.git subdir: pandoc-lua-engine flag repl Description: Include pandoc Lua repl. Default: True common common-options default-language: Haskell2010 build-depends: base >= 4.12 && < 5 ghc-options: -Wall -fno-warn-unused-do-bind -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wcpp-undef -Wincomplete-uni-patterns -Widentities -Wpartial-fields -Wmissing-export-lists -Wmissing-signatures -fhide-source-paths if impl(ghc >= 8.10) ghc-options: -Wunused-packages if impl(ghc >= 9.0) ghc-options: -Winvalid-haddock library import: common-options hs-source-dirs: src exposed-modules: Text.Pandoc.Lua other-modules: Text.Pandoc.Lua.Custom , Text.Pandoc.Lua.Documentation , Text.Pandoc.Lua.Engine , Text.Pandoc.Lua.Filter , Text.Pandoc.Lua.Global , Text.Pandoc.Lua.Init , Text.Pandoc.Lua.Module , Text.Pandoc.Lua.Marshal.Chunks , Text.Pandoc.Lua.Marshal.CommonState , Text.Pandoc.Lua.Marshal.Context , Text.Pandoc.Lua.Marshal.Format , Text.Pandoc.Lua.Marshal.ImageSize , Text.Pandoc.Lua.Marshal.LogMessage , Text.Pandoc.Lua.Marshal.PandocError , Text.Pandoc.Lua.Marshal.ReaderOptions , Text.Pandoc.Lua.Marshal.Reference , Text.Pandoc.Lua.Marshal.Sources , Text.Pandoc.Lua.Marshal.Template , Text.Pandoc.Lua.Marshal.WriterOptions , Text.Pandoc.Lua.Module.CLI , Text.Pandoc.Lua.Module.Format , Text.Pandoc.Lua.Module.Image , Text.Pandoc.Lua.Module.JSON , Text.Pandoc.Lua.Module.Log , Text.Pandoc.Lua.Module.MediaBag , Text.Pandoc.Lua.Module.Pandoc , Text.Pandoc.Lua.Module.Path , Text.Pandoc.Lua.Module.Scaffolding , Text.Pandoc.Lua.Module.Structure , Text.Pandoc.Lua.Module.System , Text.Pandoc.Lua.Module.Template , Text.Pandoc.Lua.Module.Text , Text.Pandoc.Lua.Module.Types , Text.Pandoc.Lua.Module.Utils , Text.Pandoc.Lua.Orphans , Text.Pandoc.Lua.PandocLua , Text.Pandoc.Lua.Run , Text.Pandoc.Lua.SourcePos , Text.Pandoc.Lua.Writer.Classic , Text.Pandoc.Lua.Writer.Scaffolding build-depends: aeson , bytestring >= 0.9 && < 0.13 , crypton >= 0.30 && < 1.2 , citeproc >= 0.8 && < 0.14 , containers >= 0.6.0.1 && < 0.9 , data-default >= 0.4 && < 0.9 , doclayout >= 0.5 && < 0.6 , doctemplates >= 0.11 && < 0.12 , exceptions >= 0.8 && < 0.11 , hslua >= 2.5 && < 2.6 , hslua-module-doclayout>= 1.2 && < 1.3 , hslua-module-path >= 1.2 && < 1.3 , hslua-module-system >= 1.3 && < 1.4 , hslua-module-text >= 1.2 && < 1.3 , hslua-module-version >= 1.2 && < 1.3 , hslua-module-zip >= 1.1.5 && < 1.3 , lpeg >= 1.1 && < 1.2 , mtl >= 2.2 && < 2.4 , pandoc >= 3.9 && < 3.10 , pandoc-lua-marshal >= 0.3 && < 0.4 , pandoc-types >= 1.22 && < 1.24 , parsec >= 3.1 && < 3.2 , text >= 1.1.1 && < 2.2 if flag(repl) build-depends: hslua-repl >= 0.1.1 && < 0.2 cpp-options: -DINCLUDE_REPL test-suite test-pandoc-lua-engine import: common-options type: exitcode-stdio-1.0 main-is: test-pandoc-lua-engine.hs hs-source-dirs: test build-depends: pandoc-lua-engine , bytestring , directory , data-default , exceptions >= 0.8 && < 0.11 , filepath , hslua >= 2.5 && < 2.6 , pandoc , pandoc-types >= 1.22 && < 1.24 , tasty , tasty-golden , tasty-hunit , tasty-lua >= 1.1 && < 1.2 , text >= 1.1.1 && < 2.2 other-modules: Tests.Lua , Tests.Lua.Module , Tests.Lua.Reader , Tests.Lua.Writer ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Custom.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Custom Copyright : © 2021-2024 Albert Krewinkel, John MacFarlane License : GPL-2.0-or-later Maintainer : Albert Krewinkel Supports custom parsers written in Lua which produce a Pandoc AST. -} module Text.Pandoc.Lua.Custom ( loadCustom ) where import Control.Exception import Control.Monad ((<=<), (<$!>)) import Control.Monad.IO.Class (MonadIO) import Data.Maybe (fromMaybe) import HsLua as Lua hiding (Operation (Div)) import Text.Pandoc.Class (PandocMonad, findFileWithDataFallback) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.Marshal.Format (peekExtensionsConfig) import Text.Pandoc.Lua.Marshal.Pandoc (peekPandoc) import Text.Pandoc.Lua.Marshal.WriterOptions (pushWriterOptions) import Text.Pandoc.Lua.PandocLua (unPandocLua) import Text.Pandoc.Lua.Run (runLuaWith) import Text.Pandoc.Readers (Reader (..)) import Text.Pandoc.Sources (ToSources(..)) import Text.Pandoc.Scripting (CustomComponents (..)) import Text.Pandoc.Writers (Writer (..)) import qualified Text.Pandoc.Lua.Writer.Classic as Classic import qualified Text.Pandoc.Class as PandocMonad -- | Convert custom markup to Pandoc. loadCustom :: (PandocMonad m, MonadIO m) => FilePath -> m (CustomComponents m) loadCustom luaFile = do luaState <- liftIO newGCManagedState luaFile' <- fromMaybe luaFile <$> findFileWithDataFallback "custom" luaFile either throw pure <=< runLuaWith luaState $ do let globals = [ PANDOC_SCRIPT_FILE luaFile' ] setGlobals globals dofileTrace (Just luaFile') >>= \case OK -> pure () _ -> throwErrorAsException mextsConf <- rawgetglobal "Extensions" >>= \case TypeNil -> pure Nothing TypeFunction -> Just <$!> do callTrace 0 1 forcePeek $ peekExtensionsConfig top `lastly` pop 1 _ -> Just <$!> do forcePeek $ peekExtensionsConfig top `lastly` pop 1 mtemplate <- rawgetglobal "Template" >>= \case TypeNil -> pure Nothing TypeFunction -> Just <$!> do callTrace 0 1 forcePeek $ peekText top `lastly` pop 1 _ -> Just <$!> do forcePeek $ peekText top `lastly` pop 1 mreader <- rawgetglobal "Reader" >>= \case TypeNil -> do pop 1 rawgetglobal "ByteStringReader" >>= \case TypeNil -> pure Nothing _ -> do setfield registryindex readerField pure . Just $ byteStringReader luaState _ -> do setfield registryindex readerField pure . Just $ textReader luaState mwriter <- rawgetglobal "Writer" >>= \case TypeNil -> rawgetglobal "ByteStringWriter" >>= \case TypeNil -> do -- Neither `Writer` nor `BinaryWriter` are defined. Check for -- "Doc"; if present, use the file as a classic writer. docType <- rawgetglobal "Doc" pop 3 -- remove nils/value of "Writer", "ByteStringWriter", "Doc" pure $ if docType /= TypeFunction then Nothing else Just . TextWriter $ \opts doc -> do -- See TextWriter below for why the state is updated st <- PandocMonad.getCommonState liftIO $ withGCManagedState luaState $ unPandocLua (PandocMonad.putCommonState st) >> Classic.runCustom @PandocError opts doc _ -> Just <$!> do -- Binary writer. Writer function is on top of the stack. setfield registryindex writerField pure $ ByteStringWriter $ \opts doc -> do -- See TextWriter below for why the state is updated st <- PandocMonad.getCommonState -- Call writer with document and writer options as arguments. liftIO $ withGCManagedState luaState $ do unPandocLua (PandocMonad.putCommonState st) getfield registryindex writerField push doc pushWriterOptions opts callTrace 2 1 forcePeek @PandocError $ peekLazyByteString top _ -> Just <$!> do -- New-type text writer. Writer function is on top of the stack. setfield registryindex writerField pure $ TextWriter $ \opts doc -> do -- The CommonState might have changed since the Lua file was -- loaded. That's why the state must be updated when the -- writer is run. (#9229) st <- PandocMonad.getCommonState liftIO $ withGCManagedState luaState $ do unPandocLua (PandocMonad.putCommonState st) getfield registryindex writerField push doc pushWriterOptions opts callTrace 2 1 forcePeek @PandocError $ peekText top pure $ CustomComponents { customReader = mreader , customWriter = mwriter , customTemplate = mtemplate , customExtensions = mextsConf } -- | "Raw", non-metatable lookup of a key in the global table. -- -- Most classic writers contain code that throws an error if a global -- is not present. This would break our check for the existence of a -- "Writer" function. We resort to raw access for that reason, but -- could also catch the error instead. -- -- TODO: This function ensures the proper behavior of legacy custom -- writers. It should be replaced with 'getglobal' in the future. rawgetglobal :: LuaError e => Name -> LuaE e Lua.Type rawgetglobal x = do pushglobaltable pushName x rawget (nth 2) <* remove (nth 2) -- remove global table -- | Name under which the reader function is stored in the registry. readerField :: Name readerField = "Pandoc Reader function" -- | Name under which the writer function is stored in the registry. writerField :: Name writerField = "Pandoc Writer function" -- | Runs a Lua action in a continuable environment and transfers the common -- state after the Lua action has finished. inLua :: (PandocMonad m, MonadIO m) => GCManagedState -> LuaE PandocError a -> m a inLua st luaAction = do let inLua' = liftIO . withGCManagedState @PandocError st result <- inLua' luaAction cstate <- inLua' (unPandocLua PandocMonad.getCommonState) PandocMonad.putCommonState cstate return result -- | Returns the ByteStringReader function byteStringReader :: (PandocMonad m, MonadIO m) => GCManagedState -> Reader m byteStringReader st = ByteStringReader $ \ropts input -> inLua st $ do getfield registryindex readerField push input push ropts pcallTrace 2 1 >>= \case OK -> forcePeek $ peekPandoc top _ -> throwErrorAsException -- | Returns the TextReader function textReader :: (PandocMonad m, MonadIO m) => GCManagedState -> Reader m textReader st = TextReader $ \ropts srcs -> inLua st $ do let input = toSources srcs getfield registryindex readerField push input push ropts pcallTrace 2 1 >>= \case OK -> forcePeek (peekPandoc top) _ -> throwErrorAsException ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Documentation.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {- | Module : Text.Pandoc.Lua.Documentation Copyright : Copyright © 2026 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Render Lua documentation -} module Text.Pandoc.Lua.Documentation ( renderDocumentation ) where import Data.Default (def) import Data.List (intersperse) import Data.Sequence (Seq ((:|>))) import Data.Version (showVersion) import HsLua as Lua import Text.Pandoc.Class (runPure) import Text.Pandoc.Definition (Pandoc (Pandoc)) import Text.Pandoc.Extensions (extensionsFromList, Extension (..)) import Text.Pandoc.Options (ReaderOptions (readerExtensions)) import Text.Pandoc.Readers (readCommonMark) import Text.Pandoc.Shared (compactify) import Text.Pandoc.Walk (walk) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.UTF8 as UTF8 -- | Render the documentation object as pandoc Blocks renderDocumentation :: DocumentationObject -> B.Blocks renderDocumentation = \case DocObjectFunction fn -> renderFunctionDoc Nothing fn DocObjectModule mdl -> renderModuleDoc mdl DocObjectType tp -> renderTypeDoc Nothing tp renderTypeDoc :: Maybe T.Text -> TypeDoc -> B.Blocks renderTypeDoc mbmodname td = mconcat [ B.headerWith (ident, [], []) 1 (B.str $ typeDocName td) , parseCommonMark $ typeDocDescription td , if null $ typeDocMethods td then mempty else B.header 2 "Methods" <> (shiftHeadings 2 . mconcat . map (renderFunctionDoc Nothing) $ typeDocMethods td) ] where ident = case mbmodname of Just modname -> mconcat [ "type-", modname, ".", typeDocName td ] Nothing -> mconcat [ "type-", typeDocName td ] -- Shift headings shiftHeadings :: Int -> B.Blocks -> B.Blocks shiftHeadings incr blks = flip walk blks $ \case B.Header level attr inner -> B.Header (level + incr) attr inner x -> x renderModuleDoc :: ModuleDoc -> B.Blocks renderModuleDoc moddoc = let modname = moduleDocName moddoc in mconcat [ B.headerWith ("module-" <> modname, [], []) 1 (B.str $ "Module " <> modname) , parseCommonMark (moduleDocDescription moddoc) , if null (moduleDocFields moddoc) then mempty else let ident = modname <> "-fields" in B.headerWith (ident, [], []) 2 (B.str "Fields") <> shiftHeadings 0 (mconcat (map (renderFieldDoc modname) (moduleDocFields moddoc))) , if null (moduleDocFunctions moddoc) then mempty else let ident = modname <> "-functions" in B.headerWith (ident, [], []) 2 (B.str "Functions") <> (shiftHeadings 2 . mconcat . map (renderFunctionDoc $ Just modname) $ moduleDocFunctions moddoc) , if null (moduleDocTypes moddoc) then mempty else let ident = modname <> "-types" in B.headerWith (ident, [], []) 2 (B.str "Types") <> (shiftHeadings 2 . mconcat . map (renderTypeDoc $ Just modname) . reverse $ moduleDocTypes moddoc) ] parseCommonMark :: T.Text -> B.Blocks parseCommonMark txt = let exts = extensionsFromList [ Ext_wikilinks_title_after_pipe , Ext_smart ] result = runPure $ do Pandoc _ blks <- readCommonMark (def {readerExtensions = exts}) txt return $ B.fromList blks in either mempty id result appendInlines :: B.Blocks -> B.Inlines -> B.Blocks appendInlines blks inlns = case B.unMany blks of front :|> (B.Para xs) -> B.Many front <> B.para (addTo xs) front :|> (B.Plain xs) -> B.Many front <> B.plain (addTo xs) _ -> blks <> B.para inlns where addTo xs = B.fromList xs <> B.space <> inlns appendType :: B.Blocks -> TypeSpec -> B.Blocks appendType blks typespec = appendInlines blks (B.str "(" <> typeToInlines typespec <> B.str ")") typeToInlines :: TypeSpec -> B.Inlines typeToInlines = \case bt@BasicType{} -> builtin $ tystr bt NamedType "integer" -> builtin "integer" NamedType name -> B.linkWith ("", ["documented-type"], []) ("#" <> n2t name) mempty $ B.str (n2t name) SeqType itemtype -> "{" <> typeToInlines itemtype <> ",...}" SumType summands -> mconcat . intersperse (B.str "|") $ map typeToInlines summands AnyType -> "any" x -> tystr x where tystr = B.str . T.pack . typeSpecToString n2t = UTF8.toText . fromName builtin = B.spanWith ("", ["builtin-lua-type"], []) renderFunctionDoc :: Maybe T.Text -> FunctionDoc -> B.Blocks renderFunctionDoc mbmodule fndoc = let name = case mbmodule of Just _ -> T.takeWhileEnd (/= '.') $ funDocName fndoc Nothing -> funDocName fndoc ident = funDocName fndoc level = 1 argsString = argslist (funDocParameters fndoc) paramToDefItem p = ( B.code $ parameterName p , compactify [ appendType (parseCommonMark $ parameterDescription p) (parameterType p) ] ) paramlist = B.definitionList . map paramToDefItem $ funDocParameters fndoc in mconcat [ B.headerWith (ident, [], []) level (B.str name) , B.plain (B.code $ name <> " (" <> argsString <> ")") , parseCommonMark (funDocDescription fndoc) , if null (funDocParameters fndoc) then mempty else B.para "Parameters:" <> paramlist , if funDocResults fndoc == ResultsDocList [] then mempty else B.para "Returns:" <> renderResults (funDocResults fndoc) , case funDocSince fndoc of Nothing -> mempty Just version -> B.para $ B.emph $ "Since: " <> (B.str . T.pack $ showVersion version) ] renderResults :: ResultsDoc -> B.Blocks renderResults (ResultsDocMult descr) = parseCommonMark descr renderResults (ResultsDocList rvd) = B.bulletList $ map renderResultVal rvd where renderResultVal (ResultValueDoc typespec descr) = parseCommonMark descr `appendType` typespec argslist :: [ParameterDoc] -> T.Text argslist params = -- Expect optional values to come after required values. let (required, optional') = break parameterIsOptional params reqs = map parameterName required opts = map parameterName optional' in if null opts then T.intercalate ", " reqs else T.intercalate ", " reqs <> (if null required then "[" else "[, ") <> T.intercalate "[, " opts <> T.replicate (length opts) "]" renderFieldDoc :: T.Text -> FieldDoc -> B.Blocks renderFieldDoc _modname fd = B.headerWith (ident, [], []) 3 (B.str name) <> appendType (parseCommonMark $ fieldDocDescription fd) (fieldDocType fd) where ident = fieldDocName fd name = T.takeWhileEnd (/= '.') $ fieldDocName fd ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Engine.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Engine Copyright : Copyright © 2017-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Running pandoc Lua filters. -} module Text.Pandoc.Lua.Engine ( getEngine , applyFilter ) where import Control.Exception (throw) import Control.Monad ((>=>)) import Control.Monad.IO.Class (MonadIO (liftIO)) import HsLua.Core (getglobal, openlibs, run, top, tostring) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Filter (Environment (..)) import Text.Pandoc.Error (PandocError (PandocFilterError, PandocLuaError)) import Text.Pandoc.Lua.Custom (loadCustom) import Text.Pandoc.Lua.Filter (runFilterFile) import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.Run (runLua) import Text.Pandoc.Lua.Orphans () import Text.Pandoc.Scripting (ScriptingEngine (..)) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.Text as T -- | Constructs the Lua scripting engine. getEngine :: MonadIO m => m ScriptingEngine getEngine = do versionName <- liftIO . run @PandocError $ do openlibs getglobal "_VERSION" tostring top pure $ ScriptingEngine { engineName = maybe "Lua (unknown version)" UTF8.toText versionName , engineApplyFilter = applyFilter , engineLoadCustom = loadCustom } -- | Run the Lua filter in @filterPath@ for a transformation to the -- target format (first element in args). Pandoc uses Lua init files to -- setup the Lua interpreter. applyFilter :: (PandocMonad m, MonadIO m) => Environment -> [String] -> FilePath -> Pandoc -> m Pandoc applyFilter fenv args fp doc = do let globals = [ FORMAT $ case args of x:_ -> T.pack x _ -> "" , PANDOC_READER_OPTIONS (envReaderOptions fenv) , PANDOC_WRITER_OPTIONS (envWriterOptions fenv) , PANDOC_SCRIPT_FILE fp ] runLua >=> forceResult fp $ do setGlobals globals runFilterFile fp doc forceResult :: (PandocMonad m, MonadIO m) => FilePath -> Either PandocError Pandoc -> m Pandoc forceResult fp eitherResult = case eitherResult of Right x -> return x Left err -> throw . PandocFilterError (T.pack fp) $ case err of PandocLuaError msg -> msg _ -> T.pack $ show err ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Filter.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Filter Copyright : © 2012-2024 John MacFarlane, © 2017-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Types and functions for running Lua filters. -} module Text.Pandoc.Lua.Filter ( runFilterFile , runFilterFile' ) where import Control.Monad ((>=>), (<$!>)) import HsLua as Lua import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.Filter import Text.Pandoc.Lua.PandocLua () -- | Transform document using the filter defined in the given file. -- Runs the filter in the global environment. runFilterFile :: FilePath -> Pandoc -> LuaE PandocError Pandoc runFilterFile filterPath doc = do Lua.pushglobaltable runFilterFile' Lua.top filterPath doc <* Lua.pop 1 -- | Like 'runFilterFile', but uses the table at the given index as the -- environment in which the filter is run. runFilterFile' :: StackIndex -> FilePath -> Pandoc -> LuaE PandocError Pandoc runFilterFile' envIdx filterPath doc = do oldtop <- gettop stat <- dofileTrace' envIdx (Just filterPath) if stat /= OK then throwErrorAsException else do newtop <- gettop -- Use the returned filters, or the implicitly defined global -- filter if nothing was returned. luaFilters <- forcePeek $ if newtop - oldtop >= 1 then liftLua (rawlen top) >>= \case -- explicitly returned filter, either a single one or a list 0 -> (:[]) <$!> peekFilter top -- single filter _ -> peekList peekFilter top -- list of explicit filters else (:[]) <$!> peekFilter envIdx -- get the implicit filter in _ENV settop oldtop runAll luaFilters doc -- | Apply Lua filters to a document runAll :: [Filter] -> Pandoc -> LuaE PandocError Pandoc runAll = foldr ((>=>) . applyFully) return -- | Like 'HsLua.Core.Trace.dofileTrace', but uses a local environment. dofileTrace' :: LuaError e => StackIndex -- ^ stack index of the environment table -> Maybe FilePath -- ^ file to load (or @Nothing@ for stdin) -> LuaE e Status dofileTrace' envIdx fp = do absEnv <- Lua.absindex envIdx loadfile fp >>= \case OK -> do Lua.pushvalue absEnv Just (Name "_ENV") <- Lua.setupvalue (Lua.nth 2) 1 pcallTrace 0 multret s -> pure s ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Global.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Pandoc's Lua globals. -} module Text.Pandoc.Lua.Global ( Global (..) , setGlobals ) where import HsLua as Lua import HsLua.Module.Version (pushVersion) import Text.Pandoc.Class ( getInputFiles, getOutputFile, getLog , getRequestHeaders, getResourcePath, getSourceURL , getUserDataDir, getTrace, getVerbosity ) import Text.Pandoc.Definition (Pandoc, pandocTypesVersion) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Lua.Marshal.LogMessage (pushLogMessage) import Text.Pandoc.Lua.Marshal.Pandoc (pushPandoc) import Text.Pandoc.Lua.Marshal.ReaderOptions (pushReaderOptionsReadonly) import Text.Pandoc.Lua.Marshal.WriterOptions (pushWriterOptions) import Text.Pandoc.Lua.PandocLua (unPandocLua) import Text.Pandoc.Options (ReaderOptions, WriterOptions) import Text.Pandoc.Version (pandocVersion) import qualified Data.Text as Text -- | Permissible global Lua variables. data Global = FORMAT Text.Text | PANDOC_API_VERSION | PANDOC_DOCUMENT Pandoc | PANDOC_READER_OPTIONS ReaderOptions | PANDOC_WRITER_OPTIONS WriterOptions | PANDOC_SCRIPT_FILE FilePath | PANDOC_STATE | PANDOC_VERSION -- Cannot derive instance of Data because of CommonState -- | Set all given globals. setGlobals :: [Global] -> LuaE PandocError () setGlobals = mapM_ setGlobal setGlobal :: Global -> LuaE PandocError () setGlobal global = case global of -- This could be simplified if Global was an instance of Data. FORMAT format -> do Lua.pushText format Lua.setglobal "FORMAT" PANDOC_API_VERSION -> do pushVersion pandocTypesVersion Lua.setglobal "PANDOC_API_VERSION" PANDOC_DOCUMENT doc -> do pushPandoc doc Lua.setglobal "PANDOC_DOCUMENT" PANDOC_READER_OPTIONS ropts -> do pushReaderOptionsReadonly ropts Lua.setglobal "PANDOC_READER_OPTIONS" PANDOC_WRITER_OPTIONS wopts -> do pushWriterOptions wopts Lua.setglobal "PANDOC_WRITER_OPTIONS" PANDOC_SCRIPT_FILE filePath -> do Lua.pushString filePath Lua.setglobal "PANDOC_SCRIPT_FILE" PANDOC_STATE -> do -- The common state is an opaque value. We provide a table that -- contains the values accessible through the PandocMonad API. This -- is for backwards compatibility, as the state used to be exposed -- as a read-only object. Lua.newtable Lua.newmetatable "CommonStateInterface" Lua.pushHaskellFunction $ do Lua.forcePeek (peekText (Lua.nthBottom 2)) >>= \case "input_files" -> do pushPandocList pushString =<< unPandocLua getInputFiles return 1 "output_file" -> do maybe pushnil pushString =<< unPandocLua getOutputFile return 1 "log" -> do pushPandocList pushLogMessage =<< unPandocLua getLog return 1 "request_headers" -> do pushPandocList (pushPair pushText pushText) =<< unPandocLua getRequestHeaders return 1 "resource_path" -> do pushPandocList pushString =<< unPandocLua getResourcePath return 1 "source_url" -> do maybe pushnil pushText =<< unPandocLua getSourceURL return 1 "user_data_dir" -> do maybe pushnil pushString =<< unPandocLua getUserDataDir return 1 "trace" -> do pushBool =<< unPandocLua getTrace return 1 "verbosity" -> do pushString . show =<< unPandocLua getVerbosity return 1 _ -> failLua "Unknown key" Lua.setfield (Lua.nth 2) "__index" Lua.setmetatable (Lua.nth 2) Lua.setglobal "PANDOC_STATE" PANDOC_VERSION -> do pushVersion pandocVersion Lua.setglobal "PANDOC_VERSION" ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Init.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {- | Module : Text.Pandoc.Lua.Init Copyright : © 2017-2026 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Functions to initialize the Lua interpreter. -} module Text.Pandoc.Lua.Init ( initLua , userInit ) where import Control.Monad (when) import Control.Monad.Catch (throwM) import HsLua as Lua hiding (status) import Text.Pandoc.Class (report) import Text.Pandoc.Data (readDataFile) import Text.Pandoc.Error (PandocError (PandocLuaError)) import Text.Pandoc.Logging (LogMessage (ScriptingWarning)) import Text.Pandoc.Lua.Module (initModules) import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua) import Text.Pandoc.Lua.SourcePos (luaSourcePos) import qualified Data.Text as T import qualified Text.Pandoc.UTF8 as UTF8 -- | Initialize Lua with all default and pandoc-specific libraries and default -- globals. initLua :: PandocLua () initLua = do liftPandocLua Lua.openlibs setWarnFunction initModules liftPandocLua userInit -- | User-controlled initialization, e.g., running the user's init script. userInit :: LuaE PandocError () userInit = runInitScript -- | Run the @init.lua@ data file as a Lua script. runInitScript :: LuaE PandocError () runInitScript = runDataFileScript "init.lua" -- | Get a data file and run it as a Lua script. runDataFileScript :: FilePath -> LuaE PandocError () runDataFileScript scriptFile = do script <- unPandocLua $ readDataFile scriptFile status <- Lua.dostring script when (status /= Lua.OK) $ do err <- popException let prefix = "Couldn't load '" <> T.pack scriptFile <> "':\n" throwM . PandocLuaError . (prefix <>) $ case err of PandocLuaError msg -> msg _ -> T.pack $ show err setWarnFunction :: PandocLua () setWarnFunction = liftPandocLua . setwarnf' $ \msg -> do -- reporting levels: -- 0: this hook, -- 1: userdata wrapper function for the hook, -- 2: warn, -- 3: function calling warn. pos <- luaSourcePos 3 unPandocLua . report $ ScriptingWarning (UTF8.toText msg) pos ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/Chunks.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Marshal.Chunks Copyright : © 2022 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Marshaling chunks, i.e., pandoc subdocuments. -} module Text.Pandoc.Lua.Marshal.Chunks ( peekChunk , pushChunk , peekChunkedDoc , pushChunkedDoc ) where import Control.Monad ((<$!>)) import Data.Tree (Tree (..)) import HsLua import Text.Pandoc.Chunks (Chunk (..), ChunkedDoc (..), SecInfo (..)) import Text.Pandoc.Lua.Marshal.AST -- | Retrieves a 'Chunk' from the Lua stack. peekChunk :: LuaError e => Peeker e Chunk peekChunk = peekUD typeChunk -- | Pushes a 'Chunk' to the top of the Lua stack. pushChunk :: LuaError e => Pusher e Chunk pushChunk = pushUD typeChunk typeChunk :: LuaError e => DocumentedType e Chunk typeChunk = deftype "Chunk" [ operation Tostring $ lambda ### liftPure show <#> udparam typeChunk "chunk" "chunk to print in native format" =#> functionResult pushString "string" "Haskell representation" ] [ property "heading" "heading text" (pushInlines, chunkHeading) (peekInlinesFuzzy, \chunk inlns -> chunk{ chunkHeading = inlns }) , property "id" "identifier" (pushText, chunkId) (peekText, \chunk ident -> chunk{ chunkId = ident }) , property "level" "level of topmost heading in chunk" (pushIntegral, chunkLevel) (peekIntegral, \chunk level -> chunk{ chunkLevel = level }) , property "number" "chunk number" (pushIntegral, chunkNumber) (peekIntegral, \chunk number -> chunk{ chunkNumber = number }) , property "section_number" "hierarchical section number" (pushMaybe pushText, chunkSectionNumber) (peekMaybe peekText, \chunk secnum -> chunk{ chunkSectionNumber = secnum }) , property "path" "target filepath for this chunk" (pushString, chunkPath) (peekString, \chunk path -> chunk{ chunkPath = path }) , property "up" "link to the enclosing section chunk, if any" (pushMaybe pushChunk, chunkUp) (peekMaybe peekChunk, \chunk up -> chunk{ chunkUp = up }) , property "prev" "link to the previous section, if any" (pushMaybe pushChunk, chunkPrev) (peekMaybe peekChunk, \chunk prev -> chunk{ chunkPrev = prev }) , property "next" "link to the next section, if any" (pushMaybe pushChunk, chunkNext) (peekMaybe peekChunk, \chunk next' -> chunk{ chunkNext = next' }) , property "unlisted" ( "whether the section in this chunk should be listed in the TOC" <> "even if the chunk has no section number" ) (pushBool, chunkUnlisted) (peekBool, \chunk unlisted -> chunk { chunkUnlisted = unlisted }) , property "contents" "the chunk's block contents" (pushBlocks, chunkContents) (peekBlocksFuzzy, \chunk blks -> chunk{ chunkContents = blks }) ] -- | Retrieves a 'ChunkedDoc' from the Lua stack. peekChunkedDoc :: LuaError e => Peeker e ChunkedDoc peekChunkedDoc = peekUD typeChunkedDoc -- | Pushes a 'ChunkedDoc to the top of the Lua stack. pushChunkedDoc :: LuaError e => Pusher e ChunkedDoc pushChunkedDoc = pushUD typeChunkedDoc -- | Lua type for 'ChunkedDoc' values. typeChunkedDoc :: LuaError e => DocumentedType e ChunkedDoc typeChunkedDoc = deftype "ChunkedDoc" [] [ readonly "chunks" "list of chunks that make up the document" (pushList pushChunk, chunkedChunks) , readonly "meta" "the document's metadata" (pushMeta, chunkedMeta) , readonly "toc" "table of contents information" (pushTocTree, chunkedTOC) ] -- | Pushes a TOC tree to the stack. The resulting object is a list with -- the top-level entry placed at index @0@ and all subentries as the -- rest of the list. pushTocTree :: LuaError e => Pusher e (Tree SecInfo) pushTocTree (Node secInfo subSecInfo) = do pushList pushTocTree subSecInfo pushSecInfo secInfo rawseti (nth 2) 0 pushSecInfo :: LuaError e => Pusher e SecInfo pushSecInfo = pushAsTable [ ("title" , pushInlines . secTitle) , ("number" , maybe pushnil pushText . secNumber) , ("id" , pushText . secId) , ("path" , pushText . secPath) , ("level" , pushIntegral . secLevel) ] peekMaybe :: LuaError e => Peeker e a -> Peeker e (Maybe a) peekMaybe p idx = liftLua (isnoneornil idx) >>= \case True -> pure Nothing False -> Just <$!> p idx pushMaybe :: LuaError e => Pusher e a -> Pusher e (Maybe a) pushMaybe = maybe pushnil ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/CommonState.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Marshal.CommonState Copyright : © 2012-2024 John MacFarlane © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Instances to marshal (push) and unmarshal (peek) the common state. -} module Text.Pandoc.Lua.Marshal.CommonState ( typeCommonState , peekCommonState , pushCommonState ) where import HsLua import Text.Pandoc.Class (CommonState) -- | Lua type used for the @CommonState@ object. -- -- This is an opaque value that is required for the Lua interpreter -- to become an instance of "PandocMonad". -- typeCommonState :: LuaError e => DocumentedType e CommonState typeCommonState = deftype "CommonState" [] [] -- | Retrieves the common state from Lua peekCommonState :: LuaError e => Peeker e CommonState peekCommonState = peekUD typeCommonState -- | Pushes the common pandoc state to the Lua stack. pushCommonState :: LuaError e => Pusher e CommonState pushCommonState = pushUD typeCommonState ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/Context.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.Context Copyright : © 2012-2024 John MacFarlane © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshaling instance for doctemplates Context and its components. -} module Text.Pandoc.Lua.Marshal.Context ( peekContext , pushContext ) where import Control.Monad (when, (<$!>)) import Data.Text (Text) import HsLua as Lua import HsLua.Module.DocLayout (peekDoc, pushDoc) import Text.DocTemplates (Context(..), Val(..)) instance Pushable (Context Text) where push = pushContext instance Pushable (Val Text) where push = pushVal -- | Retrieves a template context from the Lua stack. peekContext :: LuaError e => Peeker e (Context Text) peekContext idx = Context <$!> peekMap peekText peekVal idx -- | Pushes a template context to the Lua stack. pushContext :: LuaError e => Pusher e (Context Text) pushContext ctx = do pushMap pushText pushVal $ unContext ctx created <- Lua.newmetatable "pandoc Context" when created $ do pushName "__concat" pushHaskellFunction $ do c1 <- forcePeek $ peekContext (nthBottom 1) c2 <- forcePeek $ peekContext (nthBottom 2) pushContext (c1 <> c2) return 1 rawset (nth 3) setmetatable (nth 2) pushVal :: LuaError e => Pusher e (Val Text) pushVal = \case NullVal -> Lua.pushnil BoolVal b -> Lua.pushBool b MapVal ctx -> pushContext ctx ListVal xs -> pushList pushVal xs SimpleVal d -> pushDoc d peekVal :: LuaError e => Peeker e (Val Text) peekVal idx = liftLua (ltype idx) >>= \case TypeNil -> pure NullVal TypeBoolean -> BoolVal <$!> peekBool idx TypeNumber -> SimpleVal <$!> peekDoc idx TypeString -> SimpleVal <$!> peekDoc idx TypeTable -> do len <- liftLua $ Lua.rawlen idx if len <= 0 then MapVal <$!> peekContext idx else ListVal <$!> peekList peekVal idx TypeUserdata -> SimpleVal <$!> peekDoc idx _ -> failPeek =<< typeMismatchMessage "Doc, string, boolean, table, or nil" idx ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/Format.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.Format Copyright : © 2022-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Marshaling functions and instance for format related types, including 'Extensions' and 'ExtensionConfig'. -} module Text.Pandoc.Lua.Marshal.Format ( peekExtensions , pushExtensions , peekExtensionsConfig , pushExtensionsConfig , peekFlavoredFormat ) where import Control.Applicative ((<|>)) import Control.Monad ((<$!>)) import Data.Maybe (fromMaybe) import HsLua import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Extensions ( Extension, Extensions, extensionsFromList, extensionsToList , getDefaultExtensions, readExtension, showExtension ) import Text.Pandoc.Format ( ExtensionsConfig (..), ExtensionsDiff (..), FlavoredFormat (..) , diffExtensions, parseFlavoredFormat) import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua)) -- | Retrieves a single 'Extension' from the Lua stack. peekExtension :: LuaError e => Peeker e Extension peekExtension idx = do extString <- peekString idx return $ readExtension extString {-# INLINE peekExtension #-} -- | Pushes an individual 'Extension' to the Lua stack. pushExtension :: LuaError e => Pusher e Extension pushExtension = pushText . showExtension {-# INLINE pushExtension #-} -- | Retrieves an 'Extensions' set from the Lua stack. peekExtensions :: LuaError e => Peeker e Extensions peekExtensions = fmap extensionsFromList . peekList peekExtension {-# INLINE peekExtensions #-} -- | Pushes a set of 'Extensions' to the top of the Lua stack. pushExtensions :: LuaError e => Pusher e Extensions pushExtensions = pushViaJSON {-# INLINE pushExtensions #-} instance Peekable Extensions where safepeek = peekExtensions instance Pushable Extensions where push = pushExtensions -- | Retrieves an 'ExtensionsConfig' value from the Lua stack. peekExtensionsConfig :: LuaError e => Peeker e ExtensionsConfig peekExtensionsConfig idx = do diff <- peekExtensionsDiff idx return $ ExtensionsConfig { extsDefault = extsToEnable diff , extsSupported = extsToEnable diff <> extsToDisable diff } -- | Pushes an 'ExtensionsConfig' value as a table with that maps -- extensions to their default enabled/disabled status. pushExtensionsConfig :: LuaError e => Pusher e ExtensionsConfig pushExtensionsConfig (ExtensionsConfig def supported) = pushKeyValuePairs pushExtension pushBool $ map (,False) (extensionsToList supported) ++ map (,True) (extensionsToList def) instance Peekable ExtensionsConfig where safepeek = peekExtensionsConfig peekExtensionsDiff :: LuaError e => Peeker e ExtensionsDiff peekExtensionsDiff = typeChecked "table" istable $ \idx -> (do en <- peekFieldRaw (emptyOr (fmap Just . peekExtensions)) "enable" idx di <- peekFieldRaw (emptyOr (fmap Just . peekExtensions)) "disable" idx if (en, di) == (Nothing, Nothing) then failPeek "At least on of 'enable' and 'disable' must be set" else return $ ExtensionsDiff (fromMaybe mempty en) (fromMaybe mempty di)) <|> -- two lists of extensions; the first is list assumed to contain those -- extensions to be enabled (uncurry ExtensionsDiff <$!> peekPair peekExtensions peekExtensions idx) <|> (do let exts <- peekKeyValuePairs peekExtension peekEnabled idx let enabled = extensionsFromList . map fst $ filter snd exts let disabled = extensionsFromList . map fst $ filter (not . snd) exts return $ ExtensionsDiff enabled disabled) -- | Retrieves the activation status of an extension. True or the string -- @'enable'@ for activated, False or 'disable' for disabled. peekEnabled :: LuaError e => Peeker e Bool peekEnabled idx' = liftLua (ltype idx') >>= \case TypeBoolean -> peekBool idx' TypeString -> peekText idx' >>= \case "disable" -> pure False "enable" -> pure True _ -> failPeek "expected 'disable' or 'enable'" _ -> failPeek "expected boolean or string" -- | Retrieves a flavored format from the Lua stack. peekFlavoredFormat :: Peeker PandocError FlavoredFormat peekFlavoredFormat idx = retrieving "flavored format" $ liftLua (ltype idx) >>= \case TypeString -> peekText idx >>= liftLua . unPandocLua . parseFlavoredFormat TypeTable -> do let diffFor format idx' = peekExtensionsDiff idx' <|> (getDefaultExtensions format `diffExtensions`) <$> (typeChecked "table" istable peekExtensions idx') format <- peekFieldRaw peekText "format" idx extsDiff <- peekFieldRaw (emptyOr (diffFor format)) "extensions" idx return (FlavoredFormat format extsDiff) _ -> failPeek =<< typeMismatchMessage "string or table" idx -- | Returns 'mempty' if the given stack index is @nil@, and the result -- of the peeker otherwise. emptyOr :: Monoid a => Peeker e a -> Peeker e a emptyOr p idx = do nil <- liftLua (isnil idx) if nil then pure mempty else p idx ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/ImageSize.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Marshal.ImageSize Copyright : © 2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Marshaling image properties. -} module Text.Pandoc.Lua.Marshal.ImageSize ( pushImageType , pushImageSize ) where import Data.Char (toLower) import HsLua import Text.Pandoc.ImageSize -- | Pushes an 'ImageType' as a string value. pushImageType :: LuaError e => Pusher e ImageType pushImageType = pushString . map toLower . show -- | Pushes a dimensional value. pushImageSize :: LuaError e => Pusher e ImageSize pushImageSize = pushAsTable [ ("width", pushIntegral . pxX) , ("height", pushIntegral . pxY) , ("dpi_horz", pushIntegral . dpiX) , ("dpi_vert", pushIntegral . dpiY) ] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/LogMessage.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Marshal.LogMessage Copyright : © 2017-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Pushing and retrieving of pandoc log messages. -} module Text.Pandoc.Lua.Marshal.LogMessage ( peekLogMessage , pushLogMessage , typeLogMessage ) where import Control.Applicative (optional) import Data.Maybe (fromMaybe) import HsLua import Text.Pandoc.Logging (LogMessage, showLogMessage) import qualified Data.Aeson as Aeson -- | Type definition for pandoc log messages. typeLogMessage :: LuaError e => DocumentedType e LogMessage typeLogMessage = deftype "LogMessage" [ operation Index $ defun "__tostring" ### liftPure showLogMessage <#> udparam typeLogMessage "msg" "object" =#> functionResult pushText "string" "stringified log message" , operation Eq $ lambda ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b)) <#> parameter (optional . peekLogMessage) "a" "LogMessage" "" <#> parameter (optional . peekLogMessage) "b" "LogMessage" "" =#> functionResult pushBool "boolean" "whether the two are equal" , operation (CustomOperation "__tojson") $ lambda ### liftPure Aeson.encode <#> udparam typeLogMessage "msg" "object" =#> functionResult pushLazyByteString "string" "JSON encoded object" ] mempty -- no members -- | Pushes a LogMessage to the stack. pushLogMessage :: LuaError e => Pusher e LogMessage pushLogMessage = pushUD typeLogMessage peekLogMessage :: LuaError e => Peeker e LogMessage peekLogMessage = peekUD typeLogMessage ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/PandocError.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Lua.Marshal.PandocError Copyright : © 2020-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshal of @'PandocError'@ values. -} module Text.Pandoc.Lua.Marshal.PandocError ( peekPandocError , pushPandocError , typePandocError ) where import HsLua (LuaError, Peeker, Pusher, liftLua, pushText) import HsLua.Packaging import Text.Pandoc.Error (PandocError (PandocLuaError), renderError) import qualified HsLua as Lua import qualified HsLua.Core.Utf8 as UTF8 -- | Lua userdata type definition for PandocError. typePandocError :: LuaError e => DocumentedType e PandocError typePandocError = deftype "PandocError" [ operation Tostring $ defun "__tostring" ### liftPure (\case PandocLuaError e -> e err -> renderError err) <#> udparam typePandocError "obj" "PandocError object" =#> functionResult pushText "string" "string representation of error." ] mempty -- no members -- | Peek a @'PandocError'@ element to the Lua stack. pushPandocError :: LuaError e => Pusher e PandocError pushPandocError = pushUD typePandocError -- | Retrieve a @'PandocError'@ from the Lua stack. peekPandocError :: LuaError e => Peeker e PandocError peekPandocError idx = Lua.retrieving "PandocError" $ liftLua (Lua.ltype idx) >>= \case Lua.TypeUserdata -> peekUD typePandocError idx _ -> do msg <- liftLua $ do Lua.pushvalue idx Lua.state >>= \l -> Lua.liftIO (Lua.popErrorMessage l) return $ PandocLuaError (UTF8.toText msg) ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/ReaderOptions.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.ReaderOptions Copyright : © 2012-2024 John MacFarlane © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshaling instance for ReaderOptions and its components. -} module Text.Pandoc.Lua.Marshal.ReaderOptions ( peekReaderOptions , pushReaderOptions , pushReaderOptionsReadonly ) where import Data.Default (def) import HsLua as Lua import Text.Pandoc.Lua.Marshal.Format (peekExtensions, pushExtensions) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Options (ReaderOptions (..)) -- -- Reader Options -- -- | Retrieve a ReaderOptions value, either from a normal ReaderOptions -- value, from a read-only object, or from a table with the same -- keys as a ReaderOptions object. peekReaderOptions :: LuaError e => Peeker e ReaderOptions peekReaderOptions = retrieving "ReaderOptions" . \idx -> liftLua (ltype idx) >>= \case TypeUserdata -> choice [ peekUD typeReaderOptions , peekUD typeReaderOptionsReadonly ] idx TypeTable -> peekReaderOptionsTable idx _ -> failPeek =<< typeMismatchMessage "ReaderOptions userdata or table" idx -- | Pushes a ReaderOptions value as userdata object. pushReaderOptions :: LuaError e => Pusher e ReaderOptions pushReaderOptions = pushUD typeReaderOptions -- | Pushes a ReaderOptions object, but makes it read-only. pushReaderOptionsReadonly :: LuaError e => Pusher e ReaderOptions pushReaderOptionsReadonly = pushUD typeReaderOptionsReadonly -- | ReaderOptions object type for read-only values. typeReaderOptionsReadonly :: LuaError e => DocumentedType e ReaderOptions typeReaderOptionsReadonly = deftype "ReaderOptions (read-only)" [ operation Tostring $ lambda ### liftPure show <#> udparam typeReaderOptions "opts" "options to print in native format" =#> functionResult pushString "string" "Haskell representation" , operation Newindex $ lambda ### (failLua "This ReaderOptions value is read-only.") =?> "Throws an error when called, i.e., an assignment is made." ] readerOptionsMembers -- | 'ReaderOptions' object type. typeReaderOptions :: LuaError e => DocumentedType e ReaderOptions typeReaderOptions = deftype "ReaderOptions" [ operation Tostring $ lambda ### liftPure show <#> udparam typeReaderOptions "opts" "options to print in native format" =#> functionResult pushString "string" "Haskell representation" ] readerOptionsMembers -- | Member properties of 'ReaderOptions' Lua values. readerOptionsMembers :: LuaError e => [Member e (DocumentedFunction e) ReaderOptions] readerOptionsMembers = [ property "abbreviations" "" (pushSet pushText, readerAbbreviations) (peekSet peekText, \opts x -> opts{ readerAbbreviations = x }) , property "columns" "" (pushIntegral, readerColumns) (peekIntegral, \opts x -> opts{ readerColumns = x }) , property "default_image_extension" "" (pushText, readerDefaultImageExtension) (peekText, \opts x -> opts{ readerDefaultImageExtension = x }) , property "extensions" "" (pushExtensions, readerExtensions) (peekExtensions, \opts x -> opts{ readerExtensions = x }) , property "indented_code_classes" "" (pushPandocList pushText, readerIndentedCodeClasses) (peekList peekText, \opts x -> opts{ readerIndentedCodeClasses = x }) , property "standalone" "" (pushBool, readerStandalone) (peekBool, \opts x -> opts{ readerStandalone = x }) , property "strip_comments" "" (pushBool, readerStripComments) (peekBool, \opts x -> opts{ readerStripComments = x }) , property "tab_stop" "" (pushIntegral, readerTabStop) (peekIntegral, \opts x -> opts{ readerTabStop = x }) , property "track_changes" "" (pushViaJSON, readerTrackChanges) (choice [peekRead, peekViaJSON], \opts x -> opts{ readerTrackChanges = x }) ] -- | Retrieves a 'ReaderOptions' object from a table on the stack, using -- the default values for all missing fields. -- -- Internally, this pushes the default reader options, sets each -- key/value pair of the table in the userdata value, then retrieves the -- object again. This will update all fields and complain about unknown -- keys. peekReaderOptionsTable :: LuaError e => Peeker e ReaderOptions peekReaderOptionsTable idx = retrieving "ReaderOptions (table)" $ do liftLua $ do absidx <- absindex idx pushUD typeReaderOptions def let setFields = do next absidx >>= \case False -> return () -- all fields were copied True -> do pushvalue (nth 2) *> insert (nth 2) settable (nth 4) -- set in userdata object setFields pushnil -- first key setFields peekUD typeReaderOptions top `lastly` pop 1 instance Pushable ReaderOptions where push = pushReaderOptions ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/Reference.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.ReaderOptions Copyright : © 2012-2024 John MacFarlane © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshal citeproc 'Reference' values. -} module Text.Pandoc.Lua.Marshal.Reference ( pushReference ) where import Citeproc.Types ( Date (..), DateParts (..), ItemId (..), Name (..), Reference (..) , Val (..), Variable, fromVariable ) import Control.Monad (forM_) import HsLua hiding (Name, Reference, pushName, peekName) import Text.Pandoc.Builder (Inlines, toList) import Text.Pandoc.Lua.Marshal.Inline (pushInlines) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import qualified Data.Map as Map -- | Pushes a ReaderOptions value as userdata object. pushReference :: LuaError e => Pusher e (Reference Inlines) pushReference reference = do pushAsTable [ ("id", pushItemId . referenceId) , ("type", pushText . referenceType) ] reference forM_ (Map.toList $ referenceVariables reference) $ \(var, val) -> do pushVariable var pushVal val rawset (nth 3) -- | Pushes an 'ItemId' as a string. pushItemId :: Pusher e ItemId pushItemId = pushText . unItemId -- | Pushes a person's 'Name' as a table. pushName :: LuaError e => Pusher e Name pushName = pushAsTable [ ("family" , pushTextOrNil . nameFamily) , ("given" , pushTextOrNil . nameGiven) , ("dropping-particle" , pushTextOrNil . nameDroppingParticle) , ("non-dropping-particle" , pushTextOrNil . nameNonDroppingParticle) , ("suffix" , pushTextOrNil . nameSuffix) , ("literal" , pushTextOrNil . nameLiteral) , ("comma-suffix" , pushBoolOrNil . nameCommaSuffix) , ("static-ordering" , pushBoolOrNil . nameStaticOrdering) ] where pushTextOrNil = \case Nothing -> pushnil Just xs -> pushText xs -- | Pushes a boolean, but uses @nil@ instead of @false@; table fields -- are not set unless the value is true. pushBoolOrNil :: Pusher e Bool pushBoolOrNil = \case False -> pushnil True -> pushBool True -- | Pushes a 'Variable' as string. pushVariable :: Pusher e Variable pushVariable = pushText . fromVariable -- | Pushes a 'Val', i.e., a variable value. pushVal :: LuaError e => Pusher e (Val Inlines) pushVal = \case TextVal t -> pushText t FancyVal inlns -> pushInlines $ toList inlns NumVal i -> pushIntegral i NamesVal names -> pushPandocList pushName names DateVal date -> pushDate date _ -> pushText mempty -- | Pushes a 'Date' as table. pushDate :: LuaError e => Pusher e Date pushDate = pushAsTable [ ("date-parts", pushPandocList pushDateParts . dateParts) , ("circa", pushBoolOrNil . dateCirca) , ("season", maybe pushnil pushIntegral . dateSeason) , ("literal", maybe pushnil pushText . dateLiteral) ] where -- date parts are lists of Int values pushDateParts (DateParts dp) = pushPandocList pushIntegral dp ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/Sources.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.Lua.Marshaling.Sources Copyright : © 2021-2026 Albert Krewinkel License : GPL-2.0-or-later Marshal 'Sources'. -} module Text.Pandoc.Lua.Marshal.Sources ( peekSources , pushSources , typeSource ) where import Control.Monad ((<$!>)) import Data.Text (Text) import HsLua as Lua import Text.Pandoc.Lua.Marshal.List (newListMetatable) import Text.Pandoc.Sources (Sources (..), toSources) import Text.Parsec (SourcePos, sourceName) -- | Pushes the 'Sources' as a list of lazy Lua objects. pushSources :: LuaError e => Pusher e Sources pushSources (Sources srcs) = do pushList (pushUD typeSource) srcs newListMetatable "Sources" $ do pushName "__tostring" pushHaskellFunction $ do sources <- forcePeek $ peekList (peekUD typeSource) (nthBottom 1) pushText . mconcat $ map snd sources return 1 rawset (nth 3) setmetatable (nth 2) -- | Retrieves sources from the stack. peekSources :: LuaError e => Peeker e Sources peekSources idx = liftLua (ltype idx) >>= \case TypeTable -> mconcat <$!> peekList peekSourcesSingleton idx _ -> peekSourcesSingleton idx -- | Retrieves a Sources singleton, i.e., a list with exactly one item. peekSourcesSingleton :: LuaError e => Peeker e Sources peekSourcesSingleton = choice [ fmap toSources . peekText , fmap (Sources . (:[])) . peekUD typeSource , fmap (toSources . (:[])) . peekPair peekString peekText , fmap (toSources . (:[])) . (\idx -> (,) <$> peekFieldRaw peekString "name" idx <*> peekFieldRaw peekText "text" idx) ] -- | A @Sources@ item. type Source = (SourcePos, Text) -- | Source object type. typeSource :: LuaError e => DocumentedType e Source typeSource = deftype "Source" [ operation Tostring $ lambda ### liftPure snd <#> udparam typeSource "srcs" "Source to print in native format" =#> functionResult pushText "string" "source contents" ] [ readonly "name" "source name" (pushString, sourceName . fst) , readonly "text" "source text" (pushText, snd) ] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/Template.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshal.Template Copyright : © 2021-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Marshal 'Template' 'Text'. -} module Text.Pandoc.Lua.Marshal.Template ( pushTemplate , peekTemplate , typeTemplate ) where import Data.Text (Text) import HsLua as Lua import HsLua.Core.Utf8 as Lua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua (unPandocLua) import Text.Pandoc.Templates (Template, compileTemplate, runWithDefaultPartials) -- | Pushes a 'Template' as a an opaque userdata value. pushTemplate :: LuaError e => Pusher e (Template Text) pushTemplate = pushUD typeTemplate -- | Retrieves a 'Template' 'Text' value from the stack. peekTemplate :: Peeker PandocError (Template Text) peekTemplate idx = liftLua (ltype idx) >>= \case TypeString -> do let path = "templates/default.custom" let liftPM = liftLua . unPandocLua tmpl <- peekText idx liftPM (runWithDefaultPartials (compileTemplate path tmpl)) >>= \case Left e -> failPeek (Lua.fromString e) Right t -> pure t _ -> peekUD typeTemplate idx -- | Template object type. typeTemplate :: LuaError e => DocumentedType e (Template Text) typeTemplate = deftype "Template" [] [] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/WriterOptions.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.Marshaling.WriterOptions Copyright : © 2021-2024 Albert Krewinkel, John MacFarlane License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Marshaling instance for WriterOptions and its components. -} module Text.Pandoc.Lua.Marshal.WriterOptions ( peekWriterOptions , pushWriterOptions ) where import Control.Applicative (optional) import Data.Default (def) import HsLua as Lua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.Context (peekContext, pushContext) import Text.Pandoc.Lua.Marshal.Format (peekExtensions, pushExtensions) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Lua.Marshal.Template (peekTemplate, pushTemplate) import Text.Pandoc.Options (WriterOptions (..)) -- -- Writer Options -- -- | Retrieve a WriterOptions value, either from a normal WriterOptions -- value, from a read-only object, or from a table with the same -- keys as a WriterOptions object. peekWriterOptions :: Peeker PandocError WriterOptions peekWriterOptions = retrieving "WriterOptions" . \idx -> liftLua (ltype idx) >>= \case TypeUserdata -> peekUD typeWriterOptions idx TypeTable -> peekWriterOptionsTable idx _ -> failPeek =<< typeMismatchMessage "WriterOptions userdata or table" idx -- | Pushes a WriterOptions value as userdata object. pushWriterOptions :: Pusher PandocError WriterOptions pushWriterOptions = pushUD typeWriterOptions -- | 'WriterOptions' object type. typeWriterOptions :: DocumentedType PandocError WriterOptions typeWriterOptions = deftype "WriterOptions" [ operation Tostring $ lambda ### liftPure show <#> udparam typeWriterOptions "opts" "options to print in native format" =#> functionResult pushString "string" "Haskell representation" ] [ property "chunk_template" "Templates used to generate chunked HTML filenames (string)" (pushViaJSON, writerChunkTemplate) (peekViaJSON, \opts x -> opts{ writerChunkTemplate = x }) , property "cite_method" "How to print cites" (pushViaJSON, writerCiteMethod) (peekViaJSON, \opts x -> opts{ writerCiteMethod = x }) , property "columns" "Characters in a line (for text wrapping)" (pushIntegral, writerColumns) (peekIntegral, \opts x -> opts{ writerColumns = x }) , property "dpi" "DPI for pixel to/from inch/cm conversions" (pushIntegral, writerDpi) (peekIntegral, \opts x -> opts{ writerDpi = x }) , property "email_obfuscation" "How to obfuscate emails" (pushViaJSON, writerEmailObfuscation) (peekViaJSON, \opts x -> opts{ writerEmailObfuscation = x }) , property "split_level" "Level at which EPUB or chunked HTML documents are split into files" (pushIntegral, writerSplitLevel) (peekIntegral, \opts x -> opts{ writerSplitLevel = x }) , property "epub_chapter_level" "Deprecated synonym for split_level" (pushIntegral, writerSplitLevel) (peekIntegral, \opts x -> opts{ writerSplitLevel = x }) , property "epub_fonts" "Paths to fonts to embed" (pushPandocList pushString, writerEpubFonts) (peekList peekString, \opts x -> opts{ writerEpubFonts = x }) , property "epub_title_page" "Determines whether a title page is included in EPUB" (pushBool, writerEpubTitlePage) (peekBool, \opts x -> opts{ writerEpubTitlePage = x }) , property "epub_metadata" "Metadata to include in EPUB" (maybe pushnil pushText, writerEpubMetadata) (optional . peekText, \opts x -> opts{ writerEpubMetadata = x }) , property "epub_subdirectory" "Subdir for epub in OCF" (pushText, writerEpubSubdirectory) (peekText, \opts x -> opts{ writerEpubSubdirectory = x }) , property "extensions" "Markdown extensions that can be used" (pushExtensions, writerExtensions) (peekExtensions, \opts x -> opts{ writerExtensions = x }) , property "highlight_method" "Method to use for code highlighting ('none'|'default'|'idiomatic'|style)" (pushViaJSON, writerHighlightMethod) (peekViaJSON, \opts x -> opts{ writerHighlightMethod = x }) , property "html_math_method" "How to print math in HTML" (pushViaJSON, writerHTMLMathMethod) (peekViaJSON, \opts x -> opts{ writerHTMLMathMethod = x }) , property "link_images" "Include links to images instead of embedding in ODT" (pushBool, writerLinkImages) (peekBool, \opts x -> opts{ writerLinkImages = x }) , property "html_q_tags" "Use @@ tags for quotes in HTML" (pushBool, writerHtmlQTags) (peekBool, \opts x -> opts{ writerHtmlQTags = x }) , property "identifier_prefix" "Prefix for section & note ids in HTML and for footnote marks in markdown" (pushText, writerIdentifierPrefix) (peekText, \opts x -> opts{ writerIdentifierPrefix = x }) , property "incremental" "True if lists should be incremental" (pushBool, writerIncremental) (peekBool, \opts x -> opts{ writerIncremental = x }) , property "list_of_figures" "Include list of figures" (pushBool, writerListOfFigures) (peekBool, \opts x -> opts{ writerListOfFigures = x }) , property "list_of_tables" "Include list of tables" (pushBool, writerListOfTables) (peekBool, \opts x -> opts{ writerListOfTables = x }) , property "number_offset" "Starting number for section, subsection, ..." (pushPandocList pushIntegral, writerNumberOffset) (peekList peekIntegral, \opts x -> opts{ writerNumberOffset = x }) , property "number_sections" "Number sections in LaTeX" (pushBool, writerNumberSections) (peekBool, \opts x -> opts{ writerNumberSections = x }) , property "prefer_ascii" "Prefer ASCII representations of characters when possible" (pushBool, writerPreferAscii) (peekBool, \opts x -> opts{ writerPreferAscii = x }) , property "reference_doc" "Path to reference document if specified" (maybe pushnil pushString, writerReferenceDoc) (optional . peekString, \opts x -> opts{ writerReferenceDoc = x }) , property "reference_links" "Use reference links in writing markdown, rst" (pushBool, writerReferenceLinks) (peekBool, \opts x -> opts{ writerReferenceLinks = x }) , property "reference_location" "Location of footnotes and references for writing markdown" (pushViaJSON, writerReferenceLocation) (peekViaJSON, \opts x -> opts{ writerReferenceLocation = x }) , property "figure_caption_position" "Location of caption relative to the figure" (pushViaJSON, writerFigureCaptionPosition) (peekViaJSON, \opts x -> opts{ writerFigureCaptionPosition = x }) , property "table_caption_position" "Location of caption relative to the table" (pushViaJSON, writerTableCaptionPosition) (peekViaJSON, \opts x -> opts{ writerTableCaptionPosition = x }) , property "section_divs" "Put sections in div tags in HTML" (pushBool, writerSectionDivs) (peekBool, \opts x -> opts{ writerSectionDivs = x }) , property "setext_headers" "Use setext headers for levels 1-2 in markdown" (pushBool, writerSetextHeaders) (peekBool, \opts x -> opts{ writerSetextHeaders = x }) , property "list_tables" "Render tables using list tables in RST output" (pushBool, writerListTables) (peekBool, \opts x -> opts{ writerListTables = x }) , property "slide_level" "Force header level of slides" (maybe pushnil pushIntegral, writerSlideLevel) (optional . peekIntegral, \opts x -> opts{ writerSlideLevel = x }) -- , property "syntax_map" "Syntax highlighting definition" -- (pushViaJSON, writerSyntaxMap) -- (peekViaJSON, \opts x -> opts{ writerSyntaxMap = x }) -- :: SyntaxMap , property "tab_stop" "Tabstop for conversion btw spaces and tabs" (pushIntegral, writerTabStop) (peekIntegral, \opts x -> opts{ writerTabStop = x }) , property "table_of_contents" "Include table of contents" (pushBool, writerTableOfContents) (peekBool, \opts x -> opts{ writerTableOfContents = x }) , property "template" "Template to use" (maybe pushnil pushTemplate, writerTemplate) (optional . peekTemplate, \opts x -> opts{ writerTemplate = x }) -- :: Maybe (Template Text) , property "toc_depth" "Number of levels to include in TOC" (pushIntegral, writerTOCDepth) (peekIntegral, \opts x -> opts{ writerTOCDepth = x }) , property "top_level_division" "Type of top-level divisions" (pushViaJSON, writerTopLevelDivision) (peekViaJSON, \opts x -> opts{ writerTopLevelDivision = x }) , property "variables" "Variables to set in template" (pushContext, writerVariables) (peekContext, \opts x -> opts{ writerVariables = x }) , property "wrap_text" "Option for wrapping text" (pushViaJSON, writerWrapText) (peekViaJSON, \opts x -> opts{ writerWrapText = x }) ] -- | Retrieves a 'WriterOptions' object from a table on the stack, using -- the default values for all missing fields. -- -- Internally, this pushes the default writer options, sets each -- key/value pair of the table in the userdata value, then retrieves the -- object again. This will update all fields and complain about unknown -- keys. peekWriterOptionsTable :: Peeker PandocError WriterOptions peekWriterOptionsTable idx = retrieving "WriterOptions (table)" $ do liftLua $ do absidx <- absindex idx pushUD typeWriterOptions def let setFields = do next absidx >>= \case False -> return () -- all fields were copied True -> do pushvalue (nth 2) *> insert (nth 2) settable (nth 4) -- set in userdata object setFields pushnil -- first key setFields peekUD typeWriterOptions top `lastly` pop 1 ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/CLI.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.CLI Copyright : © 2022-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Command line helpers -} module Text.Pandoc.Lua.Module.CLI ( documentedModule ) where import Control.Applicative ((<|>)) import Data.Version (makeVersion) import HsLua import Text.Pandoc.App (defaultOpts, options, parseOptionsFromArgs) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () import qualified Data.Text as T #ifdef INCLUDE_REPL import HsLua.REPL (defaultConfig, replWithEnv, setup) #endif -- | Push the pandoc.types module on the Lua stack. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.cli" `withDescription` "Command line options and argument parsing." `withFields` [ deffield "default_options" `withType` "table" `withDescription` "Default CLI options, using a JSON-like representation." `withValue` pushViaJSON defaultOpts ] `withFunctions` [ defun "parse_options" ### parseOptions <#> parameter peekArgs "{string,...}" "args" "list of command line arguments" =#> functionResult pushViaJSON "table" "parsed options, using their JSON-like representation." #? T.unlines [ "Parses command line arguments into pandoc options." , "Typically this function will be used in stand-alone pandoc Lua" , "scripts, taking the list of arguments from the global `arg`." ] `since` makeVersion [3, 0] #ifdef INCLUDE_REPL , repl `since` makeVersion [3, 1, 2] #endif ] where peekArgs idx = (,) <$> (liftLua (rawgeti idx 0) *> (peekString top <|> pure "") `lastly` pop 1) <*> peekList peekString idx parseOptions (prg, args) = liftIO (parseOptionsFromArgs options defaultOpts prg args) >>= \case Left e -> failLua $ "Cannot process info option: " ++ show e Right opts -> pure opts #ifdef INCLUDE_REPL -- | Starts a REPL. repl :: DocumentedFunction PandocError repl = defun "repl" ### (\menvIdx -> do let repl' = case menvIdx of Nothing -> replWithEnv Nothing Just envIdx -> do settop envIdx fillWithGlobals envIdx replWithEnv . Just =<< ref registryindex setup defaultConfig repl') <#> opt (parameter (typeChecked "table" istable pure) "table" "env" ("Extra environment; the global environment is merged into this" <> " table.")) =?> T.unlines [ "The result(s) of the last evaluated input, or nothing if the last" , "input resulted in an error." ] #? T.unlines [ "Starts a read-eval-print loop (REPL). The function returns all" , "values of the last evaluated input. Exit the REPL by pressing" , "`ctrl-d` or `ctrl-c`; press `F1` to get a list of all key" , "bindings." , "" , "The REPL is started in the global namespace, unless the `env`" , "parameter is specified. In that case, the global namespace is" , "merged into the given table and the result is used as `_ENV` value" , "for the repl." , "" , "Specifically, local variables *cannot* be accessed, unless they" , "are explicitly passed via the `env` parameter; e.g." , "" , " function Pandoc (doc)" , " -- start repl, allow to access the `doc` parameter" , " -- in the repl" , " return pandoc.cli.repl{ doc = doc }" , " end" , "" , "**Note**: it seems that the function exits immediately on Windows," , "without prompting for user input." ] where fillWithGlobals idx = do -- Copy all global values into the table pushglobaltable pushnil let copyval = next (nth 2) >>= \case False -> return () True -> do pushvalue (nth 2) insert (nth 2) rawset idx copyval copyval pop 1 -- global table #endif ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Format.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Format Copyright : © 2022-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Lua module to handle pandoc templates. -} module Text.Pandoc.Lua.Module.Format ( documentedModule ) where import Data.Version (makeVersion) import HsLua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Extensions (getAllExtensions, getDefaultExtensions) import Text.Pandoc.Format (formatFromFilePaths, formatName, getExtensionsConfig) import Text.Pandoc.Lua.Marshal.Format (pushExtensions, pushExtensionsConfig) import Text.Pandoc.Lua.PandocLua () import qualified Data.Text as T -- | The "pandoc.format" module. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.format" `withDescription` T.unlines [ "Information about the formats supported by pandoc." ] `withFunctions` functions -- | Extension module functions. functions :: [DocumentedFunction PandocError] functions = [ defun "all_extensions" ### liftPure getAllExtensions <#> parameter peekText "string" "format" "format name" =#> functionResult pushExtensions "FormatExtensions" "all extensions supported for `format`" #? T.unlines [ "Returns the list of all valid extensions for a format." , "No distinction is made between input and output; an extension" , "can have an effect when reading a format but not when" , "writing it, or *vice versa*." ] `since` makeVersion [3,0] , defun "default_extensions" ### liftPure getDefaultExtensions <#> parameter peekText "string" "format" "format name" =#> functionResult pushExtensions "FormatExtensions" "default extensions enabled for `format`" #? T.unlines [ "Returns the list of default extensions of the given format; this" , "function does not check if the format is supported, it will return" , "a fallback list of extensions even for unknown formats." ] `since` makeVersion [3,0] , defun "extensions" ### liftPure getExtensionsConfig <#> textParam "format" "format identifier" =#> functionResult pushExtensionsConfig "table" "extensions config" #? T.unlines [ "Returns the extension configuration for the given format." , "The configuration is represented as a table with all supported" , "extensions as keys and their default status as value, with" , "`true` indicating that the extension is enabled by default," , "while `false` marks a supported extension that's disabled." , "" , "This function can be used to assign a value to the `Extensions`" , "global in custom readers and writers." ] `since` makeVersion [3,0] , defun "from_path" ### liftPure formatFromFilePaths <#> parameter (choice [ fmap (:[]) . peekString, peekList peekString]) "string|{string,...}" "path" "file path, or list of paths" =#> functionResult (maybe pushnil (pushText . formatName)) "string|nil" "format determined by heuristic" `since` makeVersion [3,1,2] ] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Image.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-| Module : Text.Pandoc.Lua.Module.Image Copyright : © 2024 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Lua module for basic image operations. -} module Text.Pandoc.Lua.Module.Image ( -- * Module documentedModule -- ** Functions , size , format ) where import Prelude hiding (null) import Data.Default (Default (def)) import Data.Maybe (fromMaybe) import Data.Version (makeVersion) import HsLua.Core import HsLua.Marshalling import HsLua.Packaging import Text.Pandoc.Error (PandocError) import Text.Pandoc.ImageSize (imageType, imageSize) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Lua.Marshal.ImageSize (pushImageType, pushImageSize) import Text.Pandoc.Lua.Marshal.WriterOptions (peekWriterOptions) import qualified Data.Text as T -- | The @pandoc.image@ module specification. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.image" `withDescription` "Basic image querying functions." `withFunctions` functions -- -- Functions -- functions :: [DocumentedFunction PandocError] functions = [ size `since` makeVersion [3, 1, 13] , format `since` makeVersion [3, 1, 13] ] -- | Find the size of an image. size :: DocumentedFunction PandocError size = defun "size" ### liftPure2 (\img mwriterOpts -> imageSize (fromMaybe def mwriterOpts) img) <#> parameter peekByteString "string" "image" "image data" <#> opt (parameter peekWriterOptions "WriterOptions|table" "opts" "writer options") =#> functionResult (either (failLua . T.unpack) pushImageSize) "table" "image size information or error message" #? T.unlines [ "Returns a table containing the size and resolution of an image;" , "throws an error if the given string is not an image, or if the size" , "of the image cannot be determined." , "" , "The resulting table has four entries: *width*, *height*, *dpi\\_horz*," , "and *dpi\\_vert*." , "" , "The `opts` parameter, when given, should be either a WriterOptions" , "object such as `PANDOC_WRITER_OPTIONS`, or a table with a `dpi` entry." , "It affects the calculation for vector image formats such as SVG." ] -- | Returns the format of an image. format :: LuaError e => DocumentedFunction e format = defun "format" ### liftPure imageType <#> parameter peekByteString "string" "image" "binary image data" =#> functionResult (maybe pushnil pushImageType) "string|nil" "image format, or nil if the format cannot be determined" #? T.unlines [ "Returns the format of an image as a lowercase string." , "" , "Formats recognized by pandoc include *png*, *gif*, *tiff*, *jpeg*," , "*pdf*, *svg*, *eps*, and *emf*." ] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/JSON.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-| Module : Text.Pandoc.Lua.Module.JSON Copyright : © 2022-2024 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Lua module to work with JSON. -} module Text.Pandoc.Lua.Module.JSON ( -- * Module documentedModule -- ** Functions , decode , encode ) where import Prelude hiding (null) import Data.Maybe (fromMaybe) import Data.Monoid (Alt (..)) import Data.Version (makeVersion) import HsLua.Aeson import HsLua.Core import HsLua.Marshalling import HsLua.Packaging import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Lua.Marshal.AST import qualified Data.Aeson as Aeson import qualified Data.Text as T -- | The @aeson@ module specification. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.json" `withDescription` "JSON module to work with JSON; based on the Aeson Haskell package." `withFields` fields `withFunctions` functions -- -- Fields -- -- | Exported fields. fields :: LuaError e => [Field e] fields = [ null ] -- | The value used to represent the JSON @null@. null :: LuaError e => Field e null = deffield "null" `withType` "light userdata" `withDescription` "Value used to represent the `null` JSON value." `withValue` pushValue Aeson.Null -- -- Functions -- functions :: [DocumentedFunction PandocError] functions = [ decode `since` makeVersion [3, 1, 1] , encode `since` makeVersion [3, 1, 1] ] -- | Decode a JSON string into a Lua object. decode :: DocumentedFunction PandocError decode = defun "decode" ### (\str usePandocTypes -> fromMaybe pushnil . getAlt . mconcat . map Alt $ (if usePandocTypes == Just False then [] else [ pushInline <$> Aeson.decode str , pushBlock <$> Aeson.decode str , pushPandoc <$> Aeson.decode str , pushInlines <$> Aeson.decode str , pushBlocks <$> Aeson.decode str ]) ++ [pushValue <$> Aeson.decode str]) <#> parameter peekLazyByteString "string" "str" "JSON string" <#> opt (parameter peekBool "boolean" "pandoc_types" "whether to use pandoc types when possible.") =#> functionResult pure "any" "decoded object" #? T.unlines [ "Creates a Lua object from a JSON string. If the input can be decoded" , "as representing an [[Inline]], [[Block]], [[Pandoc]], [[Inlines]]," , "or [[Blocks]] element the function will return an object of the" , "appropriate type. Otherwise, if the input does not represent any" , "of the AST types, the default decoding is applied: Objects and" , "arrays are represented as tables, the JSON `null` value becomes" , "[null](#pandoc.json.null), and JSON booleans, strings, and numbers" , "are converted using the Lua types of the same name." , "" , "The special handling of AST elements can be disabled by setting" , "`pandoc_types` to `false`." ] -- | Encode a Lua object as JSON. encode :: LuaError e => DocumentedFunction e encode = defun "encode" ### liftPure Aeson.encode <#> parameter peekValue "any" "object" "object to convert" =#> functionResult pushLazyByteString "string" "JSON encoding of the given `object`" #? T.unlines ["Encodes a Lua object as JSON string." , "" , "If the object has a metamethod with name `__tojson`, then the" , "result is that of a call to that method with `object` passed as" , "the sole argument. The result of that call is expected to be a" , "valid JSON string, but this is not checked." ] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Log.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Module.Log Copyright : © 2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Logging module. -} module Text.Pandoc.Lua.Module.Log ( documentedModule ) where import Data.Version (makeVersion) import HsLua import Text.Pandoc.Class (report, runSilently) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Logging (LogMessage (ScriptingInfo, ScriptingWarning)) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Lua.Marshal.LogMessage (pushLogMessage) import Text.Pandoc.Lua.PandocLua (liftPandocLua, unPandocLua) import Text.Pandoc.Lua.SourcePos (luaSourcePos) import qualified Data.Text as T import qualified HsLua.Core.Utf8 as UTF8 -- | Push the pandoc.log module on the Lua stack. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.log" `withDescription` "Access to pandoc's logging system." `withFields` [] `withFunctions` [ defun "info" ### (\msg -> do -- reporting levels: -- 0: this function, -- 1: userdata wrapper function for the function, -- 2: function calling warn. pos <- luaSourcePos 2 unPandocLua $ report $ ScriptingInfo (UTF8.toText msg) pos) <#> parameter peekByteString "string" "message" "the info message" =#> [] #? "Reports a ScriptingInfo message to pandoc's logging system." `since` makeVersion [3, 2] , defun "silence" ### const silence <#> parameter pure "function" "fn" "function to be silenced" =?> ("List of log messages triggered during the function call, " <> "and any value returned by the function.") #? T.unlines [ "Applies the function to the given arguments while" , "preventing log messages from being added to the log." , "The warnings and info messages reported during the function" , "call are returned as the first return value, with the" , "results of the function call following thereafter." ] `since` makeVersion [3, 2] , defun "warn" ### (\msg -> do -- reporting levels: -- 0: this function, -- 1: userdata wrapper function for the function, -- 2: function calling warn. pos <- luaSourcePos 2 unPandocLua $ report $ ScriptingWarning (UTF8.toText msg) pos) <#> parameter peekByteString "string" "message" "the warning message" =#> [] #? T.unlines [ "Reports a ScriptingWarning to pandoc's logging system." , "The warning will be printed to stderr unless logging" , "verbosity has been set to *ERROR*." ] `since` makeVersion [3, 2] ] -- | Calls the function given as the first argument, but suppresses logging. -- Returns the list of generated log messages as the first result, and the other -- results of the function call after that. silence :: LuaE PandocError NumResults silence = unPandocLua $ do -- call function given as the first argument ((), messages) <- runSilently . liftPandocLua $ do nargs <- (NumArgs . subtract 1 . fromStackIndex) <$> gettop call @PandocError nargs multret liftPandocLua $ do pushPandocList pushLogMessage messages insert 1 (NumResults . fromStackIndex) <$> gettop ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/MediaBag.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.MediaBag Copyright : Copyright © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel The Lua module @pandoc.mediabag@. -} module Text.Pandoc.Lua.Module.MediaBag ( documentedModule ) where import Prelude hiding (lookup) import Data.Maybe (fromMaybe) import Data.Version (makeVersion) import HsLua ( LuaE, DocumentedFunction, Module (..) , (<#>), (###), (=#>), (=?>), (#?), defun, functionResult , opt, parameter, since, stringParam, textParam) import Text.Pandoc.Class ( fetchItem, fillMediaBag, getMediaBag, setMediaBag ) import Text.Pandoc.Class.IO (writeMedia) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.Pandoc (peekPandoc, pushPandoc) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Lua.Orphans () import Text.Pandoc.Lua.PandocLua (unPandocLua) import Text.Pandoc.MIME (MimeType) import Text.Pandoc.SelfContained (makeDataURI) import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import qualified HsLua as Lua import qualified Text.Pandoc.MediaBag as MB -- -- MediaBag submodule -- documentedModule :: Module PandocError documentedModule = Lua.defmodule "pandoc.mediabag" `Lua.withDescription` T.unlines [ "The `pandoc.mediabag` module allows accessing pandoc's media" , "storage. The \"media bag\" is used when pandoc is called with the" , "`--extract-media` or (for HTML only) `--embed-resources` option." , "" , "The module is loaded as part of module `pandoc` and can either" , "be accessed via the `pandoc.mediabag` field, or explicitly" , "required, e.g.:" , "" , " local mb = require 'pandoc.mediabag'" ] `Lua.withFunctions` [ delete `since` makeVersion [2,7,3] , empty `since` makeVersion [2,7,3] , fetch `since` makeVersion [2,0] , fill `since` makeVersion [2,19] , insert `since` makeVersion [2,0] , items `since` makeVersion [2,7,3] , list `since` makeVersion [2,0] , lookup `since` makeVersion [2,0] , make_data_uri `since` makeVersion [3,7,1] , write `since` makeVersion [3,0] ] -- | Delete a single item from the media bag. delete :: DocumentedFunction PandocError delete = defun "delete" ### (\fp -> unPandocLua $ do mb <- getMediaBag setMediaBag $ MB.deleteMedia fp mb) <#> stringParam "filepath" ("Filename of the item to deleted. The media bag will be " <> "left unchanged if no entry with the given filename exists.") =#> [] #? "Removes a single entry from the media bag." -- | Delete all items from the media bag. empty :: DocumentedFunction PandocError empty = defun "empty" ### unPandocLua (setMediaBag mempty) =#> [] #? "Clear-out the media bag, deleting all items." -- | Fill the mediabag with all images in the document that aren't -- present yet. fill :: DocumentedFunction PandocError fill = defun "fill" ### unPandocLua . fillMediaBag <#> parameter peekPandoc "Pandoc" "doc" "document from which to fill the mediabag" =#> functionResult pushPandoc "Pandoc" "modified document" #? ("Fills the mediabag with the images in the given document.\n" <> "An image that cannot be retrieved will be replaced with a Span\n" <> "of class \"image\" that contains the image description.\n" <> "\n" <> "Images for which the mediabag already contains an item will\n" <> "not be processed again.") -- | Insert a new item into the media bag. insert :: DocumentedFunction PandocError insert = defun "insert" ### (\fp mmime contents -> unPandocLua $ do mb <- getMediaBag setMediaBag $ MB.insertMedia fp mmime contents mb return (Lua.NumResults 0)) <#> stringParam "filepath" "filename and path relative to the output folder." <#> parameter (Lua.peekNilOr Lua.peekText) "string|nil" "mimetype" "the item's MIME type; use `nil` if the MIME type is\ \ unknown or unavailable." <#> parameter Lua.peekLazyByteString "string" "contents" "the binary contents of the file." =#> [] #? T.unlines [ "Adds a new entry to pandoc's media bag. Replaces any existing" , "media bag entry the same `filepath`." , "" , "Usage:" , "" , " local fp = 'media/hello.txt'" , " local mt = 'text/plain'" , " local contents = 'Hello, World!'" , " pandoc.mediabag.insert(fp, mt, contents)" ] -- | Returns iterator values to be used with a Lua @for@ loop. items :: DocumentedFunction PandocError items = defun "items" ### (do mb <- unPandocLua getMediaBag let pushItem (fp, mimetype, contents) = do Lua.pushString fp Lua.pushText mimetype Lua.pushByteString $ BL.toStrict contents return (Lua.NumResults 3) Lua.pushIterator pushItem (MB.mediaItems mb)) =?> T.unlines [ "Iterator triple:" , "" , "- The iterator function; must be called with the iterator" , " state and the current iterator value." , "- Iterator state -- an opaque value to be passed to the" , " iterator function." , "- Initial iterator value." ] #? T.unlines [ "Returns an iterator triple to be used with Lua's generic `for`" , "statement. The iterator returns the filepath, MIME type, and" , "content of a media bag item on each invocation. Items are" , "processed one-by-one to avoid excessive memory use." , "" , "This function should be used only when full access to all items," , "including their contents, is required. For all other cases," , "[`list`](#pandoc.mediabag.list) should be preferred." , "" , "Usage:" , "" , " for fp, mt, contents in pandoc.mediabag.items() do" , " -- print(fp, mt, contents)" , " end" ] -- | Function to lookup a value in the mediabag. lookup :: DocumentedFunction PandocError lookup = defun "lookup" ### (\fp -> unPandocLua (MB.lookupMedia fp <$> getMediaBag)) <#> stringParam "filepath" "name of the file to look up." =#> mconcat [ functionResult (maybe Lua.pushnil (Lua.pushText . MB.mediaMimeType)) "string" "The entry's MIME type, or nil if the file was not found." , functionResult (maybe Lua.pushnil (Lua.pushLazyByteString . MB.mediaContents)) "string" "Contents of the file, or nil if the file was not found." ] #? T.unlines [ "Lookup a media item in the media bag, and return its MIME type" , "and contents." , "" , "Usage:" , "" , " local filename = 'media/diagram.png'" , " local mt, contents = pandoc.mediabag.lookup(filename)" ] -- | Function listing all mediabag items. list :: DocumentedFunction PandocError list = defun "list" ### (unPandocLua (MB.mediaDirectory <$> getMediaBag)) =#> functionResult (pushPandocList pushEntry) "table" ("A list of elements summarizing each entry in the media\n" <> "bag. The summary item contains the keys `path`, `type`, and\n" <> "`length`, giving the filepath, MIME type, and length of\n" <> "contents in bytes, respectively.") #? T.unlines [ "Get a summary of the current media bag contents." , "" , "Usage:" , "" , " -- calculate the size of the media bag." , " local mb_items = pandoc.mediabag.list()" , " local sum = 0" , " for i = 1, #mb_items do" , " sum = sum + mb_items[i].length" , " end" , " print(sum)" ] where pushEntry :: (FilePath, MimeType, Int) -> LuaE PandocError () pushEntry (fp, mimeType, contentLength) = do Lua.newtable Lua.pushName "path" *> Lua.pushString fp *> Lua.rawset (-3) Lua.pushName "type" *> Lua.pushText mimeType *> Lua.rawset (-3) Lua.pushName "length" *> Lua.pushIntegral contentLength *> Lua.rawset (-3) -- | Lua function to retrieve a new item. fetch :: DocumentedFunction PandocError fetch = defun "fetch" ### (unPandocLua . fetchItem) <#> textParam "source" "path to a resource; either a local file path or URI" =#> ( functionResult (Lua.pushText . fromMaybe "" . snd) "string" "The entry's MIME type, or `nil` if the file was not found." <> functionResult (Lua.pushByteString . fst) "string" "Contents of the file, or `nil` if the file was not found." ) #? T.unlines [ "Fetches the given source from a URL or local file. Returns two" , "values: the contents of the file and the MIME type (or an empty" , "string)." , "" , "The function will first try to retrieve `source` from the" , "mediabag; if that fails, it will try to download it or read it" , "from the local file system while respecting pandoc's \"resource" , "path\" setting." , "" , "Usage:" , "" , " local diagram_url = 'https://pandoc.org/diagram.jpg'" , " local mt, contents = pandoc.mediabag.fetch(diagram_url)" ] make_data_uri :: DocumentedFunction PandocError make_data_uri = defun "make_data_uri" ### (\mime raw -> pure $ makeDataURI (mime, raw)) <#> parameter Lua.peekText "string" "mime_type" "MIME type of the data" <#> parameter Lua.peekByteString "string" "raw_data" "data to encode" =#> functionResult Lua.pushText "string" "data uri" #? T.unlines [ "Convert the input data into a data URI as defined by RFC 2397." , "" , "Example:" , "" , " -- Embed an unofficial pandoc logo" , " local pandoc_logo_url = 'https://raw.githubusercontent.com/'" , " .. 'tarleb/pandoc-logo/main/pandoc.svg'" , "" , " local datauri = pandoc.mediabag.make_data_uri(" , " pandoc.mediabag.fetch(pandoc_logo_url)" , " )" , "" , " local image = pandoc.Image('Logo', datauri)" ] -- | Extract the mediabag or just a single entry. write :: DocumentedFunction PandocError write = defun "write" ### (\dir mfp -> do mb <- unPandocLua getMediaBag case mfp of Nothing -> unPandocLua $ mapM_ (writeMedia dir) (MB.mediaItems mb) Just fp -> do case MB.lookupMedia fp mb of Nothing -> Lua.failLua ("Resource not in mediabag: " <> fp) Just item -> unPandocLua $ do let triple = ( MB.mediaPath item , MB.mediaMimeType item , MB.mediaContents item ) writeMedia dir triple) <#> stringParam "dir" "path of the target directory" <#> opt (stringParam "fp" "canonical name (relative path) of resource") =#> [] #? T.unlines [ "Writes the contents of mediabag to the given target directory. If" , "`fp` is given, then only the resource with the given name will be" , "extracted. Omitting that parameter means that the whole mediabag" , "gets extracted. An error is thrown if `fp` is given but cannot be" , "found in the mediabag." ] `since` makeVersion [3, 0] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Pandoc.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Module.Pandoc Copyright : Copyright © 2017-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Main @pandoc@ module, containing element constructors and central functions. -} module Text.Pandoc.Lua.Module.Pandoc ( documentedModule ) where import Prelude hiding (read) import Control.Applicative ((<|>)) import Control.Monad (foldM, forM_, when) import Control.Monad.Catch (catch, handle, throwM) import Control.Monad.Except (MonadError (throwError)) import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr) import Data.Default (Default (..)) import Data.Maybe (fromMaybe) import Data.Proxy (Proxy (Proxy)) import Data.Text.Encoding.Error (UnicodeException) import Data.Version (makeVersion) import HsLua import System.Exit (ExitCode (..)) import Text.Pandoc.Class ( PandocMonad, FileInfo (..), FileTree , addToFileTree, getCurrentTime , getRequestHeaders, getResourcePath, getUserDataDir , setRequestHeaders, setResourcePath, setUserDataDir , insertInFileTree, sandboxWithFileTree ) import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Format (FlavoredFormat, parseFlavoredFormat) import Text.Pandoc.Lua.Orphans () import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.Format (peekFlavoredFormat) import Text.Pandoc.Lua.Marshal.Filter (peekFilter) import Text.Pandoc.Lua.Marshal.ReaderOptions ( peekReaderOptions , pushReaderOptions) import Text.Pandoc.Lua.Marshal.Sources (peekSources) import Text.Pandoc.Lua.Marshal.WriterOptions ( peekWriterOptions , pushWriterOptions) import Text.Pandoc.Lua.Module.Utils (sha1) import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua)) import Text.Pandoc.Lua.Writer.Classic (runCustom) import Text.Pandoc.Options ( ReaderOptions (readerExtensions) , WriterOptions (writerExtensions) ) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.Readers (Reader (..), getReader, readers) import Text.Pandoc.Sources (toSources) import Text.Pandoc.Writers (Writer (..), getWriter, writers) import qualified HsLua as Lua import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BSL import qualified Data.Text as T import qualified Text.Pandoc.UTF8 as UTF8 documentedModule :: Module PandocError documentedModule = defmodule "pandoc" `withDescription` T.unlines [ "Fields and functions for pandoc scripts; includes constructors for" , "document tree elements, functions to parse text in a given" , "format, and functions to filter and modify a subtree." ] `withFields` readersField : writersField : stringConstants ++ [inlineField, blockField] `withFunctions` mconcat [ [mkPandoc, mkMeta] , metaValueConstructors , blockConstructors , [mkBlocks] , inlineConstructors , [mkInlines] , otherConstructors , functions ] `associateType` typePandoc `associateType` typeBlock `associateType` typeInline -- | Set of input formats accepted by @read@. readersField :: Field PandocError readersField = deffield "readers" `withType` "table" `withDescription` T.unlines [ "Set of formats that pandoc can parse. All keys in this table can" , "be used as the `format` value in `pandoc.read`." ] `withValue` pushKeyValuePairs pushText (pushText . readerType) (readers @PandocLua) where readerType = \case TextReader {} -> "text" ByteStringReader {} -> "bytestring" -- | Set of input formats accepted by @write@. writersField :: Field PandocError writersField = deffield "writers" `withType` "table" `withDescription` T.unlines [ "Set of formats that pandoc can generate. All keys in this table" , "can be used as the `format` value in `pandoc.write`." ] `withValue` pushKeyValuePairs pushText (pushText . writerType) (writers @PandocLua) where writerType = \case TextWriter {} -> "text" ByteStringWriter {} -> "bytestring" -- | Inline table field inlineField :: Field PandocError inlineField = deffield "Inline" `withType` "table" `withDescription` "Inline constructors, nested under 'constructors'." -- the nesting happens for historical reasons and should probably be -- changed. `withValue` pushWithConstructorsSubtable inlineConstructors -- | @Block@ module field blockField :: Field PandocError blockField = deffield "Block" `withType` "table" `withDescription` "Inline constructors, nested under 'constructors'." -- the nesting happens for historical reasons and should probably be -- changed. `withValue` pushWithConstructorsSubtable blockConstructors pushWithConstructorsSubtable :: [DocumentedFunction PandocError] -> LuaE PandocError () pushWithConstructorsSubtable constructors = do newtable -- Field table newtable -- constructor table pushName "constructor" *> pushvalue (nth 2) *> rawset (nth 4) forM_ constructors $ \fn -> do pushName (functionName fn) pushDocumentedFunction fn rawset (nth 3) pop 1 -- pop constructor table otherConstructors :: [DocumentedFunction PandocError] otherConstructors = [ mkAttr , mkCaption `since` makeVersion [3,6,1] , mkCell , mkAttributeList , mkCitation , mkListAttributes , mkRow , mkTableFoot , mkTableHead , mkSimpleTable , defun "ReaderOptions" ### liftPure id <#> parameter peekReaderOptions "ReaderOptions|table" "opts" (T.unlines [ "Either a table with a subset of the properties of a" , "[[ReaderOptions]] object, or another ReaderOptions object." , "Uses the defaults specified in the manual for all" , "properties that are not explicitly specified. Throws an" , "error if a table contains properties which are not present" , "in a ReaderOptions object." ] ) =#> functionResult pushReaderOptions "ReaderOptions" "new object" #? T.unlines [ "Creates a new ReaderOptions value." , "" , "Usage:" , "" , " -- copy of the reader options that were defined on the command line." , " local cli_opts = pandoc.ReaderOptions(PANDOC_READER_OPTIONS)" , " -- default reader options, but columns set to 66." , " local short_colums_opts = pandoc.ReaderOptions {columns = 66}" ] , defun "WriterOptions" ### liftPure id <#> parameter peekWriterOptions "WriterOptions|table" "opts" (T.unlines [ "Either a table with a subset of the properties of a" , "[[WriterOptions]] object, or another WriterOptions object." , "Uses the defaults specified in the manual for all" , "properties that are not explicitly specified. Throws an" , "error if a table contains properties which are not present" , "in a WriterOptions object." ]) =#> functionResult pushWriterOptions "WriterOptions" "new object" #? "Creates a new WriterOptions value." ] stringConstants :: [Field e] stringConstants = let constrs :: forall a. Data a => Proxy a -> [String] constrs _ = map showConstr . dataTypeConstrs . dataTypeOf @a $ undefined nullaryConstructors = mconcat [ constrs (Proxy @ListNumberStyle) , constrs (Proxy @ListNumberDelim) , constrs (Proxy @QuoteType) , constrs (Proxy @MathType) , constrs (Proxy @Alignment) , constrs (Proxy @CitationMode) ] toField s = deffield (Name $ UTF8.fromString s) `withType` "string" `withDescription` T.pack s `withValue` pushString s in map toField nullaryConstructors functions :: [DocumentedFunction PandocError] functions = [ defun "pipe" ### (\command args input -> do (ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input `catch` (throwM . PandocIOError "pipe") case ec of ExitSuccess -> 1 <$ Lua.pushLazyByteString output ExitFailure n -> do pushPipeError (PipeError (T.pack command) n output) Lua.error) <#> parameter peekString "string" "command" "path to executable" <#> parameter (peekList peekString) "{string,...}" "args" "list of arguments" <#> parameter peekLazyByteString "string" "input" "input passed to process via stdin" =?> "output string, or error triple" , defun "read" ### (\content mformatspec mreaderOptions mreadEnv -> do let readerOpts = fromMaybe def mreaderOptions readAction :: PandocMonad m => FlavoredFormat -> m Pandoc readAction flvrd = getReader flvrd >>= \case (TextReader r, es) -> r readerOpts{readerExtensions = es} $ case content of Left bs -> toSources $ UTF8.toText bs Right sources -> sources (ByteStringReader r, es) -> case content of Left bs -> r readerOpts{readerExtensions = es} (BSL.fromStrict bs) Right _ -> throwError $ PandocLuaError "Cannot use bytestring reader with Sources" handle (failLua . show @UnicodeException) . unPandocLua $ do flvrd <- maybe (parseFlavoredFormat "markdown") pure mformatspec case mreadEnv of Nothing -> readAction flvrd Just tree -> sandboxWithFileTree tree (readAction flvrd)) <#> parameter (\idx -> (Left <$> peekByteString idx) <|> (Right <$> peekSources idx)) "string|Sources" "content" "text to parse" <#> opt (parameter peekFlavoredFormat "string|table" "formatspec" "format and extensions") <#> opt (parameter peekReaderOptions "ReaderOptions" "reader_options" "reader options") <#> opt (parameter peekReadEnv "table" "read_env" $ T.unlines [ "If the value is not given or `nil`, then the global environment" , "is used. Passing a list of filenames causes the reader to" , "be run in a sandbox. The given files are read from the file" , "system and provided to the sandbox via an ersatz file system." , "The table can also contain mappings from filenames to" , "contents, which will be used to populate the ersatz file" , "system." ]) =#> functionResult pushPandoc "Pandoc" "result document" , sha1 , defun "walk_block" ### walkElement <#> parameter peekBlockFuzzy "Block" "block" "element to traverse" <#> parameter peekFilter "Filter" "lua_filter" "filter functions" =#> functionResult pushBlock "Block" "modified Block" , defun "walk_inline" ### walkElement <#> parameter peekInlineFuzzy "Inline" "inline" "element to traverse" <#> parameter peekFilter "Filter" "lua_filter" "filter functions" =#> functionResult pushInline "Inline" "modified Inline" , defun "with_state" ### with_state <#> parameter peekStateOptions "table" "options" "state options" <#> parameter pure "function" "callback" "The action to run with the given state." =?> "The results of the call to *callback*." #? "Runs a function with a modified pandoc state.\n\ \\n\ \The given callback is invoked after setting the pandoc state to the\ \ given values. The modifiable options are restored to their original\ \ values once the callback has returned.\n\ \\n\ \The following state variables can be controlled:\n\ \\n\ \ - `request_headers` (list of key-value tuples)\n\ \ - `resource_path` (list of filepaths)\n\ \ - `user_data_dir` (string)\n\ \\n\ \Other options are ignored, and the rest of the state is not modified.\n\ \\n\ \Usage:\n\ \\n\ \ local opts = {\n\ \ request_headers = {\n\ \ {'Authorization', 'Basic my-secret'}\n\ \ }\n\ \ }\n\ \ pandoc.with_state(opts, function ()\n\ \ local mime, contents = pandoc.mediabag.fetch(image_url)\n\ \ )\n" , defun "write" ### (\doc mformatspec mwriterOpts -> unPandocLua $ do flvrd <- maybe (parseFlavoredFormat "markdown") pure mformatspec let writerOpts = fromMaybe def mwriterOpts getWriter flvrd >>= \case (TextWriter w, es) -> Right <$> w writerOpts{ writerExtensions = es } doc (ByteStringWriter w, es) -> Left <$> w writerOpts{ writerExtensions = es } doc) <#> parameter peekPandoc "Pandoc" "doc" "document to convert" <#> opt (parameter peekFlavoredFormat "string|table" "formatspec" (T.unlines [ "format specification; defaults to `\"html\"`. See the" , "documentation of [`pandoc.read`](#pandoc.read) for a complete" , "description of this parameter." ])) <#> opt (parameter peekWriterOptions "WriterOptions|table" "writer_options" (T.unlines [ "options passed to the writer; may be a WriterOptions object" , "or a table with a subset of the keys and values of a" , "WriterOptions object; defaults to the default values" , "documented in the manual." ]) ) =#> functionResult (either pushLazyByteString pushText) "string" "result document" #? T.unlines [ "Converts a document to the given target format." , "" , "Usage:" , "" , " local doc = pandoc.Pandoc(" , " {pandoc.Para {pandoc.Strong 'Tea'}}" , " )" , " local html = pandoc.write(doc, 'html')" , " assert(html == '

Tea

')" ] , defun "write_classic" ### (\doc mwopts -> runCustom (fromMaybe def mwopts) doc) <#> parameter peekPandoc "Pandoc" "doc" "document to convert" <#> opt (parameter peekWriterOptions "WriterOptions" "writer_options" (T.unlines [ "options passed to the writer; may be a WriterOptions object" , "or a table with a subset of the keys and values of a" , "WriterOptions object; defaults to the default values" , "documented in the manual." ])) =#> functionResult pushText "string" "rendered document" #? (T.unlines [ "Runs a classic custom Lua writer, using the functions defined" , "in the current environment." , "" , "Usage:" , "" , " -- Adding this function converts a classic writer into a" , " -- new-style custom writer." , " function Writer (doc, opts)" , " PANDOC_DOCUMENT = doc" , " PANDOC_WRITER_OPTIONS = opts" , " loadfile(PANDOC_SCRIPT_FILE)()" , " return pandoc.write_classic(doc, opts)" , " end" ]) ] where walkElement x f = walkInlineSplicing f x >>= walkInlinesStraight f >>= walkBlockSplicing f >>= walkBlocksStraight f data PipeError = PipeError { pipeErrorCommand :: T.Text , pipeErrorCode :: Int , pipeErrorOutput :: BL.ByteString } peekPipeError :: LuaError e => StackIndex -> LuaE e PipeError peekPipeError idx = PipeError <$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1) <*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1) <*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1) pushPipeError :: LuaError e => Pusher e PipeError pushPipeError pipeErr = do pushAsTable [ ("command" , pushText . pipeErrorCommand) , ("error_code" , pushIntegral . pipeErrorCode) , ("output" , pushLazyByteString . pipeErrorOutput) ] pipeErr pushPipeErrorMetaTable Lua.setmetatable (nth 2) where pushPipeErrorMetaTable :: LuaError e => LuaE e () pushPipeErrorMetaTable = do v <- Lua.newmetatable "pandoc pipe error" when v $ do pushName "__tostring" pushHaskellFunction pipeErrorMessage rawset (nth 3) pipeErrorMessage :: LuaError e => LuaE e NumResults pipeErrorMessage = do (PipeError cmd errorCode output) <- peekPipeError (nthBottom 1) pushByteString . BSL.toStrict . BSL.concat $ [ BSL.pack "Error running " , BSL.pack $ T.unpack cmd , BSL.pack " (error code " , BSL.pack $ show errorCode , BSL.pack "): " , if output == mempty then BSL.pack "" else output ] return (NumResults 1) -- | Peek the environment in which the `read` function operates. peekReadEnv :: Peeker PandocError FileTree peekReadEnv idx = do mtime <- liftLua . unPandocLua $ getCurrentTime -- Add files from file system files <- peekList peekString idx tree1 <- liftLua $ foldM (\tree fp -> liftIO $ addToFileTree tree fp) mempty files -- Add files from key-value pairs let toFileInfo contents = FileInfo { infoFileMTime = mtime , infoFileContents = contents } pairs <- peekKeyValuePairs peekString (fmap toFileInfo . peekByteString) idx let tree2 = foldr (uncurry insertInFileTree) tree1 pairs -- Return ersatz file system. pure tree2 -- | Helper type that holds all common state values that can be controlled. -- -- This is closely related to "CommonState", but that's an opaque value -- that can only be read and modified through accessor functions. All -- fields in this type can be modified through accessors. data StateOptions = StateOptions { stateOptsRequestHeaders :: [(T.Text, T.Text)] , stateOptsResourcePath :: [String] , stateOptsUserDataDir :: Maybe String } -- | Peek pandoc state options; the current state properties are used for -- unspecified values. peekStateOptions :: Peeker PandocError StateOptions peekStateOptions idx = do absidx <- liftLua $ absindex idx let setOptions opts = do liftLua (next absidx) >>= \case False -> return opts True -> do key <- peekByteString (nth 2) case key of "request_headers" -> do let peekHeaderPair = peekPair peekText peekText value <- peekList peekHeaderPair top `lastly` pop 1 setOptions $ opts { stateOptsRequestHeaders = value } "resource_path" -> do value <- peekList peekString top `lastly` pop 1 setOptions $ opts { stateOptsResourcePath = value } "user_data_dir" -> do value <- peekNilOr peekString top `lastly` pop 1 setOptions $ opts { stateOptsUserDataDir = value } _ -> do liftLua $ pop 2 -- remove key and value failPeek $ "Unknown or unsupported state option: " <> key liftLua pushnil -- first "key" liftLua getStateOptions >>= setOptions -- | Get the current options values from the pandoc state. getStateOptions :: LuaE PandocError StateOptions getStateOptions = unPandocLua $ StateOptions <$> getRequestHeaders <*> getResourcePath <*> getUserDataDir -- | Update the pandoc state with the new options. setStateOptions :: StateOptions -> LuaE PandocError () setStateOptions opts = unPandocLua $ do setRequestHeaders $ stateOptsRequestHeaders opts setResourcePath $ stateOptsResourcePath opts setUserDataDir $ stateOptsUserDataDir opts -- | Run a callback with a modified pandoc state. with_state :: StateOptions -> StackIndex -> LuaE PandocError NumResults with_state options callback_idx = do origState <- getStateOptions setStateOptions options -- Invoke the callback oldTop <- gettop pushvalue callback_idx call 0 multret newTop <- gettop setStateOptions origState return . NumResults . fromStackIndex $ newTop - oldTop ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Path.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Module.Path Copyright : © 2019-2026 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Pandoc's system Lua module. -} module Text.Pandoc.Lua.Module.Path ( documentedModule ) where import Data.Version (makeVersion) import HsLua import qualified HsLua.Module.Path as MPath import qualified HsLua.Module.System as MSystem -- | Push the pandoc.system module on the Lua stack. documentedModule :: forall e. LuaError e => Module e documentedModule = defmodule "pandoc.path" `withDescription` moduleDescription @e MPath.documentedModule `withFields` [ MPath.separator , MPath.search_path_separator ] `withFunctions` [ MPath.directory `since` v[2,12] , MSystem.exists `since` v[3,7,1] , MPath.filename `since` v[2,12] , MPath.is_absolute `since` v[2,12] , MPath.is_relative `since` v[2,12] , MPath.join `since` v[2,12] , MPath.make_relative `since` v[2,12] , MPath.normalize `since` v[2,12] , MPath.split `since` v[2,12] , MPath.split_extension `since` v[2,12] , MPath.split_search_path `since` v[2,12] , MPath.treat_strings_as_paths `since` v[2,12] ] where v = makeVersion ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Scaffolding.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Scaffolding Copyright : Copyright © 2022-2026 Albert Krewinkel, John MacFarlane License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Scaffolding for custom Writers. -} module Text.Pandoc.Lua.Module.Scaffolding ( documentedModule ) where import HsLua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Writer.Scaffolding (pushWriterScaffolding) import qualified Data.Text as T -- | The "pandoc.template" module. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.scaffolding" `withDescription` "Scaffolding for custom writers." `withFields` [writerScaffolding] -- | Template module functions. writerScaffolding :: Field PandocError writerScaffolding = deffield "Writer" `withType` "table" `withDescription` T.unlines [ "An object to be used as a `Writer` function; the construct handles" , "most of the boilerplate, expecting only render functions for all" , "AST elements" ] `withValue` do pushWriterScaffolding -- pretend that it's a submodule so we get better error messages getfield registryindex loaded pushvalue (nth 2) setfield (nth 2) (submod "Writer") -- same for fields "Block" and "Inline" getfield (nth 2) "Inline" *> setfield (nth 2) (submod "Writer.Inline") getfield (nth 2) "Block" *> setfield (nth 2) (submod "Writer.Block") pop 1 -- remove "LOADED_TABLE" where submod x = moduleName documentedModule <> "." <> x ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Structure.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Structure Copyright : © 2023-2026 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Command line helpers -} module Text.Pandoc.Lua.Module.Structure ( documentedModule ) where import Control.Applicative ((<|>), optional) import Data.Default (Default (..)) import Data.Maybe (fromMaybe) import Data.Version (makeVersion) import HsLua ( DocumentedFunction, LuaError, Module (..), Peeker , (###), (<#>), (=#>), (#?), defmodule , defun, functionResult, getfield, isnil, lastly, liftLua , opt, liftPure, parameter , peekBool, peekIntegral , peekFieldRaw, peekSet, peekText, pop, pushIntegral , pushText, since, top, withDescription, withFunctions ) import Text.Pandoc.Chunks ( ChunkedDoc (..), PathTemplate (..) , tocToList, splitIntoChunks ) import Text.Pandoc.Definition (Pandoc (..), Block) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Lua.Marshal.AST ( peekBlocksFuzzy, peekInlinesFuzzy , peekPandoc, pushBlock, pushBlocks ) import Text.Pandoc.Lua.Marshal.Chunks import Text.Pandoc.Lua.Marshal.Format (peekExtensions) import Text.Pandoc.Lua.Marshal.WriterOptions ( peekWriterOptions ) import Text.Pandoc.Options (WriterOptions (writerTOCDepth, writerNumberSections)) import Text.Pandoc.Slides (getSlideLevel, prepSlides) import Text.Pandoc.Writers.Shared (toTableOfContents) import qualified Data.Text as T import qualified Text.Pandoc.Shared as Shared -- | Push the pandoc.structure module on the Lua stack. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.structure" `withDescription` "Access to the higher-level document structure, including " <> "hierarchical sections and the table of contents." `withFunctions` [ make_sections `since` makeVersion [3,0] , slide_level `since` makeVersion [3,0] , split_into_chunks `since` makeVersion [3,0] , table_of_contents `since` makeVersion [3,0] , unique_identifier `since` makeVersion [3,8] ] make_sections :: LuaError e => DocumentedFunction e make_sections = defun "make_sections" ### (\blks mopts -> let (numSects, baseLevel, mslideLevel) = fromMaybe (defNumSec, Nothing, Nothing) mopts blks' = case mslideLevel of Just l | l <= 0 -> prepSlides (getSlideLevel blks) blks Just sl -> prepSlides sl blks Nothing -> blks in pure $ Shared.makeSections numSects baseLevel blks') <#> parameter peekBodyBlocks "Blocks|Pandoc" "blocks" "document blocks to process" <#> opt (parameter peekOpts "table" "opts" "options") =#> functionResult pushBlocks "Blocks" "processed blocks" #? T.unlines [ "Puts [[Blocks]] into a hierarchical structure: a list of sections" , "(each a Div with class \"section\" and first element a Header)." , "" , "The optional `opts` argument can be a table; two settings are" , "recognized: If `number_sections` is true, a `number` attribute" , "containing the section number will be added to each `Header`. If" , "`base_level` is an integer, then `Header` levels will be" , "reorganized so that there are no gaps, with numbering levels" , "shifted by the given value. Finally, an integer `slide_level`" , "value triggers the creation of slides at that heading level." , "" , "Note that a [[WriterOptions]] object can be passed as the opts" , "table; this will set the `number_section` and `slide_level` values" , "to those defined on the command line." , "" , "Usage:" , "" , " local blocks = {" , " pandoc.Header(2, pandoc.Str 'first')," , " pandoc.Header(2, pandoc.Str 'second')," , " }" , " local opts = PANDOC_WRITER_OPTIONS" , " local newblocks = pandoc.structure.make_sections(blocks, opts)" ] where defNumSec = False peekOpts idx = do numberSections <- fromMaybe defNumSec <$> do liftLua $ getfield idx "number_sections" optional (peekBool top `lastly` pop 1) baseLevel <- do liftLua $ getfield idx "base_level" optional (peekIntegral top `lastly` pop 1) slideLevel <- do liftLua $ getfield idx "slide_level" optional (peekIntegral top `lastly` pop 1) return (numberSections, baseLevel, slideLevel) slide_level :: LuaError e => DocumentedFunction e slide_level = defun "slide_level" ### liftPure getSlideLevel <#> parameter peekBodyBlocks "Blocks|Pandoc" "blocks" "document body" =#> functionResult pushIntegral "integer" "slide level" #? T.unlines [ "Find level of header that starts slides (defined as the least" , "header level that occurs before a non-header/non-hrule in the" , "blocks)." ] -- | Split 'Pandoc' into 'Chunk's. split_into_chunks :: LuaError e => DocumentedFunction e split_into_chunks = defun "split_into_chunks" ### (\doc mopts -> pure $ let defOpts = (defPathTmpl, defNumSects, Nothing, defLvl) (pathTempl, numberSect, mbBaseLevel, chunkLevel) = fromMaybe defOpts mopts in splitIntoChunks pathTempl numberSect mbBaseLevel chunkLevel doc) <#> parameter peekPandoc "Pandoc" "doc" "document to split" <#> opt (parameter peekSplitOpts "table" "opts" optionsDescr) =#> functionResult pushChunkedDoc "ChunkedDoc" "" #? T.unlines [ "Converts a [[Pandoc]] document into a [[ChunkedDoc]]." ] where defPathTmpl = PathTemplate "chunk-%n" defNumSects = False defLvl = 1 peekSplitOpts idx = (,,,) <$> peekFieldRaw ((fmap PathTemplate . peekText) `orDefault` defPathTmpl) "path_template" idx <*> peekFieldRaw (peekBool `orDefault` defNumSects) "number_sections" idx <*> peekFieldRaw (optional . peekIntegral) "base_heading_level" idx <*> peekFieldRaw (peekIntegral `orDefault` defLvl) "chunk_level" idx orDefault p defaultValue idx' = liftLua (isnil idx') >>= \case True -> pure defaultValue False -> p idx' optionsDescr = T.unlines [ "Splitting options." , "" , "The following options are supported:" , "" , " `path_template`" , " : template used to generate the chunks' filepaths" , " `%n` will be replaced with the chunk number (padded with" , " leading 0s to 3 digits), `%s` with the section number of" , " the heading, `%h` with the (stringified) heading text," , " `%i` with the section identifier. For example," , " `\"section-%s-%i.html\"` might be resolved to" , " `\"section-1.2-introduction.html\"`." , "" , " Default is `\"chunk-%n\"` (string)" , "" , " `number_sections`" , " : whether sections should be numbered; default is `false`" , " (boolean)" , "" , " `chunk_level`" , " : The heading level the document should be split into" , " chunks. The default is to split at the top-level, i.e.," , " `1`. (integer)" , "" , " `base_heading_level`" , " : The base level to be used for numbering. Default is `nil`" , " (integer|nil)" ] -- | Generate a table of contents. table_of_contents :: DocumentedFunction PandocError table_of_contents = defun "table_of_contents" ### (\tocSource mwriterOpts -> pure $ let writerOpts = fromMaybe def mwriterOpts in case tocSource of Left blks -> toTableOfContents writerOpts blks Right tree -> tocToList (writerNumberSections writerOpts) (writerTOCDepth writerOpts) tree ) <#> parameter peekTocSource "Blocks|Pandoc|ChunkedDoc" "toc_source" "list of command line arguments" <#> opt (parameter peekWriterOptions "WriterOptions" "opts" "options") =#> functionResult pushBlock "Block" "Table of contents as a BulletList object" #? T.unlines [ "Generates a table of contents for the given object." ] where peekTocSource idx = (Left <$> peekBodyBlocks idx) <|> (Right . chunkedTOC <$> peekChunkedDoc idx) -- | Generate a unique ID from a list of inlines. unique_identifier :: LuaError e => DocumentedFunction e unique_identifier = defun "unique_identifier" ### (\inlns mUsedIdents mExts -> do let usedIdents = fromMaybe mempty mUsedIdents let exts = fromMaybe mempty mExts pure $ Shared.uniqueIdent exts inlns usedIdents) <#> parameter peekInlinesFuzzy "Inlines" "inlines" "base for identifier" <#> opt (parameter (peekSet peekText) "table" "used" "set of identifiers (string keys, boolean values) that\ \ have already been used.") <#> opt (parameter peekExtensions "{string,...}" "exts" "list of format extensions") =#> functionResult pushText "string" "unique identifier" #? "Generates a unique identifier from a list of inlines, similar to\ \ what's generated by the `auto_identifiers` extension.\n\ \\n\ \ The method used to generated identifiers can be modified through\ \ `ext`, which is a list of format extensions.\n\ \\n\ \ It can be used to generate IDs similar to what the `auto_identifiers`\ \ extension provides.\n\ \\n\ \ Example:\n\ \\n\ \ local used_ids = {}\n\ \ function Header (h)\n\ \ local id =\n\ \ pandoc.structure.unique_identifier(h.content, used_ids)\n\ \ used_ids[id] = true\n\ \ h.identifier = id\n\ \ return h\n\ \ end" -- | Retrieves the body blocks of a 'Pandoc' object or from a list of -- blocks. peekBodyBlocks :: LuaError e => Peeker e [Block] peekBodyBlocks idx = ((\(Pandoc _ blks) -> blks) <$> peekPandoc idx) <|> peekBlocksFuzzy idx ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/System.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Module.System Copyright : © 2019-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Pandoc's system Lua module. -} module Text.Pandoc.Lua.Module.System ( documentedModule ) where import Data.Version (makeVersion) import HsLua import HsLua.Module.System ( arch, cmd, cp, cputime, env, getwd, ls, mkdir, os, read_file , rename, rm, rmdir, times, with_env, with_tmpdir, with_wd , write_file, xdg ) import qualified HsLua.Module.System as MSys -- | Push the pandoc.system module on the Lua stack. documentedModule :: forall e. LuaError e => Module e documentedModule = defmodule "pandoc.system" `withDescription` moduleDescription @e MSys.documentedModule `withFields` [arch, os] `withFunctions` [ cputime `since` v[3,1,1] , setName cmd "command" `since` v[3,7,1] , setName cp "copy" `since` v[3,7,1] , setName env "environment" `since` v[2,7,3] , setName getwd "get_working_directory" `since` v[2,8] , setName ls "list_directory" `since` v[2,19] , setName mkdir "make_directory" `since` v[2,19] , read_file `since` v[3,7,1] , rename `since` v[3,7,1] , setName rm "remove" `since` v[3,7,1] , setName rmdir "remove_directory" `since` v[2,19] , times `since` v[3,7,1] , setName with_env "with_environment" `since` v[2,7,3] , setName with_tmpdir "with_temporary_directory" `since` v[2,8] , setName with_wd "with_working_directory" `since` v[2,7,3] , write_file `since` v[3,7,1] , xdg `since` v[3,7,1] ] where v = makeVersion ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Template.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Template Copyright : Copyright © 2022-2026 Albert Krewinkel, John MacFarlane License : GPL-2.0-or-later Maintainer : Albert Krewinkel Lua module to handle pandoc templates. -} module Text.Pandoc.Lua.Module.Template ( documentedModule ) where import Data.Version (makeVersion) import HsLua import HsLua.Module.DocLayout (peekDoc, pushDoc) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.AST (peekMeta, pushBlocks, pushInlines) import Text.Pandoc.Lua.Marshal.Context (peekContext, pushContext) import Text.Pandoc.Lua.Marshal.Template (typeTemplate, peekTemplate, pushTemplate) import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua), liftPandocLua) import Text.Pandoc.Writers.Shared (metaToContext') import Text.Pandoc.Templates ( compileTemplate, getDefaultTemplate, getTemplate, renderTemplate , runWithPartials, runWithDefaultPartials ) import qualified Data.Text as T -- | The "pandoc.template" module. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.template" `withDescription` "Handle pandoc templates." `withFunctions` functions `associateType` typeTemplate -- | Template module functions. functions :: [DocumentedFunction PandocError] functions = [ defun "apply" ### liftPure2 renderTemplate <#> parameter peekTemplate "Template" "template" "template to apply" <#> parameter peekContext "table" "context" "variable values" =#> functionResult pushDoc "Doc" "rendered template" #? T.unlines [ "Applies a context with variable assignments to a template," , "returning the rendered template. The `context` parameter must be a" , "table with variable names as keys and [[Doc]], string, boolean, or" , "table as values, where the table can be either be a list of the" , "aforementioned types, or a nested context." ] `since` makeVersion [3,0] , defun "compile" ### (\template mfilepath -> unPandocLua $ case mfilepath of Just fp -> runWithPartials (compileTemplate fp template) Nothing -> runWithDefaultPartials (compileTemplate "templates/default" template)) <#> parameter peekText "string" "template" "template string" <#> opt (stringParam "templates_path" ("parameter to determine a default path and extension for " <> "partials; uses the data files templates path by default.")) =#> functionResult (either failLua pushTemplate) "Template" "compiled template" #? T.unlines [ "Compiles a template string into a [[Template]] object usable by" , "pandoc." , "" , "If the `templates_path` parameter is specified, then it should be the" , "file path associated with the template. It is used when checking" , "for partials. Partials will be taken only from the default data" , "files if this parameter is omitted." , "" , "An error is raised if compilation fails." ] `since` makeVersion [2,17] , defun "default" ### (\mformat -> unPandocLua $ do let getFORMAT = liftPandocLua $ do getglobal "FORMAT" forcePeek $ peekText top `lastly` pop 1 format <- maybe getFORMAT pure mformat getDefaultTemplate format) <#> opt (textParam "writer" ("name of the writer for which the template should be " <> "retrieved; defaults to the global `FORMAT`.")) =#> functionResult pushText "string" "raw template" #? T.unlines [ "Returns the default template for a given writer as a string. An" , "error is thrown if no such template can be found." ] `since` makeVersion [2,17] , defun "get" ### (unPandocLua . getTemplate) <#> stringParam "filename" "name of the template" =#> textResult "content of template file" #? T.unlines [ "Retrieve text for a template." , "" , "This function first checks the resource paths for a file of this" , "name; if none is found, the `templates` directory in the user data" , "directory is checked. Returns the content of the file, or throws" , "an error if no file is found." ] `since` makeVersion [3,2,1] , defun "meta_to_context" ### (\meta blockWriterIdx inlineWriterIdx -> unPandocLua $ do let blockWriter blks = liftPandocLua $ do pushvalue blockWriterIdx pushBlocks blks callTrace 1 1 forcePeek $ peekDoc top let inlineWriter blks = liftPandocLua $ do pushvalue inlineWriterIdx pushInlines blks callTrace 1 1 forcePeek $ peekDoc top metaToContext' blockWriter inlineWriter meta) <#> parameter peekMeta "Meta" "meta" "document metadata" <#> parameter pure "function" "blocks_writer" "converter from [[Blocks]] to [[Doc]] values" <#> parameter pure "function" "inlines_writer" "converter from [[Inlines]] to [[Doc]] values" =#> functionResult pushContext "table" "template context" #? T.unlines [ "Creates template context from the document's [[Meta]] data, using the" , "given functions to convert [[Blocks]] and [[Inlines]] to [[Doc]]" , "values." ] `since` makeVersion [3,0] ] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Text.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-| Module : Text.Pandoc.Lua.Module.Text Copyright : © 2023 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Lua module to work with UTF-8 strings. -} module Text.Pandoc.Lua.Module.Text ( documentedModule ) where import Data.Version (makeVersion) import HsLua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Writers.Shared (toSubscript, toSuperscript) import qualified Data.Text as T import qualified HsLua.Module.Text as TM -- | The @aeson@ module specification. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.text" `withFunctions` [ TM.fromencoding `since` v[3,0] , TM.len `since` v[2,0,3] , TM.lower `since` v[2,0,3] , TM.reverse `since` v[2,0,3] , TM.sub `since` v[2,0,3] , subscript `since` v[3,8] , superscript `since` v[3,8] , TM.toencoding `since` v[3,0] , TM.upper `since` v[2,0,3] ] `withDescription` T.unlines [ "UTF-8 aware text manipulation functions, implemented in Haskell." , "" , "The text module can also be loaded under the name `text`, although" , "this is discouraged and deprecated." , "" , "``` lua" , "-- uppercase all regular text in a document:" , "function Str (s)" , " s.text = pandoc.text.upper(s.text)" , " return s" , "end" , "```" ] where v = makeVersion -- | Convert all chars in a string to Unicode subscript. subscript :: LuaError e => DocumentedFunction e subscript = defun "subscript" ### pure . traverse toSubscript <#> stringParam "input" "string to convert to subscript characters" =#> functionResult (maybe pushnil pushString) "string|nil" "Subscript version of the input, or `nil` if not all characters\ \ could be converted." #? "Tries to convert the string into a Unicode subscript version of the\ \ string. Returns `nil` if not all characters of the input can be\ \ mapped to a subscript Unicode character.\ \ Supported characters include numbers, parentheses, and plus/minus." -- | Convert all chars in a string to Unicode superscript. superscript :: LuaError e => DocumentedFunction e superscript = defun "superscript" ### pure . traverse toSuperscript <#> stringParam "input" "string to convert to superscript characters" =#> functionResult (maybe pushnil pushString) "string|nil" "Superscript version of the input, or `nil` if not all characters\ \ could be converted." #? "Tries to convert the string into a Unicode superscript version of the\ \ string. Returns `nil` if not all characters of the input can be\ \ mapped to a superscript Unicode character.\ \ Supported characters include numbers, parentheses, and plus/minus." ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Types.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module.Types Copyright : © 2019-2026 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Pandoc data type constructors. -} module Text.Pandoc.Lua.Module.Types ( documentedModule ) where import Data.Version (Version, makeVersion) import HsLua ( DocumentedFunction, Module (..) , (###), (<#>), (=#>), (#?), associateType , defmodule, defun, functionResult, parameter, since , withDescription, withFunctions ) import HsLua.Module.Version (peekVersionFuzzy, pushVersion, typeVersion) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.Sources (peekSources, pushSources, typeSource) import Text.Pandoc.Lua.PandocLua () -- | Push the pandoc.types module on the Lua stack. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.types" `withDescription` "Constructors for types that are not part of the pandoc AST." `withFunctions` [ mkVersion `since` v[2,7,3] , mkSources `since` v[3,9,1] ] `associateType` typeSource `associateType` typeVersion where v :: [Int] -> Version v = makeVersion mkVersion :: DocumentedFunction PandocError mkVersion = defun "Version" ### return <#> parameter peekVersionFuzzy "string|number|{integer,...}|Version" "version_specifier" (mconcat [ "A version string like `'2.7.3'`, " , "a Lua number like `2.0`, " , "a list of integers like `{2,7,3}`, " , "or a Version object." ]) =#> functionResult pushVersion "Version" "New Version object." mkSources :: DocumentedFunction PandocError mkSources = defun "Sources" ### pure <#> parameter peekSources "string|{string,...}|table" "srcs" "sources" =#> functionResult pushSources "{Source,...}" "new Sources object" #? "Creates a new Sources element, i.e., a list of [[Source]] items.\n\ \\n\ \ Pandoc's text readers expect the input text to be paired\ \ with information on where the text originated, e.g., a\ \ file name. This abstraction is provided via the `Sources` type.\n\ \\n\ \Pandoc accepts a range of objects wherever a *Sources* list is\ \ expected:\n\ \\n\ \ - a list of [[Source]] items;\n\ \ - a simple string, which becomes an unnamed source;\n\ \ - a list of table objects, where each table contains the fields\n\ \ `name` (the filepath) and `text` (the file contents)\n\ \\n\ \A Sources list can be converted to a string via the default `tostring`\ \ Lua function. This will concatenate all source items." ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module/Utils.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Module.Utils Copyright : © 2017-2026 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Utility module for Lua, exposing internal helper functions. -} module Text.Pandoc.Lua.Module.Utils ( documentedModule , sha1 ) where import Control.Applicative ((<|>)) import Control.Monad ((<$!>)) import Control.Monad.Except (MonadError (throwError)) import Crypto.Hash (hashWith, SHA1(SHA1)) import Data.Data (showConstr, toConstr) import Data.Default (def) import Data.Maybe (fromMaybe) import Data.Version (Version, makeVersion) import HsLua as Lua import HsLua.Module.Version (peekVersionFuzzy, pushVersion) import Text.Pandoc.Citeproc (getReferences, processCitations) import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError (PandocLuaError)) import Text.Pandoc.Filter (applyJSONFilter) import Text.Pandoc.Format (FlavoredFormat (formatName), parseFlavoredFormat) import Text.Pandoc.Lua.Documentation (renderDocumentation) import Text.Pandoc.Lua.Filter (runFilterFile') import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.Format (peekFlavoredFormat) import Text.Pandoc.Lua.Marshal.Reference import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua)) import Text.Pandoc.Options (WriterOptions (writerExtensions)) import Text.Pandoc.Writers (Writer (..), getWriter) import qualified Data.Map as Map import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.Shared as Shared import qualified Text.Pandoc.UTF8 as UTF8 import qualified Text.Pandoc.Writers.Shared as Shared -- | Push the "pandoc.utils" module to the Lua stack. documentedModule :: Module PandocError documentedModule = defmodule "pandoc.utils" `withDescription` T.unlines [ "This module exposes internal pandoc functions and utility" , "functions." ] `withFunctions` [ blocks_to_inlines `since` v[2,2,3] , citeproc `since` v[2,19,1] , documentation `since` v[3,8,4] , equals `since` v[2,5] , from_simple_table `since` v[2,11] , make_sections `since` v[2,8] , normalize_date `since` v[2,0,6] , references `since` v[2,17] , run_json_filter `since` v[2,1,1] , run_lua_filter `since` v[3,2,1] , sha1 `since` v[2,0,6] , stringify `since` v[2,0,6] , to_roman_numeral `since` v[2,0,6] , to_simple_table `since` v[2,11] , type' `since` v[2,17] , defun "Version" ### liftPure (id @Version) <#> parameter peekVersionFuzzy "Version|string|{integer,...}|number" "v" "version description" =#> functionResult pushVersion "Version" "new Version object" #? "Creates a Version object." ] where v = makeVersion blocks_to_inlines :: LuaError e => DocumentedFunction e blocks_to_inlines = defun "blocks_to_inlines" ### (\blks mSep -> do let sep = maybe Shared.defaultBlocksSeparator B.fromList mSep return $ B.toList (Shared.blocksToInlinesWithSep sep blks)) <#> parameter (peekList peekBlock) "Blocks" "blocks" "List of [[Block]] elements to be flattened." <#> opt (parameter (peekList peekInline) "Inlines" "sep" ("List of [[Inline]] elements inserted as separator between\n" <> "two consecutive blocks; defaults to `{pandoc.LineBreak()}`.")) =#> functionResult pushInlines "Inlines" "" #? T.unlines [ "Squash a list of blocks into a list of inlines." , "" , "Usage" , "" , " local blocks = {" , " pandoc.Para{ pandoc.Str 'Paragraph1' }," , " pandoc.Para{ pandoc.Emph 'Paragraph2' }" , " }" , " local inlines = pandoc.utils.blocks_to_inlines(blocks)" , " assert(" , " inlines == pandoc.Inlines {" , " pandoc.Str 'Paragraph1'," , " pandoc.Linebreak()," , " pandoc.Emph{ pandoc.Str 'Paragraph2' }" , " }" , " )" ] citeproc :: DocumentedFunction PandocError citeproc = defun "citeproc" ### unPandocLua . processCitations <#> parameter peekPandoc "Pandoc" "doc" "document" =#> functionResult pushPandoc "Pandoc" "processed document" #? T.unlines [ "Process the citations in the file, replacing them with " , "rendered citations and adding a bibliography. " , "See the manual section on citation rendering for details." , "" , "Usage:" , "" , " -- Lua filter that behaves like `--citeproc`" , " function Pandoc (doc)" , " return pandoc.utils.citeproc(doc)" , " end" ] documentation :: DocumentedFunction PandocError documentation = defun "documentation" ### (\idx mformat -> do docobj <- getdocumentation idx >>= \case TypeNil -> fail "Undocumented object" _ -> forcePeek $ peekDocumentationObject top let blocks = renderDocumentation docobj if maybe mempty formatName mformat == "blocks" then pure . Left $ B.toList blocks else unPandocLua $ do flvrd <- maybe (parseFlavoredFormat "ansi") pure mformat getWriter flvrd >>= \case (TextWriter w, es) -> Right <$> w def{ writerExtensions = es } (B.doc blocks) _ -> throwError $ PandocLuaError "ByteString writers are not supported here.") <#> parameter pure "any" "object" "Retrieve documentation for this object" <#> opt (parameter peekFlavoredFormat "string|table" "format" "result format; defaults to `'ansi'`") =#> functionResult (either pushBlocks pushText) "string|Blocks" "rendered documentation" #? "Return the documentation for a function or module defined by pandoc.\ \ Throws an error if there is no documentation for the given object.\n\ \\n\ \The result format can be any textual format accepted by `pandoc.write`,\ \ and the documentation will be returned in that format.\ \ Additionally, the special format `blocks` is accepted, in which case\ \ the documentation is returned as [[Blocks]]." equals :: LuaError e => DocumentedFunction e equals = defun "equals" ### equal <#> parameter pure "any" "element1" "" <#> parameter pure "any" "element2" "" =#> functionResult pushBool "boolean" "Whether the two objects represent the same element" #? T.unlines [ "Test equality of AST elements. Elements in Lua are considered" , "equal if and only if the objects obtained by unmarshaling are" , "equal." , "" , "**This function is deprecated.** Use the normal Lua `==` equality" , "operator instead." ] -- | Converts an old/simple table into a normal table block element. from_simple_table :: LuaError e => DocumentedFunction e from_simple_table = defun "from_simple_table" ### liftPure (\(SimpleTable capt aligns widths head' body) -> Table nullAttr (Caption Nothing [Plain capt | not (null capt)]) (zipWith (\a w -> (a, toColWidth w)) aligns widths) (TableHead nullAttr [blockListToRow head' | not (null head') ]) [TableBody nullAttr 0 [] $ map blockListToRow body | not (null body)] (TableFoot nullAttr [])) <#> parameter peekSimpleTable "SimpleTable" "simple_tbl" "" =#> functionResult pushBlock "Block" "table block element" #? T.unlines [ "Creates a [[Table]] block element from a [[SimpleTable]]. This is" , "useful for dealing with legacy code which was written for pandoc" , "versions older than 2.10." , "" , "Usage:" , "" , " local simple = pandoc.SimpleTable(table)" , " -- modify, using pre pandoc 2.10 methods" , " simple.caption = pandoc.SmallCaps(simple.caption)" , " -- create normal table block again" , " table = pandoc.utils.from_simple_table(simple)" ] where blockListToRow :: [[Block]] -> Row blockListToRow = Row nullAttr . map (B.simpleCell . B.fromList) toColWidth :: Double -> ColWidth toColWidth 0 = ColWidthDefault toColWidth w = ColWidth w make_sections :: LuaError e => DocumentedFunction e make_sections = defun "make_sections" ### liftPure3 Shared.makeSections <#> parameter peekBool "boolean" "number_sections" ("whether section divs should get an additional `number`\n" <> "attribute containing the section number.") <#> parameter (\i -> (Nothing <$ peekNil i) <|> (Just <$!> peekIntegral i)) "integer|nil" "baselevel" "shift top-level headings to this level" <#> parameter (peekList peekBlock) "Blocks" "blocks" "list of blocks to process" =#> functionResult pushBlocks "Blocks" "blocks with sections" #? T.unlines [ "Converts a list of [[Block]] elements into sections." , "`Div`s will be created beginning at each `Header`" , "and containing following content until the next `Header`" , "of comparable level. If `number_sections` is true," , "a `number` attribute will be added to each `Header`" , "containing the section number. If `base_level` is" , "non-null, `Header` levels will be reorganized so" , "that there are no gaps, and so that the base level" , "is the level specified." ] normalize_date :: DocumentedFunction e normalize_date = defun "normalize_date" ### liftPure Shared.normalizeDate <#> parameter peekText "string" "date" "the date string" =#> functionResult (maybe pushnil pushText) "string|nil" "normalized date, or nil if normalization failed." #? T.unwords [ "Parse a date and convert (if possible) to \"YYYY-MM-DD\" format. We" , "limit years to the range 1601-9999 (ISO 8601 accepts greater than" , "or equal to 1583, but MS Word only accepts dates starting 1601)." , "Returns nil instead of a string if the conversion failed." ] -- | List of references in CSL format. references :: DocumentedFunction PandocError references = defun "references" ### (unPandocLua . getReferences Nothing) <#> parameter peekPandoc "Pandoc" "doc" "document" =#> functionResult (pushPandocList pushReference) "table" "lift of references." #? T.unlines [ "Get references defined inline in the metadata and via an external" , "bibliography. Only references that are actually cited in the" , "document (either with a genuine citation or with `nocite`) are" , "returned. URL variables are converted to links." , "" , "The structure used represent reference values corresponds to that" , "used in CSL JSON; the return value can be use as `references`" , "metadata, which is one of the values used by pandoc and citeproc" , "when generating bibliographies." , "" , "Usage:" , "" , " -- Include all cited references in document" , " function Pandoc (doc)" , " doc.meta.references = pandoc.utils.references(doc)" , " doc.meta.bibliography = nil" , " return doc" , " end" ] -- | Run a filter from a file. run_lua_filter :: DocumentedFunction PandocError run_lua_filter = defun "run_lua_filter" ### (\doc fp mbenv -> do envIdx <- maybe copyOfGlobalTable pure mbenv runFilterFile' envIdx fp doc) <#> parameter peekPandoc "Pandoc" "doc" "the Pandoc document to filter" <#> parameter peekString "string" "filter" "filepath of the filter to run" <#> opt (parameter (typeChecked "table" istable pure) "table" "env" "environment to load and run the filter in") =#> functionResult pushPandoc "Pandoc" "filtered document" #? ( "Filter the given doc by passing it through a Lua filter." <> "\n\nThe filter will be run in the current Lua process." <> "\n" ) where copynext :: LuaError e => StackIndex -> LuaE e StackIndex copynext to = Lua.next (nth 2) >>= \case False -> pure to True -> do pushvalue (nth 2) insert (nth 2) rawset to copynext to copyOfGlobalTable :: LuaError e => LuaE e StackIndex copyOfGlobalTable = do newtable pushglobaltable pushnil (copynext =<< absindex (nth 3)) <* pop 1 -- pop source table -- | Process the document with a JSON filter. run_json_filter :: DocumentedFunction PandocError run_json_filter = defun "run_json_filter" ### (\doc filterPath margs -> do args <- case margs of Just xs -> return xs Nothing -> do Lua.getglobal "FORMAT" (forcePeek ((:[]) <$!> peekString top) <* pop 1) applyJSONFilter def args filterPath doc ) <#> parameter peekPandoc "Pandoc" "doc" "the Pandoc document to filter" <#> parameter peekString "string" "filter" "filter to run" <#> opt (parameter (peekList peekString) "{string,...}" "args" "list of arguments passed to the filter. Defaults to `{FORMAT}`.") =#> functionResult pushPandoc "Pandoc" "filtered document" #? "Filter the given doc by passing it through a JSON filter." -- | Documented Lua function to compute the hash of a string. sha1 :: DocumentedFunction e sha1 = defun "sha1" ### liftPure (show . hashWith SHA1) <#> parameter peekByteString "string" "input" "" =#> functionResult pushString "string" "hexadecimal hash value" #? "Computes the SHA1 hash of the given string input." -- | Convert pandoc structure to a string with formatting removed. -- Footnotes are skipped (since we don't want their contents in link -- labels). stringify :: LuaError e => DocumentedFunction e stringify = defun "stringify" ### (\idx -> forcePeek . retrieving "stringifyable element" $ choice [ (fmap Shared.stringify . peekPandoc) , (fmap Shared.stringify . peekInline) , (fmap Shared.stringify . peekBlock) , (fmap Shared.stringify . peekCaption) , (fmap Shared.stringify . peekCell) , (fmap Shared.stringify . peekCitation) , (fmap Shared.stringify . peekTableHead) , (fmap Shared.stringify . peekTableFoot) , (fmap stringifyMetaValue . peekMetaValue) , (fmap (const "") . peekAttr) , (fmap (const "") . peekListAttributes) ] idx) <#> parameter pure "Pandoc|Block|Inline|Caption|Cell|MetaValue" "element" "some pandoc AST element" =#> functionResult pushText "string" "A plain string representation of the given element." #? T.unlines [ "Converts the given element (Pandoc, Meta, Block, or Inline) into" , "a string with all formatting removed." ] where stringifyMetaValue :: MetaValue -> T.Text stringifyMetaValue mv = case mv of MetaBool b -> T.toLower $ T.pack (show b) MetaString s -> s MetaList xs -> mconcat $ map stringifyMetaValue xs MetaMap m -> mconcat $ map (stringifyMetaValue . snd) (Map.toList m) _ -> Shared.stringify mv to_roman_numeral :: LuaError e => DocumentedFunction e to_roman_numeral = defun "to_roman_numeral" ### liftPure Shared.toRomanNumeral <#> parameter (peekIntegral @Int) "integer" "n" "positive integer below 4000" =#> functionResult pushText "string" "A roman numeral." #? T.unlines [ "Converts an integer < 4000 to uppercase roman numeral." , "" , "Usage:" , "" , " local to_roman_numeral = pandoc.utils.to_roman_numeral" , " local pandoc_birth_year = to_roman_numeral(2006)" , " -- pandoc_birth_year == 'MMVI'" ] -- | Converts a table into an old/simple table. to_simple_table :: DocumentedFunction PandocError to_simple_table = defun "to_simple_table" ### (\case Table _attr caption specs thead tbodies tfoot -> do let (capt, aligns, widths, headers, rows) = Shared.toLegacyTable caption specs thead tbodies tfoot return $ SimpleTable capt aligns widths headers rows blk -> Lua.failLua $ mconcat [ "Expected Table, got ", showConstr (toConstr blk), "." ]) <#> parameter peekTable "Block" "tbl" "a table" =#> functionResult pushSimpleTable "SimpleTable" "SimpleTable object" #? T.unlines [ "Converts a table into an old/simple table." , "" , "Usage:" , "" , " local simple = pandoc.utils.to_simple_table(table)" , " -- modify, using pre pandoc 2.10 methods" , " simple.caption = pandoc.SmallCaps(simple.caption)" , " -- create normal table block again" , " table = pandoc.utils.from_simple_table(simple)" ] where peekTable :: LuaError e => Peeker e Block peekTable idx = peekBlock idx >>= \case t@(Table {}) -> return t b -> Lua.failPeek $ mconcat [ "Expected Table, got " , UTF8.fromString $ showConstr (toConstr b) , "." ] type' :: DocumentedFunction e type' = defun "type" ### (\idx -> getmetafield idx "__name" >>= \case TypeString -> fromMaybe mempty <$> tostring top _ -> ltype idx >>= typename) <#> parameter pure "any" "value" "any Lua value" =#> functionResult pushByteString "string" "type of the given value" #? T.unlines [ "Pandoc-friendly version of Lua's default `type` function, returning" , "type information similar to what is presented in the manual." , "" , "The function works by checking the metafield `__name`. If the" , "argument has a string-valued metafield `__name`, then it returns" , "that string. Otherwise it behaves just like the normal `type`" , "function." , "" , "Usage:" , "" , " -- Prints one of 'string', 'boolean', 'Inlines', 'Blocks'," , " -- 'table', and 'nil', corresponding to the Haskell constructors" , " -- MetaString, MetaBool, MetaInlines, MetaBlocks, MetaMap," , " -- and an unset value, respectively." , "" , " function Meta (meta)" , " print('type of metavalue `author`:', pandoc.utils.type(meta.author))" , " end" ] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Module.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Module Copyright : © 2017-2026 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Setting up and initializing Lua modules. -} module Text.Pandoc.Lua.Module ( initModules ) where import Control.Monad (forM_, when) import Data.Version (makeVersion) import HsLua as Lua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.List (pushPandocList, pushListModule) import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua) import qualified Data.ByteString.Char8 as Char8 import qualified Lua.LPeg as LPeg import qualified HsLua.Aeson import qualified HsLua.Module.DocLayout as Module.Layout import qualified HsLua.Module.Zip as Module.Zip import qualified Text.Pandoc.Lua.Module.CLI as Pandoc.CLI import qualified Text.Pandoc.Lua.Module.Format as Pandoc.Format import qualified Text.Pandoc.Lua.Module.Image as Pandoc.Image import qualified Text.Pandoc.Lua.Module.JSON as Pandoc.JSON import qualified Text.Pandoc.Lua.Module.Log as Pandoc.Log import qualified Text.Pandoc.Lua.Module.MediaBag as Pandoc.MediaBag import qualified Text.Pandoc.Lua.Module.Pandoc as Module.Pandoc import qualified Text.Pandoc.Lua.Module.Path as Pandoc.Path import qualified Text.Pandoc.Lua.Module.Scaffolding as Pandoc.Scaffolding import qualified Text.Pandoc.Lua.Module.Structure as Pandoc.Structure import qualified Text.Pandoc.Lua.Module.System as Pandoc.System import qualified Text.Pandoc.Lua.Module.Template as Pandoc.Template import qualified Text.Pandoc.Lua.Module.Text as Pandoc.Text import qualified Text.Pandoc.Lua.Module.Types as Pandoc.Types import qualified Text.Pandoc.Lua.Module.Utils as Pandoc.Utils initModules :: PandocLua () initModules = do initPandocModule initJsonMetatable installLpegSearcher setGlobalModules initPandocModule :: PandocLua () initPandocModule = liftPandocLua $ do -- Push module table registerModule Module.Pandoc.documentedModule -- load modules and add them to the `pandoc` module table. forM_ submodules $ \mdl -> do registerModule mdl -- pandoc.text must be require-able as 'text' for backwards compat. when (moduleName mdl == "pandoc.text") $ do getfield registryindex loaded pushvalue (nth 2) setfield (nth 2) "text" pop 1 -- _LOADED -- Shorten name, drop everything before the first dot (if any). let fieldname (Name mdlname) = Name . maybe mdlname snd . Char8.uncons . snd $ Char8.break (== '.') mdlname Lua.setfield (nth 2) (fieldname $ moduleName mdl) -- pandoc.List is low-level and must be opened differently. requirehs "pandoc.List" (const pushListModule) setfield (nth 2) "List" -- assign module to global variable Lua.setglobal "pandoc" -- | Modules that are loaded at startup and assigned to fields in the -- pandoc module. -- -- Note that @pandoc.List@ is not included here for technical reasons; -- it must be handled separately. submodules :: [Module PandocError] submodules = [ Pandoc.CLI.documentedModule , Pandoc.Format.documentedModule , Pandoc.Image.documentedModule , Pandoc.JSON.documentedModule , Pandoc.Log.documentedModule , Pandoc.MediaBag.documentedModule , Pandoc.Path.documentedModule , Pandoc.Scaffolding.documentedModule , Pandoc.Structure.documentedModule , Pandoc.System.documentedModule , Pandoc.Template.documentedModule , Pandoc.Text.documentedModule , Pandoc.Types.documentedModule , Pandoc.Utils.documentedModule , ((Module.Layout.documentedModule { moduleName = "pandoc.layout" } `allSince` [2,18]) `functionsSince` ["bold", "italic", "underlined", "strikeout", "fg", "bg"]) [3, 4, 1] , Module.Zip.documentedModule { moduleName = "pandoc.zip" } `allSince` [3,0] ] where allSince mdl version = mdl { moduleFunctions = map (`since` makeVersion version) $ moduleFunctions mdl } functionsSince mdl fns version = mdl { moduleFunctions = map (\fn -> if (functionName fn) `elem` fns then fn `since` makeVersion version else fn) $ moduleFunctions mdl } -- | Load all global modules and set them to their global variables. setGlobalModules :: PandocLua () setGlobalModules = liftPandocLua $ do let globalModules = [ ("lpeg", LPeg.luaopen_lpeg_ptr) -- must be loaded first , ("re", LPeg.luaopen_re_ptr) -- re depends on lpeg ] forM_ globalModules $ \(pkgname, luaopen) -> do Lua.pushcfunction luaopen Lua.pcall 0 1 Nothing >>= \case OK -> do -- all good, loading succeeded -- register as loaded module so later modules can rely on this Lua.getfield Lua.registryindex Lua.loaded Lua.pushvalue (Lua.nth 2) Lua.setfield (Lua.nth 2) pkgname Lua.pop 1 -- pop _LOADED _ -> do -- built-in library failed, load system lib Lua.pop 1 -- ignore error message -- Try loading via the normal package loading mechanism. Lua.getglobal "require" Lua.pushName pkgname Lua.call 1 1 -- Throws an exception if loading failed again! -- Module on top of stack. Register as global Lua.setglobal pkgname installLpegSearcher :: PandocLua () installLpegSearcher = liftPandocLua $ do Lua.getglobal' "package.searchers" Lua.pushHaskellFunction $ Lua.state >>= liftIO . LPeg.lpeg_searcher Lua.rawseti (Lua.nth 2) . (+1) . fromIntegral =<< Lua.rawlen (Lua.nth 2) Lua.pop 1 -- remove 'package.searchers' from stack -- | Setup the metatable that's assigned to Lua tables that were created -- from/via JSON arrays. initJsonMetatable :: PandocLua () initJsonMetatable = liftPandocLua $ do pushPandocList (const pushnil) [] getmetatable top setfield registryindex HsLua.Aeson.jsonarray Lua.pop 1 ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Orphans.hs ================================================ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE FlexibleInstances #-} {- | Module : Text.Pandoc.Lua.Orphans Copyright : © 2012-2024 John MacFarlane © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Orphan instances for Lua's Pushable and Peekable type classes. -} module Text.Pandoc.Lua.Orphans () where import Data.Version (Version) import HsLua import HsLua.Module.Version (peekVersionFuzzy) import Text.Pandoc.Definition import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.CommonState () import Text.Pandoc.Lua.Marshal.Context () import Text.Pandoc.Lua.Marshal.PandocError() import Text.Pandoc.Lua.Marshal.ReaderOptions () import Text.Pandoc.Lua.Marshal.Sources (pushSources) import Text.Pandoc.Sources (Sources) instance Pushable Pandoc where push = pushPandoc instance Pushable Meta where push = pushMeta instance Pushable MetaValue where push = pushMetaValue instance Pushable Block where push = pushBlock instance {-# OVERLAPPING #-} Pushable [Block] where push = pushBlocks instance Pushable Alignment where push = pushString . show instance Pushable CitationMode where push = pushCitationMode instance Pushable Format where push = pushFormat instance Pushable ListNumberDelim where push = pushString . show instance Pushable ListNumberStyle where push = pushString . show instance Pushable MathType where push = pushMathType instance Pushable QuoteType where push = pushQuoteType instance Pushable Cell where push = pushCell instance Pushable Inline where push = pushInline instance {-# OVERLAPPING #-} Pushable [Inline] where push = pushInlines instance Pushable Citation where push = pushCitation instance Pushable Row where push = pushRow instance Pushable TableBody where push = pushTableBody instance Pushable TableFoot where push = pushTableFoot instance Pushable TableHead where push = pushTableHead -- These instances exist only for testing. It's a hack to avoid making -- the marshalling modules public. instance Peekable Inline where safepeek = peekInline instance Peekable Block where safepeek = peekBlock instance Peekable Cell where safepeek = peekCell instance Peekable Meta where safepeek = peekMeta instance Peekable Pandoc where safepeek = peekPandoc instance Peekable Row where safepeek = peekRow instance Peekable Version where safepeek = peekVersionFuzzy instance {-# OVERLAPPING #-} Peekable Attr where safepeek = peekAttr instance Pushable Sources where push = pushSources ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/PandocLua.hs ================================================ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.PandocLua Copyright : © 2020-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel PandocMonad instance which allows execution of Lua operations and which uses Lua to handle state. -} module Text.Pandoc.Lua.PandocLua ( PandocLua (..) , liftPandocLua ) where import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow) import Control.Monad.Except (MonadError (catchError, throwError)) import Control.Monad.IO.Class (MonadIO) import HsLua as Lua import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Lua.Marshal.CommonState (peekCommonState, pushCommonState) import Text.Pandoc.Lua.Marshal.PandocError (peekPandocError, pushPandocError) import qualified Control.Monad.Catch as Catch import qualified Data.Text as T import qualified Text.Pandoc.Class.IO as IO -- | Type providing access to both, pandoc and Lua operations. newtype PandocLua a = PandocLua { unPandocLua :: LuaE PandocError a } deriving ( Applicative , Functor , Monad , MonadCatch , MonadIO , MonadMask , MonadThrow ) -- | Lift a @'Lua'@ operation into the @'PandocLua'@ type. liftPandocLua :: LuaE PandocError a -> PandocLua a liftPandocLua = PandocLua instance {-# OVERLAPPING #-} Exposable PandocError (PandocLua NumResults) where partialApply _narg = liftLua . unPandocLua instance Pushable a => Exposable PandocError (PandocLua a) where partialApply _narg x = 1 <$ (liftLua (unPandocLua x >>= Lua.push)) instance MonadError PandocError PandocLua where catchError = Catch.catch throwError = Catch.throwM instance PandocMonad PandocLua where lookupEnv = IO.lookupEnv getCurrentTime = IO.getCurrentTime getCurrentTimeZone = IO.getCurrentTimeZone newStdGen = IO.newStdGen newUniqueHash = IO.newUniqueHash openURL = IO.openURL readFileLazy = IO.readFileLazy readFileStrict = IO.readFileStrict readStdinStrict = IO.readStdinStrict glob = IO.glob fileExists = IO.fileExists getDataFileName = IO.getDataFileName getModificationTime = IO.getModificationTime getCommonState = PandocLua $ do Lua.getfield registryindex "PANDOC_STATE" forcePeek $ peekCommonState Lua.top `lastly` pop 1 putCommonState cst = PandocLua $ do pushCommonState cst Lua.setfield registryindex "PANDOC_STATE" logOutput = IO.logOutput -- | Retrieve a @'PandocError'@ from the Lua stack. popPandocError :: LuaE PandocError PandocError popPandocError = do errResult <- runPeek $ peekPandocError top `lastly` pop 1 case resultToEither errResult of Right x -> return x Left err -> return $ PandocLuaError (T.pack err) -- | Conversions between Lua errors and 'PandocError' exceptions. instance LuaError PandocError where popException = popPandocError pushException = pushPandocError luaException = PandocLuaError . T.pack ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Run.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {- | Module : Text.Pandoc.Lua.Run Copyright : Copyright © 2017-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Run code in the Lua interpreter. -} module Text.Pandoc.Lua.Run ( runLua , runLuaNoEnv , runLuaWith ) where import Control.Monad.Catch (try) import Control.Monad.Trans (MonadIO (..)) import HsLua as Lua hiding (try) import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.Init (initLua) import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua) -- | Run the Lua interpreter, using pandoc's default way of environment -- initialization. runLua :: (PandocMonad m, MonadIO m) => LuaE PandocError a -> m (Either PandocError a) runLua action = do runPandocLuaWith Lua.run . try $ do initLua liftPandocLua action runLuaWith :: (PandocMonad m, MonadIO m) => GCManagedState -> LuaE PandocError a -> m (Either PandocError a) runLuaWith luaState action = do runPandocLuaWith (withGCManagedState luaState) . try $ do initLua liftPandocLua action -- | Like 'runLua', but ignores all environment variables like @LUA_PATH@. runLuaNoEnv :: (PandocMonad m, MonadIO m) => LuaE PandocError a -> m (Either PandocError a) runLuaNoEnv action = do runPandocLuaWith Lua.run . try $ do liftPandocLua $ do -- This is undocumented, but works -- the code is adapted from the -- `lua.c` sources for the default interpreter. Lua.pushboolean True Lua.setfield Lua.registryindex "LUA_NOENV" initLua liftPandocLua action -- | Evaluate a @'PandocLua'@ computation, running all contained Lua -- operations. runPandocLuaWith :: (PandocMonad m, MonadIO m) => (forall b. LuaE PandocError b -> IO b) -> PandocLua a -> m a runPandocLuaWith runner pLua = do origState <- getCommonState let globals = defaultGlobals (result, newState) <- liftIO . runner . unPandocLua $ do putCommonState origState liftPandocLua $ setGlobals globals r <- pLua c <- getCommonState return (r, c) putCommonState newState return result -- | Global variables which should always be set. defaultGlobals :: [Global] defaultGlobals = [ PANDOC_API_VERSION , PANDOC_STATE , PANDOC_VERSION ] ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/SourcePos.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.SourcePos Copyright : © 2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Helper function to retrieve the 'SourcePos' in a Lua script. -} module Text.Pandoc.Lua.SourcePos ( luaSourcePos ) where import HsLua import Text.Parsec.Pos (SourcePos, newPos) import Text.Read (readMaybe) import qualified Data.Text as T import qualified HsLua.Core.Utf8 as UTF8 -- | Returns the current position in a Lua script. -- -- The reporting level is the level of the call stack, for which the -- position should be reported. There might not always be a position -- available, e.g., in C functions. luaSourcePos :: LuaError e => Int -- ^ reporting level -> LuaE e (Maybe SourcePos) luaSourcePos lvl = do -- reporting levels: -- 0: this hook, -- 1: userdata wrapper function for the hook, -- 2: warn, -- 3: function calling warn. where' lvl locStr <- UTF8.toText <$> tostring' top return $ do (prfx, sfx) <- T.breakOnEnd ":" <$> T.stripSuffix ": " locStr (source, _) <- T.unsnoc prfx line <- readMaybe (T.unpack sfx) -- We have no column information, so always use column 1 Just $ newPos (T.unpack source) line 1 ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Writer/Classic.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Lua.Writer.Classic Copyright : Copyright (C) 2012-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of Pandoc documents using a \"classic\" custom Lua writer. -} module Text.Pandoc.Lua.Writer.Classic ( runCustom ) where import Control.Applicative (optional) import Control.Arrow ((***)) import Data.List (intersperse) import Data.Maybe (fromMaybe) import qualified Data.Text as T import Data.Text (Text, pack) import HsLua as Lua hiding (Operation (Div)) #if !MIN_VERSION_hslua(2,2,0) import HsLua.Aeson (peekViaJSON) #endif import Text.DocLayout (literal, render) import Text.DocTemplates (Context) import Text.Pandoc.Definition import Text.Pandoc.Lua.Marshal.Attr (pushAttributeList) import Text.Pandoc.Lua.Orphans () import Text.Pandoc.Options import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared -- | List of key-value pairs that is pushed to Lua as AttributeList -- userdata. newtype AttributeList = AttributeList [(Text, Text)] instance Pushable AttributeList where push (AttributeList kvs) = pushAttributeList kvs attrToMap :: Attr -> AttributeList attrToMap (id',classes,keyvals) = AttributeList $ ("id", id') : ("class", T.unwords classes) : keyvals newtype Stringify a = Stringify a instance Pushable (Stringify Format) where push (Stringify (Format f)) = Lua.push (T.toLower f) instance Pushable (Stringify [Inline]) where push (Stringify ils) = Lua.push =<< inlineListToCustom ils instance Pushable (Stringify [Block]) where push (Stringify blks) = Lua.push =<< blockListToCustom blks instance Pushable (Stringify MetaValue) where push (Stringify (MetaMap m)) = Lua.push (fmap Stringify m) push (Stringify (MetaList xs)) = Lua.push (map Stringify xs) push (Stringify (MetaBool x)) = Lua.push x push (Stringify (MetaString s)) = Lua.push s push (Stringify (MetaInlines ils)) = Lua.push (Stringify ils) push (Stringify (MetaBlocks bs)) = Lua.push (Stringify bs) instance Pushable (Stringify Citation) where push (Stringify cit) = pushAsTable [ ("citationId", push . citationId) , ("citationPrefix", push . Stringify . citationPrefix) , ("citationSuffix", push . Stringify . citationSuffix) , ("citationMode", push . citationMode) , ("citationNoteNum", push . citationNoteNum) , ("citationHash", push . citationHash) ] cit -- | Key-value pair, pushed as a table with @a@ as the only key and @v@ as the -- associated value. newtype KeyValue a b = KeyValue (a, b) instance (Pushable a, Pushable b) => Pushable (KeyValue a b) where push (KeyValue (k, v)) = do Lua.newtable Lua.push k Lua.push v Lua.rawset (Lua.nth 3) -- | Convert Pandoc to custom markup using a classic Lua writer. runCustom :: LuaError e => WriterOptions -> Pandoc -> LuaE e Text runCustom opts doc@(Pandoc meta _) = do (body, context) <- docToCustom opts doc -- convert metavalues to a template context (variables) metaContext <- metaToContext opts (fmap (literal . pack) . blockListToCustom) (fmap (literal . pack) . inlineListToCustom) meta -- merge contexts from metadata and variables let renderContext = context <> metaContext return $ case writerTemplate opts of Nothing -> body Just tpl -> render Nothing $ renderTemplate tpl $ setField "body" body renderContext -- | Converts a Pandoc value to custom markup using a classic Lua writer. docToCustom :: forall e. LuaError e => WriterOptions -> Pandoc -> LuaE e (Text, Context Text) docToCustom opts (Pandoc (Meta metamap) blocks) = do body <- blockListToCustom blocks -- invoke doesn't work with multiple return values, so we have to call -- `Doc` manually. Lua.getglobal "Doc" -- function push body -- argument 1 push (fmap Stringify metamap) -- argument 2 push (writerVariables opts) -- argument 3 call 3 2 rendered <- peek (nth 2) -- first return value context <- forcePeek . optional $ peekViaJSON top -- snd return value return (rendered, fromMaybe mempty context) -- | Convert Pandoc block element to Custom. blockToCustom :: forall e. LuaError e => Block -- ^ Block element -> LuaE e String blockToCustom (Plain inlines) = invoke "Plain" (Stringify inlines) blockToCustom (Para [Image attr txt (src,tit)]) = invoke "CaptionedImage" src tit (Stringify txt) (attrToMap attr) blockToCustom (Para inlines) = invoke "Para" (Stringify inlines) blockToCustom (LineBlock linesList) = invoke "LineBlock" (map (Stringify) linesList) blockToCustom (RawBlock format str) = invoke "RawBlock" (Stringify format) str blockToCustom HorizontalRule = invoke "HorizontalRule" blockToCustom (Header level attr inlines) = invoke "Header" level (Stringify inlines) (attrToMap attr) blockToCustom (CodeBlock attr str) = invoke "CodeBlock" str (attrToMap attr) blockToCustom (BlockQuote blocks) = invoke "BlockQuote" (Stringify blocks) blockToCustom (Figure attr (Caption _ cbody) content) = invoke "Figure" (Stringify cbody) (Stringify content) (attrToMap attr) blockToCustom (Table _ blkCapt specs thead tbody tfoot) = let (capt, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot aligns' = map show aligns capt' = Stringify capt headers' = map (Stringify) headers rows' = map (map (Stringify)) rows in invoke "Table" capt' aligns' widths headers' rows' blockToCustom (BulletList items) = invoke "BulletList" (map (Stringify) items) blockToCustom (OrderedList (num,sty,delim) items) = invoke "OrderedList" (map (Stringify) items) num (show sty) (show delim) blockToCustom (DefinitionList items) = invoke "DefinitionList" (map (KeyValue . (Stringify *** map (Stringify))) items) blockToCustom (Div attr items) = invoke "Div" (Stringify items) (attrToMap attr) -- | Convert list of Pandoc block elements to Custom. blockListToCustom :: forall e. LuaError e => [Block] -- ^ List of block elements -> LuaE e String blockListToCustom xs = do blocksep <- invoke "Blocksep" bs <- mapM blockToCustom xs return $ mconcat $ intersperse blocksep bs -- | Convert list of Pandoc inline elements to Custom. inlineListToCustom :: forall e. LuaError e => [Inline] -> LuaE e String inlineListToCustom lst = do xs <- mapM (inlineToCustom @e) lst return $ mconcat xs -- | Convert Pandoc inline element to Custom. inlineToCustom :: forall e. LuaError e => Inline -> LuaE e String inlineToCustom (Str str) = invoke "Str" str inlineToCustom Space = invoke "Space" inlineToCustom SoftBreak = invoke "SoftBreak" inlineToCustom (Emph lst) = invoke "Emph" (Stringify lst) inlineToCustom (Underline lst) = invoke "Underline" (Stringify lst) inlineToCustom (Strong lst) = invoke "Strong" (Stringify lst) inlineToCustom (Strikeout lst) = invoke "Strikeout" (Stringify lst) inlineToCustom (Superscript lst) = invoke "Superscript" (Stringify lst) inlineToCustom (Subscript lst) = invoke "Subscript" (Stringify lst) inlineToCustom (SmallCaps lst) = invoke "SmallCaps" (Stringify lst) inlineToCustom (Quoted SingleQuote lst) = invoke "SingleQuoted" (Stringify lst) inlineToCustom (Quoted DoubleQuote lst) = invoke "DoubleQuoted" (Stringify lst) inlineToCustom (Cite cs lst) = invoke "Cite" (Stringify lst) (map (Stringify) cs) inlineToCustom (Code attr str) = invoke "Code" str (attrToMap attr) inlineToCustom (Math DisplayMath str) = invoke "DisplayMath" str inlineToCustom (Math InlineMath str) = invoke "InlineMath" str inlineToCustom (RawInline format str) = invoke "RawInline" (Stringify format) str inlineToCustom LineBreak = invoke "LineBreak" inlineToCustom (Link attr txt (src,tit)) = invoke "Link" (Stringify txt) src tit (attrToMap attr) inlineToCustom (Image attr alt (src,tit)) = invoke "Image" (Stringify alt) src tit (attrToMap attr) inlineToCustom (Note contents) = invoke "Note" (Stringify contents) inlineToCustom (Span attr items) = invoke "Span" (Stringify items) (attrToMap attr) ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua/Writer/Scaffolding.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua.Writer.Scaffolding Copyright : © 2022-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Conversion of Pandoc documents using a custom Lua writer. -} module Text.Pandoc.Lua.Writer.Scaffolding ( pushWriterScaffolding ) where import Control.Monad ((<$!>), void) import Data.ByteString (ByteString) import Data.Data (dataTypeConstrs, dataTypeOf, showConstr, toConstr) import Data.Default (def) import Data.List (intersperse) import Data.Maybe (fromMaybe) import Data.Text (Text) import Data.String (IsString (fromString)) import HsLua import HsLua.Module.DocLayout (peekDoc, pushDoc) import Text.DocLayout (Doc, blankline, render) import Text.DocTemplates (Context) import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Options (WriterOptions (..), WrapOption(..)) import Text.Pandoc.Lua.PandocLua () import Text.Pandoc.Lua.Marshal.AST import Text.Pandoc.Lua.Marshal.Context (peekContext) import Text.Pandoc.Lua.Marshal.WriterOptions ( peekWriterOptions , pushWriterOptions) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared (metaToContext, setField) import qualified Data.Text as T import qualified Text.Pandoc.UTF8 as UTF8 -- | Convert Pandoc to custom markup. pushWriterScaffolding :: LuaE PandocError NumResults pushWriterScaffolding = do newtable *> pushWriterMT *> setmetatable (nth 2) writer <- toWriterTable top addField "Blocks" $ pushDocumentedFunction (blocksFn writer) addField "Inlines" $ pushDocumentedFunction (inlinesFn writer) addField "Block" $ newtable *> pushBlockMT writer *> setmetatable (nth 2) addField "Inline" $ newtable *> pushInlineMT writer *> setmetatable (nth 2) addField "Pandoc" $ pushDocumentedFunction $ lambda ### (\(Pandoc _ blks) -> do pushWriterTable writer getfield' top "Blocks" pushBlocks blks callTrace 1 1 pure (NumResults 1)) <#> parameter peekPandoc "Pandoc" "doc" "" =?> "rendered doc" freeWriter writer return 1 where blocksFn w = lambda ### (\blocks msep -> blockListToCustom w msep blocks) <#> parameter peekBlocks "Blocks" "blocks" "" <#> opt (parameter peekDocFuzzy "Doc" "sep" "") =#> functionResult pushDoc "Doc" "" inlinesFn w = lambda ### inlineListToCustom w <#> parameter peekInlines "Inlines" "inlines" "" =#> functionResult pushDoc "Doc" "" pushBlockMT writer = do newtable addField "__call" $ pushDocumentedFunction $ lambda ### blockToCustom <#> parameter peekWriter "table" "writer" "" <#> parameter peekBlockFuzzy "Block" "block" "" =#> functionResult pushDoc "Doc" "rendered blocks" addField "__index" $ -- lookup missing fields in the main Writer table pushWriterTable writer pushInlineMT writer = do newtable addField "__call" $ pushDocumentedFunction $ lambda ### inlineToCustom <#> parameter peekWriter "table" "writer" "" <#> parameter peekInlineFuzzy "Inline" "inline" "" =#> functionResult pushDoc "Doc" "rendered inline" addField "__index" $ do -- lookup missing fields in the main Writer table pushWriterTable writer pushWriterMT :: LuaE PandocError () pushWriterMT = do newtable addField "__call" $ pushDocumentedFunction $ lambda ### (\writer doc mopts -> runWriter writer doc mopts) <#> parameter peekWriter "table" "writer" "" <#> parameter peekPandoc "Pandoc" "doc" "" <#> opt (parameter peekWriterOptions "WriterOptions" "opts" "") =#> functionResult pushText "string" "rendered document" addField "__index" . pushDocumentedFunction $ lambda ### (\_writer key -> handleMissingField key) <#> parameter pure "table" "writer" "" <#> parameter (liftLua . tostring') "string" "key" "" =#> functionResult (const pushnil) "string" "" addField :: LuaError e => Name -> LuaE e a -> LuaE e () addField name action = do pushName name action rawset (nth 3) getfield' :: LuaError e => StackIndex -> Name -> LuaE e HsLua.Type getfield' idx name = do aidx <- absindex idx pushName name rawget aidx >>= \case TypeNil -> pop 1 *> getfield aidx name ty -> pure ty -- | A writer table is just an absolute stack index. newtype WriterTable = WriterTable Reference toWriterTable :: LuaError e => StackIndex -> LuaE e WriterTable toWriterTable idx = WriterTable <$!> do pushvalue idx ref registryindex peekWriter :: LuaError e => Peeker e WriterTable peekWriter = liftLua . toWriterTable pushWriterTable :: LuaError e => Pusher e WriterTable pushWriterTable (WriterTable wref) = void $ getref registryindex wref writerOptionsField :: Name writerOptionsField = "Pandoc Writer WriterOptions" freeWriter :: WriterTable -> LuaE e () freeWriter (WriterTable wref) = unref registryindex wref pushOpts :: LuaE PandocError () pushOpts = void $ getfield' registryindex writerOptionsField runWriter :: WriterTable -> Pandoc -> Maybe WriterOptions -> LuaE PandocError Text runWriter writer doc@(Pandoc meta _blks) mopts = do let opts = fromMaybe def mopts pushWriterOptions opts *> setfield registryindex writerOptionsField (body, mcontext) <- runPeek (pandocToCustom writer doc) >>= force . \case Failure msg contexts -> Failure (cleanupTrace msg) contexts s -> s -- convert metavalues to a template context (variables) defaultContext <- metaToContext opts (blockListToCustom writer Nothing) (inlineListToCustom writer) meta let context = setField "body" body $ fromMaybe defaultContext mcontext let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing return $ render colwidth $ case writerTemplate opts of Nothing -> body Just tpl -> renderTemplate tpl context -- | Keep exactly one traceback and clean it up. This wouldn't be -- necessary if the @pcallTrace@ function would do nothing whenever the -- error already included a trace, but that would require some bigger -- changes; removing the additional traces in this post-process step is -- much easier (for now). cleanupTrace :: ByteString -> ByteString cleanupTrace msg = UTF8.fromText . T.intercalate "\n" $ let tmsg = T.lines $ UTF8.toText msg traceStart = (== "stack traceback:") in case break traceStart tmsg of (x, t:traces) -> (x <>) . (t:) $ let (firstTrace, rest) = break traceStart traces isPeekContext = ("\twhile " `T.isPrefixOf`) isUnknownCFn = (== "\t[C]: in ?") in filter (not . isUnknownCFn) firstTrace <> filter isPeekContext rest _ -> tmsg -- | Pushes the field in the writer table. getWriterField :: LuaError e => WriterTable -> Name -> LuaE e HsLua.Type getWriterField writer name = do pushWriterTable writer getfield' top name <* remove (nth 2) -- | Looks up @Writer.subtable.field@; tries @Writer.field@ as a fallback if the -- subtable field is @nil@. getNestedWriterField :: LuaError e => WriterTable -> Name -> Name -> LuaE e HsLua.Type getNestedWriterField writer subtable field = do pushWriterTable writer getfield' top subtable >>= \case TypeNil -> TypeNil <$ remove (nth 2) -- remove Writer table _ -> getfield' top field -- remove Writer and subtable <* remove (nth 3) <* remove (nth 2) pandocToCustom :: WriterTable -> Pandoc -> Peek PandocError (Doc Text, Maybe (Context Text)) pandocToCustom writer doc = withContext "rendering Pandoc" $ do callStatus <- liftLua $ do getWriterField writer "Pandoc" pushPandoc doc pushOpts pcallTrace 2 2 case callStatus of OK -> ((,) <$> peekDocFuzzy (nth 2) <*> orNil peekContext top) `lastly` pop 2 _ -> failPeek =<< liftLua (tostring' top) blockToCustom :: WriterTable -> Block -> LuaE PandocError (Doc Text) blockToCustom writer blk = forcePeek $ renderBlock writer blk renderBlock :: WriterTable -> Block -> Peek PandocError (Doc Text) renderBlock writer blk = do let constrName = fromString . showConstr . toConstr $ blk withContext ("rendering Block `" <> constrName <> "`") $ liftLua (getNestedWriterField writer "Block" constrName) >>= \case TypeNil -> failPeek =<< typeMismatchMessage "function or Doc" top _ -> callOrDoc (pushBlock blk) inlineToCustom :: WriterTable -> Inline -> LuaE PandocError (Doc Text) inlineToCustom writer inln = forcePeek $ renderInline writer inln renderInline :: WriterTable -> Inline -> Peek PandocError (Doc Text) renderInline writer inln = do let constrName = fromString . showConstr . toConstr $ inln withContext ("rendering Inline `" <> constrName <> "`") $ do liftLua (getNestedWriterField writer "Inline" constrName) >>= \case TypeNil -> failPeek =<< typeMismatchMessage "function or Doc" top _ -> callOrDoc (pushInline inln) -- | If the value at the top of the stack can be called as a function, -- then push the element and writer options to the stack and call it; -- otherwise treat it as a plain Doc value callOrDoc :: LuaE PandocError () -> Peek PandocError (Doc Text) callOrDoc pushElement = do liftLua (ltype top) >>= \case TypeFunction -> peekCall _ -> liftLua (getmetafield top "__call") >>= \case TypeNil -> peekDocFuzzy top _ -> liftLua (pop 1) *> peekCall where peekCall :: Peek PandocError (Doc Text) peekCall = liftLua (pushElement *> pushOpts *> pcallTrace 2 1) >>= \case OK -> peekDocFuzzy top _ -> failPeek =<< liftLua (tostring' top) blockListToCustom :: WriterTable -> Maybe (Doc Text) -> [Block] -> LuaE PandocError (Doc Text) blockListToCustom writer msep blocks = forcePeek $ renderBlockList writer msep blocks inlineListToCustom :: WriterTable -> [Inline] -> LuaE PandocError (Doc Text) inlineListToCustom writer inlines = forcePeek $ renderInlineList writer inlines renderBlockList :: WriterTable -> Maybe (Doc Text) -> [Block] -> Peek PandocError (Doc Text) renderBlockList writer msep blocks = withContext "rendering Blocks" $ do let addSeps = intersperse $ fromMaybe blankline msep mconcat . addSeps <$> mapM (renderBlock writer) blocks renderInlineList :: WriterTable -> [Inline] -> Peek PandocError (Doc Text) renderInlineList writer inlines = withContext "rendering Inlines" $ do mconcat <$> mapM (renderInline writer) inlines orNil :: Peeker e a -> Peeker e (Maybe a) orNil p idx = liftLua (ltype idx) >>= \case TypeNil -> pure Nothing TypeNone -> pure Nothing _ -> Just <$> p idx peekDocFuzzy :: LuaError e => Peeker e (Doc Text) peekDocFuzzy idx = liftLua (ltype idx) >>= \case TypeTable -> mconcat <$!> peekList peekDoc idx _ -> peekDoc idx handleMissingField :: LuaError e => ByteString -> LuaE e () handleMissingField key' = let key = UTF8.toString key' blockNames = map (fromString . show) . dataTypeConstrs . dataTypeOf $ HorizontalRule inlineNames = map (fromString . show) . dataTypeConstrs . dataTypeOf $ Space mtypeName = case () of _ | key `elem` blockNames -> Just "Block" _ | key `elem` inlineNames -> Just "Inline" _ -> Nothing in case mtypeName of Just typeName -> failLua $ "No render function for " <> typeName <> " value " <> "'" <> key <> "';\ndefine a function `Writer." <> typeName <> "." <> key <> "` that returns " <> "a string or Doc." _ -> pure () ================================================ FILE: pandoc-lua-engine/src/Text/Pandoc/Lua.hs ================================================ {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Running pandoc Lua filters. -} module Text.Pandoc.Lua ( -- * High-level functions applyFilter , loadCustom -- * Low-level functions , Global(..) , setGlobals , runLua , runLuaNoEnv -- * Engine , getEngine ) where import Text.Pandoc.Lua.Custom (loadCustom) import Text.Pandoc.Lua.Engine (getEngine, applyFilter) import Text.Pandoc.Lua.Global (Global (..), setGlobals) import Text.Pandoc.Lua.Run (runLua, runLuaNoEnv) import Text.Pandoc.Lua.Orphans () ================================================ FILE: pandoc-lua-engine/test/Tests/Lua/Module.hs ================================================ {- | Module : Tests.Lua.Module Copyright : © 2019-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Lua module tests -} module Tests.Lua.Module (tests) where import System.FilePath (()) import Test.Tasty (TestName, TestTree) import Test.Tasty.Lua (testLuaFile) import Tests.Lua (runLuaTest) tests :: [TestTree] tests = [ testPandocLua "pandoc" ("lua" "module" "pandoc.lua") , testPandocLua "pandoc.List" ("lua" "module" "pandoc-list.lua") , testPandocLua "pandoc.format" ("lua" "module" "pandoc-format.lua") , testPandocLua "pandoc.image" ("lua" "module" "pandoc-image.lua") , testPandocLua "pandoc.json" ("lua" "module" "pandoc-json.lua") , testPandocLua "pandoc.log" ("lua" "module" "pandoc-log.lua") , testPandocLua "pandoc.mediabag" ("lua" "module" "pandoc-mediabag.lua") , testPandocLua "pandoc.path" ("lua" "module" "pandoc-path.lua") , testPandocLua "pandoc.structure" ("lua" "module" "pandoc-structure.lua") , testPandocLua "pandoc.template" ("lua" "module" "pandoc-template.lua") , testPandocLua "pandoc.text" ("lua" "module" "pandoc-text.lua") , testPandocLua "pandoc.types" ("lua" "module" "pandoc-types.lua") , testPandocLua "pandoc.utils" ("lua" "module" "pandoc-utils.lua") , testPandocLua "globals" ("lua" "module" "globals.lua") ] testPandocLua :: TestName -> FilePath -> TestTree testPandocLua = testLuaFile runLuaTest ================================================ FILE: pandoc-lua-engine/test/Tests/Lua/Reader.hs ================================================ {-# LANGUAGE LambdaCase #-} {- | Module : Tests.Lua.Reader Copyright : © 2022-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Tests for custom Lua readers. -} module Tests.Lua.Reader (tests) where import Control.Arrow ((>>>)) import Data.Char (chr) import Data.Default (Default (def)) import Text.Pandoc.Class (runIOorExplode) import Text.Pandoc.Lua (loadCustom) import Text.Pandoc.Readers (Reader (ByteStringReader)) import Text.Pandoc.Scripting (customReader) import Test.Tasty (TestTree) import Test.Tasty.HUnit ((@?=), testCase) import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import qualified Text.Pandoc.Builder as B tests :: [TestTree] tests = [ testCase "read binary to code block" $ do input <- BL.readFile "bytestring.bin" doc <- runIOorExplode $ loadCustom "bytestring-reader.lua" >>= (customReader >>> \case Just (ByteStringReader f) -> f def input _ -> error "Expected a bytestring reader") let bytes = mconcat $ map (B.str . T.singleton . chr) [0..255] doc @?= B.doc (B.plain bytes) ] ================================================ FILE: pandoc-lua-engine/test/Tests/Lua/Writer.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Lua.Writer Copyright : © 2019-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Tests for custom Lua writers. -} module Tests.Lua.Writer (tests) where import Data.Default (Default (def)) import Data.Maybe (fromMaybe) import Text.Pandoc.Class (runIOorExplode, readFileStrict) import Text.Pandoc.Extensions (Extension (..), extensionsFromList) import Text.Pandoc.Format (ExtensionsDiff (..), FlavoredFormat (..), applyExtensionsDiff) import Text.Pandoc.Lua (loadCustom) import Text.Pandoc.Options (WriterOptions (..)) import Text.Pandoc.Readers (readNative) import Text.Pandoc.Scripting (CustomComponents (..)) import Text.Pandoc.Writers (Writer (ByteStringWriter, TextWriter)) import Test.Tasty (TestTree) import Test.Tasty.Golden (goldenVsString) import Test.Tasty.HUnit (testCase, (@?=)) import qualified Data.ByteString.Lazy as BL import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.UTF8 as UTF8 tests :: [TestTree] tests = [ goldenVsString "default testsuite" "writer.custom" (runIOorExplode $ do source <- UTF8.toText <$> readFileStrict "testsuite.native" doc <- readNative def source txt <- customWriter <$> loadCustom "sample.lua" >>= \case Just (TextWriter f) -> f def doc _ -> error "Expected a text writer" pure $ BL.fromStrict (UTF8.fromText txt)) , goldenVsString "tables testsuite" "tables.custom" (runIOorExplode $ do source <- UTF8.toText <$> readFileStrict "tables.native" doc <- readNative def source txt <- writeCustom "sample.lua" >>= \case (TextWriter f, _, _) -> f def doc _ -> error "Expected a text writer" pure $ BL.fromStrict (UTF8.fromText txt)) , goldenVsString "bytestring writer" "bytestring.bin" (runIOorExplode $ writeCustom "bytestring.lua" >>= \case (ByteStringWriter f, _, _) -> f def mempty _ -> error "Expected a bytestring writer") , goldenVsString "template" "writer-template.out.txt" (runIOorExplode $ do (_, _, template) <- writeCustom "writer-template.lua" pure . BL.fromStrict . UTF8.fromText $ fromMaybe "" template) , testCase "preset extensions" $ do let format = FlavoredFormat "extensions.lua" mempty result <- runIOorExplode $ writeCustom "extensions.lua" >>= \case (TextWriter write, extsConf, _) -> do exts <- applyExtensionsDiff extsConf format write def{writerExtensions = exts} (B.doc mempty) _ -> error "Expected a text writer" result @?= "smart extension is enabled;\ncitations extension is disabled\n" , testCase "modified extensions" $ do let ediff = ExtensionsDiff { extsToEnable = extensionsFromList [Ext_citations] , extsToDisable = mempty } let format = FlavoredFormat "extensions.lua" ediff result <- runIOorExplode $ writeCustom "extensions.lua" >>= \case (TextWriter write, extsConf, _) -> do exts <- applyExtensionsDiff extsConf format write def{writerExtensions = exts} (B.doc mempty) _ -> error "Expected a text writer" result @?= "smart extension is enabled;\ncitations extension is enabled\n" ] where writeCustom fp = do components <- loadCustom fp let exts = fromMaybe mempty (customExtensions components) case customWriter components of Nothing -> error "Expected a writer to be defined" Just w -> return (w, exts, customTemplate components) ================================================ FILE: pandoc-lua-engine/test/Tests/Lua.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {- | Module : Tests.Lua Copyright : © 2017-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Unit and integration tests for pandoc's Lua subsystem. -} module Tests.Lua ( runLuaTest, tests ) where import HsLua as Lua hiding (Operation (Div), error) import System.FilePath (()) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit ((@=?), Assertion, HasCallStack, assertEqual, testCase) import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder (bulletList, definitionList, displayMath, divWith, doc, doubleQuoted, emph, header, lineBlock, linebreak, math, orderedList, para, plain, rawBlock, singleQuoted, space, str, strong, HasMeta (setMeta)) import Text.Pandoc.Class ( runIOorExplode, setUserDataDir, setVerbosity ) import Text.Pandoc.Definition (Attr, Block (BlockQuote, Div, Para), Pandoc, Inline (Emph, Str), pandocTypesVersion) import Text.Pandoc.Error (PandocError (PandocLuaError)) import Text.Pandoc.Logging (Verbosity (ERROR)) import Text.Pandoc.Lua (Global (..), applyFilter, runLua, setGlobals) import Text.Pandoc.Options (def) import Text.Pandoc.Version (pandocVersionText) import qualified Control.Monad.Catch as Catch import qualified Data.Text as T import qualified Data.Text.Encoding as TE tests :: [TestTree] tests = [ testCase "macro expansion via filter" $ assertFilterConversion "a '{{helloworld}}' string is expanded" "strmacro.lua" (doc . para $ str "{{helloworld}}") (doc . para . emph $ str "Hello, World") , testCase "convert all plains to paras" $ assertFilterConversion "plains become para" "plain-to-para.lua" (doc $ bulletList [plain (str "alfa"), plain (str "bravo")]) (doc $ bulletList [para (str "alfa"), para (str "bravo")]) , testCase "convert display math to inline math" $ assertFilterConversion "display math becomes inline math" "math.lua" (doc $ para (displayMath "5+5")) (doc $ para (math "5+5")) , testCase "make hello world document" $ assertFilterConversion "Document contains 'Hello, World!'" "hello-world-doc.lua" (doc . para $ str "Hey!" <> linebreak <> str "What's up?") (doc . para $ str "Hello," <> space <> str "World!") , testCase "implicit doc filter" $ assertFilterConversion "Document contains 'Hello, World!'" "implicit-doc-filter.lua" (doc . plain $ linebreak) (doc . para $ str "Hello," <> space <> str "World!") , testCase "parse raw markdown blocks" $ assertFilterConversion "raw markdown block is converted" "markdown-reader.lua" (doc $ rawBlock "markdown" "*charly* **delta**") (doc . para $ emph "charly" <> space <> strong "delta") , testCase "allow shorthand functions for quote types" $ assertFilterConversion "single quoted becomes double quoted string" "single-to-double-quoted.lua" (doc . para . singleQuoted $ str "simple") (doc . para . doubleQuoted $ str "simple") , testCase "Count inlines via metatable catch-all" $ assertFilterConversion "filtering with metatable catch-all failed" "metatable-catch-all.lua" (doc . para $ "four words, three spaces") (doc . para $ str "7") , testCase "Count blocks via Block-specific catch-all" $ assertFilterConversion "filtering with Block catch-all failed" "block-count.lua" (doc $ para "one" <> para "two") (doc $ para "2") , testCase "Smart constructors" $ assertFilterConversion "smart constructors returned a wrong result" "smart-constructors.lua" (doc $ para "") (doc $ mconcat [ bulletList [para "Hello", para "World"] , definitionList [("foo", [para "placeholder"])] , lineBlock ["Moin", "Welt"] , orderedList [plain "one", plain "two"] ]) , testCase "Convert header upper case" $ assertFilterConversion "converting header to upper case failed" "uppercase-header.lua" (doc $ header 1 "les états-unis" <> para "text") (doc $ header 1 "LES ÉTATS-UNIS" <> para "text") , testCase "Attribute lists are convenient to use" $ let kv_before = [("one", "1"), ("two", "2"), ("three", "3")] kv_after = [("one", "eins"), ("three", "3"), ("five", "5")] in assertFilterConversion "Attr doesn't behave as expected" "attr-test.lua" (doc $ divWith ("", [], kv_before) (para "nil")) (doc $ divWith ("", [], kv_after) (para "nil")) , testCase "Filter list of inlines" $ assertFilterConversion "List of inlines" "inlines-filter.lua" (doc $ para ("Hello," <> linebreak <> "World! Wassup?")) (doc $ para "Hello, World! Wassup?") , testCase "Filter list of blocks" $ assertFilterConversion "List of blocks" "blocks-filter.lua" (doc $ para "one." <> para "two." <> para "three.") (doc $ plain "3") , testCase "Filter Meta" $ let setMetaBefore = setMeta "old" ("old" :: T.Text) . setMeta "bool" False setMetaAfter = setMeta "new" ("new" :: T.Text) . setMeta "bool" True in assertFilterConversion "Meta filtering" "meta.lua" (setMetaBefore . doc $ mempty) (setMetaAfter . doc $ mempty) , testCase "Script filename is set" $ assertFilterConversion "unexpected script name" "script-name.lua" (doc $ para "ignored") (doc $ para (str $ T.pack $ "lua" "script-name.lua")) , testCase "Pandoc version is set" . runLuaTest $ do Lua.getglobal "PANDOC_VERSION" Lua.liftIO . assertEqual "pandoc version is wrong" (TE.encodeUtf8 pandocVersionText) =<< Lua.tostring' Lua.top , testCase "Pandoc types version is set" . runLuaTest $ do Lua.getglobal "PANDOC_API_VERSION" Lua.liftIO . assertEqual "pandoc-types version is wrong" pandocTypesVersion =<< Lua.peek Lua.top , testCase "require file" $ assertFilterConversion "requiring file failed" "require-file.lua" (doc $ para "ignored") (doc $ para (str . T.pack $ "lua" "require-file.lua")) , testCase "Allow singleton inline in constructors" . runLuaTest $ do Lua.liftIO . assertEqual "Not the expected Emph" (Emph [Str "test"]) =<< do Lua.OK <- Lua.dostring "return pandoc.Emph" Lua.push @Inline (Str "test") Lua.call 1 1 Lua.peek @Inline top Lua.liftIO . assertEqual "Unexpected element" (Para [Str "test"]) =<< do Lua.getglobal' "pandoc.Para" Lua.pushString "test" Lua.call 1 1 Lua.peek @Block top Lua.liftIO . assertEqual "Unexptected element" (BlockQuote [Para [Str "foo"]]) =<< ( do Lua.getglobal' "pandoc.BlockQuote" Lua.push (Para [Str "foo"]) _ <- Lua.call 1 1 Lua.peek @Block Lua.top ) , testCase "Elements with Attr have `attr` accessor" . runLuaTest $ do Lua.push (Div ("hi", ["moin"], []) [Para [Str "ignored"]]) Lua.getfield Lua.top "attr" Lua.liftIO . assertEqual "no accessor" (("hi", ["moin"], []) :: Attr) =<< Lua.peek @Attr Lua.top , testCase "module `pandoc.system` is present" . runLuaTest $ do Lua.getglobal' "pandoc.system" ty <- Lua.ltype Lua.top Lua.liftIO $ assertEqual "module should be a table" Lua.TypeTable ty , testGroup "global modules" [ testCase "module 'lpeg' is loaded into a global" . runLuaTest $ do s <- Lua.dostring "assert(type(lpeg)=='table')" Lua.liftIO $ Lua.OK @=? s , testCase "module 're' is loaded into a global" . runLuaTest $ do s <- Lua.dostring "assert(type(re)=='table')" Lua.liftIO $ Lua.OK @=? s , testCase "module 'lpeg' is available via `require`" . runLuaTest $ do s <- Lua.dostring "package.path = ''; package.cpath = ''; require 'lpeg'" Lua.liftIO $ Lua.OK @=? s , testCase "module 're' is available via `require`" . runLuaTest $ do s <- Lua.dostring "package.path = ''; package.cpath = ''; require 're'" Lua.liftIO $ Lua.OK @=? s ] , testCase "informative error messages" . runLuaTest $ do Lua.pushboolean True -- Lua.newtable eitherPandoc <- Catch.try (peek @Pandoc Lua.top) case eitherPandoc of Left (PandocLuaError msg) -> do let expectedMsg = "Pandoc expected, got boolean\n" <> "\twhile retrieving Pandoc" Lua.liftIO $ assertEqual "unexpected error message" expectedMsg msg Left e -> error ("Expected a Lua error, but got " <> show e) Right _ -> error "Getting a Pandoc element from a bool should fail." ] assertFilterConversion :: String -> FilePath -> Pandoc -> Pandoc -> Assertion assertFilterConversion msg filterPath docIn expectedDoc = do actualDoc <- runIOorExplode $ do setUserDataDir (Just "../data") applyFilter def ["HTML"] ("lua" filterPath) docIn assertEqual msg expectedDoc actualDoc runLuaTest :: HasCallStack => Lua.LuaE PandocError a -> IO a runLuaTest op = runIOorExplode $ do -- Disable printing of warnings on stderr: some tests will generate -- warnings, we don't want to see those messages. setVerbosity ERROR res <- runLua $ do setGlobals [ PANDOC_WRITER_OPTIONS def ] op case res of Left e -> error (show e) Right x -> return x ================================================ FILE: pandoc-lua-engine/test/bytestring-reader.lua ================================================ function ByteStringReader (input, opts) local chars = pandoc.List{} for i = 1, #input do chars:insert(utf8.char(input:byte(i,i))) end return pandoc.Pandoc(pandoc.Plain(pandoc.Str(table.concat(chars)))) end ================================================ FILE: pandoc-lua-engine/test/bytestring.lua ================================================ function ByteStringWriter (doc, opts) local buffer = {} for i=0, 255 do table.insert(buffer, string.char(i)) end return table.concat(buffer, '') end ================================================ FILE: pandoc-lua-engine/test/extensions.lua ================================================ function Writer (doc, opts) local output = 'smart extension is %s;\ncitations extension is %s\n' local status = function (ext) return opts.extensions:includes(ext) and 'enabled' or 'disabled' end return output:format(status('smart'), status('citations')) end Extensions = { smart = true, citations = false, } ================================================ FILE: pandoc-lua-engine/test/lua/attr-test.lua ================================================ function Div (div) div.attributes.five = ("%d"):format(div.attributes.two + div.attributes.three) div.attributes.two = nil div.attributes.one = "eins" return div end ================================================ FILE: pandoc-lua-engine/test/lua/block-count.lua ================================================ local num_blocks = 0 function Block(el) num_blocks = num_blocks + 1 end function Pandoc(blocks, meta) return pandoc.Pandoc { pandoc.Para{pandoc.Str(num_blocks)} } end ================================================ FILE: pandoc-lua-engine/test/lua/blocks-filter.lua ================================================ function Blocks (blks) -- verify that this looks like a `pandoc.List` if not blks.find or not blks.map or not blks.filter then error("table doesn't seem to be an instance of pandoc.List") end -- return plain block containing the number of elements in the list return {pandoc.Plain {pandoc.Str(tostring(#blks))}} end ================================================ FILE: pandoc-lua-engine/test/lua/hello-world-doc.lua ================================================ return { { Pandoc = function(doc) local meta = {} local hello = { pandoc.Str "Hello,", pandoc.Space(), pandoc.Str "World!" } local blocks = { pandoc.Para(hello) } return pandoc.Pandoc(blocks, meta) end } } ================================================ FILE: pandoc-lua-engine/test/lua/implicit-doc-filter.lua ================================================ function Pandoc (doc) local meta = {} local hello = { pandoc.Str "Hello,", pandoc.Space(), pandoc.Str "World!" } local blocks = { pandoc.Para(hello) } return pandoc.Pandoc(blocks, meta) end ================================================ FILE: pandoc-lua-engine/test/lua/inlines-filter.lua ================================================ function isWorldAfterSpace (fst, snd) return fst and fst.t == 'LineBreak' and snd and snd.t == 'Str' and snd.text == 'World!' end function Inlines (inlns) -- verify that this looks like a `pandoc.List` if not inlns.find or not inlns.map or not inlns.filter then error("table doesn't seem to be an instance of pandoc.List") end -- Remove spaces before string "World" for i = #inlns-1,1,-1 do if isWorldAfterSpace(inlns[i], inlns[i+1]) then inlns[i] = pandoc.Space() end end return inlns end ================================================ FILE: pandoc-lua-engine/test/lua/markdown-reader.lua ================================================ return { { RawBlock = function (elem) if elem.format == "markdown" then local pd = pandoc.read(elem.text, "markdown") return pd.blocks[1] else return elem end end, } } ================================================ FILE: pandoc-lua-engine/test/lua/math.lua ================================================ return { { Math = function (elem) if elem.mathtype == "DisplayMath" then elem.mathtype = "InlineMath" end return elem end, } } ================================================ FILE: pandoc-lua-engine/test/lua/meta.lua ================================================ function Meta (meta) meta.old = nil meta.new = "new" meta.bool = (meta.bool == false) return meta end ================================================ FILE: pandoc-lua-engine/test/lua/metatable-catch-all.lua ================================================ local num_inlines = 0 function catch_all(el) if el.tag and pandoc.Inline.constructor[el.tag] then num_inlines = num_inlines + 1 end end function Pandoc(blocks, meta) return pandoc.Pandoc { pandoc.Para{pandoc.Str(num_inlines)} } end return { setmetatable( {Pandoc = Pandoc}, {__index = function(_) return catch_all end} ) } ================================================ FILE: pandoc-lua-engine/test/lua/module/globals.lua ================================================ local tasty = require 'tasty' local test = tasty.test_case local group = tasty.test_group local assert = tasty.assert -- These tests exist mainly to catch changes to the JSON representation of -- WriterOptions and its components. UPDATE THE DOCS if anything changes. return { group 'PANDOC_WRITER_OPTIONS' { test('chunk_template', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.chunk_template), 'string') end), test('cite_method', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.cite_method), 'string') end), test('columns', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.columns), 'number') end), test('dpi', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.dpi), 'number') end), test('email_obfuscation', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.email_obfuscation), 'string') end), test('split_level', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.split_level), 'number') end), test('epub_fonts', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.epub_fonts), 'table') end), test('epub_metadata', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.epub_metadata), 'nil') end), test('epub_subdirectory', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.epub_subdirectory), 'string') end), test('extensions', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.extensions), 'table') for _, v in ipairs(PANDOC_WRITER_OPTIONS.extensions) do assert.are_equal(type(v), 'string') end end), test('highlight_method', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.highlight_method), 'string') end), test('html_math_method', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.html_math_method), 'string') end), test('html_q_tags', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.html_q_tags), 'boolean') end), test('identifier_prefix', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.identifier_prefix), 'string') end), test('incremental', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.incremental), 'boolean') end), test('number_offset', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.number_offset), 'table') for _, v in ipairs(PANDOC_WRITER_OPTIONS.number_offset) do assert.are_equal(type(v), 'number') end end), test('number_sections', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.number_sections), 'boolean') end), test('prefer_ascii', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.prefer_ascii), 'boolean') end), test('reference_doc', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.reference_doc), 'nil') end), test('reference_links', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.reference_links), 'boolean') end), test('reference_location', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.reference_location), 'string') end), test('section_divs', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.section_divs), 'boolean') end), test('setext_headers', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.setext_headers), 'boolean') end), test('slide_level', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.slide_level), 'nil') end), test('tab_stop', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.tab_stop), 'number') end), test('table_of_contents', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.table_of_contents), 'boolean') end), test('toc_depth', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.toc_depth), 'number') end), test('top_level_division', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.top_level_division), 'string') end), test('variables', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.variables), 'table') end), test('wrap_text', function () assert.are_equal(type(PANDOC_WRITER_OPTIONS.wrap_text), 'string') end), }, group 'PANDOC_STATE' { test('is a table object', function () assert.are_equal(type(PANDOC_STATE), 'table') end), test('has property "input_files"', function () assert.are_equal(type(PANDOC_STATE.input_files), 'table') end), test('has optional property "output_file"', function () -- property may be nil if PANDOC_STATE.output_file then assert.are_equal(type(PANDOC_STATE.output_file), 'string') end end), test('has property "log"', function () assert.are_equal(type(PANDOC_STATE.log), 'table') end), test('has property "request_headers"', function () assert.are_equal(type(PANDOC_STATE.request_headers), 'table') end), test('has property "resource_path"', function () assert.are_equal(type(PANDOC_STATE.resource_path), 'table') end), test('has optional property "source_url"', function () if PANDOC_STATE.source_url then assert.are_equal(type(PANDOC_STATE.source_url), 'string') end end), test('has property "trace"', function () assert.are_equal(type(PANDOC_STATE.trace), 'boolean') end), test('has optional property "user_data_dir"', function () if PANDOC_STATE.user_data_dir then assert.are_equal(type(PANDOC_STATE.user_data_dir), 'string') end end), test('has property "verbosity"', function () assert.are_equal(type(PANDOC_STATE.verbosity), 'string') end), test('can be deleted without breaking PandocLua monad functions', function() local state = PANDOC_STATE PANDOC_STATE = nil assert.is_nil(pandoc.mediabag.lookup('does-not-exist')) PANDOC_STATE = state end), }, } ================================================ FILE: pandoc-lua-engine/test/lua/module/include.tex ================================================ included ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-format.lua ================================================ local tasty = require 'tasty' local test = tasty.test_case local group = tasty.test_group local assert = tasty.assert local format = require 'pandoc.format' return { group 'default_extensions' { test('docx', function () local docx_default_exts = { 'auto_identifiers', } assert.are_same(format.default_extensions('docx'), docx_default_exts) end), }, group 'all_extensions' { test('docx', function () local docx_default_exts = { 'ascii_identifiers', 'auto_identifiers', 'citations', 'east_asian_line_breaks', 'empty_paragraphs', 'gfm_auto_identifiers', 'native_numbering', 'styles', } assert.are_same(format.all_extensions('docx'), docx_default_exts) end), }, group 'extensions' { test('org', function () local org_default_exts = { ascii_identifiers = false, auto_identifiers = true, citations = true, east_asian_line_breaks = false, fancy_lists = false, gfm_auto_identifiers = false, smart = false, smart_quotes = false, special_strings = true, task_lists = true, } assert.are_same(format.extensions 'org', org_default_exts) end), }, } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-image.lua ================================================ -- -- Tests for the system module -- local image = require 'pandoc.image' local tasty = require 'tasty' local group = tasty.test_group local test = tasty.test_case local assert = tasty.assert local svg_image = [==[ test ]==] return { -- Check existence of static fields group 'static fields' { }, group 'size' { test('returns a table', function () local imgsize = { width = 70, height = 70, dpi_horz = 96, dpi_vert = 96, } assert.are_same(image.size(svg_image), imgsize) end), test('fails on faulty eps', function () assert.error_matches( function () image.size('%!PS EPSF') end, 'could not determine EPS size' ) end), test('fails if input is not an image', function () assert.error_matches( function () image.size('not an image') end, 'could not determine image type' ) end), test('respects the dpi setting', function () local imgsize = { width = 70, height = 70, dpi_horz = 300, dpi_vert = 300, } assert.are_same(image.size(svg_image, {dpi=300}), imgsize) end), }, group 'format' { test('SVG', function () assert.are_equal(image.format(svg_image), 'svg') end), test('returns nil if input is not an image', function () assert.is_nil(image.format('not an image')) end), }, } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-json.lua ================================================ -- -- Tests for the system module -- local pandoc = require 'pandoc' local json = require 'pandoc.json' local tasty = require 'tasty' local group = tasty.test_group local test = tasty.test_case local assert = tasty.assert return { -- Check existence of static fields group 'static fields' { test('null', function () assert.are_equal(type(json.null), 'userdata') end), }, group 'encode' { test('string', function () assert.are_equal(json.encode 'one\ntwo', '"one\\ntwo"') end), test('null', function () assert.are_equal(json.encode(json.null), 'null') end), test('number', function () assert.are_equal(json.encode(42), '42') end), test('object', function () assert.are_same(json.encode{a = 5}, '{"a":5}') end), test('object with metamethod', function () local obj = setmetatable( {title = 23}, { __tojson = function (_) return '"Nichts ist so wie es scheint"' end } ) assert.are_same(json.encode(obj), [["Nichts ist so wie es scheint"]]) end), test('pandoc.List', function () local list = pandoc.List {'foo', 'bar', 'baz'} assert.are_equal( json.encode(list), '["foo","bar","baz"]' ) end), test('Inline (Space)', function () assert.are_equal( json.encode(pandoc.Space()), '{"t":"Space"}' ) end), test('Block (HorizontalRule)', function () assert.are_equal( json.encode(pandoc.HorizontalRule()), '{"t":"HorizontalRule"}' ) end), test('Inlines list', function () assert.are_equal( json.encode(pandoc.Inlines{pandoc.Space()}), '[{"t":"Space"}]' ) end), test('Pandoc', function () assert.are_equal( type(json.encode(pandoc.Pandoc{'Hello from Lua!'})), 'string' ) end), test('Nested Inline', function () assert.are_equal( json.encode({spc = pandoc.Space()}), '{"spc":{"t":"Space"}}' ) end) }, group 'decode' { test('string', function () assert.are_equal(json.decode '"one\\ntwo"', 'one\ntwo') end), test('null', function () assert.are_equal(json.decode 'null', json.null) end), test('number', function () assert.are_equal(json.decode '42', 42) end), test('object', function () assert.are_same(json.decode '{"a":5}', {a = 5}) end), test('list of strings', function () assert.are_equal(json.decode '["foo", "bar"]', pandoc.List{"foo", "bar"}) end), test('Inline (Space)', function () assert.are_equal(json.decode '{"t":"Space"}', pandoc.Space()) end), test('Inline (Str)', function () assert.are_equal(json.decode '{"t":"Str", "c":"a"}', pandoc.Str 'a') end), test('disabled AST check', function () assert.are_same( json.decode('{"t":"Str", "c":"a"}', false), {t = 'Str', c = 'a'} ) end), test('Inlines list', function () assert.are_equal( json.decode '[{"t":"Space"}]', pandoc.Inlines{pandoc.Space()} ) end) }, } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-list.lua ================================================ local tasty = require 'tasty' local List = require 'pandoc.List' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { group 'List as function' { test('equivalent to List:new', function (x) local new = List:new {'ramen'} local list = List {'ramen'} assert.are_same(new, list) assert.are_equal(getmetatable(new), getmetatable(list)) end) }, group 'clone' { test('changing the clone does not affect original', function () local orig = List:new {23, 42} local copy = orig:clone() copy[1] = 5 assert.are_same({23, 42}, orig) assert.are_same({5, 42}, copy) end), test('result is a list', function () local orig = List:new {23, 42} assert.are_equal(List, getmetatable(orig:clone())) end), }, group 'extend' { test('extends list with other list', function () local primes = List:new {2, 3, 5, 7} primes:extend {11, 13, 17} assert.are_same({2, 3, 5, 7, 11, 13, 17}, primes) end) }, group 'filter' { test('keep elements for which property is truthy', function () local is_small_prime = function (x) return List.includes({2, 3, 5, 7}, x) end local numbers = List:new {4, 7, 2, 9, 5, 11} assert.are_same({7, 2, 5}, numbers:filter(is_small_prime)) end), }, group 'find' { test('returns element and index if found', function () local list = List:new {5, 23, 71} local elem, idx = list:find(71) assert.are_same(71, elem) assert.are_same(3, idx) end), test('respects start index', function () local list = List:new {19, 23, 29, 71} assert.are_equal(23, list:find(23, 1)) assert.are_equal(23, list:find(23, 2)) assert.is_nil(list:find(23, 3)) end), test('returns nil if element not found', function () assert.is_nil((List:new {18, 20, 22, 0, 24}):find('0')) end), }, group 'find_if' { test('returns element and index if found', function () local perm_prime = List:new {2, 3, 5, 7, 11, 13, 17, 31, 37, 71} local elem, idx = perm_prime:find_if(function (x) return x >= 10 end) assert.are_same(11, elem) assert.are_same(5, idx) end), test('returns nil if element not found', function () local is_null = function (n) return List.includes({23,35,46,59}, n) end assert.is_nil((List:new {18, 20, 22, 24, 27}):find_if(is_null)) end), }, group 'includes' { test('finds elements in list', function () local lst = List:new {'one', 'two', 'three'} assert.is_truthy(lst:includes('one')) assert.is_truthy(lst:includes('two')) assert.is_truthy(lst:includes('three')) assert.is_falsy(lst:includes('four')) end) }, group 'insert' { test('insert value at end of list.', function () local count_norsk = List {'en', 'to', 'tre'} count_norsk:insert('fire') assert.are_same({'en', 'to', 'tre', 'fire'}, count_norsk) end), test('insert value in the middle of list.', function () local count_norsk = List {'fem', 'syv'} count_norsk:insert(2, 'seks') assert.are_same({'fem', 'seks', 'syv'}, count_norsk) end) }, group 'map' { test('applies function to elements', function () local primes = List:new {2, 3, 5, 7} local squares = primes:map(function (x) return x^2 end) assert.are_same({4, 9, 25, 49}, squares) end), test('leaves original list unchanged', function () local primes = List:new {2, 3, 5, 7} local squares = primes:map(function (x) return x^2 end) assert.are_same({2, 3, 5, 7}, primes) end) }, group 'new' { test('make table usable as list', function () local test = List:new{1, 1, 2, 3, 5} assert.are_same( {1, 1, 4, 9, 25}, test:map(function (x) return x^2 end) ) end), test('return empty list if no argument is given', function () assert.are_same({}, List:new()) end), test('metatable of result is pandoc.List', function () local test = List:new{5} assert.are_equal(List, getmetatable(test)) end) }, group 'remove' { test('remove value at end of list.', function () local understand = List {'jeg', 'forstår', 'ikke'} local norsk_not = understand:remove() assert.are_same({'jeg', 'forstår'}, understand) assert.are_equal('ikke', norsk_not) end), test('remove value at beginning of list.', function () local count_norsk = List {'en', 'to', 'tre'} count_norsk:remove(1) assert.are_same({'to', 'tre'}, count_norsk) end) }, group 'sort' { test('sort numeric list', function () local numbers = List {71, 5, -1, 42, 23, 0, 1} numbers:sort() assert.are_same({-1, 0, 1, 5, 23, 42, 71}, numbers) end), test('reverse-sort numeric', function () local numbers = List {71, 5, -1, 42, 23, 0, 1} numbers:sort(function (x, y) return x > y end) assert.are_same({71, 42, 23, 5, 1, 0, -1}, numbers) end) }, } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-log.lua ================================================ -- -- Tests for the pandoc.log module -- -- ========================================= -- PLEASE BE CAREFUL WHEN UPDATING THE TESTS -- ========================================= -- -- Some tests here are very, very fragile, as their correctness depends on the -- correct line number in this file. local log = require 'pandoc.log' local json = require 'pandoc.json' local tasty = require 'tasty' local group = tasty.test_group local test = tasty.test_case local assert = tasty.assert return { group 'info' { test('is a function', function () assert.are_equal(type(log.info), 'function') end), test('reports a warning', function () log.info('info test') local msg = json.decode(json.encode(PANDOC_STATE.log:at(-1))) assert.are_equal(msg.message, 'info test') assert.are_equal(msg.type, 'ScriptingInfo') end), test('info includes the correct number', function () log.info('line number test') local msg = json.decode(json.encode(PANDOC_STATE.log:at(-1))) -- THIS NEEDS UPDATING if lines above are shifted. assert.are_equal(msg.line, 30) end), }, group 'warn' { test('is a function', function () assert.are_equal(type(log.warn), 'function') end), test('reports a warning', function () log.warn('testing') local msg = json.decode(json.encode(PANDOC_STATE.log:at(-1))) assert.are_equal(msg.message, 'testing') assert.are_equal(msg.type, 'ScriptingWarning') end), }, group 'silence' { test('prevents info from being logged', function () local current_messages = PANDOC_STATE.log log.silence(log.info, 'Just so you know') assert.are_same(#current_messages, #PANDOC_STATE.log) for i = 1, #current_messages do assert.are_equal( json.encode(current_messages[i]), json.encode(PANDOC_STATE.log[i]) ) end end), test('returns the messages raised by the called function', function () local msgs = log.silence(log.info, 'Just so you know') local msg = json.decode(json.encode(msgs[1])) assert.are_equal(msg.message, 'Just so you know') end), test('returns function application results as additional return values', function () local l, x, y = log.silence(function (a, b) return b, a + b end, 5, 8) assert.are_same(l, {}) assert.are_equal(x, 8) assert.are_equal(y, 13) end ) } } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-mediabag.lua ================================================ local tasty = require 'tasty' local test = tasty.test_case local group = tasty.test_group local assert = tasty.assert local mediabag = require 'pandoc.mediabag' return { group 'insert' { test('insert adds an item to the mediabag', function () local fp = "media/hello.txt" local mt = "text/plain" local contents = "Hello, World!" assert.are_same(mediabag.list(), {}) mediabag.insert(fp, mt, contents) assert.are_same( mediabag.list(), {{['path'] = fp, ['type'] = mt, ['length'] = 13}} ) mediabag.empty() -- clean up end), test('is idempotent', function () local fp = "media/hello.txt" local mt = "text/plain" local contents = "Hello, World!" mediabag.insert(fp, mt, contents) mediabag.insert(fp, mt, contents) assert.are_same( mediabag.list(), {{['path'] = fp, ['type'] = mt, ['length'] = 13}} ) mediabag.empty() -- clean up end), }, group 'delete' { test('removes an item', function () assert.are_same(mediabag.list(), {}) mediabag.insert('test.html', 'text/html', '') mediabag.insert('test.css', 'text/plain', 'aside { color: red; }') assert.are_equal(#mediabag.list(), 2) mediabag.delete('test.html') assert.are_same( mediabag.list(), {{['path'] = 'test.css', ['type'] = 'text/plain', ['length'] = 21}} ) mediabag.empty() -- clean up end), }, group 'fetch' { test('populates media bag', function () local filename = 'lua/module/sample.svg' local mime, contents = mediabag.fetch(filename) assert.are_equal(mime, 'image/svg+xml') assert.are_equal(contents:sub(1,5), 'Really?'}, ['test.css'] = {'text/plain', 'aside { color: red; }'}, ['test.js'] = {'application/javascript', 'alert("HI MOM!")'} } -- fill mediabag for name, v in pairs(input_items) do mediabag.insert(name, v[1], v[2]) end local seen_items = {} for fp, mt, c in mediabag.items() do seen_items[fp] = {mt, c} end assert.are_same(seen_items, input_items) mediabag.empty() -- clean up end) }, group 'lookup' { test('returns MIME type and contents', function () mediabag.insert('test.html', 'text/html', '') local mime, contents = mediabag.lookup('test.html') assert.are_equal(mime, 'text/html') assert.are_equal(contents, '') mediabag.empty() -- clean up end), }, group 'make_data_uri' { test('returns a data URI', function () local uri = mediabag.make_data_uri('text/plain', 'foo') assert.are_equal(uri:sub(1,5), 'data:') end), test('URI specifies the given MIME type', function () local mimetype = 'text/plain' local uri = mediabag.make_data_uri(mimetype, 'foo') assert.are_equal(uri:sub(6, 5 + #mimetype), mimetype) end), } } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-path.lua ================================================ local tasty = require 'tasty' local path = require 'pandoc.path' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { group 'path separator' { test('is string', function () assert.are_same(type(path.separator), 'string') end), test('is slash or backslash', function () assert.is_truthy(path.separator:match '^[/\\]$') end), }, group 'search path separator' { test('is string', function () assert.are_same(type(path.search_path_separator), 'string') end), test('is colon or semicolon', function () assert.is_truthy(path.search_path_separator:match '^[:;]$') end) }, group 'module' { test('check function existence', function () local functions = { 'directory', 'filename', 'is_absolute', 'is_relative', 'join', 'make_relative', 'normalize', 'split', 'split_extension', 'split_search_path', } for _, f in ipairs(functions) do assert.are_equal(type(path[f]), 'function') end end) } } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-structure.lua ================================================ local tasty = require 'tasty' local structure = require 'pandoc.structure' local path = require 'pandoc.path' local system = require 'pandoc.system' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { test('is table', function () assert.are_equal(type(structure), 'table') end), group 'make_sections' { test('sanity check', function () local blks = { pandoc.Header(1, {pandoc.Str 'First'}), pandoc.Header(2, {pandoc.Str 'Second'}), pandoc.Header(2, {pandoc.Str 'Third'}), } local opts = PANDOC_WRITER_OPTIONS local hblks = structure.make_sections(blks, opts) assert.are_equal('Div', hblks[1].t) assert.are_equal('Header', hblks[1].content[1].t) end), test('respects number_sections', function () local blks = { pandoc.Header(1, {pandoc.Str 'First'}), pandoc.Para 'Vestibulum convallis, lorem a tempus semper.' } local hblks = structure.make_sections(blks, {number_sections = true}) assert.are_equal('Div', hblks[1].t) assert.are_equal('Header', hblks[1].content[1].t) assert.are_equal('1', hblks[1].content[1].attributes['number']) end), test('respects base_level', function () local blks = { pandoc.Header(1, {pandoc.Str 'First'}), pandoc.Para 'Curabitur lacinia pulvinar nibh.', pandoc.Header(3, {pandoc.Str 'First'}), -- Skipping level 2 } local opts = { number_sections = true, base_level = 1 } local hblks = structure.make_sections(blks, opts) assert.are_equal('Div', hblks[1].t) assert.are_equal('Header', hblks[1].content[1].t) assert.are_equal('1', hblks[1].content[1].attributes['number']) assert.are_equal('1.0.1', hblks[1].content[3].attributes['number']) end) }, group 'split_into_chunks' { test('is function', function () assert.are_equal(type(structure.split_into_chunks), 'function') end), test('returns a chunked doc', function () assert.are_equal( pandoc.utils.type(structure.split_into_chunks(pandoc.Pandoc{})), 'ChunkedDoc' ) end), }, group 'table_of_contents' { test('is function', function () assert.are_equal(type(structure.table_of_contents), 'function') end), test('returns a bullet list', function () assert.are_equal( pandoc.utils.type(structure.table_of_contents{}), 'Block' ) assert.are_equal( structure.table_of_contents{}.t, 'BulletList' ) end), test('returns a toc for a list of blocks', function () local body = pandoc.Blocks{ pandoc.Header(1, 'First'), pandoc.Para('A sentence placed below the first structure.'), pandoc.Header(2, 'Subsection'), pandoc.Para('Mauris ac felis vel velit tristique imperdiet.'), pandoc.Header(1, 'Second'), pandoc.Para('Integer placerat tristique nisl.') } assert.are_equal( structure.table_of_contents(body), pandoc.BulletList{ {pandoc.Plain('First'), pandoc.BulletList{{pandoc.Plain 'Subsection'}} }, {pandoc.Plain('Second')} } ) end), test('returns a toc for a chunked doc', function () local doc = pandoc.Pandoc { pandoc.Header(1, 'First', {id='first'}), pandoc.Para('A sentence placed below the first structure.'), pandoc.Header(2, 'Subsection', {id='subsection'}), pandoc.Para('Mauris ac felis vel velit tristique imperdiet.'), pandoc.Header(1, 'Second', {id='second'}), pandoc.Para('Integer placerat tristique nisl.') } local chunked = structure.split_into_chunks(doc, {chunk_level = 2}) assert.are_equal( structure.table_of_contents(chunked), pandoc.BulletList{ {pandoc.Plain({pandoc.Link('First', 'chunk-001#first', '', {id='toc-first'})}), pandoc.BulletList{{pandoc.Plain({pandoc.Link('Subsection', 'chunk-002#subsection', '', {id='toc-subsection'})})}} }, {pandoc.Plain({pandoc.Link('Second', 'chunk-003#second', '', {id='toc-second'})})} } ) end), test('respects toc-depth option', function () local doc = pandoc.Pandoc { pandoc.Header(1, 'First', {id='first'}), pandoc.Para('A sentence placed below the first structure.'), pandoc.Header(2, 'Subsection', {id='subsection'}), pandoc.Para('Mauris ac felis vel velit tristique imperdiet.'), pandoc.Header(1, 'Second', {id='second'}), pandoc.Para('Integer placerat tristique nisl.') } local chunked = structure.split_into_chunks(doc) assert.are_equal( structure.table_of_contents(chunked, {toc_depth = 1}), pandoc.BulletList{ {pandoc.Plain({pandoc.Link('First', 'chunk-001#first', '', {id='toc-first'})})}, {pandoc.Plain({pandoc.Link('Second', 'chunk-002#second', '', {id='toc-second'})})} } ) end), }, group 'unique_identifier' { test('returns an identifier based on the input', function () local inlines = pandoc.Inlines{pandoc.Emph{'This'}, ' is nice'} local id = structure.unique_identifier(inlines) assert.are_equal('this-is-nice', id) end), test('respects the list of used IDs', function () local inlines = pandoc.Inlines('Hello, World!') local used = {['hello-world'] = true} local id = structure.unique_identifier(inlines, used) assert.are_equal('hello-world-1', id) end), test('defaults to pandoc Markdown identifiers', function () local inlines = pandoc.Inlines('Mr. Jones') local id = structure.unique_identifier(inlines, {}) assert.are_equal('mr.-jones', id) end), test('can generate gfm identifiers', function () local inlines = pandoc.Inlines('Mr. Jones') local exts = {'gfm_auto_identifiers'} local id = structure.unique_identifier(inlines, {}, exts) assert.are_equal('mr-jones', id) end), } } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-template.lua ================================================ local tasty = require 'tasty' local pandoc = require 'pandoc' local template = require 'pandoc.template' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { test('is table', function () assert.are_equal(type(template), 'table') end), group 'default' { test('is function', function () assert.are_equal(type(template.default), 'function') end), test('returns a string for known format', function () assert.are_equal( pandoc.utils.type(template.default 'json'), 'string' ) assert.are_equal( pandoc.utils.type(template.default 'markdown'), 'string' ) end), test('fails on unknown format', function () local success, msg = pcall(function () return pandoc.utils.type(template.default 'nosuchformat') end) assert.is_falsy(success) end), }, group 'get' { test('is function', function () assert.are_equal(type(template.get), 'function') end), test('searches the template data directory', function () assert.are_equal( template.default 'html5', template.get 'default.html5' ) end), test('fails on non-existent file', function () local success, msg = pcall(function () return pandoc.utils.type(template.get 'nosuchfile.nope') end) assert.is_falsy(success) end), }, group 'compile' { test('is function', function () assert.are_equal(type(template.compile), 'function') end), test('returns a Template', function () assert.are_equal( pandoc.utils.type(template.compile('$title$')), 'Template' ) end), test('returns a Template', function () local templ_path = pandoc.path.join{'lua', 'module', 'default.test'} assert.are_equal( pandoc.utils.type(template.compile('${ partial() }', templ_path)), 'Template' ) end), test('fails if template has non-existing partial', function () assert.error_matches( function () return template.compile('${ nosuchpartial() }') end, 'Could not find data file' ) end), test('works with default template that uses partials', function () local jats_template = template.default 'jats' assert.are_equal(type(jats_template), 'string') assert.are_equal( pandoc.utils.type(template.compile(jats_template)), 'Template' ) end), }, group 'apply' { test('is function', function () assert.are_equal(type(template.apply), 'function') end), test('returns a Doc value', function () local tmpl = template.compile('placeholder') assert.are_equal( pandoc.utils.type(template.apply(tmpl, {})), 'Doc' ) end), test('applies the given context', function () local tmpl = template.compile('song: $title$') local context = {title = 'Along Comes Mary'} assert.are_equal( template.apply(tmpl, context):render(), 'song: Along Comes Mary' ) end), test('accepts string as template', function () local context = {number = '2'} assert.are_equal( template.apply('Song $number$', context):render(), 'Song 2' ) end) }, } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-text.lua ================================================ -- -- Tests for the pandoc.text module -- local text = require 'pandoc.text' local tasty = require 'tasty' local group = tasty.test_group local test = tasty.test_case local assert = tasty.assert assert.is_function = function (x) assert.are_equal(type(x), 'function') end -- We rely mostly on the tests in the `hslua-module-text` module. The -- only thing we need to test is whether `pandoc.text` is available, -- whether all functions are defined, and whether `require 'text'` works -- (for backwards compatibility). return { group 'module' { test('is table', function () assert.are_equal(type(text), 'table') end), test('can be required as "text"', function () assert.are_equal(require 'text', require 'pandoc.text') end) }, group 'functions' { test('fromencoding', function () assert.is_function(text.fromencoding) end), test('len', function () assert.is_function(text.len) end), test('lower', function () assert.is_function(text.lower) end), test('reverse', function () assert.is_function(text.reverse) end), test('sub', function () assert.is_function(text.sub) end), group 'subscript' { test('is a function', function () assert.is_function(text.subscript) end), test('converts a string to Unicode subscript chars', function () assert.are_equal(text.subscript '1+(9-7)', '₁₊₍₉₋₇₎') end), test('returns nil if the input contains unsupported chars', function () assert.is_nil(text.subscript '00ä') end), }, group 'superscript' { test('is a function', function () assert.is_function(text.superscript) end), test('converts a string to Unicode superscript chars', function () assert.are_equal(text.superscript '1+(9-7)', '¹⁺⁽⁹⁻⁷⁾') end), test('returns nil if the input contains unsupported chars', function () assert.is_nil(text.superscript '00ä') end), }, test('toencoding', function () assert.is_function(text.toencoding) end), test('upper', function () assert.is_function(text.upper) end), }, } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-types.lua ================================================ local tasty = require 'tasty' local system = require 'pandoc.system' local types = require 'pandoc.types' local Sources = types.Sources local Version = types.Version local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { group 'Sources' { group 'constructor' { test('has type `table`', function () assert.are_same(type(Sources ""), 'table') end), test('accepts a single string', function () assert.are_same(type(Sources('a')), 'table') end), test('accepts a list of strings', function () local srcs = Sources{'first text\n', 'second text\n'} assert.are_equal(srcs[1].name, '') assert.are_equal(srcs[1].text, 'first text\n') end), test('accepts list of filepath/content tuples', function () local srcs = Sources{{'test.txt', 'semi-random content'}} assert.are_equal(srcs[1].name, 'test.txt') assert.are_equal(srcs[1].text, 'semi-random content\n') end), test('accepts lists of Source-like tables', function () local srcs = Sources{{name = 'test.txt', text = 'semi-random content'}} assert.are_equal(srcs[1].name, 'test.txt') assert.are_equal(srcs[1].text, 'semi-random content\n') end), }, }, group 'Version' { group 'constructor' { test('has type `userdata`', function () assert.are_same(type(Version {2}), 'userdata') end), test('accepts list of integers', function () assert.are_same(type(Version {2, 7, 3}), 'userdata') end), test('accepts a single integer', function () assert.are_same(Version(5), Version {5}) end), test('accepts version as string', function () assert.are_same( Version '4.45.1', Version {4, 45, 1} ) end), test('non-version string is rejected', function () local success, msg = pcall(function () Version '11friends' end) assert.is_falsy(success) assert.is_truthy(tostring(msg):match('11friends')) end) }, group 'comparison' { test('smaller (equal) than', function () assert.is_truthy(Version {2, 58, 3} < Version {2, 58, 4}) assert.is_falsy(Version {2, 60, 1} < Version {2, 59, 2}) assert.is_truthy(Version {0, 14, 3} < Version {0, 14, 3, 1}) assert.is_truthy(Version {3, 58, 3} <= Version {4}) assert.is_truthy(Version {0, 14, 3} <= Version {0, 14, 3, 1}) end), test('larger (equal) than', function () assert.is_truthy(Version{2,58,3} > Version {2, 57, 4}) assert.is_truthy(Version{2,58,3} > Version {2, 58, 2}) assert.is_truthy(Version {0, 8} >= Version {0, 8}) assert.is_falsy(Version {0, 8} >= Version {0, 8, 2}) end), test('equality', function () assert.is_truthy(Version '8.8', Version {8, 8}) end), test('second argument can be a version string', function () assert.is_truthy(Version '8' < '9.1') assert.is_falsy(Version '8.8' < '8.7') end), }, group 'conversion to string' { test('converting from and to string is a noop', function () local version_string = '1.19.4' assert.are_equal(tostring(Version(version_string)), version_string) end) }, group 'convenience functions' { test('throws error if version is too old', function () local actual = Version {2, 8} local expected = Version {2, 9} assert.error_matches( function () actual:must_be_at_least(expected) end, 'expected version 2.9 or newer, got 2.8' ) end), test('does nothing if expected version is older than actual', function () local actual = Version '2.9' local expected = Version '2.8' actual:must_be_at_least(expected) end), test('does nothing if expected version equals to actual', function () local actual = Version '2.8' local expected = Version '2.8' actual:must_be_at_least(expected) end) } } } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc-utils.lua ================================================ local tasty = require 'tasty' local pandoc = require 'pandoc' local utils = require 'pandoc.utils' local io = require 'io' local assert = tasty.assert local test = tasty.test_case local group = tasty.test_group return { group 'blocks_to_inlines' { test('default separator', function () local blocks = { pandoc.Para { pandoc.Str 'Paragraph1' }, pandoc.Para { pandoc.Emph { pandoc.Str 'Paragraph2' } } } local expected = { pandoc.Str 'Paragraph1', pandoc.LineBreak(), pandoc.Emph { pandoc.Str 'Paragraph2' } } assert.are_same( expected, utils.blocks_to_inlines(blocks) ) end), test('custom separator', function () local blocks = { pandoc.Para{ pandoc.Str 'Paragraph1' }, pandoc.Para{ pandoc.Emph 'Paragraph2' } } local expected = { pandoc.Str 'Paragraph1', pandoc.LineBreak(), pandoc.Emph { pandoc.Str 'Paragraph2' } } assert.are_same( expected, utils.blocks_to_inlines(blocks, { pandoc.LineBreak() }) ) end) }, group 'equals' { test('compares Pandoc elements', function () assert.is_truthy( utils.equals(pandoc.Pandoc{'foo'}, pandoc.Pandoc{'foo'}) ) end), test('compares Block elements', function () assert.is_truthy( utils.equals(pandoc.Plain{'foo'}, pandoc.Plain{'foo'}) ) assert.is_falsy( utils.equals(pandoc.Para{'foo'}, pandoc.Plain{'foo'}) ) end), test('compares Inline elements', function () assert.is_truthy( utils.equals(pandoc.Emph{'foo'}, pandoc.Emph{'foo'}) ) assert.is_falsy( utils.equals(pandoc.Emph{'foo'}, pandoc.Strong{'foo'}) ) end), test('compares Inline with Block elements', function () assert.is_falsy( utils.equals(pandoc.Emph{'foo'}, pandoc.Plain{'foo'}) ) assert.is_falsy( utils.equals(pandoc.Para{'foo'}, pandoc.Strong{'foo'}) ) end), test('compares Pandoc with Block elements', function () assert.is_falsy( utils.equals(pandoc.Pandoc{'foo'}, pandoc.Plain{'foo'}) ) assert.is_falsy( utils.equals(pandoc.Para{'foo'}, pandoc.Pandoc{'foo'}) ) end), }, group 'make_sections' { test('sanity check', function () local blks = { pandoc.Header(1, {pandoc.Str 'First'}), pandoc.Header(2, {pandoc.Str 'Second'}), pandoc.Header(2, {pandoc.Str 'Third'}), } local hblks = utils.make_sections(true, 1, blks) assert.are_equal('Div', hblks[1].t) assert.are_equal('Header', hblks[1].content[1].t) assert.are_equal('1', hblks[1].content[1].attributes['number']) end) }, group 'normalize_date' { test('09 Nov 1989', function () assert.are_equal('1989-11-09', utils.normalize_date '09 Nov 1989') end), test('12/31/2017', function () assert.are_equal('2017-12-31', utils.normalize_date '12/31/2017') end), }, group 'references' { test('gets references from doc', function () local ref = { ['author'] = { {given = 'Max', family = 'Mustermann'} }, ['container-title'] = pandoc.Inlines('JOSS'), ['id'] = 'test', ['issued'] = {['date-parts'] = {{2021}}}, ['title'] = pandoc.Inlines{ pandoc.Quoted('DoubleQuote', 'Interesting'), pandoc.Space(), 'work' }, ['type'] = 'article-journal', } local nocite = pandoc.Cite( '@test', {pandoc.Citation('test', 'NormalCitation')} ) local doc = pandoc.Pandoc({}, {nocite = nocite, references = {ref}}) assert.are_same({ref}, pandoc.utils.references(doc)) end) }, group 'run_lua_filter' { test('runs a filter', function () local doc = pandoc.Pandoc("indivisible words") pandoc.system.with_temporary_directory('lua-filter', function (dir) local filter_path = pandoc.path.join{dir, 'test.lua'} local filter = 'function Space() return " " end' local fh = io.open(filter_path, 'wb') fh:write(filter) fh:close() assert.are_equal( utils.run_lua_filter(doc, filter_path), pandoc.Pandoc( pandoc.Plain(pandoc.Inlines{"indivisible", " ", "words"}) ) ) end) end), test("doesn't change the local environment by default", function () pandoc.system.with_temporary_directory('lua-filter', function (dir) local filter_path = pandoc.path.join{dir, 'test.lua'} local foo local filter = 'foo = 42' local fh = io.open(filter_path, 'wb') fh:write(filter) fh:close() utils.run_lua_filter(pandoc.Pandoc{}, filter_path) assert.is_nil(foo) end) end), test("accepts an environment in which the filter is executed", function () pandoc.system.with_temporary_directory('lua-filter', function (dir) local filter_path = pandoc.path.join{dir, 'test.lua'} local filter = 'foo = 42' local fh = io.open(filter_path, 'wb') fh:write(filter) fh:close() utils.run_lua_filter(pandoc.Pandoc{}, filter_path, _ENV) assert.are_equal(_ENV.foo, 42) end) end) }, group 'sha1' { test('hashing', function () local ref_hash = '0a0a9f2a6772942557ab5355d76af442f8f65e01' assert.are_equal(ref_hash, utils.sha1 'Hello, World!') end) }, group 'stringify' { test('Inline', function () local inline = pandoc.Emph{ pandoc.Str 'Cogito', pandoc.Space(), pandoc.Str 'ergo', pandoc.Space(), pandoc.Str 'sum.', } assert.are_equal('Cogito ergo sum.', utils.stringify(inline)) end), test('Block', function () local block = pandoc.Para{ pandoc.Str 'Make', pandoc.Space(), pandoc.Str 'it', pandoc.Space(), pandoc.Str 'so.', } assert.are_equal('Make it so.', utils.stringify(block)) end), test('boolean', function () assert.are_equal('true', utils.stringify(true)) assert.are_equal('false', utils.stringify(false)) end), test('number', function () assert.are_equal('5', utils.stringify(5)) assert.are_equal('23.23', utils.stringify(23.23)) end), test('Attr', function () local attr = pandoc.Attr('foo', {'bar'}, {a = 'b'}) assert.are_equal('', utils.stringify(attr)) end), test('List', function () local list = pandoc.List{pandoc.Str 'a', pandoc.Blocks('b')} assert.are_equal('ab', utils.stringify(list)) end), test('Blocks', function () local blocks = pandoc.Blocks{pandoc.Para 'a', pandoc.Header(1, 'b')} assert.are_equal('ab', utils.stringify(blocks)) end), test('Inlines', function () local inlines = pandoc.Inlines{pandoc.Str 'a', pandoc.Subscript('b')} assert.are_equal('ab', utils.stringify(inlines)) end), test('Caption', function () local capt = pandoc.Caption(pandoc.Para{pandoc.Str 'a', pandoc.Emph('b')}) assert.are_equal('ab', utils.stringify(capt)) end), test('Cell', function () local cell = pandoc.Cell(pandoc.Para{pandoc.Str 'a', pandoc.Emph('b')}) assert.are_equal('ab', utils.stringify(cell)) end), test('TableFoot', function () local tf = pandoc.TableFoot{pandoc.Row{pandoc.Cell{pandoc.Plain "x y"}}} assert.are_equal('x y', utils.stringify(tf)) end), test('TableHead', function () local th = pandoc.TableHead{pandoc.Row{pandoc.Cell{pandoc.Plain "head1"}}} assert.are_equal('head1', utils.stringify(th)) end), test('Meta', function () local meta = pandoc.Meta{ a = pandoc.Inlines 'funny and ', b = 'good movie', c = pandoc.List{pandoc.Inlines{pandoc.Str '!'}} } assert.are_equal('funny and good movie!', utils.stringify(meta)) end), }, group 'to_roman_numeral' { test('convertes number', function () assert.are_equal('MDCCCLXXXVIII', utils.to_roman_numeral(1888)) end), test('fails on non-convertible argument', function () assert.is_falsy(pcall(utils.to_roman_numeral, 'not a number')) end) }, group 'type' { test('nil', function () assert.are_equal(utils.type(nil), 'nil') end), test('boolean', function () assert.are_equal(utils.type(true), 'boolean') assert.are_equal(utils.type(false), 'boolean') end), test('number', function () assert.are_equal(utils.type(5), 'number') assert.are_equal(utils.type(-3.02), 'number') end), test('string', function () assert.are_equal(utils.type(''), 'string') assert.are_equal(utils.type('asdf'), 'string') end), test('plain table', function () assert.are_equal(utils.type({}), 'table') end), test('List', function () assert.are_equal(utils.type(pandoc.List{}), 'List') end), test('Inline', function () assert.are_equal(utils.type(pandoc.Str 'a'), 'Inline') assert.are_equal(utils.type(pandoc.Emph 'emphasized'), 'Inline') end), test('Inlines', function () assert.are_equal(utils.type(pandoc.Inlines{pandoc.Str 'a'}), 'Inlines') assert.are_equal(utils.type(pandoc.Inlines{pandoc.Emph 'b'}), 'Inlines') end), test('Blocks', function () assert.are_equal(utils.type(pandoc.Para 'a'), 'Block') assert.are_equal(utils.type(pandoc.CodeBlock 'true'), 'Block') end), test('Inlines', function () assert.are_equal(utils.type(pandoc.Blocks{'a'}), 'Blocks') assert.are_equal(utils.type(pandoc.Blocks{pandoc.CodeBlock 'b'}), 'Blocks') end), }, group 'to_simple_table' { test('convertes Table', function () function simple_cell (blocks) return { attr = pandoc.Attr(), alignment = "AlignDefault", contents = blocks, col_span = 1, row_span = 1, } end local tbl = pandoc.Table( {long = {pandoc.Plain { pandoc.Str "the", pandoc.Space(), pandoc.Str "caption"}}}, {{pandoc.AlignDefault, nil}}, pandoc.TableHead{pandoc.Row{simple_cell{pandoc.Plain "head1"}}}, {{ attr = pandoc.Attr(), body = {pandoc.Row{simple_cell{pandoc.Plain "cell1"}}}, head = {}, row_head_columns = 0 }}, pandoc.TableFoot(), pandoc.Attr() ) local stbl = utils.to_simple_table(tbl) assert.are_equal('SimpleTable', stbl.t) assert.are_equal('head1', utils.stringify(stbl.headers[1])) assert.are_equal('cell1', utils.stringify(stbl.rows[1][1])) assert.are_equal('the caption', utils.stringify(pandoc.Span(stbl.caption))) end), test('fails on para', function () assert.is_falsy(pcall(utils.to_simple_table, pandoc.Para "nope")) end), }, group 'from_simple_table' { test('converts SimpleTable to Table', function () local caption = {pandoc.Str "Overview"} local aligns = {pandoc.AlignDefault, pandoc.AlignDefault} local widths = {0, 0} -- let pandoc determine col widths local headers = { {pandoc.Plain "Language"}, {pandoc.Plain "Typing"} } local rows = { {{pandoc.Plain "Haskell"}, {pandoc.Plain "static"}}, {{pandoc.Plain "Lua"}, {pandoc.Plain "Dynamic"}}, } local simple_table = pandoc.SimpleTable( caption, aligns, widths, headers, rows ) local tbl = utils.from_simple_table(simple_table) assert.are_equal("Table", tbl.t) assert.are_same( {pandoc.Plain(caption)}, tbl.caption.long ) -- reversible assert.are_same(simple_table, utils.to_simple_table(tbl)) end), test('empty caption', function () local simple_table = pandoc.SimpleTable( {}, {pandoc.AlignDefault}, {0}, {{pandoc.Plain 'a'}}, {{{pandoc.Plain 'b'}}} ) local tbl = utils.from_simple_table(simple_table) assert.are_equal( pandoc.Blocks{}, tbl.caption.long ) assert.is_nil(tbl.caption.short) end), test('empty body', function () local simple_table = pandoc.SimpleTable( pandoc.Inlines('a nice caption'), {pandoc.AlignDefault}, {0}, {{pandoc.Plain 'a'}}, {} ) local tbl = utils.from_simple_table(simple_table) tbl.bodies:map(print) assert.are_same(pandoc.List(), tbl.bodies) end), } } ================================================ FILE: pandoc-lua-engine/test/lua/module/pandoc.lua ================================================ local tasty = require 'tasty' local test = tasty.test_case local group = tasty.test_group local assert = tasty.assert function os_is_windows () return package.config:sub(1,1) == '\\' end -- Constructor behavior is tested in the hslua-pandoc-types module, so -- we just make sure the functions are present. return { group 'Constructors' { group 'Misc' { test('pandoc.Attr is a function', function () assert.are_equal(type(pandoc.Attr), 'function') end), test('pandoc.AttributeList is a function', function () assert.are_equal(type(pandoc.AttributeList), 'function') end), test('pandoc.Blocks is a function', function () assert.are_equal(type(pandoc.Blocks), 'function') end), test('pandoc.Citation is a function', function () assert.are_equal(type(pandoc.Citation), 'function') end), test('pandoc.Inlines is a function', function () assert.are_equal(type(pandoc.Inlines), 'function') end), test('pandoc.SimpleTable is a function', function () assert.are_equal(type(pandoc.SimpleTable), 'function') end), test('pandoc.Meta is a function', function () assert.are_equal(type(pandoc.Meta), 'function') end), test('pandoc.Pandoc is a function', function () assert.are_equal(type(pandoc.Pandoc), 'function') end), }, group "Inline elements" { test('pandoc.AttributeList is a function', function () assert.are_equal(type(pandoc.Cite), 'function') end), test('pandoc.AttributeList is a function', function () assert.are_equal(type(pandoc.Code), 'function') end), test('pandoc.Emph is a function', function () assert.are_equal(type(pandoc.Emph), 'function') end), test('pandoc.Image is a function', function () assert.are_equal(type(pandoc.Image), 'function') end), test('pandoc.Link is a function', function () assert.are_equal(type(pandoc.Link), 'function') end), test('pandoc.Math is a function', function () assert.are_equal(type(pandoc.Math), 'function') end), test('pandoc.Note is a function', function () assert.are_equal(type(pandoc.Note), 'function') end), test('pandoc.Quoted is a function', function () assert.are_equal(type(pandoc.Quoted), 'function') end), test('pandoc.SmallCaps is a function', function () assert.are_equal(type(pandoc.SmallCaps), 'function') end), test('pandoc.SoftBreak is a function', function () assert.are_equal(type(pandoc.SoftBreak), 'function') end), test('pandoc.Span is a function', function () assert.are_equal(type(pandoc.Span), 'function') end), test('pandoc.Str is a function', function () assert.are_equal(type(pandoc.Str), 'function') end), test('pandoc.Strikeout is a function', function () assert.are_equal(type(pandoc.Strikeout), 'function') end), test('pandoc.Strong is a function', function () assert.are_equal(type(pandoc.Strong), 'function') end), test('pandoc.Subscript is a function', function () assert.are_equal(type(pandoc.Subscript), 'function') end), test('pandoc.Superscript is a function', function () assert.are_equal(type(pandoc.Superscript), 'function') end), test('pandoc.Underline is a function', function () assert.are_equal(type(pandoc.Underline), 'function') end), }, group "Block elements" { test('pandoc.BlockQuote is a function', function () assert.are_equal(type(pandoc.BlockQuote), 'function') end), test('pandoc.BulletList is a function', function () assert.are_equal(type(pandoc.BulletList), 'function') end), test('pandoc.CodeBlock is a function', function () assert.are_equal(type(pandoc.CodeBlock), 'function') end), test('pandoc.DefinitionList is a function', function () assert.are_equal(type(pandoc.DefinitionList), 'function') end), test('pandoc.Div is a function', function () assert.are_equal(type(pandoc.Div), 'function') end), test('pandoc.Header is a function', function () assert.are_equal(type(pandoc.Header), 'function') end), test('pandoc.LineBlock is a function', function () assert.are_equal(type(pandoc.LineBlock), 'function') end), test('pandoc.Figure is a function', function () assert.are_equal(type(pandoc.Figure), 'function') end), test('pandoc.OrderedList is a function', function () assert.are_equal(type(pandoc.OrderedList), 'function') end), test('pandoc.Para is a function', function () assert.are_equal(type(pandoc.Para), 'function') end), test('pandoc.Plain is a function', function () assert.are_equal(type(pandoc.Plain), 'function') end), test('pandoc.RawBlock is a function', function () assert.are_equal(type(pandoc.Plain), 'function') end), test('pandoc.Table is a function', function () assert.are_equal(type(pandoc.Table), 'function') end), } }, group 'MetaValue elements' { test('MetaList elements behave like lists', function () local metalist = pandoc.MetaList{} assert.are_equal(type(metalist.insert), 'function') assert.are_equal(type(metalist.remove), 'function') end), test('`tag` is an alias for `t``', function () assert.are_equal((pandoc.MetaList{}).tag, (pandoc.MetaList{}).t) assert.are_equal((pandoc.MetaMap{}).tag, (pandoc.MetaMap{}).t) assert.are_equal((pandoc.MetaInlines{}).tag, (pandoc.MetaInlines{}).t) assert.are_equal((pandoc.MetaBlocks{}).tag, (pandoc.MetaBlocks{}).t) end), }, group 'Meta' { test('inline list is treated as MetaInlines', function () local meta = pandoc.Pandoc({}, {test = {pandoc.Emph 'check'}}).meta assert.are_same(meta.test, {pandoc.Emph{pandoc.Str 'check'}}) end), test('inline element is treated as MetaInlines singleton', function () local meta = pandoc.Pandoc({}, {test = pandoc.Emph 'check'}).meta assert.are_same(meta.test, {pandoc.Emph{pandoc.Str 'check'}}) end), test('block list is treated as MetaBlocks', function () local meta = pandoc.Pandoc({}, {test = {pandoc.Plain 'check'}}).meta assert.are_same(meta.test, {pandoc.Plain{pandoc.Str 'check'}}) end), test('block element is treated as MetaBlocks singleton', function () local meta = pandoc.Pandoc({}, {test = pandoc.Plain 'check'}).meta assert.are_same(meta.test, {pandoc.Plain{pandoc.Str 'check'}}) end), }, group 'Pandoc' { test('normalize', function () local doc = pandoc.Pandoc({{'a', pandoc.Space(), pandoc.Space(), 'b'}}) local normalized = pandoc.Pandoc({{'a', pandoc.Space(), 'b'}}) assert.are_equal(normalized, doc:normalize()) end), }, group 'Other types' { group 'ReaderOptions' { test('returns a userdata value', function () local opts = pandoc.ReaderOptions {} assert.are_equal(type(opts), 'userdata') end), test('can construct from table', function () local opts = pandoc.ReaderOptions {columns = 66} assert.are_equal(opts.columns, 66) end), test('can construct from other ReaderOptions value', function () local orig = pandoc.ReaderOptions{columns = 65} local copy = pandoc.ReaderOptions(orig) for k, v in pairs(orig) do assert.are_same(copy[k], v) end assert.are_equal(copy.columns, 65) end), }, }, group 'clone' { test('clones Attr', function () local attr = pandoc.Attr('test', {'my-class'}, {foo = 'bar'}) local cloned = attr:clone() attr.identifier = '' attr.classes = {} attr.attributes = {} assert.are_same(cloned.identifier, 'test') assert.are_same(cloned.classes, {'my-class'}) assert.are_same(cloned.attributes.foo, 'bar') end), test('clones ListAttributes', function () local la = pandoc.ListAttributes(2, pandoc.DefaultStyle, pandoc.Period) local cloned = la:clone() la.start = 9 assert.are_same(cloned.start, 2) end), test('clones Para', function () local para = pandoc.Para {pandoc.Str 'Hello'} local cloned = para:clone() para.content[1].text = 'bye' assert.are_same(cloned, pandoc.Para {pandoc.Str 'Hello'}) end), test('clones Str', function () local str = pandoc.Str 'Hello' local cloned = str:clone() str.text = 'bye' assert.are_same(cloned.text, 'Hello') end), test('clones Citation', function () local cite = pandoc.Citation('leibniz', pandoc.AuthorInText) local cloned = cite:clone() cite.id = 'newton' assert.are_same(cloned.id, 'leibniz') assert.are_same(cite.id, 'newton') assert.are_same(cite.mode, cloned.mode) end), }, group 'pipe' { test('external string processing', function () if os_is_windows() then local pipe_result = pandoc.pipe('find', {'hi'}, 'hi') assert.are_equal('hi', pipe_result:match '%a+') else local pipe_result = pandoc.pipe('tr', {'a', 'b'}, 'abc') assert.are_equal('bbc', pipe_result:match '%a+') end end), test('failing pipe', function () if os_is_windows() then local success, err = pcall(pandoc.pipe, 'find', {'/a'}, 'hi') assert.is_falsy(success) assert.are_equal('find', err.command) assert.is_truthy(err.error_code ~= 0) else local success, err = pcall(pandoc.pipe, 'false', {}, 'abc') assert.is_falsy(success) assert.are_equal('false', err.command) assert.are_equal(1, err.error_code) assert.are_equal('', err.output) end end) }, group 'read' { test('Markdown', function () local valid_markdown = '*Hello*, World!\n' local expected = pandoc.Pandoc({ pandoc.Para { pandoc.Emph { pandoc.Str 'Hello' }, pandoc.Str ',', pandoc.Space(), pandoc.Str 'World!' } }) assert.are_same(expected, pandoc.read(valid_markdown)) end), test('unsupported extension', function () assert.error_matches( function () pandoc.read('foo', 'gfm+empty_paragraphs') end, 'The extension empty_paragraphs is not supported for gfm' ) end), test('read with other indented code classes', function() local indented_code = ' return true' local expected = pandoc.Pandoc({ pandoc.CodeBlock('return true', {class='foo'}) }) assert.are_same( expected, pandoc.read(indented_code, 'markdown', {indented_code_classes={'foo'}}) ) end), test('can read epub', function () local epub = io.open('lua/module/tiny.epub', 'rb') local blocks = pandoc.read(epub:read'a', 'epub').blocks assert.are_equal( blocks[#blocks], pandoc.Para { pandoc.Emph 'EPUB' } ) end), test('failing read', function () assert.error_matches( function () pandoc.read('foo', 'nosuchreader') end, 'Unknown input format nosuchreader' ) end), group 'read_env' { test('images are added to the mediabag', function () local epub = io.open('lua/module/sample.epub', 'rb'):read('a') local _ = pandoc.read(epub, 'epub') assert.are_equal(#pandoc.mediabag.list(), 1) end), test('images from EPUB are added when using the sandbox', function () local epub = io.open('lua/module/sample.epub', 'rb'):read('a') local _ = pandoc.read(epub, 'epub', nil, {}) assert.are_equal(#pandoc.mediabag.list(), 1) end), test('includes work in global env', function () local tex = '\\include{lua/module/include.tex}' local doc = pandoc.read(tex, 'latex') assert.are_equal( doc.blocks, pandoc.Blocks{pandoc.Para 'included'} ) end), test('sandbox disallows access to the filesystem', function () local tex = '\\include{lua/module/include.tex}' local doc = pandoc.read(tex, 'latex', nil, {}) assert.are_equal(doc.blocks, pandoc.Blocks{}) end), test('files can be added to the sandbox', function () local tex = '\\include{lua/module/include.tex}' local doc = pandoc.read(tex, 'latex', nil, {'lua/module/include.tex'}) assert.are_equal( doc.blocks, pandoc.Blocks{pandoc.Para 'included'} ) end), test('sandbox files can be given as key-value pairs', function () local tex = '\\include{lua/module/include.tex}' local files = { ['lua/module/include.tex'] = 'Hello' } local doc = pandoc.read(tex, 'latex', nil, files) assert.are_equal( doc.blocks, pandoc.Blocks{pandoc.Para 'Hello'} ) end), test('kv-pairs override contents read from file system', function () local tex = '\\include{lua/module/include.tex}' local files = { 'lua/module/include.tex', ['lua/module/include.tex'] = 'Hello' } local doc = pandoc.read(tex, 'latex', nil, files) assert.are_equal( doc.blocks, pandoc.Blocks{pandoc.Para 'Hello'} ) end), }, group 'extensions' { test('string spec', function () local doc = pandoc.read('"vice versa"', 'markdown-smart') assert.are_equal(doc, pandoc.Pandoc{pandoc.Para '"vice versa"'}) end), test('unsupported extension', function () assert.error_matches( function () pandoc.read('foo', 'gfm+empty_paragraphs') end, 'The extension empty_paragraphs is not supported for gfm' ) end), test('unknown extension', function () local format_spec = { format = 'markdown', extensions = {'nope'}} assert.error_matches( function () pandoc.read('x', format_spec) end, 'The extension nope is not supported for markdown' ) end), test('fails on invalid extension', function () local format_spec = { format = 'markdown', extensions = {'nope'}} assert.error_matches( function () pandoc.read('nu-uh', format_spec) end, 'The extension nope is not supported for markdown' ) end), }, }, group 'walk_block' { test('block walking order', function () local acc = {} local nested_nums = pandoc.Div { pandoc.Para{pandoc.Str'1'}, pandoc.Div{ pandoc.Para{pandoc.Str'2'}, pandoc.Para{pandoc.Str'3'} }, pandoc.Para{pandoc.Str'4'} } pandoc.walk_block( nested_nums, {Para = function (p) table.insert(acc, p.content[1].text) end} ) assert.are_equal('1234', table.concat(acc)) end) }, group 'walk_inline' { test('inline walking order', function () local acc = {} local nested_nums = pandoc.Span { pandoc.Str'1', pandoc.Emph { pandoc.Str'2', pandoc.Str'3' }, pandoc.Str'4' } pandoc.walk_inline( nested_nums, {Str = function (s) table.insert(acc, s.text) end} ) assert.are_equal('1234', table.concat(acc)) end) }, group 'write' { test('string spec', function () local doc = pandoc.Pandoc{pandoc.Quoted('DoubleQuote', 'vice versa')} local plain = pandoc.write(doc, 'plain+smart') assert.are_equal(plain, '"vice versa"\n') end), test('table format spec with extensions list', function () local doc = pandoc.Pandoc{pandoc.Quoted('DoubleQuote', 'vice versa')} local format_spec = { format = 'plain', extensions = {'smart'}} local plain = pandoc.write(doc, format_spec) assert.are_equal(plain, '"vice versa"\n') end), test('table format spec with `enable`/`disable` diff', function () local diff = { enable = {'smart'} } local doc = pandoc.Pandoc{pandoc.Quoted('DoubleQuote', 'vice versa')} local format_spec = { format = 'plain', extensions = diff} local plain = pandoc.write(doc, format_spec) assert.are_equal(plain, '"vice versa"\n') end), test('table format spec with set-like diff', function () local diff = { smart = true, auto_identifiers = false } local doc = pandoc.Pandoc{pandoc.Quoted('DoubleQuote', 'vice versa')} local format_spec = { format = 'plain', extensions = diff} local plain = pandoc.write(doc, format_spec) assert.are_equal(plain, '"vice versa"\n') end), test('fails on invalid extension', function () local doc = pandoc.Pandoc{'nope'} local format_spec = { format = 'plain', extensions = {'nope'}} assert.error_matches( function () pandoc.write(doc, format_spec) end, 'The extension nope is not supported for plain' ) end), }, group 'with_state' { test('request_headers can be modified', function () local headers = { {"Authorization", "Basic my-secret"} } pandoc.with_state({request_headers = headers}, function () assert.are_same(PANDOC_STATE.request_headers, headers) end) end), test('resource_path can be modified', function () local paths = {'.', '/test/resource/path' } pandoc.with_state({resource_path = paths}, function () assert.are_same(PANDOC_STATE.resource_path, paths) end) end), test('user_data_dir can be modified', function () local opts = {user_data_dir = '/my/test/path'} pandoc.with_state(opts, function () assert.are_equal(PANDOC_STATE.user_data_dir, '/my/test/path') end) end), test('original value is restored afterwards', function () local orig_user_data_dir = PANDOC_STATE.user_data_dir local opts = {user_data_dir = '/my/test/path'} pandoc.with_state(opts, function () end) assert.are_equal(PANDOC_STATE.user_data_dir, orig_user_data_dir) end), test('unsupported options trigger an error', function () local orig_log = PANDOC_STATE.log local opts = {log = 'nonsense'} assert.error_matches( function () pandoc.with_state(opts, function () assert.are_same(PANDOC_STATE.log, orig_log) end) end, "Unknown or unsupported" ) assert.are_same(PANDOC_STATE.log, orig_log) end), }, group 'Marshal' { group 'Inlines' { test('Strings are broken into words', function () assert.are_equal( pandoc.Emph 'Nice, init?', pandoc.Emph{pandoc.Str 'Nice,', pandoc.Space(), pandoc.Str 'init?'} ) end) }, group 'Blocks' { test('Strings are broken into words and wrapped in Plain', function () assert.are_equal( pandoc.Div{ pandoc.Plain{pandoc.Str 'Nice,', pandoc.Space(), pandoc.Str 'init?'} }, pandoc.Div{'Nice, init?'} ) end) } } } ================================================ FILE: pandoc-lua-engine/test/lua/module/partial.test ================================================ ================================================ FILE: pandoc-lua-engine/test/lua/plain-to-para.lua ================================================ return { { Plain = function (elem) return pandoc.Para(elem.content) end, } } ================================================ FILE: pandoc-lua-engine/test/lua/require-file.lua ================================================ package.path = package.path .. ';lua/?.lua' require 'script-name' ================================================ FILE: pandoc-lua-engine/test/lua/script-name.lua ================================================ function Para (_) return pandoc.Para{pandoc.Str(PANDOC_SCRIPT_FILE)} end ================================================ FILE: pandoc-lua-engine/test/lua/single-to-double-quoted.lua ================================================ return { Quoted = function (elem) if elem.quotetype == "SingleQuote" then elem.quotetype = "DoubleQuote" end return elem end, } ================================================ FILE: pandoc-lua-engine/test/lua/smallcaps-title.lua ================================================ return { { Meta = function(meta) -- The call to `MetaInlines` is redundant and used for testing purposes -- only. The explicit use of a MetaValue constructor is only useful when -- used with an empty table: `MetaInlines{}` is read differently than -- `MetaBlocks{}`. meta.title = pandoc.MetaInlines{pandoc.SmallCaps(meta.title)} return meta end } } ================================================ FILE: pandoc-lua-engine/test/lua/smart-constructors.lua ================================================ -- Test that constructors are "smart" in that they autoconvert -- types where sensible. function Para (_) return { pandoc.BulletList{pandoc.Para "Hello", pandoc.Para "World"}, pandoc.DefinitionList{{"foo", pandoc.Para "placeholder"}}, pandoc.LineBlock{"Moin", "Welt"}, pandoc.OrderedList{pandoc.Plain{pandoc.Str "one"}, pandoc.Plain "two"} } end ================================================ FILE: pandoc-lua-engine/test/lua/strmacro.lua ================================================ return { { Str = function (elem) if elem.text == "{{helloworld}}" then return pandoc.Emph {pandoc.Str "Hello, World"} else return elem end end, } } ================================================ FILE: pandoc-lua-engine/test/lua/undiv.lua ================================================ function Div(el) return el.content end ================================================ FILE: pandoc-lua-engine/test/lua/uppercase-header.lua ================================================ local text = require 'text' local function str_to_uppercase (s) return pandoc.Str(text.upper(s.text)) end function Header (el) return pandoc.walk_block(el, {Str = str_to_uppercase}) end ================================================ FILE: pandoc-lua-engine/test/sample.lua ================================================ -- This is a sample custom writer for pandoc. It produces output -- that is very similar to that of pandoc's HTML writer. -- There is one new feature: code blocks marked with class 'dot' -- are piped through graphviz and images are included in the HTML -- output using 'data:' URLs. The image format can be controlled -- via the `image_format` metadata field. -- -- Invoke with: pandoc -t sample.lua -- -- Note: you need not have lua installed on your system to use this -- custom writer. However, if you do have lua installed, you can -- use it to test changes to the script. 'lua sample.lua' will -- produce informative error messages if your code contains -- syntax errors. function Writer (doc, opts) PANDOC_DOCUMENT = doc PANDOC_WRITER_OPTIONS = opts loadfile(PANDOC_SCRIPT_FILE)() return pandoc.write_classic(doc, opts) end local pipe = pandoc.pipe local stringify = (require 'pandoc.utils').stringify -- Choose the image format based on the value of the -- `image_format` environment variable. local image_format = os.getenv 'image_format' or 'png' local image_mime_type = ({ jpeg = 'image/jpeg', jpg = 'image/jpeg', gif = 'image/gif', png = 'image/png', svg = 'image/svg+xml', })[image_format] or error('unsupported image format `' .. image_format .. '`') -- Character escaping local function escape(s, in_attribute) return s:gsub('[<>&"\']', function(x) if x == '<' then return '<' elseif x == '>' then return '>' elseif x == '&' then return '&' elseif in_attribute and x == '"' then return '"' elseif in_attribute and x == "'" then return ''' else return x end end) end -- Helper function to convert an attributes table into -- a string that can be put into HTML tags. local function attributes(attr) local attr_table = {} for x,y in pairs(attr) do if y and y ~= '' then table.insert(attr_table, ' ' .. x .. '="' .. escape(y,true) .. '"') end end return table.concat(attr_table) end -- Table to store footnotes, so they can be included at the end. local notes = {} -- Blocksep is used to separate block elements. function Blocksep() return '\n\n' end -- This function is called once for the whole document. Parameters: -- body is a string, metadata is a table, variables is a table. -- This gives you a fragment. You could use the metadata table to -- fill variables in a custom lua template. Or, pass `--template=...` -- to pandoc, and pandoc will do the template processing as usual. function Doc(body, metadata, variables) local buffer = {} local function add(s) table.insert(buffer, s) end add(body) if #notes > 0 then add('
    ') for _,note in pairs(notes) do add(note) end add('
') end return table.concat(buffer,'\n') .. '\n' end -- The functions that follow render corresponding pandoc elements. -- s is always a string, attr is always a table of attributes, and -- items is always an array of strings (the items in a list). -- Comments indicate the types of other variables. function Str(s) return escape(s) end function Space() return ' ' end function SoftBreak() return '\n' end function LineBreak() return '
' end function Emph(s) return '' .. s .. '' end function Strong(s) return '' .. s .. '' end function Subscript(s) return '' .. s .. '' end function Superscript(s) return '' .. s .. '' end function SmallCaps(s) return '' .. s .. '' end function Strikeout(s) return '' .. s .. '' end function Link(s, tgt, tit, attr) return '' .. s .. '' end function Image(s, src, tit, attr) return '' end function Code(s, attr) return '' .. escape(s) .. '' end function InlineMath(s) return '\\(' .. escape(s) .. '\\)' end function DisplayMath(s) return '\\[' .. escape(s) .. '\\]' end function SingleQuoted(s) return '‘' .. s .. '’' end function DoubleQuoted(s) return '“' .. s .. '”' end function Note(s) local num = #notes + 1 -- insert the back reference right before the final closing tag. s = string.gsub(s, '(.*)' .. s .. '') -- return the footnote reference, linked to the note. return '' .. num .. '' end function Span(s, attr) return '' .. s .. '
' end function RawInline(format, str) if format == 'html' then return str else return '' end end function Cite(s, cs) local ids = {} for _,cit in ipairs(cs) do table.insert(ids, cit.citationId) end return '' .. s .. '' end function Plain(s) return s end function Para(s) return '

' .. s .. '

' end -- lev is an integer, the header level. function Header(lev, s, attr) return '' .. s .. '' end function BlockQuote(s) return '
\n' .. s .. '\n
' end function HorizontalRule() return "
" end function LineBlock(ls) return '
' .. table.concat(ls, '\n') .. '
' end function CodeBlock(s, attr) -- If code block has class 'dot', pipe the contents through dot -- and base64, and include the base64-encoded png as a data: URL. if attr.class and string.match(' ' .. attr.class .. ' ',' dot ') then local img = pipe('base64', {}, pipe('dot', {'-T' .. image_format}, s)) return '' -- otherwise treat as code (one could pipe through a highlighter) else return '
' .. escape(s) ..
           '
' end end function BulletList(items) local buffer = {} for _, item in pairs(items) do table.insert(buffer, '
  • ' .. item .. '
  • ') end return '
      \n' .. table.concat(buffer, '\n') .. '\n
    ' end function OrderedList(items) local buffer = {} for _, item in pairs(items) do table.insert(buffer, '
  • ' .. item .. '
  • ') end return '
      \n' .. table.concat(buffer, '\n') .. '\n
    ' end function DefinitionList(items) local buffer = {} for _,item in pairs(items) do local k, v = next(item) table.insert(buffer, '
    ' .. k .. '
    \n
    ' .. table.concat(v, '
    \n
    ') .. '
    ') end return '
    \n' .. table.concat(buffer, '\n') .. '\n
    ' end -- Convert pandoc alignment to something HTML can use. -- align is AlignLeft, AlignRight, AlignCenter, or AlignDefault. local function html_align(align) if align == 'AlignLeft' then return 'left' elseif align == 'AlignRight' then return 'right' elseif align == 'AlignCenter' then return 'center' else return 'left' end end function CaptionedImage(src, tit, caption, attr) if #caption == 0 then return '

    ' else local ecaption = escape(caption) return '
    \n' .. ecaption  .. '' .. '
    ' .. ecaption .. '
    \n
    ' end end function Figure(caption, contents, attr) return '\n' .. contents .. '\n
    ' .. caption .. '
    \n' .. '' end -- Caption is a string, aligns is an array of strings, -- widths is an array of floats, headers is an array of -- strings, rows is an array of arrays of strings. function Table(caption, aligns, widths, headers, rows) local buffer = {} local function add(s) table.insert(buffer, s) end add('') if caption ~= '' then add('') end if widths and widths[1] ~= 0 then for _, w in pairs(widths) do add('') end end local header_row = {} local empty_header = true for i, h in pairs(headers) do local align = html_align(aligns[i]) table.insert(header_row,'') empty_header = empty_header and h == '' end if not empty_header then add('') for _,h in pairs(header_row) do add(h) end add('') end local class = 'even' for _, row in pairs(rows) do class = (class == 'even' and 'odd') or 'even' add('') for i,c in pairs(row) do add('') end add('') end add('
    ' .. escape(caption) .. '
    ' .. h .. '
    ' .. c .. '
    ') return table.concat(buffer,'\n') end function RawBlock(format, str) if format == 'html' then return str else return '' end end function Div(s, attr) return '\n' .. s .. '
    ' end -- The following code will produce runtime warnings when you haven't defined -- all of the functions you need for the custom writer, so it's useful -- to include when you're working on a writer. local meta = {} meta.__index = function(_, key) io.stderr:write(string.format("WARNING: Undefined function '%s'\n",key)) return function() return '' end end setmetatable(_G, meta) ================================================ FILE: pandoc-lua-engine/test/tables.custom ================================================

    Simple table with caption:

    Demonstration of simple table syntax.
    Right Left Center Default
    12 12 12 12
    123 123 123 123
    1 1 1 1

    Simple table without caption:

    Right Left Center Default
    12 12 12 12
    123 123 123 123
    1 1 1 1

    Simple table indented two spaces:

    Demonstration of simple table syntax.
    Right Left Center Default
    12 12 12 12
    123 123 123 123
    1 1 1 1

    Multiline table with caption:

    Here’s the caption. It may span multiple lines.
    Centered Header Left Aligned Right Aligned Default aligned
    First row 12.0 Example of a row that spans multiple lines.
    Second row 5.0 Here’s another one. Note the blank line between rows.

    Multiline table without caption:

    Centered Header Left Aligned Right Aligned Default aligned
    First row 12.0 Example of a row that spans multiple lines.
    Second row 5.0 Here’s another one. Note the blank line between rows.

    Table without column headers:

    12 12 12 12
    123 123 123 123
    1 1 1 1

    Multiline table without column headers:

    First row 12.0 Example of a row that spans multiple lines.
    Second row 5.0 Here’s another one. Note the blank line between rows.
    ================================================ FILE: pandoc-lua-engine/test/test-pandoc-lua-engine.hs ================================================ module Main (main) where import Test.Tasty (TestTree, defaultMain, testGroup) import qualified Tests.Lua import qualified Tests.Lua.Module import qualified Tests.Lua.Reader import qualified Tests.Lua.Writer import System.Directory (withCurrentDirectory) main :: IO () main = withCurrentDirectory "test" $ defaultMain tests tests :: TestTree tests = testGroup "pandoc Lua engine" [ testGroup "Lua filters" Tests.Lua.tests , testGroup "Lua modules" Tests.Lua.Module.tests , testGroup "Custom writers" Tests.Lua.Writer.tests , testGroup "Custom readers" Tests.Lua.Reader.tests ] ================================================ FILE: pandoc-lua-engine/test/writer-template.lua ================================================ function Writer (doc, opts) return pandoc.write(doc, 'gfm', opts) end function Template () return '\n$body$\n\n' end ================================================ FILE: pandoc-lua-engine/test/writer-template.out.txt ================================================ $body$ ================================================ FILE: pandoc-lua-engine/test/writer.custom ================================================

    This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.


    Headers

    Level 3 with emphasis

    Level 4

    Level 5

    Level 1

    Level 2 with emphasis

    Level 3

    with no blank line

    Level 2

    with no blank line


    Paragraphs

    Here’s a regular paragraph.

    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 a list item.

    Here’s one with a bullet. * criminey.

    There should be a hard line break
    here.


    Block Quotes

    E-mail style:

    This is a block quote. It is pretty short.

    Code in a block quote:

    sub status {
        print "working";
    }

    A list:

    1. item one
    2. item two

    Nested block quotes:

    nested

    nested

    This should not be a block quote: 2 > 1.

    And a following paragraph.


    Code Blocks

    Code:

    ---- (should be four hyphens)
    
    sub status {
        print "working";
    }
    
    this code block is indented by one tab

    And:

        this code block is indented by two tabs
    
    These should not be escaped:  \$ \\ \> \[ \{

    Lists

    Unordered

    Asterisks tight:

    • asterisk 1
    • asterisk 2
    • asterisk 3

    Asterisks loose:

    • asterisk 1

    • asterisk 2

    • asterisk 3

    Pluses tight:

    • Plus 1
    • Plus 2
    • Plus 3

    Pluses loose:

    • Plus 1

    • Plus 2

    • Plus 3

    Minuses tight:

    • Minus 1
    • Minus 2
    • Minus 3

    Minuses loose:

    • Minus 1

    • Minus 2

    • Minus 3

    Ordered

    Tight:

    1. First
    2. Second
    3. Third

    and:

    1. One
    2. Two
    3. Three

    Loose using tabs:

    1. First

    2. Second

    3. Third

    and using spaces:

    1. One

    2. Two

    3. Three

    Multiple paragraphs:

    1. Item 1, graf one.

      Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

    2. Item 2.

    3. Item 3.

    Nested

    • Tab
      • Tab
        • Tab

    Here’s another:

    1. First
    2. Second:
      • Fee
      • Fie
      • Foe
    3. Third

    Same thing but with paragraphs:

    1. First

    2. Second:

      • Fee
      • Fie
      • Foe
    3. Third

    Tabs and spaces

    • this is a list item indented with tabs

    • this is a list item indented with spaces

      • this is an example list item indented with tabs

      • this is an example list item indented with spaces

    Fancy list markers

    1. begins with 2

    2. and now 3

      with a continuation

      1. sublist with roman numerals, starting with 4
      2. more items
        1. a subsublist
        2. a subsublist

    Nesting:

    1. Upper Alpha
      1. Upper Roman.
        1. Decimal start with 6
          1. Lower alpha with paren

    Autonumbering:

    1. Autonumber.
    2. More.
      1. Nested.

    Should not be a list item:

    M.A. 2007

    B. Williams


    Definition Lists

    Tight using spaces:

    apple
    red fruit
    orange
    orange fruit
    banana
    yellow fruit

    Tight using tabs:

    apple
    red fruit
    orange
    orange fruit
    banana
    yellow fruit

    Loose:

    apple

    red fruit

    orange

    orange fruit

    banana

    yellow fruit

    Multiple blocks with italics:

    apple

    red fruit

    contains seeds, crisp, pleasant to taste

    orange

    orange fruit

    { orange code block }

    orange block quote

    Multiple definitions, tight:

    apple
    red fruit
    computer
    orange
    orange fruit
    bank

    Multiple definitions, loose:

    apple

    red fruit

    computer

    orange

    orange fruit

    bank

    Blank line after term, indented marker, alternate markers:

    apple

    red fruit

    computer

    orange

    orange fruit

    1. sublist
    2. sublist

    HTML Blocks

    Simple block on one line:

    foo

    And nested without indentation:

    foo

    bar

    Interpreted markdown in a table:

    This is emphasized And this is strong

    Here’s a simple block:

    foo

    This should be a code block, though:

    <div>
        foo
    </div>

    As should this:

    <div>foo</div>

    Now, nested:

    foo

    This should just be an HTML comment:

    Multiline:

    Code block:

    <!-- Comment -->

    Just plain comment, with trailing spaces on the line:

    Code:

    <hr />

    Hr’s:











    Inline Markup

    This is emphasized, and so is this.

    This is strong, and so is this.

    An emphasized link.

    This is strong and em.

    So is this word.

    This is strong and em.

    So is this word.

    This is code: >, $, \, \$, <html>.

    This is strikeout.

    Superscripts: abcd ahello ahello there.

    Subscripts: H2O, H23O, Hmany of themO.

    These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.


    Smart quotes, ellipses, dashes

    “Hello,” said the spider. “‘Shelob’ is my name.”

    ‘A’, ‘B’, and ‘C’ are letters.

    ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

    ‘He said, “I want to go.”’ Were you alive in the 70’s?

    Here is some quoted ‘code’ and a “quoted link”.

    Some dashes: one—two — three—four — five.

    Dashes between numbers: 5–7, 255–66, 1987–1999.

    Ellipses…and…and….


    LaTeX

    • \(2+2=4\)
    • \(x \in y\)
    • \(\alpha \wedge \omega\)
    • \(223\)
    • \(p\)-Tree
    • Here’s some display math: \[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\]
    • Here’s one that has a line break in it: \(\alpha + \omega \times x^2\).

    These shouldn’t be math:

    • To get the famous equation, write $e = mc^2$.
    • $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)
    • Shoes ($20) and socks ($5).
    • Escaped $: $73 this should be emphasized 23$.

    Here’s a LaTeX table:


    Special Characters

    Here is some unicode:

    • I hat: Î
    • o umlaut: ö
    • section: §
    • set membership: ∈
    • copyright: ©

    AT&T has an ampersand in their name.

    AT&T is another way to write it.

    This & that.

    4 < 5.

    6 > 5.

    Backslash: \

    Backtick: `

    Asterisk: *

    Underscore: _

    Left brace: {

    Right brace: }

    Left bracket: [

    Right bracket: ]

    Left paren: (

    Right paren: )

    Greater-than: >

    Hash: #

    Period: .

    Bang: !

    Plus: +

    Minus: -


    Links

    Explicit

    Just a URL.

    URL and title.

    URL and title.

    URL and title.

    URL and title

    URL and title

    with_underscore

    Email link

    Empty.

    Reference

    Foo bar.

    With embedded [brackets].

    b by itself should be a link.

    Indented once.

    Indented twice.

    Indented thrice.

    This should [not][] be a link.

    [not]: /url

    Foo bar.

    Foo biz.

    With ampersands

    Here’s a link with an ampersand in the URL.

    Here’s a link with an amersand in the link text: AT&T.

    Here’s an inline link.

    Here’s an inline link in pointy braces.

    With an ampersand: http://example.com/?foo=1&bar=2

    An e-mail address:

    Blockquoted: http://example.com/

    Auto-links should not occur here: <http://example.com/>

    or here: <http://example.com/>

    Images

    From “Voyage dans la Lune” by Georges Melies (1902):

    lalune

    Here is a movie icon.


    Footnotes

    Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

    Notes can go in quotes.4

    1. And in list items.5

    This paragraph should not be part of the note, as it is not indented.

    1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

    2. Here’s the long note. This one contains multiple blocks.

      Subsequent blocks are indented to show that they belong to the footnote (as with list items).

        { <code> }

      If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

    3. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

    4. In quote.

    5. In list.

    ================================================ FILE: pandoc-server/README.md ================================================ # pandoc-server `pandoc-server` is a Haskell library providing access to pandoc's document conversions as an HTTP server. For a description of the API, see [pandoc-server.md](https://github.com/jgm/pandoc/blob/master/doc/pandoc-server.md) in the pandoc source repository. Example of use: ``` hs module Main where import Text.Pandoc.Server (app) import qualified Network.Wai.Handler.Warp as Warp main :: IO () main = Warp.run 3000 app ``` ## License © 2006-2024 John MacFarlane (jgm@berkeley.edu). Released under the [GPL](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html "GNU General Public License"), version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) ================================================ FILE: pandoc-server/pandoc-server.cabal ================================================ cabal-version: 2.4 name: pandoc-server version: 0.1.2 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md copyright: (c) 2006-2024 John MacFarlane author: John MacFarlane maintainer: John MacFarlane bug-reports: https://github.com/jgm/pandoc/issues stability: alpha homepage: https://pandoc.org category: Text tested-with: GHC == 8.6.5, GHC == 8.8.4, GHC == 8.10.7, GHC == 9.0.2, GHC == 9.2.5, GHC == 9.4.4 synopsis: Pandoc document conversion as an HTTP servant-server description: Pandoc-server provides pandoc's document conversion functions in an HTTP server. source-repository head type: git location: https://github.com/jgm/pandoc.git common common-options default-language: Haskell2010 build-depends: base >= 4.12 && < 5 ghc-options: -Wall -fno-warn-unused-do-bind -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wcpp-undef -Wincomplete-uni-patterns -Widentities -Wpartial-fields -Wmissing-signatures -fhide-source-paths -- -Wmissing-export-lists if impl(ghc >= 8.10) ghc-options: -Wunused-packages if impl(ghc >= 9.0) ghc-options: -Winvalid-haddock if os(windows) cpp-options: -D_WINDOWS common common-executable import: common-options ghc-options: -rtsopts -with-rtsopts=-A8m -threaded library import: common-options build-depends: pandoc >= 3.9 && < 3.10, pandoc-types >= 1.22 && < 1.24, containers >= 0.6.0.1 && < 0.9, aeson >= 2.0 && < 2.3, bytestring >= 0.9 && < 0.13, base64-bytestring >= 0.1 && < 1.3, doctemplates >= 0.11 && < 0.12, data-default >= 0.4 && < 0.9, text >= 1.1.1.0 && < 2.2, unicode-collation >= 0.1.1 && < 0.2, servant-server >= 0.19 && < 0.21, skylighting >= 0.13 && < 0.15, wai >= 3.2 && < 3.3, wai-cors >= 0.2.7 && < 0.3 hs-source-dirs: src exposed-modules: Text.Pandoc.Server buildable: True ================================================ FILE: pandoc-server/src/Text/Pandoc/Server.hs ================================================ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} module Text.Pandoc.Server ( app , API , ServerOpts(..) , Params(..) , Blob(..) , parseServerOptsFromArgs ) where import Data.Aeson import qualified Data.Aeson.KeyMap as KeyMap import Network.Wai import Servant import Text.DocTemplates as DocTemplates import Text.Pandoc import Text.Pandoc.Writers.Shared (lookupMetaString) import Text.Pandoc.Citeproc (processCitations) import Text.Pandoc.Highlighting (lookupHighlightingStyle) import Text.Pandoc.Chunks (PathTemplate(..)) import qualified Text.Pandoc.UTF8 as UTF8 import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Encoding as TLE import Data.Maybe (fromMaybe) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Base64 as Base64 (decodeLenient, encode) import Data.Default import Control.Monad (when, unless, foldM) import qualified Data.Set as Set import Skylighting (defaultSyntaxMap) import qualified Data.Map as M import Text.Collate.Lang (Lang (..), parseLang) import System.Console.GetOpt import System.Environment (getProgName) import qualified Control.Exception as E import Text.Pandoc.Shared (safeStrRead) import Text.Pandoc.App ( IpynbOutput (..), Opt(..), defaultOpts ) import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Format (parseFlavoredFormat, formatName) import Text.Pandoc.SelfContained (makeSelfContained) import Text.Pandoc.Transforms (headerShift, filterIpynbOutput, eastAsianLineBreakFilter) import System.Exit import GHC.Generics (Generic) import Network.Wai.Middleware.Cors ( cors, simpleCorsResourcePolicy, CorsResourcePolicy(corsRequestHeaders) ) data ServerOpts = ServerOpts { serverPort :: Int , serverTimeout :: Int } deriving (Show) defaultServerOpts :: ServerOpts defaultServerOpts = ServerOpts { serverPort = 3030, serverTimeout = 2 } cliOptions :: [OptDescr (ServerOpts -> IO ServerOpts)] cliOptions = [ Option ['p'] ["port"] (ReqArg (\s opts -> case safeStrRead s of Just i -> return opts{ serverPort = i } Nothing -> E.throwIO $ PandocOptionError $ T.pack s <> " is not a number") "NUMBER") "port number" , Option ['t'] ["timeout"] (ReqArg (\s opts -> case safeStrRead s of Just i -> return opts{ serverTimeout = i } Nothing -> E.throwIO $ PandocOptionError $ T.pack s <> " is not a number") "NUMBER") "timeout (seconds)" , Option ['h'] ["help"] (NoArg (\_ -> do prg <- getProgName let header = "Usage: " <> prg <> " [OPTION...]" putStrLn $ usageInfo header cliOptions exitSuccess)) "help message" , Option ['v'] ["version"] (NoArg (\_ -> do prg <- getProgName putStrLn $ prg <> " " <> T.unpack pandocVersionText exitSuccess)) "version info" ] parseServerOptsFromArgs :: [String] -> IO ServerOpts parseServerOptsFromArgs args = do let handleUnknownOpt x = "Unknown option: " <> x case getOpt' Permute cliOptions args of (os, ns, unrecognizedOpts, es) -> do when (not (null es) || not (null unrecognizedOpts)) $ E.throwIO $ PandocOptionError $ T.pack $ concat es ++ unlines (map handleUnknownOpt unrecognizedOpts) ++ ("Try --help for more information.") unless (null ns) $ E.throwIO $ PandocOptionError $ T.pack $ "Unknown arguments: " <> unwords ns foldM (flip ($)) defaultServerOpts os newtype Blob = Blob BL.ByteString deriving (Show, Eq) instance ToJSON Blob where toJSON (Blob bs) = toJSON (UTF8.toText . Base64.encode $ BL.toStrict bs) instance FromJSON Blob where parseJSON = withText "Blob" $ pure . Blob . BL.fromStrict . Base64.decodeLenient . UTF8.fromText -- This is the data to be supplied by the JSON payload -- of requests. Maybe values may be omitted and will be -- given default values. data Params = Params { options :: Opt , text :: Text , files :: Maybe (M.Map FilePath Blob) , citeproc :: Maybe Bool } deriving (Show) instance Default Params where def = Params { options = defaultOpts , text = mempty , files = Nothing , citeproc = Nothing } -- Automatically derive code to convert to/from JSON. instance FromJSON Params where parseJSON = withObject "Params" $ \o -> Params <$> parseJSON (Object o) <*> o .: "text" <*> o .:? "files" <*> o .:? "citeproc" instance ToJSON Params where toJSON params = case toJSON (options params) of (Object o) -> Object $ KeyMap.insert "text" (toJSON $ text params) . KeyMap.insert "files" (toJSON $ files params) . KeyMap.insert "citeproc" (toJSON $ citeproc params) $ o x -> x data Message = Message { verbosity :: Verbosity , message :: Text } deriving (Generic, Show) instance ToJSON Message where toEncoding = genericToEncoding defaultOptions type Base64 = Bool data Output = Succeeded Text Base64 [Message] | Failed Text deriving (Generic, Show) instance ToJSON Output where toEncoding (Succeeded o b m) = pairs ( "output" .= o <> "base64" .= b <> "messages" .= m ) toEncoding (Failed errmsg) = pairs ( "error" .= errmsg ) -- This is the API. The "/convert" endpoint takes a request body -- consisting of a JSON-encoded Params structure and responds to -- Get requests with either plain text or JSON, depending on the -- Accept header. type API = ReqBody '[JSON] Params :> Post '[OctetStream] BS.ByteString :<|> ReqBody '[JSON] Params :> Post '[PlainText] Text :<|> ReqBody '[JSON] Params :> Post '[JSON] Output :<|> "batch" :> ReqBody '[JSON] [Params] :> Post '[JSON] [Output] :<|> "babelmark" :> QueryParam' '[Required] "text" Text :> QueryParam "from" Text :> QueryParam "to" Text :> QueryFlag "standalone" :> Get '[JSON] Value :<|> "version" :> Get '[PlainText, JSON] Text app :: Application app = corsWithContentType $ serve api server -- | Allow Content-Type header with values other then allowed by simpleCors. corsWithContentType :: Middleware corsWithContentType = cors (const $ Just policy) where policy = simpleCorsResourcePolicy { corsRequestHeaders = ["Content-Type"] } api :: Proxy API api = Proxy server :: Server API server = convertBytes :<|> convertText :<|> convertJSON :<|> mapM convertJSON :<|> babelmark -- for babelmark which expects {"html": "", "version": ""} :<|> pure pandocVersionText where babelmark text' from' to' standalone' = do res <- convertText def{ text = text', options = defaultOpts{ optFrom = from', optTo = to', optStandalone = standalone' } } return $ toJSON $ object [ "html" .= res, "version" .= pandocVersion ] -- We use runPure for the pandoc conversions, which ensures that -- they will do no IO. This makes the server safe to use. However, -- it will mean that features requiring IO, like RST includes, will not work. -- Changing this to -- handleErr =<< liftIO (runIO (convert' params)) -- will allow the IO operations. convertText params = handleErr $ runPure (convert' return (return . UTF8.toText . Base64.encode . BL.toStrict) params) convertBytes params = handleErr $ runPure (convert' (return . UTF8.fromText) (return . BL.toStrict) params) convertJSON params = handleErrJSON $ runPure (convert' (\t -> Succeeded t False . map toMessage <$> getLog) (\bs -> Succeeded (UTF8.toText $ Base64.encode (BL.toStrict bs)) True . map toMessage <$> getLog) params) toMessage m = Message { verbosity = messageVerbosity m , message = showLogMessage m } convert' :: (Text -> PandocPure a) -> (BL.ByteString -> PandocPure a) -> Params -> PandocPure a convert' textHandler bsHandler params = do curtime <- getCurrentTime -- put files params in ersatz file system let addFile :: FilePath -> Blob -> FileTree -> FileTree addFile fp (Blob lbs) = insertInFileTree fp FileInfo{ infoFileMTime = curtime , infoFileContents = BL.toStrict lbs } case files params of Nothing -> return () Just fs -> do let filetree = M.foldrWithKey addFile mempty fs modifyPureState $ \st -> st{ stFiles = filetree } let opts = options params readerFormat <- parseFlavoredFormat <$> fromMaybe "markdown" $ optFrom opts writerFormat <- parseFlavoredFormat <$> fromMaybe "html" $ optTo opts (readerSpec, readerExts) <- getReader readerFormat (writerSpec, writerExts) <- getWriter writerFormat let isStandalone = optStandalone opts let toformat = formatName writerFormat hlStyle <- case optSyntaxHighlighting opts of "none" -> pure NoHighlighting "idiomatic" -> pure IdiomaticHighlighting "default" -> pure DefaultHighlighting s -> Skylighting <$> lookupHighlightingStyle (T.unpack s) mbTemplate <- if isStandalone then case optTemplate opts of Nothing -> Just <$> compileDefaultTemplate toformat Just t -> Just <$> compileCustomTemplate toformat t else return Nothing abbrevs <- Set.fromList . filter (not . T.null) . T.lines . UTF8.toText <$> case optAbbreviations opts of Nothing -> readDataFile "abbreviations" Just f -> readFileStrict f let readeropts = def{ readerExtensions = readerExts , readerStandalone = isStandalone , readerTabStop = optTabStop opts , readerIndentedCodeClasses = optIndentedCodeClasses opts , readerAbbreviations = abbrevs , readerDefaultImageExtension = optDefaultImageExtension opts , readerTrackChanges = optTrackChanges opts , readerStripComments = optStripComments opts } let writeropts = WriterOptions { writerExtensions = writerExts , writerTabStop = optTabStop opts , writerWrapText = optWrap opts , writerColumns = optColumns opts , writerTemplate = mbTemplate , writerSyntaxMap = defaultSyntaxMap , writerVariables = optVariables opts , writerTableOfContents = optTableOfContents opts , writerListOfFigures = optListOfFigures opts , writerListOfTables = optListOfTables opts , writerIncremental = optIncremental opts , writerHTMLMathMethod = optHTMLMathMethod opts , writerNumberSections = optNumberSections opts , writerNumberOffset = optNumberOffset opts , writerSectionDivs = optSectionDivs opts , writerReferenceLinks = optReferenceLinks opts , writerDpi = optDpi opts , writerEmailObfuscation = optEmailObfuscation opts , writerIdentifierPrefix = optIdentifierPrefix opts , writerCiteMethod = optCiteMethod opts , writerHtmlQTags = optHtmlQTags opts , writerSlideLevel = optSlideLevel opts , writerTopLevelDivision = optTopLevelDivision opts , writerHighlightMethod = hlStyle , writerSetextHeaders = optSetextHeaders opts , writerListTables = optListTables opts , writerEpubSubdirectory = T.pack $ optEpubSubdirectory opts , writerEpubMetadata = T.pack <$> optEpubMetadata opts , writerEpubFonts = optEpubFonts opts , writerEpubTitlePage = optEpubTitlePage opts , writerSplitLevel = optSplitLevel opts , writerChunkTemplate = maybe (PathTemplate "%s-%i.html") PathTemplate (optChunkTemplate opts) , writerTOCDepth = optTOCDepth opts , writerReferenceDoc = optReferenceDoc opts , writerReferenceLocation = optReferenceLocation opts , writerFigureCaptionPosition = optFigureCaptionPosition opts , writerTableCaptionPosition = optTableCaptionPosition opts , writerPreferAscii = optAscii opts , writerLinkImages = optLinkImages opts } let reader = case readerSpec of TextReader r -> r readeropts ByteStringReader r -> r readeropts . BL.fromStrict . Base64.decodeLenient . UTF8.fromText let writer d@(Pandoc meta _) = do case lookupMetaString "lang" meta of "" -> setTranslations $ Lang "en" Nothing (Just "US") [] [] [] l -> case parseLang l of Left _ -> report $ InvalidLang l Right l' -> setTranslations l' case writerSpec of TextWriter w -> w writeropts d >>= (if optEmbedResources opts && htmlFormat (optTo opts) then makeSelfContained else return) >>= textHandler ByteStringWriter w -> w writeropts d >>= bsHandler let transforms :: Pandoc -> Pandoc transforms = (case optShiftHeadingLevelBy opts of 0 -> id x -> headerShift x) . (if extensionEnabled Ext_east_asian_line_breaks readerExts && not (extensionEnabled Ext_east_asian_line_breaks writerExts && optWrap opts == WrapPreserve) then eastAsianLineBreakFilter else id) . (case optIpynbOutput opts of IpynbOutputAll -> id IpynbOutputNone -> filterIpynbOutput Nothing IpynbOutputBest -> filterIpynbOutput (Just $ case optTo opts of Just "latex" -> Format "latex" Just "beamer" -> Format "latex" Nothing -> Format "html" Just f | htmlFormat (optTo opts) -> Format "html" | otherwise -> Format f)) let meta = (case optBibliography opts of [] -> id fs -> setMeta "bibliography" (MetaList (map (MetaString . T.pack) fs))) . maybe id (setMeta "csl" . MetaString . T.pack) (optCSL opts) . maybe id (setMeta "citation-abbreviations" . MetaString . T.pack) (optCitationAbbreviations opts) $ optMetadata opts let addMetadata m' (Pandoc m bs) = Pandoc (m <> m') bs reader (text params) >>= return . transforms . addMetadata meta >>= (case citeproc params of Just True -> processCitations _ -> return) >>= writer htmlFormat :: Maybe Text -> Bool htmlFormat Nothing = True htmlFormat (Just f) = any (`T.isPrefixOf` f) ["html","html4","html5","s5","slidy", "slideous","dzslides","revealjs"] handleErr (Right t) = return t handleErr (Left err) = throwError $ err500 { errBody = TLE.encodeUtf8 $ TL.fromStrict $ renderError err } handleErrJSON (Right o) = return o handleErrJSON (Left err) = return $ Failed (renderError err) compileCustomTemplate toformat t = do res <- runWithPartials $ compileTemplate ("custom." <> T.unpack toformat) (T.pack t) case res of Left e -> throwError $ PandocTemplateError (T.pack e) Right tpl -> return tpl ================================================ FILE: pandoc.cabal ================================================ cabal-version: 2.4 name: pandoc version: 3.9.0.2 build-type: Simple license: GPL-2.0-or-later license-file: COPYING.md copyright: (c) 2006-2024 John MacFarlane author: John MacFarlane maintainer: John MacFarlane bug-reports: https://github.com/jgm/pandoc/issues stability: alpha homepage: https://pandoc.org category: Text tested-with: GHC == 9.6.7, GHC == 9.8.4, GHC == 9.10.3, GHC == 9.12.2 synopsis: Conversion between markup formats description: Pandoc is a Haskell library for converting from one markup format to another. The formats it can handle include . - light markup formats (many variants of Markdown, reStructuredText, AsciiDoc, Org-mode, Muse, Textile, txt2tags, djot) - HTML formats (HTML 4 and 5) - Ebook formats (EPUB v2 and v3, FB2) - Documentation formats (GNU TexInfo, Haddock, Vimdoc) - Roff formats (man, ms) - TeX formats (LaTeX, ConTeXt) - Typst - XML formats (DocBook 4 and 5, JATS, TEI Simple, OpenDocument) - Outline formats (OPML) - Bibliography formats (BibTeX, BibLaTeX, CSL JSON, CSL YAML, RIS) - Word processor formats (Docx, RTF, ODT) - Interactive notebook formats (Jupyter notebook ipynb) - Page layout formats (InDesign ICML) - Wiki markup formats (MediaWiki, DokuWiki, TikiWiki, TWiki, Vimwiki, XWiki, ZimWiki, Jira wiki, Creole) - Slide show formats (LaTeX Beamer, PowerPoint, Slidy, reveal.js, Slideous, S5, DZSlides) - Data formats (CSV and TSV tables, Excel spreadsheets) - PDF (via external programs such as pdflatex or wkhtmltopdf) . Pandoc can convert mathematical content in documents between TeX, MathML, Word equations, roff eqn, typst, and plain text. It includes a powerful system for automatic citations and bibliographies, and it can be customized extensively using templates, filters, and custom readers and writers written in Lua. . For the pandoc command-line program, see the @pandoc-cli@ package. data-files: -- templates data/templates/styles.html data/templates/styles.citations.html data/templates/default.html4 data/templates/default.html5 data/templates/default.chunkedhtml data/templates/default.djot data/templates/default.docbook4 data/templates/default.docbook5 data/templates/default.jats_archiving data/templates/default.jats_articleauthoring data/templates/default.jats_publishing data/templates/default.tei data/templates/default.opendocument data/templates/default.openxml data/templates/default.icml data/templates/default.opml data/templates/default.beamer data/templates/default.latex data/templates/default.bibtex data/templates/default.biblatex data/templates/default.context data/templates/default.texinfo data/templates/default.jira data/templates/default.man data/templates/default.ms data/templates/default.markdown data/templates/default.muse data/templates/default.commonmark data/templates/default.rst data/templates/default.plain data/templates/default.mediawiki data/templates/default.dokuwiki data/templates/default.xwiki data/templates/default.zimwiki data/templates/default.rtf data/templates/default.s5 data/templates/default.slidy data/templates/default.slideous data/templates/default.revealjs data/templates/default.dzslides data/templates/default.asciidoc data/templates/default.haddock data/templates/default.textile data/templates/default.org data/templates/default.epub2 data/templates/default.epub3 data/templates/default.ansi data/templates/article.jats_publishing 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 data/templates/passoptions.latex data/templates/fonts.latex data/templates/font-settings.latex data/templates/after-header-includes.latex data/templates/default.vimdoc data/templates/default.bbcode -- translations data/translations/*.yaml -- entities data/docbook-entities.txt -- source files for reference.docx data/docx/[Content_Types].xml data/docx/_rels/.rels data/docx/docProps/app.xml data/docx/docProps/core.xml data/docx/docProps/custom.xml data/docx/word/document.xml data/docx/word/fontTable.xml data/docx/word/comments.xml data/docx/word/footnotes.xml data/docx/word/numbering.xml data/docx/word/settings.xml data/docx/word/webSettings.xml data/docx/word/styles.xml data/docx/word/_rels/document.xml.rels data/docx/word/_rels/footnotes.xml.rels data/docx/word/theme/theme1.xml -- source files for reference.odt data/odt/mimetype data/odt/manifest.rdf data/odt/styles.xml data/odt/content.xml data/odt/meta.xml data/odt/META-INF/manifest.xml -- source files for reference.pptx data/pptx/_rels/.rels data/pptx/docProps/app.xml data/pptx/docProps/core.xml data/pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels data/pptx/ppt/slideLayouts/slideLayout1.xml data/pptx/ppt/slideLayouts/slideLayout2.xml data/pptx/ppt/slideLayouts/slideLayout3.xml data/pptx/ppt/slideLayouts/slideLayout4.xml data/pptx/ppt/slideLayouts/slideLayout5.xml data/pptx/ppt/slideLayouts/slideLayout6.xml data/pptx/ppt/slideLayouts/slideLayout7.xml data/pptx/ppt/slideLayouts/slideLayout8.xml data/pptx/ppt/slideLayouts/slideLayout9.xml data/pptx/ppt/slideLayouts/slideLayout10.xml data/pptx/ppt/slideLayouts/slideLayout11.xml data/pptx/ppt/_rels/presentation.xml.rels data/pptx/ppt/theme/theme1.xml data/pptx/ppt/presProps.xml data/pptx/ppt/slides/_rels/slide1.xml.rels data/pptx/ppt/slides/_rels/slide2.xml.rels data/pptx/ppt/slides/slide2.xml data/pptx/ppt/slides/slide1.xml data/pptx/ppt/slides/_rels/slide3.xml.rels data/pptx/ppt/slides/_rels/slide4.xml.rels data/pptx/ppt/slides/slide3.xml data/pptx/ppt/slides/slide4.xml data/pptx/ppt/viewProps.xml data/pptx/ppt/tableStyles.xml data/pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels data/pptx/ppt/slideMasters/slideMaster1.xml data/pptx/ppt/presentation.xml data/pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels data/pptx/ppt/notesMasters/notesMaster1.xml data/pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels data/pptx/ppt/notesSlides/notesSlide1.xml data/pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels data/pptx/ppt/notesSlides/notesSlide2.xml data/pptx/ppt/theme/theme2.xml data/pptx/[Content_Types].xml -- stylesheet for EPUB writer data/epub.css -- data for dzslides writer data/dzslides/template.html -- default abbreviations file data/abbreviations -- sample lua custom reader data/creole.lua -- lua init script data/init.lua -- bash completion template data/bash_completion.tpl -- citeproc data/default.csl citeproc/biblatex-localization/*.lbx.strings -- documentation MANUAL.txt, COPYRIGHT extra-doc-files: changelog.md, AUTHORS.md, INSTALL.md, README.md, CONTRIBUTING.md, BUGS extra-source-files: -- tests test/bodybg.gif test/*.native test/command/*.md test/command/*.csl test/command/*.svg test/command/7691.docx test/command/9391.docx test/command/9358.docx test/command/9002.docx test/command/9603.docx test/command/11113.docx test/command/biblio.bib test/command/averroes.bib test/command/A.txt test/command/B.txt test/command/C.txt test/command/D.txt test/command/file1.txt test/command/file2.txt test/command/three.txt test/command/11090/ch1.typ test/command/01.csv test/command/chap1/spider.png test/command/chap2/spider.png test/command/chap1/text.md test/command/chap2/text.md test/command/defaults1.yaml test/command/defaults2.yaml test/command/defaults3.yaml test/command/defaults4.yaml test/command/defaults5.yaml test/command/defaults6.yaml 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 test/command/5700-metadata-file-1.yml test/command/5700-metadata-file-2.yml test/command/abbrevs test/command/sub-file-chapter-1.tex test/command/sub-file-chapter-2.tex test/command/bar.tex test/command/bar-endinput.tex test/command/yaml-metadata.yaml test/command/7813-meta.yaml test/command/3510-subdoc.org test/command/3510-export.latex test/command/3510-src.hs test/command/3971b.tex test/command/5876.yaml test/command/5876/metadata/5876.yaml test/command/5876/metadata/command/5876.yaml test/command/6466-beg.hs test/command/6466-end.hs test/command/6466-mid.hs test/command/6466-whole.hs test/command/7861.yaml test/command/7861/metadata/placeholder test/command/11486/scroll.revealjs test/command/11498.png test/asciidoc-reader.adoc test/asciidoc-reader.native test/asciidoc-reader-include.rb test/asciidoc-reader-include.adoc test/docbook-chapter.docbook test/docbook-reader.docbook test/docbook-xref.docbook test/endnotexml-reader.xml test/html-reader.html test/opml-reader.opml test/org-select-tags.org test/haddock-reader.haddock test/insert test/lalune.jpg test/man-reader.man test/movie.jpg test/media/rId25.jpg test/media/rId26.jpg test/media/rId27.jpg test/typst-reader.typ test/undergradmath.typ test/djot-reader.djot test/latex-reader.latex test/textile-reader.textile test/markdown-reader-more.txt test/markdown-citations.txt test/textile-reader.textile test/mediawiki-reader.wiki test/vimwiki-reader.wiki test/creole-reader.txt test/rst-reader.rst test/jats-reader.xml test/jira-reader.jira test/s5-basic.html test/s5-fancy.html test/s5-fragment.html test/s5-inserts.html test/tables.context test/tables.docbook4 test/tables.docbook5 test/tables.jats_archiving test/tables.jats_articleauthoring test/tables.jats_publishing test/tables.jira test/tables.djot test/tables.dokuwiki test/tables.zimwiki test/tables.icml test/tables.html4 test/tables.html5 test/tables.latex test/tables.man test/tables.ms test/tables.plain test/tables.markdown test/tables.markua test/tables.mediawiki test/tables.tei test/tables.textile test/tables.opendocument test/tables.org test/tables.asciidoc test/tables.asciidoc_legacy test/tables.haddock test/tables.texinfo test/tables.typst test/tables.rst test/tables.rtf test/tables.txt test/tables.fb2 test/tables.muse test/tables.vimdoc test/tables.bbcode test/tables.xwiki test/tables/*.html4 test/tables/*.html5 test/tables/*.latex test/tables/*.typst test/tables/*.native test/tables/*.mediawiki test/tables/*.jats_archiving test/tables/*.markdown test/testsuite.txt test/ansi-test.txt test/writer.latex test/writer.context test/writer.djot test/writer.docbook4 test/writer.docbook5 test/writer.jats_archiving test/writer.jats_articleauthoring test/writer.jats_publishing test/writer.jira test/writer.html4 test/writer.html5 test/writer.man test/writer.ms test/writer.markdown test/writer.markua test/writer.plain test/writer.mediawiki test/writer.textile test/writer.typst test/writer.opendocument test/writer.org test/writer.asciidoc test/writer.asciidoc_legacy test/writer.haddock test/writer.rst test/writer.icml test/writer.rtf test/writer.tei test/writer.texinfo test/writer.fb2 test/writer.opml test/writer.dokuwiki test/writer.zimwiki test/writer.xwiki test/writer.muse test/writer.vimdoc test/writer.bbcode test/ansi-test.ansi test/writers-lang-and-dir.latex test/writers-lang-and-dir.context test/dokuwiki_inline_formatting.dokuwiki test/lhs-test.markdown test/lhs-test.markdown+lhs test/lhs-test.rst test/lhs-test.rst+lhs test/lhs-test.latex test/lhs-test.latex+lhs test/lhs-test.html test/lhs-test.html+lhs test/lhs-test.fragment.html+lhs test/pipe-tables.txt test/dokuwiki_external_images.dokuwiki test/dokuwiki_multiblock_table.dokuwiki test/fb2/*.markdown test/fb2/*.fb2 test/fb2/images-embedded.html test/fb2/images-embedded.fb2 test/fb2/test-small.png test/fb2/reader/*.fb2 test/fb2/reader/*.native test/fb2/test.jpg test/docx/*.docx test/docx/golden/*.docx test/docx/*.native test/epub/*.epub test/epub/*.native test/rtf/*.native test/rtf/*.rtf test/pptx/*.pptx test/pptx/**/*.pptx test/pptx/**/*.native test/pptx-reader/basic.pptx test/pptx-reader/basic.native test/ipynb/*.native test/ipynb/*.in.native test/ipynb/*.out.native test/ipynb/*.ipynb test/ipynb/*.out.ipynb test/ipynb/*.out.html test/txt2tags.t2t test/twiki-reader.twiki test/tikiwiki-reader.tikiwiki test/odt/odt/*.odt test/odt/markdown/*.md test/odt/native/*.native test/xlsx-reader/*.xlsx test/xlsx-reader/*.native test/pod-reader.pod test/vimdoc/*.markdown test/vimdoc/*.vimdoc source-repository head type: git location: https://github.com/jgm/pandoc.git flag embed_data_files 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.18 && < 5 ghc-options: -Wall -fno-warn-unused-do-bind -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wcpp-undef -Wincomplete-uni-patterns -Widentities -Wpartial-fields -Wmissing-signatures -fhide-source-paths -Wmissing-export-lists -Wunused-packages -Winvalid-haddock if impl(ghc >= 9.12) ghc-options: -Wno-deriving-typeable if os(windows) cpp-options: -D_WINDOWS common common-executable import: common-options build-depends: pandoc ghc-options: -rtsopts -with-rtsopts=-A8m -threaded library xml-light import: common-options build-depends: xml >= 1.3.12 && < 1.4, xml-conduit >= 1.9.1.1 && < 1.11, xml-types >= 0.3 && < 0.4, containers >= 0.6.0.1 && < 0.9, text >= 1.1.1.0 && < 2.2 hs-source-dirs: xml-light exposed-modules: Text.Pandoc.XML.Light, Text.Pandoc.XML.Light.Types, Text.Pandoc.XML.Light.Proc, Text.Pandoc.XML.Light.Output library import: common-options build-depends: xml-light, Glob >= 0.7 && < 0.11, JuicyPixels >= 3.1.6.1 && < 3.4, aeson >= 2.0.1.0 && < 2.3, aeson-pretty >= 0.8.9 && < 0.9, array >= 0.5 && < 0.6, attoparsec >= 0.12 && < 0.15, base64-bytestring >= 0.1 && < 1.3, binary >= 0.7 && < 0.11, blaze-html >= 0.9 && < 0.10, blaze-markup >= 0.8 && < 0.9, bytestring >= 0.9 && < 0.13, case-insensitive >= 1.2 && < 1.3, 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.2, data-default >= 0.4 && < 0.9, deepseq >= 1.3 && < 1.6, directory >= 1.2.3 && < 1.4, doclayout >= 0.5.0.1 && < 0.6, doctemplates >= 0.11 && < 0.12, emojis >= 0.1.4.1 && < 0.2, exceptions >= 0.8 && < 0.11, file-embed >= 0.0 && < 0.1, filepath >= 1.1 && < 1.6, gridtables >= 0.1 && < 0.2, haddock-library >= 1.10 && < 1.12, 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-uri >= 2.6 && < 2.8, pandoc-types >= 1.23.1 && < 1.24, parsec >= 3.1 && < 3.2, pretty >= 1.1 && < 1.2, pretty-show >= 1.10 && < 1.11, process >= 1.2.3 && < 1.7, random >= 1.2 && < 1.4, safe >= 0.3.18 && < 0.4, scientific >= 0.3 && < 0.4, skylighting >= 0.14.7 && < 0.15, skylighting-core >= 0.14.7 && < 0.15, split >= 0.2 && < 0.3, syb >= 0.1 && < 0.8, tagsoup >= 0.14.6 && < 0.15, temporary >= 1.1 && < 1.4, texmath >= 0.13.1.1 && < 0.14, text >= 1.1.1.0 && < 2.2, text-conversions >= 0.3 && < 0.4, time >= 1.5 && < 1.16, unicode-collation >= 0.1.1 && < 0.2, unicode-data >= 0.6 && < 0.9, unicode-transforms >= 0.3 && < 0.5, yaml >= 0.11 && < 0.12, libyaml >= 0.1.4 && < 0.2, zip-archive >= 0.4.3.1 && < 0.5, zlib >= 0.5 && < 0.8, xml >= 1.3.12 && < 1.4, typst >= 0.9.0.1 && < 0.10, vector >= 0.12 && < 0.14, djot >= 0.1.4 && < 0.2, asciidoc >= 0.1.0.2 && < 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.10, http-client >= 0.4.30 && < 0.8, http-client-tls >= 0.2.4 && < 0.5, network >= 2.6 && < 3.3, tls >= 2.0.1 && < 2.4 hs-source-dirs: src exposed-modules: Text.Pandoc, Text.Pandoc.App, Text.Pandoc.Data, Text.Pandoc.Options, Text.Pandoc.Extensions, Text.Pandoc.Format, Text.Pandoc.Shared, Text.Pandoc.Sources, Text.Pandoc.MediaBag, Text.Pandoc.Error, Text.Pandoc.Filter, Text.Pandoc.Translations, Text.Pandoc.Translations.Types, Text.Pandoc.Readers, Text.Pandoc.Readers.AsciiDoc, Text.Pandoc.Readers.HTML, Text.Pandoc.Readers.LaTeX, Text.Pandoc.Readers.Markdown, Text.Pandoc.Readers.CommonMark, Text.Pandoc.Readers.Creole, Text.Pandoc.Readers.BibTeX, Text.Pandoc.Readers.EndNote, Text.Pandoc.Readers.RIS, Text.Pandoc.Readers.CslJson, Text.Pandoc.Readers.MediaWiki, Text.Pandoc.Readers.Vimwiki, Text.Pandoc.Readers.RST, Text.Pandoc.Readers.Org, Text.Pandoc.Readers.DocBook, Text.Pandoc.Readers.JATS, Text.Pandoc.Readers.Jira, Text.Pandoc.Readers.OPML, Text.Pandoc.Readers.Textile, Text.Pandoc.Readers.Native, Text.Pandoc.Readers.Haddock, Text.Pandoc.Readers.TWiki, Text.Pandoc.Readers.TikiWiki, Text.Pandoc.Readers.Txt2Tags, Text.Pandoc.Readers.Docx, Text.Pandoc.Readers.Pptx, Text.Pandoc.Readers.Xlsx, Text.Pandoc.Readers.ODT, Text.Pandoc.Readers.EPUB, Text.Pandoc.Readers.Muse, Text.Pandoc.Readers.Man, Text.Pandoc.Readers.Mdoc, Text.Pandoc.Readers.FB2, Text.Pandoc.Readers.DokuWiki, Text.Pandoc.Readers.Ipynb, Text.Pandoc.Readers.CSV, Text.Pandoc.Readers.RTF, Text.Pandoc.Readers.Typst, Text.Pandoc.Readers.Djot, Text.Pandoc.Readers.Pod, Text.Pandoc.Writers, Text.Pandoc.Writers.Native, Text.Pandoc.Writers.XML, Text.Pandoc.Writers.DocBook, Text.Pandoc.Writers.JATS, Text.Pandoc.Writers.OPML, Text.Pandoc.Writers.HTML, Text.Pandoc.Writers.ChunkedHTML, Text.Pandoc.Writers.Ipynb, Text.Pandoc.Writers.ICML, Text.Pandoc.Writers.Jira, Text.Pandoc.Writers.LaTeX, Text.Pandoc.Writers.ConTeXt, Text.Pandoc.Writers.Djot, Text.Pandoc.Writers.Typst, Text.Pandoc.Writers.OpenDocument, Text.Pandoc.Writers.Texinfo, Text.Pandoc.Writers.Man, Text.Pandoc.Writers.Ms, Text.Pandoc.Writers.Markdown, Text.Pandoc.Writers.CommonMark, Text.Pandoc.Writers.Haddock, Text.Pandoc.Writers.RST, Text.Pandoc.Writers.Org, Text.Pandoc.Writers.AsciiDoc, Text.Pandoc.Writers.Textile, Text.Pandoc.Writers.MediaWiki, Text.Pandoc.Writers.DokuWiki, Text.Pandoc.Writers.XWiki, Text.Pandoc.Writers.ZimWiki, Text.Pandoc.Writers.RTF, Text.Pandoc.Writers.ODT, Text.Pandoc.Writers.Docx, Text.Pandoc.Writers.Powerpoint, Text.Pandoc.Writers.EPUB, Text.Pandoc.Writers.FB2, Text.Pandoc.Writers.TEI, Text.Pandoc.Writers.Muse, Text.Pandoc.Writers.CslJson, Text.Pandoc.Writers.Math, Text.Pandoc.Writers.Shared, Text.Pandoc.Writers.OOXML, Text.Pandoc.Writers.AnnotatedTable, Text.Pandoc.Writers.BibTeX, Text.Pandoc.Writers.ANSI, Text.Pandoc.Writers.Vimdoc, Text.Pandoc.Writers.BBCode, Text.Pandoc.PDF, Text.Pandoc.UTF8, Text.Pandoc.Scripting, Text.Pandoc.Slides, Text.Pandoc.Templates, Text.Pandoc.XML, Text.Pandoc.SelfContained, Text.Pandoc.Highlighting, Text.Pandoc.Logging, Text.Pandoc.Process, Text.Pandoc.MIME, Text.Pandoc.Parsing, Text.Pandoc.Asciify, Text.Pandoc.Emoji, Text.Pandoc.ImageSize, Text.Pandoc.Class, Text.Pandoc.Class.IO, Text.Pandoc.Citeproc, Text.Pandoc.Chunks, Text.Pandoc.Transforms, Text.Pandoc.Version other-modules: Text.Pandoc.App.CommandLineOptions, Text.Pandoc.App.Input, Text.Pandoc.App.Opt, Text.Pandoc.App.OutputSettings, Text.Pandoc.Class.CommonState, Text.Pandoc.Class.PandocMonad, Text.Pandoc.Class.PandocIO, Text.Pandoc.Class.PandocPure, Text.Pandoc.Class.Sandbox, Text.Pandoc.Filter.Environment, Text.Pandoc.Filter.JSON, Text.Pandoc.Parsing.Capabilities, Text.Pandoc.Parsing.Citations, Text.Pandoc.Parsing.General, Text.Pandoc.Parsing.GridTable, Text.Pandoc.Parsing.Lists, Text.Pandoc.Parsing.Math, Text.Pandoc.Parsing.Smart, Text.Pandoc.Parsing.State, Text.Pandoc.Parsing.Future, Text.Pandoc.Readers.Docx.Lists, Text.Pandoc.Readers.Docx.Combine, Text.Pandoc.Readers.Docx.Parse, Text.Pandoc.Readers.Docx.Parse.Styles, Text.Pandoc.Readers.Docx.Util, Text.Pandoc.Readers.Docx.Symbols, Text.Pandoc.Readers.Docx.Fields, Text.Pandoc.Readers.OOXML.Shared, Text.Pandoc.Readers.Pptx.Parse, Text.Pandoc.Readers.Pptx.Shapes, Text.Pandoc.Readers.Pptx.Slides, Text.Pandoc.Readers.Pptx.SmartArt, Text.Pandoc.Readers.Xlsx.Parse, Text.Pandoc.Readers.Xlsx.Cells, Text.Pandoc.Readers.Xlsx.Sheets, Text.Pandoc.Readers.HTML.Parsing, Text.Pandoc.Readers.HTML.Table, Text.Pandoc.Readers.HTML.TagCategories, Text.Pandoc.Readers.HTML.Types, Text.Pandoc.Readers.LaTeX.Inline, Text.Pandoc.Readers.LaTeX.Citation, Text.Pandoc.Readers.LaTeX.Lang, Text.Pandoc.Readers.LaTeX.Macro, Text.Pandoc.Readers.LaTeX.Math, Text.Pandoc.Readers.LaTeX.Parsing, Text.Pandoc.Readers.LaTeX.SIunitx, Text.Pandoc.Readers.LaTeX.Table, Text.Pandoc.Readers.Mdoc.Lex, Text.Pandoc.Readers.Mdoc.Macros, Text.Pandoc.Readers.Mdoc.Standards, Text.Pandoc.Readers.Typst.Parsing, Text.Pandoc.Readers.Typst.Math, Text.Pandoc.Readers.ODT.Base, Text.Pandoc.Readers.ODT.Namespaces, Text.Pandoc.Readers.ODT.StyleReader, Text.Pandoc.Readers.ODT.ContentReader, Text.Pandoc.Readers.ODT.Generic.Fallible, Text.Pandoc.Readers.ODT.Generic.SetMap, Text.Pandoc.Readers.ODT.Generic.Utils, Text.Pandoc.Readers.ODT.Generic.Namespaces, Text.Pandoc.Readers.ODT.Generic.XMLConverter, Text.Pandoc.Readers.ODT.Arrows.State, Text.Pandoc.Readers.ODT.Arrows.Utils, Text.Pandoc.Readers.Org.BlockStarts, Text.Pandoc.Readers.Org.Blocks, Text.Pandoc.Readers.Org.DocumentTree, Text.Pandoc.Readers.Org.ExportSettings, Text.Pandoc.Readers.Org.Inlines, Text.Pandoc.Readers.Org.Meta, Text.Pandoc.Readers.Org.ParserState, Text.Pandoc.Readers.Org.Parsing, Text.Pandoc.Readers.Org.Shared, Text.Pandoc.Readers.Metadata, Text.Pandoc.Readers.Roff, Text.Pandoc.Readers.Roff.Escape, Text.Pandoc.Readers.XML, Text.Pandoc.Writers.Docx.OpenXML, Text.Pandoc.Writers.Docx.StyleMap, Text.Pandoc.Writers.Docx.Table, Text.Pandoc.Writers.Docx.Types, Text.Pandoc.Writers.GridTable Text.Pandoc.Writers.JATS.References, Text.Pandoc.Writers.JATS.Table, Text.Pandoc.Writers.JATS.Types, Text.Pandoc.Writers.LaTeX.Caption, Text.Pandoc.Writers.LaTeX.Notes, Text.Pandoc.Writers.LaTeX.Table, Text.Pandoc.Writers.LaTeX.Lang, Text.Pandoc.Writers.LaTeX.Types, Text.Pandoc.Writers.LaTeX.Citation, Text.Pandoc.Writers.LaTeX.Util, Text.Pandoc.Writers.Markdown.Table, Text.Pandoc.Writers.Markdown.Types, Text.Pandoc.Writers.Markdown.Inline, Text.Pandoc.Writers.Roff, Text.Pandoc.Writers.Blaze, Text.Pandoc.Writers.Powerpoint.Presentation, Text.Pandoc.Writers.Powerpoint.Output, Text.Pandoc.Char, Text.Pandoc.TeX, Text.Pandoc.URI, Text.Pandoc.XMLFormat, Text.Pandoc.CSS, Text.Pandoc.CSV, Text.Pandoc.RoffChar, Text.Pandoc.UUID, Text.Pandoc.Image, Text.Pandoc.Citeproc.BibTeX, Text.Pandoc.Citeproc.Name, Text.Pandoc.Citeproc.CslJson, Text.Pandoc.Citeproc.Data, Text.Pandoc.Citeproc.Locator, Text.Pandoc.Citeproc.MetaValue, Text.Pandoc.Citeproc.Util, Paths_pandoc autogen-modules: Paths_pandoc buildable: True test-suite test-pandoc import: common-executable type: exitcode-stdio-1.0 main-is: test-pandoc.hs hs-source-dirs: test build-depends: pandoc, Diff >= 0.2 && < 1.1, Glob >= 0.7 && < 0.11, bytestring >= 0.9 && < 0.13, containers >= 0.4.2.1 && < 0.9, directory >= 1.2.3 && < 1.4, doctemplates >= 0.11 && < 0.12, filepath >= 1.1 && < 1.6, mtl >= 2.2 && < 2.4, pandoc-types >= 1.23.1 && < 1.24, process >= 1.2.3 && < 1.7, tasty >= 0.11 && < 1.6, tasty-golden >= 2.3 && < 2.4, tasty-hunit >= 0.9 && < 0.11, tasty-quickcheck >= 0.8 && < 0.12, text >= 1.1.1.0 && < 2.2, temporary >= 1.1 && < 1.4, time >= 1.5 && < 1.16, xml >= 1.3.12 && < 1.4, zip-archive >= 0.4.3 && < 0.5 other-modules: Tests.Old Tests.Command Tests.Helpers Tests.Shared Tests.MediaBag Tests.XML Tests.Readers.LaTeX Tests.Readers.HTML Tests.Readers.JATS Tests.Readers.Jira Tests.Readers.Markdown Tests.Readers.Org Tests.Readers.Org.Block Tests.Readers.Org.Block.CodeBlock Tests.Readers.Org.Block.Figure Tests.Readers.Org.Block.Header Tests.Readers.Org.Block.List Tests.Readers.Org.Block.Table Tests.Readers.Org.Directive Tests.Readers.Org.Inline Tests.Readers.Org.Inline.Citation Tests.Readers.Org.Inline.Note Tests.Readers.Org.Inline.Smart Tests.Readers.Org.Meta Tests.Readers.Org.Shared Tests.Readers.RST Tests.Readers.RTF Tests.Readers.Docx Tests.Readers.Pptx Tests.Readers.Xlsx Tests.Readers.ODT Tests.Readers.Txt2Tags Tests.Readers.EPUB Tests.Readers.Muse Tests.Readers.Creole Tests.Readers.Man Tests.Readers.Mdoc Tests.Readers.FB2 Tests.Readers.Pod Tests.Readers.DokuWiki Tests.Writers.Native Tests.Writers.ConTeXt Tests.Writers.DocBook Tests.Writers.HTML Tests.Writers.JATS Tests.Writers.Jira Tests.Writers.Markdown Tests.Writers.Org Tests.Writers.Plain Tests.Writers.AsciiDoc Tests.Writers.LaTeX Tests.Writers.Docx Tests.Writers.RST Tests.Writers.TEI Tests.Writers.Markua Tests.Writers.Muse Tests.Writers.FB2 Tests.Writers.Powerpoint Tests.Writers.OOXML Tests.Writers.Ms Tests.Writers.AnnotatedTable Tests.Writers.BBCode benchmark benchmark-pandoc import: common-executable type: exitcode-stdio-1.0 main-is: benchmark-pandoc.hs hs-source-dirs: benchmark build-depends: bytestring, tasty-bench >= 0.4 && <= 0.5, mtl >= 2.2 && < 2.4, text >= 1.1.1.0 && < 2.2, deepseq -- we increase heap size to avoid benchmarking garbage collection: ghc-options: -rtsopts -with-rtsopts=-A8m -threaded ================================================ FILE: release.nix ================================================ let pkgs = import { }; in pkgs.haskellPackages.callPackage ./project.nix { } ================================================ FILE: shell.nix ================================================ # based on https://gist.github.com/TikhonJelvis/be42400fc31bac0cd1736740fe5eb83b { nixpkgs ? import {}, compiler ? "default" }: let inherit (nixpkgs) pkgs; # Build a default.nix file from our .cabal file: here = ./.; project = pkgs.stdenv.mkDerivation ({ name = "default.nix"; buildCommand = '' ${pkgs.cabal2nix}/bin/cabal2nix file://${here} > $out ''; }); # Use the package set for our compiler: haskellPackages = if compiler == "default" then pkgs.haskellPackages else pkgs.haskell.packages.${compiler}; # Helper function that gets Nix-packaged dependencies off GitHub. # GitHub project needs a default.nix file for this to work. fetchHaskell = { url, rev, sha256 }: haskellPackages.callPackage (pkgs.fetchgit { inherit url rev sha256; }) {}; # Note: fetchHaskell shouldn't download the package if you already # have it in the system. base = haskellPackages.callPackage project { ## Specify GitHub dependencies here. ## You can get url, rev and sha256 by running 'nix-prefetch-git git@...' # extraPackage = fetchHaskell { # url = "git@..."; # rev = ""; # sha256 = ""; # }; }; in if pkgs.lib.inNixShell then base.env else base ================================================ FILE: src/Text/Pandoc/App/CommandLineOptions.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-} {- | Module : Text.Pandoc.App.CommandLineOptions Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Does a pandoc conversion based on command-line options. -} module Text.Pandoc.App.CommandLineOptions ( parseOptions , parseOptionsFromArgs , handleOptInfo , options , engines , setVariable , versionInfo ) where import Control.Monad.Trans import Control.Monad.State.Strict import Data.Containers.ListUtils (nubOrd) import Data.Aeson (eitherDecode) import Data.Aeson.Encode.Pretty (encodePretty', Config(..), keyOrder, defConfig, Indent(..), NumberFormat(..)) import Data.Bifunctor (second) import Data.Char (toLower) import Data.List (intercalate, sort) import qualified Data.List as L #ifdef _WINDOWS import Data.List (isPrefixOf) #endif import Data.Maybe (fromMaybe, isJust) import Data.Text (Text) import Safe (tailDef) import Skylighting (Syntax (..), defaultSyntaxMap) import System.Console.GetOpt import System.Environment (getArgs, getProgName) import System.Exit (exitSuccess) import System.FilePath import System.IO (stdout) import Text.DocTemplates (Context (..), ToContext (toVal), Val (..)) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Data.Version (showVersion) import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), IpynbOutput (..), DefaultsState (..), applyDefaults, fullDefaultsPath, OptInfo(..)) import Text.Pandoc.Filter (Filter (..)) import Text.Pandoc.Highlighting (highlightingStyles, lookupHighlightingStyle) import Text.Pandoc.Scripting (ScriptingEngine (..), customTemplate) import Text.Pandoc.Shared (safeStrRead) import Text.Printf import qualified Control.Exception as E import Control.Monad.Except (ExceptT(..), runExceptT, throwError) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import qualified Data.Map as M import qualified Data.Set as Set import qualified Data.Text as T import qualified Text.Pandoc.UTF8 as UTF8 parseOptions :: [OptDescr (Opt -> ExceptT OptInfo IO Opt)] -> Opt -> IO (Either OptInfo Opt) parseOptions options' defaults = do rawArgs <- liftIO getArgs prg <- liftIO getProgName parseOptionsFromArgs options' defaults prg rawArgs parseOptionsFromArgs :: [OptDescr (Opt -> ExceptT OptInfo IO Opt)] -> Opt -> String -> [String] -> IO (Either OptInfo Opt) parseOptionsFromArgs options' defaults prg rawArgs = do let (actions, args, unrecognizedOpts, errors) = getOpt' Permute options' (preprocessArgs rawArgs) let unknownOptionErrors = foldr (handleUnrecognizedOption . takeWhile (/= '=')) [] unrecognizedOpts let mbArgs = case args of [] -> Nothing xs -> Just xs let adjustOpts opts = opts{ optInputFiles = map normalizePath <$> (optInputFiles opts <> mbArgs) , optStandalone = -- certain other options imply standalone optStandalone opts || isJust (optTemplate opts) || optSelfContained opts || not (null (optIncludeInHeader opts)) || not (null (optIncludeBeforeBody opts)) || not (null (optIncludeAfterBody opts)) } if (null errors && null unknownOptionErrors) then -- thread option data structure through all supplied option 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.") -- | React to an 'OptInfo' by printing the requested information -- and exiting or (if there was a parsing error) raising an error. handleOptInfo :: ScriptingEngine -> OptInfo -> IO () handleOptInfo engine info = E.handle (handleError . Left) $ do case info of BashCompletion -> do datafiles <- getDataFileNames tpl <- runIOorExplode $ UTF8.toString <$> readDefaultDataFile "bash_completion.tpl" let optnames (Option shorts longs _ _) = map (\c -> ['-',c]) shorts ++ map ("--" ++) longs let allopts = unwords (concatMap optnames options) UTF8.hPutStrLn stdout $ T.pack $ printf tpl allopts (T.unpack $ T.unwords readersNames) (T.unpack $ T.unwords writersNames) (T.unpack $ T.unwords $ map fst highlightingStyles) (unwords datafiles) ListInputFormats -> mapM_ (UTF8.hPutStrLn stdout) readersNames ListOutputFormats -> mapM_ (UTF8.hPutStrLn stdout) writersNames ListExtensions mbfmt -> do let formatName = fromMaybe "markdown" mbfmt let allExts = getAllExtensions formatName if formatName `notElem` (map fst (readers :: [(Text, Reader PandocPure)]) ++ map fst (writers :: [(Text, Writer PandocPure)])) then E.throwIO $ PandocOptionError $ formatName <> " is not a recognized reader or writer format" else do let defExts = getDefaultExtensions formatName let showExt x = (if extensionEnabled x defExts then '+' else if extensionEnabled x allExts then '-' else ' ') : drop 4 (show x) mapM_ (UTF8.hPutStrLn stdout . T.pack . showExt) (extensionsToList allExts) ListHighlightLanguages -> do let langs = [ T.unpack (T.toLower (sShortname s)) | s <- M.elems defaultSyntaxMap , sShortname s `notElem` [T.pack "Alert", T.pack "Alert_indent"] ] mapM_ (UTF8.hPutStrLn stdout . T.pack) (sort langs) ListHighlightStyles -> do mapM_ (UTF8.hPutStrLn stdout . fst) highlightingStyles PrintDefaultTemplate mbout fmt -> do let write = maybe (UTF8.hPutStr stdout) (UTF8.writeFile) mbout templ <- runIO $ case splitExtension (T.unpack fmt) of (_, "") -> do -- built-in format setUserDataDir Nothing getDefaultTemplate fmt _ -> do -- format looks like a filepath => custom writer components <- engineLoadCustom engine (T.unpack fmt) case customTemplate components of Just t -> pure t Nothing -> E.throw $ PandocNoTemplateError fmt case templ of Right t | T.null t -> -- e.g. for docx, odt, json: E.throwIO $ PandocCouldNotFindDataFileError $ T.pack ("templates/default." ++ T.unpack fmt) | otherwise -> write t Left e -> E.throwIO e PrintDefaultDataFile mbout f -> do let write = maybe BS.putStr BS.writeFile mbout runIOorExplode $ readDefaultDataFile (T.unpack f) >>= liftIO . write PrintHighlightStyle mbout styleName -> do let write = maybe B.putStr B.writeFile mbout sty <- runIOorExplode $ lookupHighlightingStyle (T.unpack styleName) write $ encodePretty' defConfig{confIndent = Spaces 4 ,confCompare = keyOrder (map T.pack ["text-color" ,"background-color" ,"line-number-color" ,"line-number-background-color" ,"bold" ,"italic" ,"underline" ,"text-styles"]) ,confNumFormat = Generic ,confTrailingNewline = True} sty VersionInfo -> versionInfo [] Nothing "" Help -> do prg <- getProgName UTF8.hPutStr stdout (T.pack $ usageMessage prg options) OptError e -> E.throwIO e exitSuccess -- | Supported LaTeX engines; the first item is used as default engine -- when going through LaTeX. latexEngines :: [String] latexEngines = [ "pdflatex", "lualatex", "xelatex", "latexmk", "tectonic" , "pdflatex-dev", "lualatex-dev" ] -- | Supported HTML PDF engines; the first item is used as default -- engine when going through HTML. htmlEngines :: [String] htmlEngines = ["weasyprint", "wkhtmltopdf", "pagedjs-cli", "prince"] engines :: [(Text, String)] engines = map ("html",) htmlEngines ++ map ("html5",) htmlEngines ++ map ("latex",) latexEngines ++ map ("beamer",) latexEngines ++ [ ("ms", "pdfroff") , ("ms", "groff") , ("typst", "typst") , ("context", "context") ] pdfEngines :: [String] pdfEngines = nubOrd $ map snd engines -- For motivation see #8956. We want to allow things like `-si` without -- causing the `i` to be parsed as an optional boolean argument of `-s`. -- This is for backwards compatibility given the addition of optional -- boolean arguments in #8879. preprocessArgs :: [String] -> [String] preprocessArgs [] = [] preprocessArgs ("--":xs) = "--" : xs -- a bare '--' ends option parsing -- note that -strue is interpreted as -strue while -- -stmarkdown is interpreted as -s -tmarkdown preprocessArgs (('-':c:d:cs):xs) | isShortBooleanOpt c , case toLower <$> (d:cs) of "true" -> True "false" -> True _ -> False = ('-':c:d:cs) : preprocessArgs xs | isShortBooleanOpt c , isShortOpt d = splitArg (c:d:cs) ++ preprocessArgs xs preprocessArgs (x:xs) = x : preprocessArgs xs isShortBooleanOpt :: Char -> Bool isShortBooleanOpt = (`Set.member` shortBooleanOpts) where shortBooleanOpts = Set.fromList [c | Option [c] _ (OptArg _ "true|false") _ <- options] isShortOpt :: Char -> Bool isShortOpt = (`Set.member` shortOpts) where shortOpts = Set.fromList $ concat [cs | Option cs _ _ _ <- options] splitArg :: String -> [String] splitArg (c:d:cs) | isShortBooleanOpt c , isShortOpt d = ['-',c] : splitArg (d:cs) splitArg (c:cs) = ['-':c:cs] splitArg [] = [] -- | A list of functions, each transforming the options data structure -- in response to a command-line option. options :: [OptDescr (Opt -> ExceptT OptInfo IO Opt)] options = [ Option "fr" ["from","read"] (ReqArg (\arg opt -> return opt { optFrom = Just $ T.pack arg }) "FORMAT") "" , Option "tw" ["to","write"] (ReqArg (\arg opt -> return opt { optTo = Just $ T.pack arg }) "FORMAT") "" , Option "o" ["output"] (ReqArg (\arg opt -> return opt { optOutputFile = Just (normalizePath arg) }) "FILE") "" -- "Name of output file" , Option "" ["data-dir"] (ReqArg (\arg opt -> return opt { optDataDir = Just (normalizePath arg) }) "DIRECTORY") -- "Directory containing pandoc data files." "" , Option "M" ["metadata"] (ReqArg (\arg opt -> do let (key, val) = splitField arg return opt{ optMetadata = addMeta key val $ optMetadata opt }) "KEY[:VALUE]") "" , Option "" ["metadata-file"] (ReqArg (\arg opt -> return opt{ optMetadataFiles = optMetadataFiles opt ++ [normalizePath arg] }) "FILE") "" , Option "d" ["defaults"] (ReqArg (\arg opt -> do res <- liftIO $ runIO $ do let defsState = DefaultsState { curDefaults = Nothing, inheritanceGraph = [] } fp <- fullDefaultsPath (optDataDir opt) arg evalStateT (applyDefaults opt fp) defsState case res of Left e -> optError e Right x -> return x ) "FILE") "" , Option "" ["file-scope"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--file-scope" arg return opt { optFileScope = boolValue }) "true|false") "" -- "Parse input files before combining" , Option "" ["sandbox"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--sandbox" arg return opt { optSandbox = boolValue }) "true|false") "" , Option "s" ["standalone"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--standalone/-s" arg return opt { optStandalone = boolValue }) "true|false") "" -- "Include needed header and footer on output" , Option "" ["template"] (ReqArg (\arg opt -> return opt{ optTemplate = Just (normalizePath arg) }) "FILE") "" -- "Use custom template" , Option "V" ["variable"] (ReqArg (\arg opt -> do let (key, val) = splitField arg return opt{ optVariables = setVariable (T.pack key) (T.pack val) $ optVariables opt }) "KEY[:VALUE]") "" , Option "" ["variable-json"] (ReqArg (\arg opt -> do let (key, json) = splitField arg case eitherDecode (B.fromStrict . UTF8.fromString $ json) of Right (val :: Val Text) -> return opt{ optVariables = let Context m = optVariables opt in Context $ M.insert (T.pack key) val m } -- note that this replaces any existing value, which -- is different from what --variable does Left err' -> optError $ PandocOptionError $ "Could not parse '" <> T.pack json <> "' as JSON:\n" <> T.pack err') "KEY[:JSON]") "" , Option "" ["wrap"] (ReqArg (\arg opt -> case arg of "auto" -> return opt{ optWrap = WrapAuto } "none" -> return opt{ optWrap = WrapNone } "preserve" -> return opt{ optWrap = WrapPreserve } _ -> optError $ PandocOptionError "--wrap must be auto, none, or preserve") "auto|none|preserve") "" -- "Option for wrapping text in output" , Option "" ["ascii"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--ascii" arg return opt { optAscii = boolValue }) "true|false") "" -- "Prefer ASCII output" , Option "" ["toc", "table-of-contents"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--toc/--table-of-contents" arg return opt { optTableOfContents = boolValue }) "true|false") "" -- "Include table of contents" , Option "" ["toc-depth"] (ReqArg (\arg opt -> case safeStrRead arg of Just t | t >= 1 && t <= 6 -> return opt { optTOCDepth = t } _ -> optError $ PandocOptionError "Argument of --toc-depth must be a number 1-6") "NUMBER") "" -- "Number of levels to include in TOC" , Option "" ["lof", "list-of-figures"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--lof/--list-of-figures" arg return opt { optListOfFigures = boolValue }) "true|false") "" -- "Include list of figures" , Option "" ["lot", "list-of-tables"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--lot/--list-of-tables" arg return opt { optListOfTables = boolValue }) "true|false") "" -- "Include list of tables" , Option "N" ["number-sections"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--number-sections/-N" arg return opt { optNumberSections = boolValue }) "true|false") "" -- "Number sections" , Option "" ["number-offset"] (ReqArg (\arg opt -> case safeStrRead ("[" <> arg <> "]") of Just ns -> return opt { optNumberOffset = ns, optNumberSections = True } _ -> optError $ PandocOptionError "could not parse argument of --number-offset") "NUMBERS") "" -- "Starting number for sections, subsections, etc." , Option "" ["top-level-division"] (ReqArg (\arg opt -> case arg of "section" -> return opt{ optTopLevelDivision = TopLevelSection } "chapter" -> return opt{ optTopLevelDivision = TopLevelChapter } "part" -> return opt{ optTopLevelDivision = TopLevelPart } "default" -> return opt{ optTopLevelDivision = TopLevelDefault } _ -> optError $ PandocOptionError $ "Argument of --top-level division must be " <> "section, chapter, part, or default" ) "section|chapter|part") "" -- "Use top-level division type in LaTeX, ConTeXt, DocBook" , Option "" ["extract-media"] (ReqArg (\arg opt -> return opt { optExtractMedia = Just (normalizePath arg) }) "PATH") "" -- "Directory to which to extract embedded media" , Option "" ["resource-path"] (ReqArg (\arg opt -> return opt { optResourcePath = splitSearchPath arg ++ optResourcePath opt }) "SEARCHPATH") "" -- "Paths to search for images and other resources" , Option "H" ["include-in-header"] (ReqArg (\arg opt -> return opt{ optIncludeInHeader = optIncludeInHeader opt ++ [normalizePath arg] }) "FILE") "" -- "File to include at end of header (implies -s)" , Option "B" ["include-before-body"] (ReqArg (\arg opt -> return opt{ optIncludeBeforeBody = optIncludeBeforeBody opt ++ [normalizePath arg] }) "FILE") "" -- "File to include before document body" , Option "A" ["include-after-body"] (ReqArg (\arg opt -> return opt{ optIncludeAfterBody = optIncludeAfterBody opt ++ [normalizePath arg] }) "FILE") "" -- "File to include after document body" , Option "" ["no-highlight"] (NoArg (\opt -> do deprecatedOption "--no-highlight" "Use --syntax-highlighting=none instead." return opt { optSyntaxHighlighting = NoHighlightingString })) "" -- "Don't highlight source code" , Option "" ["highlight-style"] (ReqArg (\arg opt -> do deprecatedOption "--highlight-style" "Use --syntax-highlighting instead." return opt{ optSyntaxHighlighting = T.pack $ normalizePath arg }) "STYLE|FILE") "" -- "Style for highlighted code" , Option "" ["syntax-definition"] (ReqArg (\arg opt -> return opt{ optSyntaxDefinitions = normalizePath arg : optSyntaxDefinitions opt }) "FILE") "" -- "Syntax definition (xml) file" , Option "" ["syntax-highlighting"] (ReqArg (\arg opt -> return opt{ optSyntaxHighlighting = T.pack $ normalizePath arg }) "none|default|idiomatic||") "" -- "syntax highlighting method for code" , Option "" ["dpi"] (ReqArg (\arg opt -> case safeStrRead arg of Just t | t > 0 -> return opt { optDpi = t } _ -> optError $ PandocOptionError "Argument of --dpi must be a number greater than 0") "NUMBER") "" -- "Dpi (default 96)" , Option "" ["eol"] (ReqArg (\arg opt -> case toLower <$> arg of "crlf" -> return opt { optEol = CRLF } "lf" -> return opt { optEol = LF } "native" -> return opt { optEol = Native } -- mac-syntax (cr) is not supported in ghc-base. _ -> optError $ PandocOptionError "Argument of --eol must be crlf, lf, or native") "crlf|lf|native") "" -- "EOL (default OS-dependent)" , Option "" ["columns"] (ReqArg (\arg opt -> case safeStrRead arg of Just t | t > 0 -> return opt { optColumns = t } _ -> optError $ PandocOptionError "Argument of --columns must be a number greater than 0") "NUMBER") "" -- "Length of line in characters" , Option "p" ["preserve-tabs"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--preserve-tabs/-p" arg return opt { optPreserveTabs = boolValue }) "true|false") "" -- "Preserve tabs instead of converting to spaces" , Option "" ["tab-stop"] (ReqArg (\arg opt -> case safeStrRead arg of Just t | t > 0 -> return opt { optTabStop = t } _ -> optError $ PandocOptionError "Argument of --tab-stop must be a number greater than 0") "NUMBER") "" -- "Tab stop (default 4)" , Option "" ["pdf-engine"] (ReqArg (\arg opt -> do let b = takeBaseName arg if b `elem` pdfEngines then return opt { optPdfEngine = Just arg } else optError $ PandocOptionError $ T.pack $ "Argument of --pdf-engine must be one of\n" ++ concatMap (\e -> "\t" <> e <> "\n") pdfEngines) "PROGRAM") "" -- "Name of program to use in generating PDF" , Option "" ["pdf-engine-opt"] (ReqArg (\arg opt -> do let oldArgs = optPdfEngineOpts opt return opt { optPdfEngineOpts = oldArgs ++ [arg]}) "STRING") "" -- "Flags to pass to the PDF-engine, all instances of this option are accumulated and used" , Option "" ["reference-doc"] (ReqArg (\arg opt -> return opt { optReferenceDoc = Just $ normalizePath arg }) "FILE") "" -- "Path of custom reference doc" , Option "" ["self-contained"] (OptArg (\arg opt -> do deprecatedOption "--self-contained" "use --embed-resources --standalone" boolValue <- readBoolFromOptArg "--self-contained" arg return opt { optSelfContained = boolValue }) "true|false") "" -- "Make slide shows include all the needed js and css (deprecated)" , Option "" ["embed-resources"] -- maybe True (\argStr -> argStr == "true") arg (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--embed-resources" arg return opt { optEmbedResources = boolValue }) "true|false") "" -- "Make slide shows include all the needed js and css" , Option "" ["link-images"] -- maybe True (\argStr -> argStr == "true") arg (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--link-images" arg return opt { optLinkImages = boolValue }) "true|false") "" -- "Link images in ODT rather than embedding them" , Option "" ["request-header"] (ReqArg (\arg opt -> do let (key, val) = splitField arg return opt{ optRequestHeaders = (T.pack key, T.pack val) : optRequestHeaders opt }) "NAME:VALUE") "" , Option "" ["no-check-certificate"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--no-check-certificate" arg return opt { optNoCheckCertificate = boolValue }) "true|false") "" -- "Disable certificate validation" , Option "" ["abbreviations"] (ReqArg (\arg opt -> return opt { optAbbreviations = Just $ normalizePath arg }) "FILE") "" -- "Specify file for custom abbreviations" , Option "" ["indented-code-classes"] (ReqArg (\arg opt -> return opt { optIndentedCodeClasses = T.words $ T.map (\c -> if c == ',' then ' ' else c) $ T.pack arg }) "STRING") "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks" , Option "" ["default-image-extension"] (ReqArg (\arg opt -> return opt { optDefaultImageExtension = T.pack arg }) "extension") "" -- "Default extension for extensionless images" , Option "F" ["filter"] (ReqArg (\arg opt -> return opt { optFilters = optFilters opt ++ [JSONFilter (normalizePath arg)] }) "PROGRAM") "" -- "External JSON filter" , Option "L" ["lua-filter"] (ReqArg (\arg opt -> return opt { optFilters = optFilters opt ++ [LuaFilter (normalizePath arg)] }) "SCRIPTPATH") "" -- "Lua filter" , Option "" ["shift-heading-level-by"] (ReqArg (\arg opt -> case safeStrRead arg of Just t -> return opt{ optShiftHeadingLevelBy = t } _ -> optError $ PandocOptionError "Argument of --shift-heading-level-by must be an integer") "NUMBER") "" -- "Shift heading level" , Option "" ["base-header-level"] (ReqArg (\arg opt -> do deprecatedOption "--base-header-level" "Use --shift-heading-level-by instead." case safeStrRead arg of Just t | t > 0 && t < 6 -> return opt{ optShiftHeadingLevelBy = t - 1 } _ -> optError $ PandocOptionError "Argument of --base-header-level must be 1-5") "NUMBER") "" -- "Headers base level" , Option "" ["track-changes"] (ReqArg (\arg opt -> do action <- case arg of "accept" -> return AcceptChanges "reject" -> return RejectChanges "all" -> return AllChanges _ -> optError $ PandocOptionError $ T.pack "Argument of --track-changes must be accept, reject, or all" return opt { optTrackChanges = action }) "accept|reject|all") "" -- "Accepting or reject MS Word track-changes."" , Option "" ["strip-comments"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--strip-comments" arg return opt { optStripComments = boolValue }) "true|false") "" -- "Strip HTML comments" , Option "" ["reference-links"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--reference-links" arg return opt { optReferenceLinks = boolValue }) "true|false") "" -- "Use reference links in parsing HTML" , Option "" ["reference-location"] (ReqArg (\arg opt -> do action <- case arg of "block" -> return EndOfBlock "section" -> return EndOfSection "document" -> return EndOfDocument _ -> optError $ PandocOptionError $ T.pack "Argument of --reference-location must be block, section, or document" return opt { optReferenceLocation = action }) "block|section|document") "" -- "Specify where reference links and footnotes go" , Option "" ["figure-caption-position"] (ReqArg (\arg opt -> do pos <- case arg of "above" -> return CaptionAbove "below" -> return CaptionBelow _ -> optError $ PandocOptionError $ T.pack "Argument of --figure-caption-position must be above or below" return opt { optFigureCaptionPosition = pos }) "above|below") "" -- "Specify where figure captions go" , Option "" ["table-caption-position"] (ReqArg (\arg opt -> do pos <- case arg of "above" -> return CaptionAbove "below" -> return CaptionBelow _ -> optError $ PandocOptionError $ T.pack "Argument of --table-caption-position must be above or below" return opt { optTableCaptionPosition = pos }) "above|below") "" -- "Specify where table captions go" , Option "" ["markdown-headings"] (ReqArg (\arg opt -> do headingFormat <- case arg of "setext" -> pure True "atx" -> pure False _ -> optError $ PandocOptionError $ T.pack "Argument of --markdown-headings must be setext or atx" pure opt { optSetextHeaders = headingFormat } ) "setext|atx") "" , Option "" ["list-tables"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--list-tables" arg return opt { optListTables = boolValue }) "true|false") "" -- "Use list tables for RST" , Option "" ["listings"] (OptArg (\arg opt -> do deprecatedOption "--listings" "Use --syntax-highlighting=idiomatic instead." boolValue <- readBoolFromOptArg "--listings" arg return $ if boolValue then opt { optSyntaxHighlighting = IdiomaticHighlightingString } else opt) "true|false") "" -- "Use listings package for LaTeX code blocks" , Option "i" ["incremental"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--incremental/-i" arg return opt { optIncremental = boolValue }) "true|false") "" -- "Make list items display incrementally in Slidy/Slideous/S5" , Option "" ["slide-level"] (ReqArg (\arg opt -> case safeStrRead arg of Just t | t >= 0 && t <= 6 -> return opt { optSlideLevel = Just t } _ -> optError $ PandocOptionError "Argument of --slide-level must be a number between 0 and 6") "NUMBER") "" -- "Force header level for slides" , Option "" ["section-divs"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--section-divs" arg return opt { optSectionDivs = boolValue }) "true|false") "" -- "Put sections in div tags in HTML" , Option "" ["html-q-tags"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--html-q-tags" arg return opt { optHtmlQTags = boolValue }) "true|false") "" -- "Use tags for quotes in HTML" , Option "" ["email-obfuscation"] (ReqArg (\arg opt -> do method <- case arg of "references" -> return ReferenceObfuscation "javascript" -> return JavascriptObfuscation "none" -> return NoObfuscation _ -> optError $ PandocOptionError $ T.pack "Argument of --email-obfuscation must be references, javascript, or none" return opt { optEmailObfuscation = method }) "none|javascript|references") "" -- "Method for obfuscating email in HTML" , Option "" ["id-prefix"] (ReqArg (\arg opt -> return opt { optIdentifierPrefix = T.pack arg }) "STRING") "" -- "Prefix to add to automatically generated HTML identifiers" , Option "T" ["title-prefix"] (ReqArg (\arg opt -> return opt { optVariables = setVariable "title-prefix" (T.pack arg) $ optVariables opt, optStandalone = True }) "STRING") "" -- "String to prefix to HTML window title" , Option "c" ["css"] (ReqArg (\arg opt -> return opt{ optCss = optCss opt ++ [arg] }) -- add new link to end, so it is included in proper order "URL") "" -- "Link to CSS style sheet" , Option "" ["epub-subdirectory"] (ReqArg (\arg opt -> return opt { optEpubSubdirectory = arg }) "DIRNAME") "" -- "Name of subdirectory for epub content in OCF container" , Option "" ["epub-cover-image"] (ReqArg (\arg opt -> return opt { optVariables = setVariable "epub-cover-image" (T.pack $ normalizePath arg) $ optVariables opt }) "FILE") "" -- "Path of epub cover image" , Option "" ["epub-title-page"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--epub-title-page" arg return opt{ optEpubTitlePage = boolValue }) "true|false") "" , Option "" ["epub-metadata"] (ReqArg (\arg opt -> return opt { optEpubMetadata = Just $ normalizePath arg }) "FILE") "" -- "Path of epub metadata file" , Option "" ["epub-embed-font"] (ReqArg (\arg opt -> return opt{ optEpubFonts = normalizePath arg : optEpubFonts opt }) "FILE") "" -- "Directory of fonts to embed" , Option "" ["split-level"] (ReqArg (\arg opt -> case safeStrRead arg of Just t | t >= 1 && t <= 6 -> return opt { optSplitLevel = t } _ -> optError $ PandocOptionError "Argument of --split-level must be a number between 1 and 6") "NUMBER") "" -- "Header level at which to split documents in chunked HTML or EPUB" , Option "" ["chunk-template"] (ReqArg (\arg opt -> return opt{ optChunkTemplate = Just (T.pack arg) }) "PATHTEMPLATE") "" -- "Template for file paths in chunkedhtml" , Option "" ["epub-chapter-level"] (ReqArg (\arg opt -> do deprecatedOption "--epub-chapter-level" "use --split-level" case safeStrRead arg of Just t | t >= 1 && t <= 6 -> return opt { optSplitLevel = t } _ -> optError $ PandocOptionError "Argument of --epub-chapter-level must be a number between 1 and 6") "NUMBER") "" -- "Header level at which to split documents in chunked HTML or EPUB" , Option "" ["ipynb-output"] (ReqArg (\arg opt -> case arg of "all" -> return opt{ optIpynbOutput = IpynbOutputAll } "best" -> return opt{ optIpynbOutput = IpynbOutputBest } "none" -> return opt{ optIpynbOutput = IpynbOutputNone } _ -> optError $ PandocOptionError "Argument of --ipynb-output must be all, none, or best") "all|none|best") "" -- "Starting number for sections, subsections, etc." , Option "C" ["citeproc"] (NoArg (\opt -> return opt { optFilters = optFilters opt ++ [CiteprocFilter] })) "" -- "Process citations" , Option "" ["bibliography"] (ReqArg (\arg opt -> return opt{ optMetadata = addMeta "bibliography" (normalizePath arg) $ optMetadata opt }) "FILE") "" , Option "" ["csl"] (ReqArg (\arg opt -> do case lookupMeta (T.pack "csl") $ optMetadata opt of Just _ -> optError $ PandocOptionError "--csl option can only be used once" Nothing -> return opt{ optMetadata = addMeta "csl" (normalizePath arg) $ optMetadata opt }) "FILE") "" , Option "" ["citation-abbreviations"] (ReqArg (\arg opt -> return opt{ optMetadata = addMeta "citation-abbreviations" (normalizePath arg) $ optMetadata opt }) "FILE") "" , Option "" ["natbib"] (NoArg (\opt -> return opt { optCiteMethod = Natbib })) "" -- "Use natbib cite commands in LaTeX output" , Option "" ["biblatex"] (NoArg (\opt -> return opt { optCiteMethod = Biblatex })) "" -- "Use biblatex cite commands in LaTeX output" , Option "" ["mathml"] (NoArg (\opt -> return opt { optHTMLMathMethod = MathML })) "" -- "Use mathml for HTML math" , Option "" ["webtex"] (OptArg (\arg opt -> do let url' = maybe defaultWebTeXURL T.pack arg return opt { optHTMLMathMethod = WebTeX url' }) "URL") "" -- "Use web service for HTML math" , Option "" ["mathjax"] (OptArg (\arg opt -> do let url' = maybe defaultMathJaxURL T.pack arg return opt { optHTMLMathMethod = MathJax url'}) "URL") "" -- "Use MathJax for HTML math" , Option "" ["katex"] (OptArg (\arg opt -> return opt { optHTMLMathMethod = KaTeX $ maybe defaultKaTeXURL T.pack arg }) "URL") "" -- Use KaTeX for HTML Math , Option "" ["gladtex"] (NoArg (\opt -> return opt { optHTMLMathMethod = GladTeX })) "" -- "Use gladtex for HTML math" , Option "" ["trace"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--trace" arg return opt { optTrace = boolValue }) "true|false") "" -- "Turn on diagnostic tracing in readers." , Option "" ["dump-args"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--dump-args" arg return opt { optDumpArgs = boolValue }) "true|false") "" -- "Print output filename and arguments to stdout." , Option "" ["ignore-args"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--ignore-args" arg return opt { optIgnoreArgs = boolValue }) "true|false") "" -- "Ignore command-line arguments." , Option "" ["verbose"] (NoArg (\opt -> return opt { optVerbosity = INFO })) "" -- "Verbose diagnostic output." , Option "" ["quiet"] (NoArg (\opt -> return opt { optVerbosity = ERROR })) "" -- "Suppress warnings." , Option "" ["fail-if-warnings"] (OptArg (\arg opt -> do boolValue <- readBoolFromOptArg "--fail-if-warnings" arg return opt { optFailIfWarnings = boolValue }) "true|false") "" -- "Exit with error status if there were warnings." , Option "" ["log"] (ReqArg (\arg opt -> return opt{ optLogFile = Just $ normalizePath arg }) "FILE") "" -- "Log messages in JSON format to this file." , Option "" ["bash-completion"] (NoArg (\_ -> optInfo BashCompletion)) "" -- "Print bash completion script" , Option "" ["list-input-formats"] (NoArg (\_ -> optInfo ListInputFormats)) "" , Option "" ["list-output-formats"] (NoArg (\_ -> optInfo ListOutputFormats)) "" , Option "" ["list-extensions"] (OptArg (\arg _ -> optInfo $ ListExtensions $ T.pack <$> arg) "FORMAT") "" , Option "" ["list-highlight-languages"] (NoArg (\_ -> optInfo ListHighlightLanguages)) "" , Option "" ["list-highlight-styles"] (NoArg (\_ -> optInfo ListHighlightStyles)) "" , Option "D" ["print-default-template"] (ReqArg (\arg opts -> optInfo $ PrintDefaultTemplate (optOutputFile opts) (T.pack arg)) "FORMAT") "" -- "Print default template for FORMAT" , Option "" ["print-default-data-file"] (ReqArg (\arg opts -> optInfo $ PrintDefaultDataFile (optOutputFile opts) (T.pack arg)) "FILE") "" -- "Print default data file" , Option "" ["print-highlight-style"] (ReqArg (\arg opts -> optInfo $ PrintHighlightStyle (optOutputFile opts) (T.pack arg)) "STYLE|FILE") "" -- "Print default template for FORMAT" , Option "v" ["version"] (NoArg (\_ -> optInfo VersionInfo)) "" -- "Print version" , Option "h" ["help"] (NoArg (\_ -> optInfo Help)) "" -- "Show help" ] optError :: PandocError -> ExceptT OptInfo IO a optError = throwError . OptError optInfo :: OptInfo -> ExceptT OptInfo IO a optInfo = throwError -- Returns usage message usageMessage :: String -> [OptDescr (Opt -> ExceptT OptInfo IO Opt)] -> String usageMessage programName = usageInfo (programName ++ " [OPTIONS] [FILES]") copyrightMessage :: String copyrightMessage = intercalate "\n" [ "Copyright (C) 2006-2025 John MacFarlane. Web: https://pandoc.org", "This is free software; see the source for copying conditions. There is no", "warranty, not even for merchantability or fitness for a particular purpose." ] handleUnrecognizedOption :: String -> [String] -> [String] handleUnrecognizedOption "--smart" = (("--smart/-S has been removed. Use +smart or -smart extension instead.\n" ++ "For example: pandoc -f markdown+smart -t markdown-smart.") :) handleUnrecognizedOption "--normalize" = ("--normalize has been removed. Normalization is now automatic." :) handleUnrecognizedOption "-S" = handleUnrecognizedOption "--smart" handleUnrecognizedOption "--old-dashes" = ("--old-dashes has been removed. Use +old_dashes extension instead." :) handleUnrecognizedOption "--no-wrap" = ("--no-wrap has been removed. Use --wrap=none instead." :) handleUnrecognizedOption "--latex-engine" = ("--latex-engine has been removed. Use --pdf-engine instead." :) handleUnrecognizedOption "--latex-engine-opt" = ("--latex-engine-opt has been removed. Use --pdf-engine-opt instead." :) handleUnrecognizedOption "--chapters" = ("--chapters has been removed. Use --top-level-division=chapter instead." :) handleUnrecognizedOption "--reference-docx" = ("--reference-docx has been removed. Use --reference-doc instead." :) handleUnrecognizedOption "--reference-odt" = ("--reference-odt has been removed. Use --reference-doc instead." :) handleUnrecognizedOption "--parse-raw" = ("--parse-raw/-R has been removed. Use +raw_html or +raw_tex extension.\n" :) handleUnrecognizedOption "--epub-stylesheet" = ("--epub-stylesheet has been removed. Use --css instead.\n" :) handleUnrecognizedOption "-R" = handleUnrecognizedOption "--parse-raw" handleUnrecognizedOption x = (("Unknown option " ++ x ++ ".") :) readersNames :: [Text] readersNames = sort (map fst (readers :: [(Text, Reader PandocIO)])) writersNames :: [Text] writersNames = sort ("pdf" : map fst (writers :: [(Text, Writer PandocIO)])) splitField :: String -> (String, String) splitField = second (tailDef "true") . break (\c -> c == ':' || c == '=') deprecatedOption :: String -> String -> ExceptT OptInfo IO () deprecatedOption o msg = do res <- liftIO $ runIO (report $ Deprecated (T.pack o) (T.pack msg)) case res of Right () -> return () Left e -> optError e -- | Set text value in text context. Create list if it has a value already, -- or add to a list value. setVariable :: Text -> Text -> Context Text -> Context Text setVariable key val (Context ctx) = Context $ M.alter go key ctx where go Nothing = Just $ toVal val go (Just (ListVal xs)) = Just $ ListVal $ xs ++ [toVal val] go (Just x) = Just $ ListVal [x, toVal val] addMeta :: String -> String -> Meta -> Meta addMeta k v meta = case lookupMeta k' meta of Nothing -> setMeta k' v' meta Just (MetaList xs) -> setMeta k' (MetaList (xs ++ [v'])) meta Just x -> setMeta k' (MetaList [x, v']) meta where v' = readMetaValue v k' = T.pack k readMetaValue :: String -> MetaValue readMetaValue s | s == "true" = MetaBool True | s == "True" = MetaBool True | s == "TRUE" = MetaBool True | s == "false" = MetaBool False | s == "False" = MetaBool False | s == "FALSE" = MetaBool False | otherwise = MetaString $ T.pack s readBoolFromOptArg :: Text -> Maybe String -> ExceptT OptInfo IO Bool readBoolFromOptArg opt = maybe (return True) readBoolFromArg where readBoolFromArg arg = case toLower <$> arg of "true" -> return True "false" -> return False _ -> optError $ PandocOptionError $ "Argument of " <> opt <> " must be either true or false" -- On Windows with ghc 8.6+, we need to rewrite paths -- beginning with \\ to \\?\UNC\. -- See #5127. normalizePath :: FilePath -> FilePath #ifdef _WINDOWS normalizePath fp = if "\\\\" `isPrefixOf` fp && not ("\\\\?\\" `isPrefixOf` fp) then "\\\\?\\UNC\\" ++ drop 2 fp else fp #else normalizePath = id #endif -- | Print version information with customizable features and scripting engine versionInfo :: [String] -> Maybe String -> String -> IO () versionInfo features mbScriptingEngineName suffix = do defaultDatadir <- defaultUserDataDir let featuresLine = if null features then [] else ["Features: " ++ unwords features] let scriptingLine = case mbScriptingEngineName of Nothing -> [] Just name -> ["Scripting engine: " ++ name] UTF8.putStr $ T.unlines $ map T.pack $ ["pandoc " ++ showVersion pandocVersion ++ suffix] ++ featuresLine ++ scriptingLine ++ ["User data directory: " ++ defaultDatadir, copyrightMessage] exitSuccess ================================================ FILE: src/Text/Pandoc/App/Input.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.App.Input Copyright : © 2006-2024 John MacFarlane License : GPL-2.0-or-later Maintainer : John MacFarlane Read from the file system into a pandoc document. -} module Text.Pandoc.App.Input ( InputParameters (..) , readInput ) where import Control.Monad ((>=>), when) import Control.Monad.Except (throwError, catchError) import Data.Text (Text) import Network.URI (URI (..), parseURI) import Text.Pandoc.Transforms (adjustLinksAndIds) import Text.Pandoc.Class ( PandocMonad, openURL, toTextM , readFileStrict, readStdinStrict, report) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Logging (LogMessage (..)) import Text.Pandoc.MIME (getCharset, MimeType) import Text.Pandoc.Options (ReaderOptions (..)) import Text.Pandoc.Readers (Reader (..)) import Text.Pandoc.Shared (tabFilter) import Text.Pandoc.URI (uriPathToPath) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T -- | Settings specifying how and which input should be processed. data InputParameters m = InputParameters { inputReader :: Reader m , inputReaderName :: Text , inputReaderOptions :: ReaderOptions , inputSources :: [FilePath] , inputSpacesPerTab :: Maybe Int , inputFileScope :: Bool } -- | Read all input into a pandoc document. readInput :: PandocMonad m => InputParameters m -> m Pandoc readInput params = do let sources = inputSources params let readerName = inputReaderName params let readerOpts = inputReaderOptions params let convertTabs :: Text -> Text convertTabs = tabFilter $ case inputSpacesPerTab params of Nothing -> 0 Just ts -> if readerName `elem` ["t2t", "man", "tsv"] then 0 else ts inputs <- readSources sources case inputReader params of TextReader r | readerName == "json" -> mconcat <$> mapM (inputToText convertTabs >=> r readerOpts . (:[])) inputs | inputFileScope params -> mconcat <$> mapM (\source -> do (fp, txt) <- inputToText convertTabs source adjustLinksAndIds (readerExtensions readerOpts) (T.pack fp) (map (T.pack . fst) inputs) <$> r readerOpts [(fp, txt)]) inputs | otherwise -> mapM (inputToText convertTabs) inputs >>= r readerOpts ByteStringReader r -> mconcat <$> mapM (r readerOpts . inputToLazyByteString) inputs readSources :: PandocMonad m => [FilePath] -> m [(FilePath, (BS.ByteString, Maybe MimeType))] readSources srcs = mapM (\fp -> do t <- readSource fp return (if fp == "-" then "" else fp, t)) srcs -- | Read input from a resource, i.e., either a file, a URL, or stdin -- (@-@). readSource :: PandocMonad m => FilePath -> m (BS.ByteString, Maybe MimeType) readSource "-" = (,Nothing) <$> readStdinStrict readSource src = case parseURI src of Just u | uriScheme u `elem` ["http:","https:"] -> openURL (T.pack src) | uriScheme u == "file:" -> (,Nothing) <$> readFileStrict (uriPathToPath $ T.pack $ uriPath u) _ -> (,Nothing) <$> readFileStrict src inputToText :: PandocMonad m => (Text -> Text) -> (FilePath, (BS.ByteString, Maybe MimeType)) -> m (FilePath, Text) inputToText convTabs (fp, (bs,mt)) = (fp,) . convTabs . T.filter (/='\r') <$> case mt >>= getCharset of Just "UTF-8" -> toTextM fp bs Just "ISO-8859-1" -> return $ T.pack $ B8.unpack bs Just charset -> throwError $ PandocUnsupportedCharsetError charset Nothing -> catchError (toTextM fp bs) (\case PandocUTF8DecodingError{} -> do when (hasKnownSignature bs) $ throwError $ PandocInputNotTextError (T.pack fp) report $ NotUTF8Encoded (if null fp then "input" else fp) return $ T.pack $ B8.unpack bs e -> throwError e) where -- "50 4B 03 04" is zip file signature isZip bs' = "\x50\x4B\x03\x04" `BS.isPrefixOf` bs' -- "25 50 44 46 2D" is PDF file signature isPDF bs' = "\x25\x50\x44\x46\x2D" `BS.isPrefixOf` bs' -- "D0 CF 11 E0 A1 B1 1A E1" is Compound File Binary Format signature used in -- variety of old Microsoft formats (.doc and .xls among others) isCFBF bs' = "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" `BS.isPrefixOf` bs' -- "41 54 26 54 46 4F 52 4D ?? ?? ?? ?? 44 4A 56" is DjVu signature isDjVu bs' = case BS.stripPrefix "\x41\x54\x26\x54\x46\x4F\x52\x4D" bs' of Nothing -> False Just x -> BS.isPrefixOf "\x44\x4A\x56" $ BS.drop 4 x hasKnownSignature bs' = any ($ bs') [isZip, isPDF, isCFBF, isDjVu] inputToLazyByteString :: (FilePath, (BS.ByteString, Maybe MimeType)) -> BL.ByteString inputToLazyByteString (_, (bs,_)) = BL.fromStrict bs ================================================ FILE: src/Text/Pandoc/App/Opt.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {- | Module : Text.Pandoc.App.Opt Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Options for pandoc when used as an app. -} module Text.Pandoc.App.Opt ( Opt(..) , OptInfo(..) , LineEnding (..) , IpynbOutput (..) , DefaultsState (..) , defaultOpts , applyDefaults , fullDefaultsPath ) where import Control.Monad.Except (throwError) 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, isSpace) import Data.Maybe (fromMaybe) import GHC.Generics hiding (Meta) import Text.Pandoc.Filter (Filter (..)) import Text.Pandoc.Logging (Verbosity (WARNING), LogMessage(..)) import Text.Pandoc.Options (TopLevelDivision (TopLevelDefault), TrackChanges (AcceptChanges), WrapOption (WrapAuto), HTMLMathMethod (PlainMath), ReferenceLocation (EndOfDocument), CaptionPosition (..), ObfuscationMethod (NoObfuscation), CiteMethod (Citeproc), pattern DefaultHighlightingString) import Text.Pandoc.Class (readFileStrict, fileExists, setVerbosity, report, PandocMonad(lookupEnv), getUserDataDir) import Text.Pandoc.Error (PandocError (PandocParseError, PandocSomeError)) import Data.Containers.ListUtils (nubOrd) import Text.Pandoc.Data (defaultUserDataDir) import qualified Text.Pandoc.Parsing as P import Text.Pandoc.Readers.Metadata (yamlMap) import Text.Pandoc.Class.PandocPure import Text.DocTemplates (Context(..)) import Data.Text (Text, unpack) import Data.Default (def) import qualified Data.Text as T import qualified Data.Map as M import qualified Data.ByteString.Char8 as B8 import Text.Pandoc.Definition (Meta(..), MetaValue(..)) import Data.Aeson (defaultOptions, Options(..), Result(..), genericToJSON, fromJSON, camelTo2, eitherDecodeStrict) import Data.Aeson.TH (deriveJSON) import Control.Applicative ((<|>)) import Data.Yaml -- | The type of line-endings to be used when writing plain-text. data LineEnding = LF | CRLF | Native deriving (Show, Generic) -- see https://github.com/jgm/pandoc/pull/4083 -- using generic deriving caused long compilation times $(deriveJSON defaultOptions{ constructorTagModifier = map toLower } ''LineEnding) -- | How to handle output blocks in ipynb. data IpynbOutput = IpynbOutputAll | IpynbOutputNone | IpynbOutputBest deriving (Show, Generic) $(deriveJSON defaultOptions{ fieldLabelModifier = map toLower . drop 11 } ''IpynbOutput) -- | Option parser results requesting informational output. data OptInfo = BashCompletion | ListInputFormats | ListOutputFormats | ListExtensions (Maybe Text) | ListHighlightLanguages | ListHighlightStyles | PrintDefaultTemplate (Maybe FilePath) Text | PrintDefaultDataFile (Maybe FilePath) Text | PrintHighlightStyle (Maybe FilePath) Text | VersionInfo | Help | OptError PandocError deriving (Show, Generic) -- | Data structure for command line options. data Opt = Opt { optTabStop :: Int -- ^ Number of spaces per tab , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces , optStandalone :: Bool -- ^ Include header, footer , optFrom :: Maybe Text -- ^ Reader format , optTo :: Maybe Text -- ^ Writer format , optTableOfContents :: Bool -- ^ Include table of contents , optListOfFigures :: Bool -- ^ Include list of figures , optListOfTables :: Bool -- ^ Include list of tables , optShiftHeadingLevelBy :: Int -- ^ Shift heading level by , optTemplate :: Maybe FilePath -- ^ Custom template , optVariables :: Context Text -- ^ Template variables to set , optMetadata :: Meta -- ^ Metadata fields to set , optMetadataFiles :: [FilePath] -- ^ Name of YAML metadata files , optOutputFile :: Maybe FilePath -- ^ Name of output file , optInputFiles :: Maybe [FilePath] -- ^ Names of input files , optNumberSections :: Bool -- ^ Number sections in LaTeX , optNumberOffset :: [Int] -- ^ Starting number for sections , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5 , optSelfContained :: Bool -- ^ Make HTML accessible offline (deprecated) , optEmbedResources :: Bool -- ^ Make HTML accessible offline , optLinkImages :: Bool -- ^ Link ODT images rather than embedding , optHtmlQTags :: Bool -- ^ Use tags in HTML , optSyntaxDefinitions :: [FilePath] -- ^ xml syntax defs to load , optSyntaxHighlighting :: Text -- ^ Syntax highlighting method for code , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math , optAbbreviations :: Maybe FilePath -- ^ Path to abbrevs file , optReferenceDoc :: Maybe FilePath -- ^ Path of reference doc , optSplitLevel :: Int -- ^ Header level at which to split documents in epub and chunkedhtml , optChunkTemplate :: Maybe Text -- ^ Template to use for chunk filenames , optEpubSubdirectory :: String -- ^ EPUB subdir in OCF container , optEpubMetadata :: Maybe FilePath -- ^ EPUB metadata , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed , optEpubCoverImage :: Maybe FilePath -- ^ Cover image for epub , optEpubTitlePage :: Bool -- ^ INclude title page in EPUB , optTOCDepth :: Int -- ^ Number of levels to include in TOC , optDumpArgs :: Bool -- ^ Output command-line arguments , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments , optVerbosity :: Verbosity -- ^ Verbosity of diagnostic output , optTrace :: Bool -- ^ Enable tracing , optLogFile :: Maybe FilePath -- ^ File to write JSON log output , optFailIfWarnings :: Bool -- ^ Fail on warnings , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output , optFigureCaptionPosition :: CaptionPosition -- ^ position for figure caption , optTableCaptionPosition :: CaptionPosition -- ^ position for table caption , optDpi :: Int -- ^ Dpi , optWrap :: WrapOption -- ^ Options for wrapping text , optColumns :: Int -- ^ Line length in characters , optFilters :: [Filter] -- ^ Filters to apply , optEmailObfuscation :: ObfuscationMethod , optIdentifierPrefix :: Text , optIndentedCodeClasses :: [Text] -- ^ Default classes for indented code blocks , optDataDir :: Maybe FilePath , optCiteMethod :: CiteMethod -- ^ Method to output cites , optPdfEngine :: Maybe String -- ^ Program to use for latex/html -> pdf , optPdfEngineOpts :: [String] -- ^ Flags to pass to the engine , optSlideLevel :: Maybe Int -- ^ Header level that creates slides , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2 , optListTables :: Bool -- ^ Use list tables for RST , optAscii :: Bool -- ^ Prefer ascii output , optDefaultImageExtension :: Text -- ^ Default image extension , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes. , optFileScope :: Bool -- ^ Parse input files before combining , optTitlePrefix :: Maybe Text -- ^ Prefix for title , optCss :: [FilePath] -- ^ CSS files to link to , optIpynbOutput :: IpynbOutput -- ^ How to treat ipynb output blocks , optIncludeBeforeBody :: [FilePath] -- ^ Files to include before , optIncludeAfterBody :: [FilePath] -- ^ Files to include after body , optIncludeInHeader :: [FilePath] -- ^ Files to include in header , optResourcePath :: [FilePath] -- ^ Path to search for images etc , optRequestHeaders :: [(Text, Text)] -- ^ Headers for HTTP requests , optNoCheckCertificate :: Bool -- ^ Disable certificate validation , optEol :: LineEnding -- ^ Style of line-endings to use , optStripComments :: Bool -- ^ Skip HTML comments , optCSL :: Maybe FilePath -- ^ CSL stylesheet , optBibliography :: [FilePath] -- ^ Bibliography files , optCitationAbbreviations :: Maybe FilePath -- ^ Citation abbreviations , optSandbox :: Bool } deriving (Generic, Show) instance FromJSON Opt where parseJSON = withObject "Opt" $ \o -> Opt <$> o .:? "tab-stop" .!= optTabStop defaultOpts <*> o .:? "preserve-tabs" .!= optPreserveTabs defaultOpts <*> o .:? "standalone" .!= optStandalone defaultOpts <*> o .:? "from" <*> o .:? "to" <*> o .:? "table-of-contents" .!= optTableOfContents defaultOpts <*> o .:? "list-of-figures" .!= optListOfFigures defaultOpts <*> o .:? "list-of-tables" .!= optListOfTables defaultOpts <*> o .:? "shift-heading-level-by" .!= optShiftHeadingLevelBy defaultOpts <*> o .:? "template" <*> o .:? "variables" .!= optVariables defaultOpts <*> o .:? "metadata" .!= optMetadata defaultOpts <*> o .:? "metadata-files" .!= optMetadataFiles defaultOpts <*> o .:? "output-file" <*> o .:? "input-files" <*> o .:? "number-sections" .!= optNumberSections defaultOpts <*> o .:? "number-offset" .!= optNumberOffset defaultOpts <*> o .:? "section-divs" .!= optSectionDivs defaultOpts <*> o .:? "incremental" .!= optIncremental defaultOpts <*> o .:? "self-contained" .!= optSelfContained defaultOpts <*> o .:? "embed-resources" .!= optEmbedResources defaultOpts <*> o .:? "link-images" .!= optLinkImages defaultOpts <*> o .:? "html-q-tags" .!= optHtmlQTags defaultOpts <*> o .:? "syntax-definitions" .!= optSyntaxDefinitions defaultOpts <*> o .:? "syntax-highlighting" .!= optSyntaxHighlighting defaultOpts <*> o .:? "top-level-division" .!= optTopLevelDivision defaultOpts <*> o .:? "html-math-method" .!= optHTMLMathMethod defaultOpts <*> o .:? "abbreviations" <*> o .:? "reference-doc" <*> ((o .:? "split-level") <|> (o .:? "epub-chapter-level")) .!= optSplitLevel defaultOpts <*> o .:? "chunk-template" <*> o .:? "epub-subdirectory" .!= optEpubSubdirectory defaultOpts <*> o .:? "epub-metadata" <*> o .:? "epub-fonts" .!= optEpubFonts defaultOpts <*> o .:? "epub-cover-image" <*> o .:? "epub-title-page" .!= optEpubTitlePage defaultOpts <*> o .:? "toc-depth" .!= optTOCDepth defaultOpts <*> o .:? "dump-args" .!= optDumpArgs defaultOpts <*> o .:? "ignore-args" .!= optIgnoreArgs defaultOpts <*> o .:? "verbosity" .!= optVerbosity defaultOpts <*> o .:? "trace" .!= optTrace defaultOpts <*> o .:? "log-file" <*> o .:? "fail-if-warnings" .!= optFailIfWarnings defaultOpts <*> o .:? "reference-links" .!= optReferenceLinks defaultOpts <*> o .:? "reference-location" .!= optReferenceLocation defaultOpts <*> o .:? "figure-caption-position" .!= optFigureCaptionPosition defaultOpts <*> o .:? "table-caption-position" .!= optTableCaptionPosition defaultOpts <*> o .:? "dpi" .!= optDpi defaultOpts <*> o .:? "wrap" .!= optWrap defaultOpts <*> o .:? "columns" .!= optColumns defaultOpts <*> o .:? "filters" .!= optFilters defaultOpts <*> o .:? "email-obfuscation" .!= optEmailObfuscation defaultOpts <*> o .:? "identifier-prefix" .!= optIdentifierPrefix defaultOpts <*> o .:? "indented-code-classes" .!= optIndentedCodeClasses defaultOpts <*> o .:? "data-dir" <*> o .:? "cite-method" .!= optCiteMethod defaultOpts <*> o .:? "pdf-engine" <*> o .:? "pdf-engine-opts" .!= optPdfEngineOpts defaultOpts <*> o .:? "slide-level" <*> o .:? "setext-headers" .!= optSetextHeaders defaultOpts <*> o .:? "list-tables" .!= optListTables defaultOpts <*> o .:? "ascii" .!= optAscii defaultOpts <*> o .:? "default-image-extension" .!= optDefaultImageExtension defaultOpts <*> o .:? "extract-media" <*> o .:? "track-changes" .!= optTrackChanges defaultOpts <*> o .:? "file-scope" .!= optFileScope defaultOpts <*> o .:? "title-prefix" .!= optTitlePrefix defaultOpts <*> o .:? "css" .!= optCss defaultOpts <*> o .:? "ipynb-output" .!= optIpynbOutput defaultOpts <*> o .:? "include-before-body" .!= optIncludeBeforeBody defaultOpts <*> o .:? "include-after-body" .!= optIncludeAfterBody defaultOpts <*> o .:? "include-in-header" .!= optIncludeInHeader defaultOpts <*> o .:? "resource-path" .!= optResourcePath defaultOpts <*> o .:? "request-headers" .!= optRequestHeaders defaultOpts <*> o .:? "no-check-certificate" .!= optNoCheckCertificate defaultOpts <*> o .:? "eol" .!= optEol defaultOpts <*> o .:? "strip-comments" .!= optStripComments defaultOpts <*> o .:? "csl" <*> o .:? "bibliography" .!= optBibliography defaultOpts <*> o .:? "citation-abbreviations" <*> o .:? "sandbox" .!= optSandbox defaultOpts instance ToJSON Opt where toJSON = genericToJSON defaultOptions{ fieldLabelModifier = camelTo2 '-' . drop 3, omitNothingFields = True } instance FromJSON (Opt -> Opt) where parseJSON (Object m) = case fromJSON (Object m) of Error err' -> fail err' Success (m' :: M.Map Text Value) -> chain doOpt (M.toList m') parseJSON _ = fail "Expected a mapping" data DefaultsState = DefaultsState { curDefaults :: Maybe FilePath -- currently parsed file , inheritanceGraph :: [[FilePath]] -- defaults file inheritance graph } deriving (Show) instance (PandocMonad m, MonadIO m) => FromJSON (Opt -> StateT DefaultsState m Opt) where parseJSON (Object o) = case fromJSON (Object o) of Error err' -> fail err' Success (opts :: M.Map Text Value) -> do dataDir <- case M.lookup "data-dir" opts of Nothing -> return Nothing Just v -> Just . unpack <$> parseJSON v f <- parseOptions (M.toList opts) case M.lookup "defaults" opts of Just v -> do g <- parseDefaults v dataDir return $ g >=> f >=> resolveVarsInOpt Nothing -> return $ f >=> resolveVarsInOpt parseJSON _ = fail "Expected a mapping" resolveVarsInOpt :: forall m. (PandocMonad m, MonadIO m) => Opt -> StateT DefaultsState m Opt resolveVarsInOpt opt@Opt { optTo = oTo , optFrom = oFrom , optTemplate = oTemplate , optMetadataFiles = oMetadataFiles , optOutputFile = oOutputFile , optInputFiles = oInputFiles , optSyntaxDefinitions = oSyntaxDefinitions , optSyntaxHighlighting = oSyntaxHighlighting , optAbbreviations = oAbbreviations , optReferenceDoc = oReferenceDoc , optEpubMetadata = oEpubMetadata , optEpubFonts = oEpubFonts , optEpubCoverImage = oEpubCoverImage , optLogFile = oLogFile , optFilters = oFilters , optDataDir = oDataDir , optExtractMedia = oExtractMedia , optCss = oCss , optIncludeBeforeBody = oIncludeBeforeBody , optIncludeAfterBody = oIncludeAfterBody , optIncludeInHeader = oIncludeInHeader , optResourcePath = oResourcePath , optCSL = oCSL , optBibliography = oBibliography , optCitationAbbreviations = oCitationAbbreviations , optPdfEngine = oPdfEngine } = do oTo' <- mapM (fmap T.pack . resolveVars . T.unpack) oTo oFrom' <- mapM (fmap T.pack . resolveVars . T.unpack) oFrom oTemplate' <- mapM resolveVars oTemplate oMetadataFiles' <- mapM resolveVars oMetadataFiles oOutputFile' <- mapM resolveVars oOutputFile oInputFiles' <- mapM (mapM resolveVars) oInputFiles oSyntaxDefinitions' <- mapM resolveVars oSyntaxDefinitions oAbbreviations' <- mapM resolveVars oAbbreviations oReferenceDoc' <- mapM resolveVars oReferenceDoc oEpubMetadata' <- mapM resolveVars oEpubMetadata oEpubFonts' <- mapM resolveVars oEpubFonts oEpubCoverImage' <- mapM resolveVars oEpubCoverImage oLogFile' <- mapM resolveVars oLogFile oFilters' <- mapM resolveVarsInFilter oFilters oDataDir' <- mapM resolveVars oDataDir oExtractMedia' <- mapM resolveVars oExtractMedia oCss' <- mapM resolveVars oCss oIncludeBeforeBody' <- mapM resolveVars oIncludeBeforeBody oIncludeAfterBody' <- mapM resolveVars oIncludeAfterBody oIncludeInHeader' <- mapM resolveVars oIncludeInHeader oResourcePath' <- mapM resolveVars oResourcePath oCSL' <- mapM resolveVars oCSL oBibliography' <- mapM resolveVars oBibliography oCitationAbbreviations' <- mapM resolveVars oCitationAbbreviations oPdfEngine' <- mapM resolveVars oPdfEngine oSyntaxHighlighting' <- T.pack <$> resolveVars (T.unpack oSyntaxHighlighting) return opt{ optTo = oTo' , optFrom = oFrom' , optTemplate = oTemplate' , optMetadataFiles = oMetadataFiles' , optOutputFile = oOutputFile' , optInputFiles = oInputFiles' , optSyntaxDefinitions = oSyntaxDefinitions' , optSyntaxHighlighting = oSyntaxHighlighting' , optAbbreviations = oAbbreviations' , optReferenceDoc = oReferenceDoc' , optEpubMetadata = oEpubMetadata' , optEpubFonts = oEpubFonts' , optEpubCoverImage = oEpubCoverImage' , optLogFile = oLogFile' , optFilters = oFilters' , optDataDir = oDataDir' , optExtractMedia = oExtractMedia' , optCss = oCss' , optIncludeBeforeBody = oIncludeBeforeBody' , optIncludeAfterBody = oIncludeAfterBody' , optIncludeInHeader = oIncludeInHeader' , optResourcePath = oResourcePath' , optCSL = oCSL' , optBibliography = oBibliography' , optCitationAbbreviations = oCitationAbbreviations' , optPdfEngine = oPdfEngine' } where 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 <- 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 expandEnv mbDataDir "USERDATA" = do mbodatadir <- mapM (expandVars mbDataDir) mbDataDir mbdatadir' <- getUserDataDir defdatadir <- liftIO defaultUserDataDir 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 parseDefaults :: (PandocMonad m, MonadIO m) => Value -> Maybe FilePath -> Parser (Opt -> StateT DefaultsState m Opt) 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 (\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 modify $ \defsState -> defsState{ inheritanceGraph = defsGraphExp } -- check for cyclic inheritance: if cyclic defsGraphExp then throwError $ PandocSomeError $ T.pack $ "Error: Circular defaults file reference in " ++ "'" ++ defsParent ++ "'" else foldM applyDefaults o defsChildren where parseDefsNames x = (parseJSON x >>= \xs -> return $ map unpack xs) <|> (parseJSON x >>= \x' -> return [unpack x']) parseOptions :: Monad m => [(Text, Value)] -> Parser (Opt -> StateT DefaultsState m Opt) parseOptions ns = do f <- chain doOpt' ns return $ return . f chain :: Monad m => (a -> m (b -> b)) -> [a] -> m (b -> b) chain f = foldM g id where g o n = f n >>= \o' -> return $ o' . o doOpt' :: (Text, Value) -> Parser (Opt -> Opt) doOpt' (k,v) = do case k of "defaults" -> return id _ -> doOpt (k,v) doOpt :: (Text, Value) -> Parser (Opt -> Opt) doOpt (k,v) = do case k of "tab-stop" -> parseJSON v >>= \x -> return (\o -> o{ optTabStop = x }) "preserve-tabs" -> parseJSON v >>= \x -> return (\o -> o{ optPreserveTabs = x }) "standalone" -> parseJSON v >>= \x -> return (\o -> o{ optStandalone = x }) "table-of-contents" -> parseJSON v >>= \x -> return (\o -> o{ optTableOfContents = x }) "toc" -> parseJSON v >>= \x -> return (\o -> o{ optTableOfContents = x }) "list-of-figures" -> parseJSON v >>= \x -> return (\o -> o{ optListOfFigures = x }) "lof" -> parseJSON v >>= \x -> return (\o -> o{ optListOfFigures = x }) "list-of-tables" -> parseJSON v >>= \x -> return (\o -> o{ optListOfTables = x }) "lot" -> parseJSON v >>= \x -> return (\o -> o{ optListOfTables = x }) "from" -> parseJSON v >>= \x -> return (\o -> o{ optFrom = x }) "reader" -> parseJSON v >>= \x -> return (\o -> o{ optFrom = x }) "to" -> parseJSON v >>= \x -> return (\o -> o{ optTo = x }) "writer" -> parseJSON v >>= \x -> return (\o -> o{ optTo = x }) "shift-heading-level-by" -> parseJSON v >>= \x -> return (\o -> o{ optShiftHeadingLevelBy = x }) "template" -> parseJSON v >>= \x -> return (\o -> o{ optTemplate = unpack <$> x }) "variables" -> parseJSON v >>= \x -> return (\o -> o{ optVariables = x <> optVariables o }) -- Note: x comes first because <> for Context is left-biased union -- and we want to favor later default files. See #5988. "metadata" -> yamlToMeta v >>= \x -> return (\o -> o{ optMetadata = optMetadata o <> x }) "metadata-files" -> parseJSON v >>= \x -> return (\o -> o{ optMetadataFiles = optMetadataFiles o <> map unpack x }) "metadata-file" -> -- allow either a list or a single value (parseJSON v >>= \x -> return (\o -> o{ optMetadataFiles = optMetadataFiles o <> map unpack x })) <|> (parseJSON v >>= \x -> return (\o -> o{ optMetadataFiles = optMetadataFiles o <>[unpack x] })) "output-file" -> parseJSON v >>= \x -> return (\o -> o{ optOutputFile = unpack <$> x }) "input-files" -> parseJSON v >>= \x -> return (\o -> o{ optInputFiles = optInputFiles o <> (map unpack <$> x) }) "input-file" -> -- allow either a list or a single value (parseJSON v >>= \x -> return (\o -> o{ optInputFiles = optInputFiles o <> (map unpack <$> x) })) <|> (parseJSON v >>= \x -> return (\o -> o{ optInputFiles = optInputFiles o <> ((\z -> [unpack z]) <$> x) })) "number-sections" -> parseJSON v >>= \x -> return (\o -> o{ optNumberSections = x }) "number-offset" -> parseJSON v >>= \x -> return (\o -> o{ optNumberOffset = x }) "section-divs" -> parseJSON v >>= \x -> return (\o -> o{ optSectionDivs = x }) "incremental" -> parseJSON v >>= \x -> return (\o -> o{ optIncremental = x }) "self-contained" -> parseJSON v >>= \x -> return (\o -> o{ optSelfContained = x }) "embed-resources" -> parseJSON v >>= \x -> return (\o -> o{ optEmbedResources = x }) "link-images" -> parseJSON v >>= \x -> return (\o -> o{ optLinkImages = x }) "html-q-tags" -> parseJSON v >>= \x -> return (\o -> o{ optHtmlQTags = x }) -- Deprecated "highlight-style" -> parseJSON v >>= \x -> return (\o -> o{ optSyntaxHighlighting = x }) "syntax-definition" -> (parseJSON v >>= \x -> return (\o -> o{ optSyntaxDefinitions = optSyntaxDefinitions o <> map unpack x })) <|> (parseJSON v >>= \x -> return (\o -> o{ optSyntaxDefinitions = optSyntaxDefinitions o <> [unpack x] })) "syntax-definitions" -> parseJSON v >>= \x -> return (\o -> o{ optSyntaxDefinitions = optSyntaxDefinitions o <> map unpack x }) "syntax-highlighting" -> parseJSON v >>= \x -> return (\o -> o{ optSyntaxHighlighting = x }) "top-level-division" -> parseJSON v >>= \x -> return (\o -> o{ optTopLevelDivision = x }) "html-math-method" -> parseJSON v >>= \x -> return (\o -> o{ optHTMLMathMethod = x }) "abbreviations" -> parseJSON v >>= \x -> return (\o -> o{ optAbbreviations = unpack <$> x }) "reference-doc" -> parseJSON v >>= \x -> return (\o -> o{ optReferenceDoc = unpack <$> x }) "epub-subdirectory" -> parseJSON v >>= \x -> return (\o -> o{ optEpubSubdirectory = unpack x }) "epub-metadata" -> parseJSON v >>= \x -> return (\o -> o{ optEpubMetadata = unpack <$> x }) "epub-fonts" -> parseJSON v >>= \x -> return (\o -> o{ optEpubFonts = optEpubFonts o <> map unpack x }) "epub-chapter-level" -> parseJSON v >>= \x -> return (\o -> o{ optSplitLevel = x }) "split-level" -> parseJSON v >>= \x -> return (\o -> o{ optSplitLevel = x }) "chunk-template" -> parseJSON v >>= \x -> return (\o -> o{ optChunkTemplate = Just x }) "epub-cover-image" -> parseJSON v >>= \x -> return (\o -> o{ optEpubCoverImage = unpack <$> x }) "epub-title-page" -> parseJSON v >>= \x -> return (\o -> o{ optEpubTitlePage = x }) "toc-depth" -> parseJSON v >>= \x -> return (\o -> o{ optTOCDepth = x }) "dump-args" -> parseJSON v >>= \x -> return (\o -> o{ optDumpArgs = x }) "ignore-args" -> parseJSON v >>= \x -> return (\o -> o{ optIgnoreArgs = x }) "verbosity" -> parseJSON v >>= \x -> return (\o -> o{ optVerbosity = x }) "trace" -> parseJSON v >>= \x -> return (\o -> o{ optTrace = x }) "log-file" -> parseJSON v >>= \x -> return (\o -> o{ optLogFile = unpack <$> x }) "fail-if-warnings" -> parseJSON v >>= \x -> return (\o -> o{ optFailIfWarnings = x }) "reference-links" -> parseJSON v >>= \x -> return (\o -> o{ optReferenceLinks = x }) "reference-location" -> parseJSON v >>= \x -> return (\o -> o{ optReferenceLocation = x }) "figure-caption-position" -> parseJSON v >>= \x -> return (\o -> o{ optFigureCaptionPosition = x }) "table-caption-position" -> parseJSON v >>= \x -> return (\o -> o{ optTableCaptionPosition = x }) "dpi" -> parseJSON v >>= \x -> return (\o -> o{ optDpi = x }) "wrap" -> parseJSON v >>= \x -> return (\o -> o{ optWrap = x }) "columns" -> parseJSON v >>= \x -> return (\o -> o{ optColumns = x }) "filters" -> parseJSON v >>= \x -> return (\o -> o{ optFilters = optFilters o <> x }) "citeproc" -> parseJSON v >>= \x -> if x then return (\o -> o{ optFilters = CiteprocFilter : optFilters o }) else return id "email-obfuscation" -> parseJSON v >>= \x -> return (\o -> o{ optEmailObfuscation = x }) "identifier-prefix" -> parseJSON v >>= \x -> return (\o -> o{ optIdentifierPrefix = x }) "indented-code-classes" -> parseJSON v >>= \x -> return (\o -> o{ optIndentedCodeClasses = x }) "data-dir" -> parseJSON v >>= \x -> return (\o -> o{ optDataDir = unpack <$> x }) "cite-method" -> parseJSON v >>= \x -> return (\o -> o{ optCiteMethod = x }) "listings" -> parseJSON v >>= \x -> if x then return (\o -> o{ optSyntaxHighlighting = "idiomatic" }) else return id "pdf-engine" -> parseJSON v >>= \x -> return (\o -> o{ optPdfEngine = unpack <$> x }) "pdf-engine-opts" -> parseJSON v >>= \x -> return (\o -> o{ optPdfEngineOpts = map unpack x }) "pdf-engine-opt" -> (parseJSON v >>= \x -> return (\o -> o{ optPdfEngineOpts = map unpack x })) <|> (parseJSON v >>= \x -> return (\o -> o{ optPdfEngineOpts = [unpack x] })) "slide-level" -> parseJSON v >>= \x -> return (\o -> o{ optSlideLevel = x }) "markdown-headings" -> parseJSON v >>= \x -> return (\o -> case T.toLower x of "atx" -> o{ optSetextHeaders = False } "setext" -> o{ optSetextHeaders = True } _ -> o) "list-tables" -> parseJSON v >>= \x -> return (\o -> o{ optListTables = x }) "ascii" -> parseJSON v >>= \x -> return (\o -> o{ optAscii = x }) "default-image-extension" -> parseJSON v >>= \x -> return (\o -> o{ optDefaultImageExtension = x }) "extract-media" -> parseJSON v >>= \x -> return (\o -> o{ optExtractMedia = unpack <$> x }) "track-changes" -> parseJSON v >>= \x -> return (\o -> o{ optTrackChanges = x }) "file-scope" -> parseJSON v >>= \x -> return (\o -> o{ optFileScope = x }) "title-prefix" -> parseJSON v >>= \x -> return (\o -> o{ optTitlePrefix = x, optStandalone = True }) "css" -> (parseJSON v >>= \x -> return (\o -> o{ optCss = optCss o <> map unpack x })) <|> (parseJSON v >>= \x -> return (\o -> o{ optCss = optCss o <> [unpack x] })) "bibliography" -> (parseJSON v >>= \x -> return (\o -> o{ optBibliography = optBibliography o <> map unpack x })) <|> (parseJSON v >>= \x -> return (\o -> o{ optBibliography = optBibliography o <> [unpack x] })) "csl" -> parseJSON v >>= \x -> return (\o -> o{ optCSL = unpack <$> x }) "citation-abbreviations" -> parseJSON v >>= \x -> return (\o -> o{ optCitationAbbreviations = unpack <$> x }) "ipynb-output" -> parseJSON v >>= \x -> return (\o -> o{ optIpynbOutput = x }) "include-before-body" -> (parseJSON v >>= \x -> return (\o -> o{ optIncludeBeforeBody = optIncludeBeforeBody o <> map unpack x })) <|> (parseJSON v >>= \x -> return (\o -> o{ optIncludeBeforeBody = optIncludeBeforeBody o <> [unpack x] })) "include-after-body" -> (parseJSON v >>= \x -> return (\o -> o{ optIncludeAfterBody = optIncludeAfterBody o <> map unpack x })) <|> (parseJSON v >>= \x -> return (\o -> o{ optIncludeAfterBody = optIncludeAfterBody o <> [unpack x] })) "include-in-header" -> (parseJSON v >>= \x -> return (\o -> o{ optIncludeInHeader = optIncludeInHeader o <> map unpack x })) <|> (parseJSON v >>= \x -> return (\o -> o{ optIncludeInHeader = optIncludeInHeader o <> [unpack x] })) "resource-path" -> parseJSON v >>= \x -> return (\o -> o{ optResourcePath = map unpack x <> optResourcePath o }) "request-headers" -> parseJSON v >>= \x -> return (\o -> o{ optRequestHeaders = x }) "no-check-certificate" -> parseJSON v >>= \x -> return (\o -> o{ optNoCheckCertificate = x }) "eol" -> parseJSON v >>= \x -> return (\o -> o{ optEol = x }) "strip-comments" -> parseJSON v >>= \x -> return (\o -> o { optStripComments = x }) "sandbox" -> parseJSON v >>= \x -> return (\o -> o { optSandbox = x }) _ -> fail $ "Unknown option " ++ show k -- | Defaults for command-line options. defaultOpts :: Opt defaultOpts = Opt { optTabStop = 4 , optPreserveTabs = False , optStandalone = False , optFrom = Nothing , optTo = Nothing , optTableOfContents = False , optListOfFigures = False , optListOfTables = False , optShiftHeadingLevelBy = 0 , optTemplate = Nothing , optVariables = mempty , optMetadata = mempty , optMetadataFiles = [] , optOutputFile = Nothing , optInputFiles = Nothing , optNumberSections = False , optNumberOffset = [] , optSectionDivs = False , optIncremental = False , optSelfContained = False , optEmbedResources = False , optLinkImages = False , optHtmlQTags = False , optSyntaxDefinitions = [] , optSyntaxHighlighting = DefaultHighlightingString , optTopLevelDivision = TopLevelDefault , optHTMLMathMethod = PlainMath , optAbbreviations = Nothing , optReferenceDoc = Nothing , optSplitLevel = 1 , optChunkTemplate = Nothing , optEpubSubdirectory = "EPUB" , optEpubMetadata = Nothing , optEpubFonts = [] , optEpubCoverImage = Nothing , optEpubTitlePage = True , optTOCDepth = 3 , optDumpArgs = False , optIgnoreArgs = False , optVerbosity = WARNING , optTrace = False , optLogFile = Nothing , optFailIfWarnings = False , optReferenceLinks = False , optReferenceLocation = EndOfDocument , optFigureCaptionPosition = CaptionBelow , optTableCaptionPosition = CaptionAbove , optDpi = 96 , optWrap = WrapAuto , optColumns = 72 , optFilters = [] , optEmailObfuscation = NoObfuscation , optIdentifierPrefix = "" , optIndentedCodeClasses = [] , optDataDir = Nothing , optCiteMethod = Citeproc , optPdfEngine = Nothing , optPdfEngineOpts = [] , optSlideLevel = Nothing , optSetextHeaders = False , optListTables = False , optAscii = False , optDefaultImageExtension = "" , optExtractMedia = Nothing , optTrackChanges = AcceptChanges , optFileScope = False , optTitlePrefix = Nothing , optCss = [] , optIpynbOutput = IpynbOutputBest , optIncludeBeforeBody = [] , optIncludeAfterBody = [] , optIncludeInHeader = [] , optResourcePath = ["."] , optRequestHeaders = [] , optNoCheckCertificate = False , optEol = Native , optStripComments = False , optCSL = Nothing , optBibliography = [] , optCitationAbbreviations = Nothing , optSandbox = False } yamlToMeta :: Value -> Parser Meta yamlToMeta (Object o) = either (fail . show) return $ runEverything (yamlMap pMetaString o) where pMetaString = pure . MetaString <$> P.manyChar P.anyChar runEverything p = runPure (P.readWithM p (def :: P.ParserState) ("" :: Text)) >>= fmap (Meta . flip P.runF def) yamlToMeta _ = return mempty -- | Apply defaults from --defaults file. applyDefaults :: (PandocMonad m, MonadIO m) => Opt -> FilePath -> StateT DefaultsState m Opt applyDefaults opt file = do setVerbosity $ optVerbosity opt modify $ \defsState -> defsState{ curDefaults = Just file } inp <- readFileStrict file 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 -> FilePath -> m FilePath fullDefaultsPath dataDir file = do let fp = if null (takeExtension file) then addExtension file "yaml" else file defaultDataDir <- liftIO defaultUserDataDir let defaultFp = fromMaybe defaultDataDir dataDir "defaults" fp fpExists <- fileExists fp if fpExists then return fp else do defaultFpExists <- fileExists defaultFp if defaultFpExists then return defaultFp else return fp -- | In a list of lists, append another list in front of every list which -- starts with specific element. expand :: Ord a => [[a]] -> [a] -> a -> [[a]] expand [] ns n = fmap (\x -> x : [n]) ns expand ps ns n = concatMap (ext n ns) ps where ext x xs p = case p of (l : _) | x == l -> fmap (: p) xs _ -> [p] cyclic :: Ord a => [[a]] -> Bool cyclic = any hasDuplicate where hasDuplicate xs = length (nubOrd xs) /= length xs ================================================ FILE: src/Text/Pandoc/App/OutputSettings.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.App Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Does a pandoc conversion based on command-line options. -} module Text.Pandoc.App.OutputSettings ( OutputSettings (..) , optToOutputSettings , sandbox' ) where import qualified Data.Map as M import qualified Data.Text as T import Text.DocTemplates (toVal, Context(..), Val(..)) import qualified Control.Exception as E import Control.Monad import Control.Monad.Except (throwError, catchError) import Control.Monad.Trans import Data.Char (toLower) import Data.List (find) import Data.Maybe (catMaybes, fromMaybe) import Skylighting (defaultSyntaxMap) import Skylighting.Parser (addSyntaxDefinition, parseSyntaxDefinition) import System.Directory (getCurrentDirectory) import System.Exit (exitSuccess) import System.FilePath import System.IO (stdout) import Text.Pandoc.Chunks (PathTemplate(..)) import Text.Pandoc import Text.Pandoc.Filter (Filter(CiteprocFilter)) import Text.Pandoc.App.Opt (Opt (..)) import Text.Pandoc.App.CommandLineOptions (engines) import Text.Pandoc.Format (FlavoredFormat (..), applyExtensionsDiff, parseFlavoredFormat, formatFromFilePaths) import Text.Pandoc.Highlighting (lookupHighlightingStyle) import Text.Pandoc.Scripting (ScriptingEngine (engineLoadCustom), CustomComponents(..)) import qualified Text.Pandoc.UTF8 as UTF8 readUtf8File :: PandocMonad m => FilePath -> m T.Text readUtf8File fp = readFileStrict fp >>= toTextM fp -- | Settings specifying how document output should be produced. data OutputSettings m = OutputSettings { outputFormat :: T.Text , outputWriter :: Writer m , outputWriterOptions :: WriterOptions , outputPdfProgram :: Maybe String } -- | Get output settings from command line options. optToOutputSettings :: (PandocMonad m, MonadIO m) => ScriptingEngine -> Opt -> m (OutputSettings m) optToOutputSettings scriptingEngine opts = do let outputFile = fromMaybe "-" (optOutputFile opts) when (optDumpArgs opts) . liftIO $ do UTF8.hPutStrLn stdout (T.pack outputFile) mapM_ (UTF8.hPutStrLn stdout . T.pack) (fromMaybe [] $ optInputFiles opts) exitSuccess epubMetadata <- traverse readUtf8File $ optEpubMetadata opts let pdfOutput = map toLower (takeExtension outputFile) == ".pdf" || optTo opts == Just "pdf" let defaultOutput = "html" defaultOutputFlavor <- parseFlavoredFormat defaultOutput (flvrd@(FlavoredFormat format _extsDiff), maybePdfProg) <- if pdfOutput then do outflavor <- case optTo opts of Just x | x /= "pdf" -> Just <$> parseFlavoredFormat x _ -> pure Nothing liftIO $ pdfWriterAndProg outflavor (optPdfEngine opts) else case optTo opts of Just f -> (, optPdfEngine opts) <$> parseFlavoredFormat f Nothing | outputFile == "-" -> return (defaultOutputFlavor, optPdfEngine opts) | otherwise -> case formatFromFilePaths [outputFile] of Nothing -> do report $ CouldNotDeduceFormat [T.pack $ takeExtension outputFile] defaultOutput return (defaultOutputFlavor, optPdfEngine opts) Just f -> return (f, optPdfEngine opts) when (format == "asciidoctor") $ do report $ Deprecated "asciidoctor" "use asciidoc instead" let makeSandboxed pureWriter = case pureWriter of TextWriter w -> TextWriter $ \o d -> sandbox' opts (w o d) ByteStringWriter w -> ByteStringWriter $ \o d -> sandbox' opts (w o d) let standalone = optStandalone opts || isBinaryFormat format || pdfOutput let templateOrThrow = \case Left e -> throwError $ PandocTemplateError (T.pack e) Right t -> pure t let processCustomTemplate getDefault = case optTemplate opts of _ | not standalone -> return Nothing Nothing -> Just <$> getDefault Just tp -> do let getAndCompile fp = getTemplate fp >>= runWithPartials . compileTemplate fp >>= fmap Just . templateOrThrow catchError (getAndCompile tp) (\e -> if null (takeExtension tp) then getAndCompile (tp <.> T.unpack format) else throwError e) (writer, writerExts', mtemplate) <- if "lua" `T.isSuffixOf` format then do let path = T.unpack format components <- engineLoadCustom scriptingEngine path w <- case customWriter components of Nothing -> throwError $ PandocAppError $ format <> " does not contain a custom writer" Just w -> return w let extsConf = fromMaybe mempty $ customExtensions components wexts <- applyExtensionsDiff extsConf flvrd templ <- processCustomTemplate $ case customTemplate components of Nothing -> throwError $ PandocNoTemplateError format Just t -> runWithDefaultPartials (compileTemplate path t) >>= templateOrThrow return (w, wexts, templ) else if optSandbox opts then do tmpl <- processCustomTemplate (compileDefaultTemplate format) case runPure (getWriter flvrd) of Right (w, wexts) -> return (makeSandboxed w, wexts, tmpl) Left e -> throwError e else do (w, wexts) <- getWriter flvrd tmpl <- processCustomTemplate (compileDefaultTemplate format) return (w, wexts, tmpl) -- see #10662: let writerExts = if CiteprocFilter `elem` optFilters opts then disableExtension Ext_citations writerExts' else writerExts' let addSyntaxMap existingmap f = do res <- liftIO (parseSyntaxDefinition f) case res of Left errstr -> throwError $ PandocSyntaxMapError $ T.pack errstr Right syn -> return $ addSyntaxDefinition syn existingmap syntaxMap <- foldM addSyntaxMap defaultSyntaxMap (optSyntaxDefinitions opts) hlStyle <- case optSyntaxHighlighting opts of NoHighlightingString -> pure NoHighlighting DefaultHighlightingString -> pure DefaultHighlighting IdiomaticHighlightingString -> pure IdiomaticHighlighting style -> Skylighting <$> lookupHighlightingStyle (T.unpack style) let setListVariableM _ [] ctx = return ctx setListVariableM k vs ctx = do let ctxMap = unContext ctx return $ Context $ case M.lookup k ctxMap of Just (ListVal xs) -> M.insert k (ListVal $ xs ++ map toVal vs) ctxMap Just v -> M.insert k (ListVal $ v : map toVal vs) ctxMap Nothing -> M.insert k (toVal vs) ctxMap let getTextContents fp = (fst <$> fetchItem (T.pack fp)) >>= toTextM fp let setFilesVariableM k fps ctx = do xs <- mapM getTextContents fps setListVariableM k xs ctx curdir <- liftIO getCurrentDirectory variables <- return (optVariables opts) >>= setListVariableM "sourcefile" (maybe ["-"] (fmap T.pack) (optInputFiles opts)) >>= setVariableM "outputfile" (T.pack outputFile) >>= setVariableM "pandoc-version" pandocVersionText >>= maybe return (setVariableM "pdf-engine" . T.pack) maybePdfProg >>= setFilesVariableM "include-before" (optIncludeBeforeBody opts) >>= setFilesVariableM "include-after" (optIncludeAfterBody opts) >>= setFilesVariableM "header-includes" (optIncludeInHeader opts) >>= setListVariableM "css" (map T.pack $ optCss opts) >>= maybe return (setVariableM "title-prefix") (optTitlePrefix opts) >>= maybe return (setVariableM "epub-cover-image" . T.pack) (optEpubCoverImage opts) >>= setVariableM "curdir" (T.pack curdir) >>= (\vars -> if format == "dzslides" then do dztempl <- let fp = "dzslides" "template.html" in readDataFile fp >>= toTextM fp let dzline = "" stripComments <- getOption readerStripComments if stripComments then return (next, "") else return (next, "") | otherwise -> Prelude.fail "bogus comment mode, HTML5 parse error" TagOpen tagname attr -> do guard $ isPI tagname || all (isName . fst) attr handleTag tagname TagClose tagname -> handleTag tagname _ -> mzero -- Utilities -- | Adjusts a url according to the document's base URL. canonicalizeUrl :: PandocMonad m => Text -> TagParser m Text canonicalizeUrl url | "data:" `T.isPrefixOf` url = return url | otherwise = do mbBaseHref <- baseHref <$> getState return $ case (parseURIReference (T.unpack url), mbBaseHref) of (Just rel, Just bs) -> tshow (rel `nonStrictRelativeTo` bs) _ -> url ================================================ FILE: src/Text/Pandoc/Readers/Haddock.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Haddock Copyright : Copyright (C) 2013 David Lazar License : GNU GPL, version 2 or above Maintainer : David Lazar , John MacFarlane Stability : alpha Conversion of Haddock markup to 'Pandoc' document. -} module Text.Pandoc.Readers.Haddock ( readHaddock ) where import Control.Monad.Except (throwError) import Data.List (intersperse) import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe) import Data.Text (unpack) import qualified Data.Text as T import Documentation.Haddock.Parser import Documentation.Haddock.Types as H import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Options import Text.Pandoc.Sources (ToSources(..), sourcesToText) import Text.Pandoc.Shared (splitTextBy, trim) -- | Parse Haddock markup and return a 'Pandoc' document. readHaddock :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readHaddock opts s = case readHaddockEither opts (unpack . sourcesToText . toSources $ s) of Right result -> return result Left e -> throwError e readHaddockEither :: ReaderOptions -- ^ Reader options -> String -- ^ String to parse -> Either PandocError Pandoc readHaddockEither _opts = Right . B.doc . docHToBlocks . _doc . parseParas Nothing docHToBlocks :: DocH String Identifier -> Blocks docHToBlocks d' = case d' of DocEmpty -> mempty DocAppend (DocParagraph (DocHeader h)) (DocParagraph (DocAName ident)) -> B.headerWith (T.pack ident,[],[]) (headerLevel h) (docHToInlines False $ headerTitle h) DocAppend d1 d2 -> mappend (docHToBlocks d1) (docHToBlocks d2) DocString _ -> inlineFallback DocParagraph (DocAName h) -> B.plain $ docHToInlines False $ DocAName h DocParagraph x -> B.para $ docHToInlines False x DocIdentifier _ -> inlineFallback DocIdentifierUnchecked _ -> inlineFallback DocModule s -> B.plain $ docHToInlines False $ DocModule s DocWarning _ -> mempty -- TODO DocEmphasis _ -> inlineFallback DocMonospaced _ -> inlineFallback DocBold _ -> inlineFallback DocMathInline _ -> inlineFallback DocMathDisplay _ -> inlineFallback DocHeader h -> B.header (headerLevel h) (docHToInlines False $ headerTitle h) DocUnorderedList items -> B.bulletList (map docHToBlocks items) #if MIN_VERSION_haddock_library(1,11,0) DocOrderedList items -> B.orderedListWith attr (map (docHToBlocks . snd) items) where attr = (start, DefaultStyle, DefaultDelim) start = case items of [] -> 1 ((n,_):_) -> n #else DocOrderedList items -> B.orderedList (map docHToBlocks items) #endif DocDefList items -> B.definitionList (map (\(d,t) -> (docHToInlines False d, [consolidatePlains $ docHToBlocks t])) items) DocCodeBlock (DocString s) -> B.codeBlockWith ("",[],[]) $ T.pack s DocCodeBlock d -> B.para $ docHToInlines True d DocHyperlink _ -> inlineFallback DocPic _ -> inlineFallback DocAName _ -> inlineFallback DocProperty s -> B.codeBlockWith ("",["property","haskell"],[]) (trim $ T.pack s) DocExamples es -> mconcat $ map (\e -> makeExample ">>>" (exampleExpression e) (exampleResult e)) es DocTable H.Table{ tableHeaderRows = headerRows , tableBodyRows = bodyRows } -> let toCells = map (docHToBlocks . tableCellContents) . tableRowCells toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] (header, body) = case headerRows of [] -> ([], map toCells bodyRows) (x:xs) -> (toCells x, map toCells (xs ++ bodyRows)) colspecs = replicate (maybe 0 maximum (nonEmpty (map length body))) (AlignDefault, ColWidthDefault) in B.table B.emptyCaption colspecs (TableHead nullAttr $ toHeaderRow header) [TableBody nullAttr 0 [] $ map toRow body] (TableFoot nullAttr []) where inlineFallback = B.plain $ docHToInlines False d' consolidatePlains = B.fromList . consolidatePlains' . B.toList consolidatePlains' zs@(Plain _ : _) = let (xs, ys) = span isPlain zs in Para (concatMap extractContents xs) : consolidatePlains' ys consolidatePlains' (x : xs) = x : consolidatePlains' xs consolidatePlains' [] = [] isPlain (Plain _) = True isPlain _ = False extractContents (Plain xs) = xs extractContents _ = [] docHToInlines :: Bool -> DocH String Identifier -> Inlines docHToInlines isCode d' = case d' of DocEmpty -> mempty DocAppend d1 d2 -> mappend (docHToInlines isCode d1) (docHToInlines isCode d2) DocString s | isCode -> mconcat $ intersperse B.linebreak $ map B.code $ splitTextBy (=='\n') $ T.pack s | otherwise -> B.text $ T.pack s DocParagraph _ -> mempty DocIdentifier ident -> case toRegular (DocIdentifier ident) of DocIdentifier s -> B.codeWith ("",["haskell","identifier"],[]) $ T.pack s _ -> mempty DocIdentifierUnchecked s -> B.codeWith ("",["haskell","identifier"],[]) $ T.pack s DocModule s -> B.codeWith ("",["haskell","module"],[]) $ T.pack (modLinkName s) DocWarning _ -> mempty -- TODO DocEmphasis d -> B.emph (docHToInlines isCode d) DocMonospaced (DocString s) -> B.code $ T.pack s DocMonospaced d -> docHToInlines True d DocBold d -> B.strong (docHToInlines isCode d) DocMathInline s -> B.math $ T.pack s DocMathDisplay s -> B.displayMath $ T.pack s DocHeader _ -> mempty DocUnorderedList _ -> mempty DocOrderedList _ -> mempty DocDefList _ -> mempty DocCodeBlock _ -> mempty DocHyperlink h -> B.link (T.pack $ hyperlinkUrl h) (T.pack $ hyperlinkUrl h) (maybe (B.text $ T.pack $ hyperlinkUrl h) (docHToInlines isCode) (hyperlinkLabel h)) DocPic p -> B.image (T.pack $ pictureUri p) (T.pack $ fromMaybe (pictureUri p) $ pictureTitle p) (maybe mempty (B.text . T.pack) $ pictureTitle p) DocAName s -> B.spanWith (T.pack s,["anchor"],[]) mempty DocProperty _ -> mempty DocExamples _ -> mempty DocTable _ -> mempty -- | Create an 'Example', stripping superfluous characters as appropriate makeExample :: T.Text -> String -> [String] -> Blocks makeExample prompt expression result = B.para $ B.codeWith ("",["prompt"],[]) prompt <> B.space <> B.codeWith ("", ["haskell","expr"], []) (trim $ T.pack expression) <> B.linebreak <> mconcat (intersperse B.linebreak $ map coder result') where -- 1. drop trailing whitespace from the prompt, remember the prefix prefix = T.takeWhile (`elem` (" \t" :: String)) prompt -- 2. drop, if possible, the exact same sequence of whitespace -- characters from each result line -- -- 3. interpret lines that only contain the string "" as an -- empty line result' = map (substituteBlankLine . tryStripPrefix prefix . T.pack) result where tryStripPrefix xs ys = fromMaybe ys $ T.stripPrefix xs ys substituteBlankLine "" = "" substituteBlankLine line = line coder = B.codeWith ("", ["result"], []) ================================================ FILE: src/Text/Pandoc/Readers/Ipynb.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Readers.Ipynb Copyright : Copyright (C) 2019-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Ipynb (Jupyter notebook JSON format) reader for pandoc. -} module Text.Pandoc.Readers.Ipynb ( readIpynb ) where import Crypto.Hash (hashWith, SHA1(SHA1)) import Data.Char (isDigit) import Data.Maybe (fromMaybe) import Text.Pandoc.Options import Control.Applicative ((<|>)) import qualified Data.Scientific as Scientific import qualified Text.Pandoc.Builder as B import Text.Pandoc.Logging import Text.Pandoc.Definition import Data.Ipynb as Ipynb import Text.Pandoc.Class.PandocMonad import Text.Pandoc.MIME (extensionFromMimeType) import Text.Pandoc.Shared (tshow) import Text.Pandoc.UTF8 import Text.Pandoc.Walk (walk) import Text.Pandoc.Error import Data.Text (Text) import qualified Data.Map as M import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.ByteString.Lazy as BL import Data.Aeson as Aeson import Control.Monad.Except (throwError) import Text.Pandoc.Readers.Markdown (readMarkdown) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Sources (ToSources(..), sourcesToText) readIpynb :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readIpynb opts x = do let src = BL.fromStrict . TE.encodeUtf8 . sourcesToText $ toSources x case eitherDecode src of Right (notebook4 :: Notebook NbV4) -> notebookToPandoc opts notebook4 Left _ -> case eitherDecode src of Right (notebook3 :: Notebook NbV3) -> notebookToPandoc opts notebook3 Left err -> throwError $ PandocIpynbDecodingError $ T.pack err notebookToPandoc :: PandocMonad m => ReaderOptions -> Notebook a -> m Pandoc notebookToPandoc opts notebook = do let cells = notebookCells notebook let (fmt,fmtminor) = notebookFormat notebook let m = M.insert "nbformat" (MetaString $ tshow fmt) $ M.insert "nbformat_minor" (MetaString $ tshow fmtminor) $ jsonMetaToMeta (notebookMetadata notebook) let lang = case M.lookup "kernelspec" m of Just (MetaMap ks) -> case M.lookup "language" ks of Just (MetaString l) -> l _ -> "python" _ -> "python" bs <- mconcat <$> mapM (cellToBlocks opts lang) cells let Pandoc _ blocks = B.doc bs return $ Pandoc (Meta $ M.insert "jupyter" (MetaMap m) mempty) blocks cellToBlocks :: PandocMonad m => ReaderOptions -> Text -> Ipynb.Cell a -> m B.Blocks cellToBlocks opts lang c = do let Source ts = cellSource c let source = mconcat ts let kvs = jsonMetaToPairs (cellMetadata c) let attachments = case cellAttachments c of Nothing -> mempty Just (MimeAttachments m) -> M.toList m let ident = fromMaybe mempty $ cellId c mapM_ (addAttachment (cellId c)) attachments case cellType c of Ipynb.Markdown -> do bs <- if isEnabled Ext_raw_markdown opts then return [RawBlock (Format "markdown") source] else do Pandoc _ bs <- walk (fixImage (cellId c)) <$> readMarkdown opts source return bs return $ B.divWith (ident,["cell","markdown"],kvs) $ B.fromList bs Ipynb.Heading lev -> do Pandoc _ bs <- readMarkdown opts (T.replicate lev "#" <> " " <> source) return $ B.divWith (ident,["cell","markdown"],kvs) $ B.fromList bs Ipynb.Raw -> do -- we use ipynb to indicate no format given (a wildcard in nbformat) let format = fromMaybe "ipynb" $ lookup "raw_mimetype" kvs <|> lookup "format" kvs let format' = case format of "text/html" -> "html" "slides" -> "html" "text/latex" -> "latex" "application/pdf" -> "latex" "pdf" -> "latex" "text/markdown" -> "markdown" "text/x-rst" -> "rst" "text/restructuredtext" -> "rst" "text/asciidoc" -> "asciidoc" _ -> format return $ B.divWith (ident,["cell","raw"],kvs) $ B.rawBlock format' source Ipynb.Code{ codeOutputs = outputs, codeExecutionCount = ec } -> do outputBlocks <- mconcat <$> mapM outputToBlock outputs let kvs' = maybe kvs (\x -> ("execution_count", tshow x):kvs) ec return $ B.divWith (ident,["cell","code"],kvs') $ B.codeBlockWith ("",[lang],[]) source <> outputBlocks -- Remove attachment: prefix from images... fixImage :: Maybe Text -> Inline -> Inline fixImage mbident (Image attr lab (src,tit)) | "attachment:" `T.isPrefixOf` src = let src' = T.drop 11 src qualifiedSrc = maybe src' (<> ("-" <> src')) mbident in Image attr lab (qualifiedSrc, tit) fixImage _ x = x addAttachment :: PandocMonad m => Maybe Text -> (Text, MimeBundle) -> m () addAttachment mbident (fname, mimeBundle) = do let fp = T.unpack $ maybe fname (<> ("-" <> fname)) mbident case M.toList (unMimeBundle mimeBundle) of (mimeType, BinaryData bs):_ -> insertMedia fp (Just mimeType) (BL.fromStrict bs) (mimeType, TextualData t):_ -> insertMedia fp (Just mimeType) (BL.fromStrict $ TE.encodeUtf8 t) (mimeType, JsonData v):_ -> insertMedia fp (Just mimeType) (encode v) [] -> report $ CouldNotFetchResource fname "no attachment" outputToBlock :: PandocMonad m => Output a -> m B.Blocks outputToBlock Stream{ streamName = sName, streamText = Source text } = return $ B.divWith ("",["output","stream",sName],[]) $ B.codeBlock $ T.concat text outputToBlock DisplayData{ displayData = data', displayMetadata = metadata' } = B.divWith ("",["output", "display_data"],[]) <$> handleData metadata' data' outputToBlock ExecuteResult{ executeCount = ec, executeData = data', executeMetadata = metadata' } = B.divWith ("",["output", "execute_result"],[("execution_count",tshow ec)]) <$> handleData metadata' data' outputToBlock Err{ errName = ename, errValue = evalue, errTraceback = traceback } = return $ B.divWith ("",["output","error"], [("ename",ename), ("evalue",evalue)]) $ B.codeBlock $ T.unlines traceback -- We want to display the richest output possible given -- the output format. handleData :: PandocMonad m => JSONMeta -> MimeBundle -> m B.Blocks handleData (JSONMeta metadata) (MimeBundle mb) = mconcat <$> mapM dataBlock (M.toList mb) where dataBlock :: PandocMonad m => (MimeType, MimeData) -> m B.Blocks dataBlock (mt, d) | "image/" `T.isPrefixOf` mt || mt == "application/pdf" = do -- normally metadata maps from mime types to key-value map; -- but not always... let meta = case M.lookup mt metadata of Just v@Object{} -> case fromJSON v of Success m' -> m' Error _ -> mempty _ -> mempty let metaPairs = jsonMetaToPairs meta let bs = case d of BinaryData bs' -> bs' TextualData t -> UTF8.fromText t JsonData v -> BL.toStrict $ encode v -- SHA1 hash for filename let fname = T.pack (show (hashWith SHA1 bs)) <> case extensionFromMimeType mt of Nothing -> "" Just ext -> "." <> ext insertMedia (T.unpack fname) (Just mt) (BL.fromStrict bs) return $ B.para $ B.imageWith ("",[],metaPairs) fname "" mempty dataBlock ("text/html", TextualData t) = return $ B.rawBlock "html" t dataBlock ("text/latex", TextualData t) = return $ B.rawBlock "latex" t dataBlock ("text/markdown", TextualData t) = return $ B.rawBlock "markdown" t dataBlock ("text/plain", TextualData t) = return $ B.codeBlock t dataBlock (_, JsonData v) = return $ B.codeBlockWith ("",["json"],[]) $ T.pack $ toStringLazy $ encode v dataBlock _ = return mempty jsonMetaToMeta :: JSONMeta -> M.Map Text MetaValue jsonMetaToMeta (JSONMeta m) = M.map valueToMetaValue m where valueToMetaValue :: Value -> MetaValue valueToMetaValue x@Object{} = case fromJSON x of Error s -> MetaString $ T.pack s Success jm' -> MetaMap $ jsonMetaToMeta jm' valueToMetaValue x@Array{} = case fromJSON x of Error s -> MetaString $ T.pack s Success xs -> MetaList $ map valueToMetaValue xs valueToMetaValue (Bool b) = MetaBool b valueToMetaValue (String t) = MetaString t valueToMetaValue (Number n) | Scientific.isInteger n = MetaString (tshow (floor n :: Integer)) | otherwise = MetaString (tshow n) valueToMetaValue Aeson.Null = MetaString "" jsonMetaToPairs :: JSONMeta -> [(Text, Text)] jsonMetaToPairs (JSONMeta m) = M.toList . M.map (\case String t | not (T.all isDigit t) , t /= "true" , t /= "false" -> t x -> T.pack $ UTF8.toStringLazy $ Aeson.encode x) $ m ================================================ FILE: src/Text/Pandoc/Readers/JATS.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.JATS Copyright : Copyright (C) 2017-2020 Hamish Mackenzie License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of JATS XML to 'Pandoc' document. -} module Text.Pandoc.Readers.JATS ( readJATS ) where import Control.Monad.State.Strict ( StateT(runStateT), gets, modify ) import Control.Monad (forM_, when, unless) import Control.Monad.Except (throwError) import Text.Pandoc.Error (PandocError(..)) import Data.Char (isDigit, isSpace) import Data.Default import Data.Generics import qualified Data.List as L import qualified Data.Map as Map import Data.Maybe (maybeToList, fromMaybe, catMaybes) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Text.Pandoc.XML (lookupEntity) import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Options import Text.Pandoc.Shared (safeRead) import Text.Pandoc.Walk (walk) import Text.Pandoc.XML.Light import Text.TeXMath (readMathML, writeTeX) import qualified Data.Set as S (fromList, member) import Data.Set ((\\)) import Text.Pandoc.Sources (ToSources(..), sourcesToText) import Safe (headMay) import Text.Printf (printf) type JATS m = StateT JATSState m data JATSState = JATSState{ jatsSectionLevel :: Int , jatsQuoteType :: QuoteType , jatsMeta :: Meta , jatsBook :: Bool , jatsFootnotes :: Map.Map Text Blocks , jatsContent :: [Content] , jatsInFigure :: Bool } deriving Show instance Default JATSState where def = JATSState{ jatsSectionLevel = 0 , jatsQuoteType = DoubleQuote , jatsMeta = mempty , jatsBook = False , jatsFootnotes = mempty , jatsContent = [] , jatsInFigure = False } readJATS :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readJATS _ inp = do let sources = toSources inp tree <- either (throwError . PandocXMLError "") return $ parseXMLContents (TL.fromStrict . sourcesToText $ sources) (bs, st') <- flip runStateT (def{ jatsContent = tree }) $ mapM parseBlock tree let footnotes = jatsFootnotes st' let blockList = toList $ mconcat bs let linkToFootnotes :: Inline -> Inline linkToFootnotes link'@(Link _attr _txt (href, _title)) = case T.uncons href of Just ('#', rid) -> case Map.lookup rid footnotes of Just footnote -> Note (toList footnote) Nothing -> link' _ -> link' linkToFootnotes inline = inline return $ Pandoc (jatsMeta st') (walk linkToFootnotes blockList) -- convenience function to get an attribute value, defaulting to "" attrValue :: Text -> Element -> Text attrValue attr = fromMaybe "" . maybeAttrValue attr maybeAttrValue :: Text -> Element -> Maybe Text maybeAttrValue attr elt = lookupAttrBy (\x -> qName x == attr) (elAttribs elt) -- convenience function named :: Text -> Element -> Bool named s e = qName (elName e) == s -- addMeta :: PandocMonad m => ToMetaValue a => Text -> a -> JATS m () addMeta field val = modify (setMeta field val) instance HasMeta JATSState where setMeta field v s = s {jatsMeta = setMeta field v (jatsMeta s)} deleteMeta field s = s {jatsMeta = deleteMeta field (jatsMeta s)} isBlockElement :: Content -> Bool isBlockElement (Elem e) = case qName (elName e) of "disp-formula" -> if onlyOneChild e then if hasFormulaChild e then False else case filterChild (named "alternatives") e of Just a -> if hasFormulaChild a then False else True Nothing -> True else True "alternatives" -> if hasFormulaChild e then False else True _ -> qName (elName e) `S.member` blocktags where blocktags = S.fromList (paragraphLevel ++ lists ++ formulae ++ other) \\ S.fromList canBeInline paragraphLevel = ["address", "answer", "answer-set", "array", "boxed-text", "chem-struct-wrap", "code", "explanation", "fig", "fig-group", "graphic", "media", "preformat", "question", "question-wrap", "question-wrap-group", "supplementary-material", "table-wrap", "table-wrap-group", "alternatives", "disp-formula", "disp-formula-group"] lists = ["def-list", "list"] formulae = ["tex-math", "mml:math"] other = ["p", "related-article", "related-object", "ack", "disp-quote", "speech", "statement", "verse-group", "x"] canBeInline = ["tex-math", "mml:math", "related-object", "x"] onlyOneChild x = length (allChildren x) == 1 allChildren x = filterChildren (const True) x isBlockElement _ = False -- Trim leading and trailing newline characters trimNl :: Text -> Text trimNl = T.dropAround (== '\n') -- function that is used by both graphic (in parseBlock) -- and inline-graphic (in parseInline) getGraphic :: PandocMonad m => Element -> JATS m Inlines getGraphic e = do let atVal a = attrValue a e let altText = case filterElement (named "alt-text") e of Just alt -> textContent alt Nothing -> mempty (ident, title, altText') = (atVal "id", atVal "title", text altText) attr = (ident, T.words $ atVal "role", []) imageUrl = atVal "href" return $ imageWith attr imageUrl title altText' getBlocks :: PandocMonad m => Element -> JATS m Blocks getBlocks e = mconcat <$> mapM parseBlock (elContent e) parseBlock :: PandocMonad m => Content -> JATS m Blocks parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE parseBlock (Text (CData _ s _)) = if T.all isSpace s then return mempty else return $ plain $ trimInlines $ text s parseBlock (CRef x) = return $ plain $ str $ T.toUpper x parseBlock (Elem e) = do sectionLevel <- gets jatsSectionLevel let parseBlockWithHeader = wrapWithHeader (sectionLevel+1) (getBlocks e) case qName (elName e) of "book" -> parseBook "book-part-wrapper" -> parseBook "p" -> parseMixed para (elContent e) "code" -> codeBlockWithLang "preformat" -> codeBlockWithLang "disp-quote" -> wrapWithHeader (sectionLevel+1) parseBlockquote "list" -> wrapWithHeader (sectionLevel+1) parseList "def-list" -> wrapWithHeader (sectionLevel+1) (definitionList <$> deflistitems) "sec" -> parseBlockWithHeader "abstract" -> parseBlockWithHeader "ack" -> parseBlockWithHeader "answer" -> parseBlockWithHeader "answer-set" -> parseBlockWithHeader "app" -> parseBlockWithHeader "app-group" -> parseBlockWithHeader "author-comment" -> parseBlockWithHeader "author-notes" -> parseBlockWithHeader "back" -> parseBlockWithHeader "bio" -> parseBlockWithHeader "explanation" -> parseBlockWithHeader "glossary" -> parseBlockWithHeader "kwd-group" -> parseBlockWithHeader "list-item" -> parseBlockWithHeader "notes" -> parseBlockWithHeader "option" -> parseBlockWithHeader "question" -> parseBlockWithHeader "question-preamble" -> parseBlockWithHeader "question-wrap-group" -> parseBlockWithHeader "statement" -> parseBlockWithHeader "supplement" -> parseBlockWithHeader "table-wrap-foot" -> parseBlockWithHeader "trans-abstract" -> parseBlockWithHeader "verse-group" -> parseBlockWithHeader "graphic" -> para <$> getGraphic e "journal-meta" -> parseMetadata e "article-meta" -> parseMetadata e "custom-meta" -> parseMetadata e "processing-meta" -> return mempty "book-meta" -> parseMetadata e "title" -> return mempty -- processed by header "label" -> return mempty -- processed by header "table" -> parseTable "fig" -> parseFigure "fig-group" -> divWith (attrValue "id" e, ["fig-group"], []) <$> getBlocks e "table-wrap" -> divWith (attrValue "id" e, ["table-wrap"], []) <$> getBlocks e "caption" -> do inFigure <- gets jatsInFigure if inFigure -- handled by parseFigure then return mempty else divWith (attrValue "id" e, ["caption"], []) <$> wrapWithHeader 6 (getBlocks e) "fn-group" -> parseFootnoteGroup "ref-list" -> parseRefList e "alternatives" -> if hasFormulaChild e then blockFormula displayMath e else getBlocks e "disp-formula" -> if hasFormulaChild e then blockFormula displayMath e else divWith (attrValue "id" e, ["disp-formula"], []) <$> getBlocks e "index" -> parseBlockWithHeader "index-div" -> parseBlockWithHeader "index-group" -> parseBlockWithHeader "index-title-group" -> return mempty -- handled by index and index-div "toc" -> parseBlockWithHeader "toc-div" -> parseBlockWithHeader "toc-entry" -> parseBlockWithHeader "toc-group" -> parseBlockWithHeader "toc-title-group" -> return mempty -- handled by toc "legend" -> parseBlockWithHeader "dedication" -> parseBlockWithHeader "foreword" -> parseBlockWithHeader "preface" -> parseBlockWithHeader "?xml" -> return mempty _ -> getBlocks e where parseMixed container conts = do let (ils,rest) = break isBlockElement conts ils' <- trimInlines . mconcat <$> mapM parseInline ils let p = if ils' == mempty then mempty else container ils' case rest of [] -> return p (r:rs) -> do b <- parseBlock r x <- parseMixed container rs return $ p <> b <> x codeBlockWithLang = do let classes' = case attrValue "language" e of "" -> [] x -> [x] return $ codeBlockWith (attrValue "id" e, classes', []) $ trimNl $ strContentRecursive e parseBlockquote = do attrib <- case filterChild (named "attrib") e of Nothing -> return mempty Just z -> para . (str "— " <>) . mconcat <$> mapM parseInline (elContent z) contents <- getBlocks e return $ blockQuote (contents <> attrib) parseList = do case attrValue "list-type" e of "bullet" -> bulletList <$> listitems listType -> do let start = fromMaybe 1 $ ( filterElement (named "list-item") e >>= filterElement (named "label") ) >>= safeRead . textContent orderedListWith (start, parseListStyleType listType, DefaultDelim) <$> listitems parseListStyleType "roman-lower" = LowerRoman parseListStyleType "roman-upper" = UpperRoman parseListStyleType "alpha-lower" = LowerAlpha parseListStyleType "alpha-upper" = UpperAlpha parseListStyleType _ = DefaultStyle listitems = mapM getBlocks $ filterChildren (named "list-item") e deflistitems = mapM parseVarListEntry $ filterChildren (named "def-item") e parseVarListEntry e' = do let terms = filterChildren (named "term") e' let items = filterChildren (named "def") e' terms' <- mapM getInlines terms items' <- mapM getBlocks 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 . L.intersperse linebreak <$> mapM getInlines (filterChildren (const True) t) Nothing -> return mempty contents <- getBlocks e modify $ \st -> st{ jatsInFigure = False } return $ figureWith (attrValue "id" e, [], []) (simpleCaption $ plain capt) contents parseFootnoteGroup = do forM_ (filterChildren (named "fn") e) $ \fn -> do let id' = attrValue "id" fn contents <- getBlocks fn modify $ \st -> st { jatsFootnotes = Map.insert id' contents (jatsFootnotes st) } return mempty parseTable = do let isCaption x = named "title" x || named "caption" x capt <- case filterChild isCaption e of Just t -> getInlines t Nothing -> return mempty let e' = fromMaybe e $ filterChild (named "tgroup") e let isColspec x = named "colspec" x || named "col" x let colspecs = case filterChild (named "colgroup") e' of Just c -> filterChildren isColspec c _ -> filterChildren isColspec e' let isRow x = named "row" x || named "tr" x let parseRows elementWithRows = map parseElement $ filterChildren isRow elementWithRows -- list of list of body cell elements let multipleBodyRowElements = map parseRows $ filterChildren (named "tbody") e' -- list of list header cell elements let headRowElements = maybe [] parseRows (filterChild (named "thead") e') -- list of foot cell elements let footRowElements = maybe [] parseRows (filterChild (named "tfoot") e') let toAlignment c = case findAttr (unqual "align") c of Just "left" -> AlignLeft Just "right" -> AlignRight Just "center" -> AlignCenter _ -> AlignDefault let toColSpan element = fromMaybe 1 $ findAttr (unqual "colspan") element >>= safeRead let toRowSpan element = fromMaybe 1 $ findAttr (unqual "rowspan") element >>= safeRead let toWidth c = do w <- findAttr (unqual "colwidth") c 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 = L.foldl' max 0 $ map length firstBody let aligns = case colspecs of [] -> replicate numrows AlignDefault cs -> map toAlignment cs let widths = case colspecs of [] -> replicate numrows ColWidthDefault cs -> let ws = map toWidth cs in case sequence ws of Just ws' -> let tot = sum ws' in ColWidth . (/ tot) <$> ws' Nothing -> replicate numrows ColWidthDefault let parseCell = parseMixed plain . elContent let elementToCell element = cell (toAlignment element) (RowSpan $ toRowSpan element) (ColSpan $ toColSpan element) <$> (parseCell element) let rowElementsToCells elements = mapM elementToCell elements let toRow = fmap (Row nullAttr) . rowElementsToCells toRows elements = mapM toRow elements headerRows <- toRows headRowElements footerRows <- toRows footRowElements bodyRows <- mapM toRows multipleBodyRowElements return $ table (simpleCaption $ plain capt) (zip aligns widths) (TableHead nullAttr headerRows) (map (TableBody nullAttr 0 []) bodyRows) (TableFoot nullAttr footerRows) isEntry x = named "entry" x || named "td" x || named "th" x parseElement = filterChildren isEntry wrapWithHeader n mBlocks = do isBook <- gets jatsBook let n' = case (filterChild (named "title") e >>= maybeAttrValue "display-as") of Just t -> read $ T.unpack t Nothing -> if isBook || n == 0 then n + 1 else n headerText <- case filterChild (named "title") e of Just t -> case maybeAttrValue "suppress" t of Just s -> if s == "no" then getInlines t else return mempty Nothing -> getInlines t Nothing -> do let name = qName (elName e) if (name == "dedication" || name == "foreword" || name == "preface") then return $ str $ T.toTitle name else case filterChild (named "index-title-group") e >>= filterChild (named "title") of Just i -> getInlines i Nothing -> case filterChild (named "toc-title-group") e >>= filterChild (named "title") of Just t -> getInlines t Nothing -> return mempty oldN <- gets jatsSectionLevel modify $ \st -> st{ jatsSectionLevel = n } blocks <- mBlocks let ident = attrValue "id" e modify $ \st -> st{ jatsSectionLevel = oldN } return $ (if headerText == mempty then mempty else headerWith (ident,[],[]) n' headerText) <> blocks parseBook = do modify $ \st -> st{ jatsBook = True } getBlocks e getInlines :: PandocMonad m => Element -> JATS m Inlines getInlines e' = trimInlines . mconcat <$> mapM parseInline (elContent e') parseMetadata :: PandocMonad m => Element -> JATS m Blocks parseMetadata e = do isBook <- gets jatsBook if isBook then getBookTitle e else getArticleTitle e if isBook then getBookAuthors e else getArticleAuthors e getAffiliations e getAbstract e getPubDate e getPermissions e return mempty getArticleTitle :: PandocMonad m => Element -> JATS m () getArticleTitle e = do tit <- case filterElement (named "article-title") e of Just s -> getInlines s Nothing -> return mempty subtit <- case filterElement (named "subtitle") e of Just s -> (text ": " <>) <$> getInlines s Nothing -> return mempty when (tit /= mempty) $ addMeta "title" tit when (subtit /= mempty) $ addMeta "subtitle" subtit getBookTitle :: PandocMonad m => Element -> JATS m () getBookTitle e = do tit <- case (filterElement (named "book-title-group") e >>= filterElement (named "book-title")) of Just s -> getInlines s Nothing -> return mempty subtit <- case (filterElement (named "book-title-group") e >>= filterElement (named "subtitle")) of Just s -> (text ": " <>) <$> getInlines s Nothing -> return mempty when (tit /= mempty) $ addMeta "title" tit when (subtit /= mempty) $ addMeta "subtitle" subtit getArticleAuthors :: PandocMonad m => Element -> JATS m () getArticleAuthors e = do authors <- mapM getContrib $ filterElements (\x -> named "contrib" x && attrValue "contrib-type" x == "author") e authorNotes <- mapM getInlines $ filterElements (named "author-notes") e let authors' = case (reverse authors, authorNotes) of ([], _) -> [] (_, []) -> authors (a:as, ns) -> reverse as ++ [a <> mconcat ns] unless (null authors) $ addMeta "author" authors' getBookAuthors :: PandocMonad m => Element -> JATS m () getBookAuthors e = do authors <- mapM getContrib $ filterElements (\x -> named "contrib-group" x) e >>= filterElements (\x -> named "contrib" x && attrValue "contrib-type" x == "author") authorNotes <- mapM getInlines $ filterElements (named "author-notes") e let authors' = case (reverse authors, authorNotes) of ([], _) -> [] (_, []) -> authors (a:as, ns) -> reverse as ++ [a <> mconcat ns] unless (null authors) $ addMeta "author" authors' getAffiliations :: PandocMonad m => Element -> JATS m () getAffiliations x = do affs <- mapM getInlines $ filterChildren (named "aff") x unless (null affs) $ addMeta "institute" affs getAbstract :: PandocMonad m => Element -> JATS m () getAbstract e = case filterElement (named "abstract") e of Just s -> do blks <- getBlocks s addMeta "abstract" blks Nothing -> pure () getPubDate :: PandocMonad m => Element -> JATS m () getPubDate e = case filterElement (named "pub-date") e of Just d -> getDate d >>= addMeta "date" . text Nothing -> pure () -- extract a structured date and create an ISO-8901 string date from it getDate :: PandocMonad m => Element -> JATS m Text getDate e = case maybeAttrValue "iso-8601-date" e of Just isod -> pure isod Nothing -> do let extractDate :: Element -> Maybe Int extractDate = safeRead . strContent let yr = filterElement (named "year") e >>= extractDate let mon = filterElement (named "month") e >>= extractDate let day = filterElement (named "day") e >>= extractDate let stringDate = strContent <$> filterElement (named "string-date") e pure $ case (yr, mon, day) of (Just y, Just m, Just d) -> T.pack $ printf "%04d-%02d-%02d" y m d (Just y, Just m, Nothing) -> T.pack $ printf "%04d-%02d" y m (Just y, Nothing, Nothing) -> T.pack $ printf "%04d" y _ -> fromMaybe mempty stringDate getPermissions :: PandocMonad m => Element -> JATS m () getPermissions e = do copyright <- getCopyright e license <- case filterElement (named "license") e of Just s -> getLicense s Nothing -> return mempty when (copyright /= mempty) $ addMeta "copyright" copyright when (license /= mempty) $ addMeta "license" license getCopyright :: PandocMonad m => Element -> JATS m (Map.Map Text MetaValue) getCopyright e = do let holder = metaElement e "copyright-holder" "holder" let statement = metaElement e "copyright-statement" "statement" let year = metaElement e "copyright-year" "year" return $ Map.fromList (catMaybes $ [holder, statement, year]) getLicense :: PandocMonad m => Element -> JATS m (Map.Map Text MetaValue) getLicense e = do let licenseType = metaAttribute e "license-type" "type" let licenseLink = metaElementAliRef e "link" let licenseText = metaElement e "license-p" "text" return $ Map.fromList (catMaybes $ [licenseType, licenseLink, licenseText]) metaElement :: Element -> Text -> Text -> Maybe (Text, MetaValue) metaElement e child key = case filterElement (named child) e of Just content -> Just (key, toMetaValue $ strContent content) Nothing -> Nothing metaElementAliRef :: Element -> Text -> Maybe (Text, MetaValue) metaElementAliRef e key = case filterElement isAliLicenseRef e of Just content -> Just (key, toMetaValue $ strContent content) Nothing -> Nothing metaAttribute :: Element -> Text -> Text -> Maybe (Text, MetaValue) metaAttribute e attr key = case maybeAttrValue attr e of Just content -> Just (key, toMetaValue content) Nothing -> Nothing getContrib :: PandocMonad m => Element -> JATS m Inlines getContrib x = do given <- maybe (return mempty) getInlines $ filterElement (named "given-names") x family <- maybe (return mempty) getInlines $ filterElement (named "surname") x if given == mempty && family == mempty then return mempty else if given == mempty || family == mempty then return $ given <> family else return $ given <> space <> family parseRefList :: PandocMonad m => Element -> JATS m Blocks parseRefList e = do refs <- mapM parseRef $ filterChildren (named "ref") e let mbtitle = filterChild (named "title") e title <- case mbtitle of Nothing -> pure mempty Just te -> header 1 <$> parseInline (Elem te) addMeta "references" refs return $ title <> divWith ("refs",[],[]) mempty parseRef :: PandocMonad m => Element -> JATS m (Map.Map Text MetaValue) parseRef e = do let combineWithDash x y = x <> text "-" <> y let getName nm = do given <- maybe (return mempty) getInlines $ filterChild (named "given-names") nm family <- maybe (return mempty) getInlines $ filterChild (named "surname") nm return $ toMetaValue $ Map.fromList [ ("given" :: Text, given) , ("family", family) ] let refElement :: PandocMonad m => Element -> Element -> JATS m (Maybe (Text, MetaValue)) refElement c el = case qName (elName el) of "article-title" -> Just . ("title",) . toMetaValue <$> getInlines el "source" -> case filterChild (named "article-title") c of Just _ -> Just . ("container-title",) . toMetaValue <$> getInlines el Nothing -> Just . ("title",) . toMetaValue <$> getInlines el "label" -> Just . ("citation-label",) . toMetaValue <$> getInlines el "year" -> case filterChild (named "month") c of Just m -> Just . ("issued",) . toMetaValue <$> (combineWithDash <$> getInlines el <*> getInlines m) Nothing -> Just . ("issued",) . toMetaValue <$> getInlines el "volume" -> Just . ("volume",) . toMetaValue <$> getInlines el "issue" -> Just . ("issue",) . toMetaValue <$> getInlines el "isbn" -> Just . ("ISBN",) . toMetaValue <$> getInlines el "issn" -> Just . ("ISSN",) . toMetaValue <$> getInlines el "uri" -> Just . ("url",) . toMetaValue <$> getInlines el "fpage" -> case filterChild (named "lpage") c of Just lp -> Just . ("page",) . toMetaValue <$> (combineWithDash <$> getInlines el <*> getInlines lp) Nothing -> Just . ("page-first",) . toMetaValue <$> getInlines el "publisher-name" -> Just . ("publisher",) . toMetaValue <$> getInlines el "publisher-loc" -> Just . ("publisher-place",) . toMetaValue <$> getInlines el "edition" -> pure $ Just ("edition", toMetaValue . T.filter isDigit $ strContent el) "person-group" -> do names <- mapM getName (filterChildren (named "name") el) pure $ Just (attrValue "person-group-type" el, toMetaValue names) "pub-id" -> case attrValue "pub-id-type" el of "doi" -> pure $ Just ("DOI", toMetaValue $ strContent el) "pmid" -> pure $ Just ("PMID", toMetaValue $ strContent el) _ -> pure Nothing "object-id" -> case attrValue "pub-id-type" el of "doi" -> pure $ Just ("DOI", toMetaValue $ strContent el) "pmid" -> pure $ Just ("PMID", toMetaValue $ strContent el) _ -> pure Nothing _ -> pure Nothing refVariables <- case filterChild (named "element-citation") e of Just c -> (("type", toMetaValue $ case attrValue "publication-type" c of "journal" -> "article-journal" x -> x) :) . catMaybes <$> mapM (refElement c) (elChildren c) Nothing -> pure [] -- TODO handle mixed-citation -- allows round-tripping, since JATS writer puts ref- in front of citation ids: let stripPref t = fromMaybe t $ T.stripPrefix "ref-" t return $ Map.fromList (("id", toMetaValue $ stripPref $ attrValue "id" e) : refVariables) textContent :: Element -> Text textContent = strContent strContentRecursive :: Element -> Text strContentRecursive = strContent . (\e' -> e'{ elContent = map elementToStr $ elContent e' }) elementToStr :: Content -> Content elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing elementToStr x = x parseInline :: PandocMonad m => Content -> JATS m Inlines parseInline (Text (CData _ s _)) = return $ text s parseInline (CRef ref) = return $ maybe (text $ T.toUpper ref) text $ lookupEntity ref parseInline (Elem e) = case qName (elName e) of "italic" -> innerInlines emph "bold" -> innerInlines strong "strike" -> innerInlines strikeout "sub" -> innerInlines subscript "sup" -> innerInlines superscript "underline" -> innerInlines underline "break" -> return linebreak "sc" -> innerInlines smallcaps "code" -> codeWithLang "monospace" -> codeWithLang "inline-graphic" -> getGraphic e "disp-quote" -> do qt <- gets jatsQuoteType let qt' = if qt == SingleQuote then DoubleQuote else SingleQuote modify $ \st -> st{ jatsQuoteType = qt' } contents <- innerInlines id modify $ \st -> st{ jatsQuoteType = qt } return $ if qt == SingleQuote then singleQuoted contents else doubleQuoted contents "xref" -> do ils <- innerInlines id let rid = attrValue "rid" e let rids = T.words rid let refType = ("ref-type",) <$> maybeAttrValue "ref-type" e let attr = (attrValue "id" e, [], maybeToList refType) return $ if refType == Just ("ref-type","bibr") then cite (map (\id' -> let id'' = fromMaybe id' $ T.stripPrefix "ref-" id' in Citation { citationId = id'' , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0}) rids) ils else linkWith attr ("#" <> rid) "" ils "ext-link" -> do ils <- innerInlines id let title = fromMaybe "" $ findAttr (QName "title" (Just "http://www.w3.org/1999/xlink") Nothing) e let href = case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of Just h -> h _ -> "#" <> attrValue "rid" e let ils' = if ils == mempty then str href else ils let attr = (attrValue "id" e, [], []) return $ linkWith attr href title ils' "alternatives" -> if hasFormulaChild e then inlineFormula math e else innerInlines id "disp-formula" -> inlineFormula displayMath e "inline-formula" -> inlineFormula math e "math" | qURI (elName e) == Just "http://www.w3.org/1998/Math/MathML" -> return . math $ mathML e "tex-math" -> return . math $ textContent e "email" -> return $ link ("mailto:" <> textContent e) "" $ str $ textContent e "uri" -> return $ link (textContent e) "" $ str $ textContent e "fn" -> note . mconcat <$> mapM parseBlock (elContent e) _ -> innerInlines id where innerInlines f = f . mconcat <$> mapM parseInline (elContent e) codeWithLang = do let classes' = case attrValue "language" e of "" -> [] l -> [l] return $ codeWith (attrValue "id" e,classes',[]) $ strContentRecursive e inlineFormula :: PandocMonad m => (Text->Inlines) -> Element -> JATS m Inlines inlineFormula constructor e = do let whereToLook = fromMaybe e $ filterElement (named "alternatives") e texMaths = map textContent $ filterChildren (named "tex-math") whereToLook mathMLs = map mathML $ filterChildren isMathML whereToLook return . mconcat . take 1 . map constructor $ texMaths ++ mathMLs blockFormula :: PandocMonad m => (Text->Inlines) -> Element -> JATS m Blocks blockFormula constructor e = do let whereToLook = fromMaybe e $ filterElement (named "alternatives") e texMaths = map textContent $ filterChildren (named "tex-math") whereToLook mathMLs = map mathML $ filterChildren isMathML whereToLook case texMaths ++ mathMLs of [] -> return mempty (m:_) -> return $ para (constructor m) mathML :: Element -> Text mathML x = case readMathML . showElement $ everywhere (mkT removePrefix) x of Left _ -> mempty Right m -> writeTeX m where removePrefix elname = elname { qPrefix = Nothing } isMathML :: Element -> Bool isMathML x = qName (elName x) == "math" && qURI (elName x) == Just "http://www.w3.org/1998/Math/MathML" formulaChildren :: Element -> [Element] formulaChildren x = filterChildren isMathML x ++ filterChildren (named "tex-math") x hasFormulaChild :: Element -> Bool hasFormulaChild x = length (formulaChildren x) > 0 isAliLicenseRef :: Element -> Bool isAliLicenseRef x = qName (elName x) == "license_ref" && qURI (elName x) == Just "http://www.niso.org/schemas/ali/1.0/" ================================================ FILE: src/Text/Pandoc/Readers/Jira.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Jira Copyright : © 2019-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Conversion of jira wiki formatted plain text to 'Pandoc' document. -} module Text.Pandoc.Readers.Jira ( readJira ) where import Control.Monad.Except (throwError) import Data.List (partition) import Data.Text (Text, append, pack, singleton) import Text.Pandoc.XML (lookupEntity) import Text.Jira.Parser (parse) import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.Builder hiding (cell) import Text.Pandoc.Error (PandocError (PandocParseError)) import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Shared (stringify) import Text.Pandoc.Sources (ToSources(..), sourcesToText) import qualified Text.Jira.Markup as Jira -- | Read Jira wiki markup. readJira :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readJira _opts inp = do let sources = toSources inp case parse (sourcesToText sources) of Right d -> return $ jiraToPandoc d Left e -> throwError . PandocParseError $ "Jira parse error" `append` pack (show e) jiraToPandoc :: Jira.Doc -> Pandoc jiraToPandoc (Jira.Doc blks) = doc $ foldMap jiraToPandocBlocks blks -- -- Blocks -- -- | Converts a Jira block to a Pandoc block. jiraToPandocBlocks :: Jira.Block -> Blocks jiraToPandocBlocks = \case Jira.BlockQuote blcks -> blockQuote $ foldMap jiraToPandocBlocks blcks Jira.Code lang ps txt -> toPandocCodeBlocks (Just lang) ps txt Jira.Color c blcks -> divWith (mempty, mempty, [("color", colorName c)]) $ foldMap jiraToPandocBlocks blcks Jira.Header lvl inlns -> header lvl $ foldMap jiraToPandocInlines inlns Jira.HorizontalRule -> horizontalRule Jira.List style items -> toPandocList style items Jira.NoFormat ps txt -> toPandocCodeBlocks Nothing ps txt Jira.Panel ps blcks -> toPandocDiv ps blcks Jira.Para inlns -> para $ foldMap jiraToPandocInlines inlns Jira.Table rows -> toPandocTable rows -- | Create a pandoc list – either to a @'BulletList'@ or an @'OrderedList'@. toPandocList :: Jira.ListStyle -> [[Jira.Block]] -> Blocks toPandocList style items = let items' = map (foldMap jiraToPandocBlocks) items in if style == Jira.Enumeration then orderedList items' else bulletList items' -- | Create a pandoc @'CodeBlock'@ toPandocCodeBlocks :: Maybe Jira.Language -> [Jira.Parameter] -> Text -> Blocks toPandocCodeBlocks langMay params txt = let classes = case langMay of Just (Jira.Language lang) -> [lang] Nothing -> [] in codeBlockWith ("", classes, map paramToPair params) txt -- | Create a pandoc @'Div'@ from a panel. toPandocDiv :: [Jira.Parameter] -> [Jira.Block] -> Blocks toPandocDiv params = let (titles, params') = partition ((== "title") . Jira.parameterKey) params addTitle = case titles of [] -> id (title:_) -> \blks -> (divWith ("", ["panelheader"], []) . plain . strong $ text (Jira.parameterValue title)) <> blks in divWith ("", ["panel"], map paramToPair params') . addTitle . foldMap jiraToPandocBlocks paramToPair :: Jira.Parameter -> (Text, Text) paramToPair (Jira.Parameter key value) = (key, value) -- | Give textual representation of a color. colorName :: Jira.ColorName -> Text colorName (Jira.ColorName name) = name -- | Create a pandoc @'Table'@. -- This relies on 'simpleTable' to sanitize the table. toPandocTable :: [Jira.Row] -> Blocks toPandocTable rows = let (headerRow, bodyRows) = splitIntoHeaderAndBody rows in simpleTable (rowToBlocksList headerRow) (map rowToBlocksList bodyRows) rowToBlocksList :: Jira.Row -> [Blocks] rowToBlocksList (Jira.Row cells) = map cellContent cells where cellContent cell = let content = case cell of Jira.HeaderCell x -> x Jira.BodyCell x -> x in foldMap jiraToPandocBlocks content splitIntoHeaderAndBody :: [Jira.Row] -> (Jira.Row, [Jira.Row]) splitIntoHeaderAndBody [] = (Jira.Row [], []) splitIntoHeaderAndBody rows@(first@(Jira.Row cells) : rest) = let isHeaderCell Jira.HeaderCell{} = True isHeaderCell Jira.BodyCell{} = False in if all isHeaderCell cells then (first, rest) else (Jira.Row [], rows) -- -- Inlines -- -- | Converts a Jira inline to a Pandoc block. jiraToPandocInlines :: Jira.Inline -> Inlines jiraToPandocInlines = \case Jira.Anchor t -> spanWith (t, [], []) mempty Jira.AutoLink url -> link (Jira.fromURL url) "" (str (Jira.fromURL url)) Jira.Citation ils -> str "—" <> space <> emph (fromInlines ils) Jira.ColorInline c ils -> spanWith ("", [], [("color", colorName c)]) $ fromInlines ils Jira.Emoji icon -> str . iconUnicode $ icon Jira.Entity entity -> str . fromEntity $ entity Jira.Image params url -> let (title, attr) = imgParams params in imageWith attr (Jira.fromURL url) title mempty Jira.Link lt alias url -> jiraLinkToPandoc lt alias url Jira.Linebreak -> linebreak Jira.Monospaced inlns -> code . stringify . toList . fromInlines $ inlns Jira.Space -> space Jira.SpecialChar c -> str (Data.Text.singleton c) Jira.Str t -> str t Jira.Styled style inlns -> fromStyle style $ fromInlines inlns where fromInlines = foldMap jiraToPandocInlines fromEntity e = case lookupEntity (e <> ";") of Nothing -> "&" `append` e `append` ";" Just t ->t fromStyle = \case Jira.Emphasis -> emph Jira.Insert -> underline Jira.Strikeout -> strikeout Jira.Strong -> strong Jira.Subscript -> subscript Jira.Superscript -> superscript imgParams :: [Jira.Parameter] -> (Text, Attr) imgParams = foldr addImgParam ("", ("", [], [])) addImgParam :: Jira.Parameter -> (Text, Attr) -> (Text, Attr) addImgParam p (title, attr@(ident, classes, kvs)) = case Jira.parameterKey p of "title" -> (Jira.parameterValue p, attr) "thumbnail" -> (title, (ident, "thumbnail":classes, kvs)) _ -> let kv = (Jira.parameterKey p, Jira.parameterValue p) in (title, (ident, classes, kv:kvs)) -- | Convert a Jira link to pandoc inlines. jiraLinkToPandoc :: Jira.LinkType -> [Jira.Inline] -> Jira.URL -> Inlines jiraLinkToPandoc linkType alias url = let url' = (if linkType == Jira.User then ("~" <>) else id) $ Jira.fromURL url alias' = case alias of [] -> str url' _ -> foldMap jiraToPandocInlines alias in case linkType of Jira.External -> link url' "" alias' Jira.Email -> link ("mailto:" <> url') "" alias' Jira.Attachment -> linkWith ("", ["attachment"], []) url' "" alias' Jira.User -> linkWith ("", ["user-account"], []) url' "" alias' Jira.SmartCard -> linkWith ("", ["smart-card"], []) url' "" alias' Jira.SmartLink -> linkWith ("", ["smart-link"], []) url' "" alias' -- | Get unicode representation of a Jira icon. iconUnicode :: Jira.Icon -> Text iconUnicode = \case Jira.IconSlightlySmiling -> "🙂" Jira.IconFrowning -> "🙁" Jira.IconTongue -> "😛" Jira.IconSmiling -> "😃" Jira.IconWinking -> "😉" Jira.IconThumbsUp -> "👍" Jira.IconThumbsDown -> "👎" Jira.IconInfo -> "ℹ" Jira.IconCheckmark -> "✔" Jira.IconX -> "❌" Jira.IconAttention -> "❗" Jira.IconPlus -> "➕" Jira.IconMinus -> "➖" Jira.IconQuestionmark -> "❓" Jira.IconOn -> "💡" Jira.IconOff -> "🌙" Jira.IconStar -> "⭐" Jira.IconStarRed -> "⭐" Jira.IconStarGreen -> "⭐" Jira.IconStarBlue -> "⭐" Jira.IconStarYellow -> "⭐" Jira.IconFlag -> "⚑" Jira.IconFlagOff -> "⚐" ================================================ FILE: src/Text/Pandoc/Readers/LaTeX/Citation.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} module Text.Pandoc.Readers.LaTeX.Citation ( citationCommands , cites ) where import Text.Pandoc.Class import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.Builder as B import qualified Data.Map as M import Data.Text (Text) import Control.Applicative ((<|>), optional, many) import Control.Monad (mzero) import Control.Monad.Trans (lift) import Control.Monad.Except (throwError) import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline, optional, space, spaces, withRaw, (<|>)) citationCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines) citationCommands inline = let citation = citationWith inline tok = spaces *> grouped inline in M.fromList [ ("cite", citation "cite" NormalCitation False) , ("Cite", citation "Cite" NormalCitation False) , ("citep", citation "citep" NormalCitation False) , ("citep*", citation "citep*" NormalCitation False) , ("citeal", citation "citeal" NormalCitation False) , ("citealp", citation "citealp" NormalCitation False) , ("citealp*", citation "citealp*" NormalCitation False) , ("autocite", citation "autocite" NormalCitation False) , ("smartcite", citation "smartcite" NormalCitation False) , ("footcite", inNote <$> citation "footcite" NormalCitation False) , ("parencite", citation "parencite" NormalCitation False) , ("supercite", citation "supercite" NormalCitation False) , ("footcitetext", inNote <$> citation "footcitetext" NormalCitation False) , ("citeyearpar", citation "citeyearpar" SuppressAuthor False) , ("citeyear", citation "citeyear" SuppressAuthor False) , ("autocite*", citation "autocite*" SuppressAuthor False) , ("cite*", citation "cite*" SuppressAuthor False) , ("parencite*", citation "parencite*" SuppressAuthor False) , ("textcite", citation "textcite" AuthorInText False) , ("citet", citation "citet" AuthorInText False) , ("citet*", citation "citet*" AuthorInText False) , ("citealt", citation "citealt" AuthorInText False) , ("citealt*", citation "citealt*" AuthorInText False) , ("textcites", citation "textcites" AuthorInText True) , ("cites", citation "cites" NormalCitation True) , ("autocites", citation "autocites" NormalCitation True) , ("footcites", inNote <$> citation "footcites" NormalCitation True) , ("parencites", citation "parencites" NormalCitation True) , ("supercites", citation "supercites" NormalCitation True) , ("footcitetexts", inNote <$> citation "footcitetexts" NormalCitation True) , ("Autocite", citation "Autocite" NormalCitation False) , ("Smartcite", citation "Smartcite" NormalCitation False) , ("Footcite", inNote <$> citation "Footcite" NormalCitation False) , ("Parencite", citation "Parencite" NormalCitation False) , ("Supercite", citation "Supercite" NormalCitation False) , ("Footcitetext", inNote <$> citation "Footcitetext" NormalCitation False) , ("Citeyearpar", citation "Citeyearpar" SuppressAuthor False) , ("Citeyear", citation "Citeyear" SuppressAuthor False) , ("Autocite*", citation "Autocite*" SuppressAuthor False) , ("Cite*", citation "Cite*" SuppressAuthor False) , ("Parencite*", citation "Parencite*" SuppressAuthor False) , ("Textcite", citation "Textcite" AuthorInText False) , ("Textcites", citation "Textcites" AuthorInText True) , ("Cites", citation "Cites" NormalCitation True) , ("Autocites", citation "Autocites" NormalCitation True) , ("Footcites", inNote <$> citation "Footcites" NormalCitation True) , ("Parencites", citation "Parencites" NormalCitation True) , ("Supercites", citation "Supercites" NormalCitation True) , ("Footcitetexts", inNote <$> citation "Footcitetexts" NormalCitation True) , ("citetext", complexNatbibCitation inline NormalCitation) , ("citeauthor", (try (tok *> sp *> controlSeq "citetext") *> complexNatbibCitation inline AuthorInText) <|> citation "citeauthor" AuthorInText False) , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>= addMeta "nocite")) ] -- citations addPrefix :: [Inline] -> [Citation] -> [Citation] addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks addPrefix _ _ = [] addSuffix :: [Inline] -> [Citation] -> [Citation] addSuffix s ks@(_:_) = let k = last ks in init ks ++ [k {citationSuffix = citationSuffix k ++ s}] addSuffix _ _ = [] simpleCiteArgs :: forall m . PandocMonad m => LP m Inlines -> LP m [Citation] simpleCiteArgs inline = try $ do first <- optionMaybe $ toList <$> opt second <- optionMaybe $ toList <$> opt keys <- try $ bgroup *> manyTill citationLabel egroup let (pre, suf) = case (first , second ) of (Just s , Nothing) -> (mempty, s ) (Just s , Just t ) -> (s , t ) _ -> (mempty, mempty) conv k = Citation { citationId = k , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationHash = 0 , citationNoteNum = 0 } return $ addPrefix pre $ addSuffix suf $ map conv keys where opt :: PandocMonad m => LP m Inlines opt = do toks <- try (sp *> bracketedToks <* sp) -- now parse the toks as inlines st <- getState parsed <- lift $ runParserT (mconcat <$> many inline) st "bracketed option" (TokStream False toks) case parsed of Right result -> return result Left e -> throwError $ fromParsecError (toSources toks) e citationLabel :: PandocMonad m => LP m Text citationLabel = do sp untokenize <$> (many1 (satisfyTok isWordTok <|> symbolIn bibtexKeyChar) <* sp <* optional (symbol ',') <* sp) where bibtexKeyChar = ".:;?!`'()/*@_+=-&[]" :: [Char] cites :: PandocMonad m => LP m Inlines -> CitationMode -> Bool -> LP m [Citation] cites inline mode multi = try $ do let paropt = parenWrapped inline cits <- if multi then do multiprenote <- optionMaybe $ toList <$> paropt multipostnote <- optionMaybe $ toList <$> paropt let (pre, suf) = case (multiprenote, multipostnote) of (Just s , Nothing) -> (mempty, s) (Nothing , Just t) -> (mempty, t) (Just s , Just t ) -> (s, t) _ -> (mempty, mempty) tempCits <- many1 $ simpleCiteArgs inline case tempCits of (k:ks) -> case ks of (_:_) -> return $ (addMprenote pre k : init ks) ++ [addMpostnote suf (last ks)] _ -> return [addMprenote pre (addMpostnote suf k)] _ -> return [[]] else count 1 $ simpleCiteArgs inline let cs = concat cits return $ case mode of AuthorInText -> case cs of (c:rest) -> c {citationMode = mode} : rest [] -> [] _ -> map (\a -> a {citationMode = mode}) cs where mprenote (k:ks) = (k:ks) ++ [Space] mprenote _ = mempty mpostnote (k:ks) = [Str ",", Space] ++ (k:ks) mpostnote _ = mempty addMprenote mpn (k:ks) = let mpnfinal = case citationPrefix k of (_:_) -> mprenote mpn _ -> mpn in addPrefix mpnfinal (k:ks) addMprenote _ _ = [] addMpostnote = addSuffix . mpostnote citationWith :: PandocMonad m => LP m Inlines -> Text -> CitationMode -> Bool -> LP m Inlines citationWith inline name mode multi = do (c,raw) <- withRaw $ cites inline mode multi return $ cite c (rawInline "latex" $ "\\" <> name <> untokenize raw) handleCitationPart :: Inlines -> [Citation] handleCitationPart ils = let isCite Cite{} = True isCite _ = False (pref, rest) = break isCite (toList ils) in case rest of (Cite cs _:suff) -> addPrefix pref $ addSuffix suff cs _ -> [] complexNatbibCitation :: PandocMonad m => LP m Inlines -> CitationMode -> LP m Inlines complexNatbibCitation inline mode = try $ do (cs, raw) <- withRaw $ concat <$> do bgroup items <- mconcat <$> many1 (notFollowedBy (symbol ';') >> inline) `sepBy1` symbol ';' egroup return $ map handleCitationPart items case cs of [] -> mzero (c:cits) -> return $ cite (c{ citationMode = mode }:cits) (rawInline "latex" $ "\\citetext" <> untokenize raw) inNote :: Inlines -> Inlines inNote ils = note $ para $ ils <> str "." ================================================ FILE: src/Text/Pandoc/Readers/LaTeX/Inline.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.LaTeX.Inline Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable -} module Text.Pandoc.Readers.LaTeX.Inline ( acronymCommands , verbCommands , charCommands , accentCommands , miscCommands , nameCommands , biblatexInlineCommands , refCommands , rawInlineOr , listingsLanguage ) where import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Builder import Text.Pandoc.Shared (toRomanNumeral, safeRead) import Text.Pandoc.TeX (Tok (..), TokType (..)) import Control.Applicative (optional, (<|>)) import Control.Monad (guard, mzero, mplus, unless) import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.Translations (translateTerm) import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.Extensions (extensionEnabled, Extension(..)) import Text.Pandoc.Parsing (getOption, updateState, getState, notFollowedBy, manyTill, getInput, setInput, incSourceColumn, option, many1) import Data.Char (isDigit) import Text.Pandoc.Highlighting (fromListingsLanguage,) import Data.Maybe (maybeToList, fromMaybe) import Text.Pandoc.Options (ReaderOptions(..)) import qualified Data.Text.Normalize as Normalize import qualified Text.Pandoc.Translations as Translations rawInlineOr :: PandocMonad m => Text -> LP m Inlines -> LP m Inlines rawInlineOr name' fallback = do parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions if parseRaw then rawInline "latex" <$> getRawCommand name' ("\\" <> name') else fallback dolabel :: PandocMonad m => LP m Inlines dolabel = do v <- braced let refstr = untokenize v updateState $ \st -> st{ sLastLabel = Just refstr } return $ spanWith (refstr,[],[("label", refstr)]) mempty doref :: PandocMonad m => Text -> LP m Inlines doref cls = do v <- braced let refstr = untokenize v return $ linkWith ("",[],[ ("reference-type", cls) , ("reference", refstr)]) ("#" <> refstr) "" (inBrackets $ str refstr) inBrackets :: Inlines -> Inlines inBrackets x = str "[" <> x <> str "]" doTerm :: PandocMonad m => Translations.Term -> LP m Inlines doTerm term = str <$> translateTerm term lit :: Text -> LP m Inlines lit = pure . str doverb :: PandocMonad m => LP m Inlines doverb = do Tok _ Symbol t <- anySymbol marker <- case T.uncons t of Just (c, ts) | T.null ts -> return c _ -> mzero withVerbatimMode $ code . untokenize <$> manyTill (notFollowedBy newlineTok >> verbTok marker) (symbol marker) verbTok :: PandocMonad m => Char -> LP m Tok verbTok stopchar = do t@(Tok pos toktype txt) <- anyTok case T.findIndex (== stopchar) txt of Nothing -> return t Just i -> do let (t1, t2) = T.splitAt i txt TokStream macrosExpanded inp <- getInput setInput $ TokStream macrosExpanded $ Tok (incSourceColumn pos i) Symbol (T.singleton stopchar) : tokenize (incSourceColumn pos (i + 1)) (T.drop 1 t2) ++ inp return $ Tok pos toktype t1 listingsLanguage :: [(Text, Text)] -> Maybe Text listingsLanguage opts = case lookup "language" opts of Nothing -> Nothing Just l -> fromListingsLanguage l `mplus` Just l dolstinline :: PandocMonad m => LP m Inlines dolstinline = do options <- option [] keyvals let classes = maybeToList $ listingsLanguage options doinlinecode classes domintinline :: PandocMonad m => LP m Inlines domintinline = do skipopts cls <- untokenize <$> braced doinlinecode [cls] doinlinecode :: PandocMonad m => [Text] -> LP m Inlines doinlinecode classes = do Tok _ Symbol t <- anySymbol marker <- case T.uncons t of Just (c, ts) | T.null ts -> return c _ -> mzero let stopchar = if marker == '{' then '}' else marker withVerbatimMode $ codeWith ("",classes,[]) . T.map nlToSpace . untokenize <$> manyTill (verbTok stopchar) (symbol stopchar) nlToSpace :: Char -> Char nlToSpace '\n' = ' ' nlToSpace x = x romanNumeralUpper :: (PandocMonad m) => LP m Inlines romanNumeralUpper = str . toRomanNumeral <$> romanNumeralArg romanNumeralLower :: (PandocMonad m) => LP m Inlines romanNumeralLower = str . T.toLower . toRomanNumeral <$> romanNumeralArg romanNumeralArg :: (PandocMonad m) => LP m Int romanNumeralArg = spaces *> (parser <|> inBraces) where inBraces = do symbol '{' spaces res <- parser spaces symbol '}' return res parser = do s <- untokenize <$> many1 (satisfyTok isWordTok) let (digits, rest) = T.span isDigit s unless (T.null rest) $ Prelude.fail "Non-digits in argument to \\Rn or \\RN" safeRead digits accentWith :: PandocMonad m => LP m Inlines -> Char -> Maybe Char -> LP m Inlines accentWith tok combiningAccent fallBack = do ils <- option mempty tok case toList ils of (Str (T.uncons -> Just (x, xs)) : ys) -> return $ fromList $ -- try to normalize to the combined character: Str (Normalize.normalize Normalize.NFC (T.pack [x, combiningAccent]) <> xs) : ys [Space] -> return $ str $ T.singleton $ fromMaybe combiningAccent fallBack [] -> return $ str $ T.singleton $ fromMaybe combiningAccent fallBack _ -> return ils verbCommands :: PandocMonad m => M.Map Text (LP m Inlines) verbCommands = M.fromList [ ("verb", doverb) , ("lstinline", dolstinline) , ("mintinline", domintinline) , ("Verb", doverb) ] miscCommands :: PandocMonad m => M.Map Text (LP m Inlines) miscCommands = M.fromList [ ("pounds", lit "£") , ("euro", lit "€") , ("copyright", lit "©") , ("textasciicircum", lit "^") , ("textasciitilde", lit "~") , ("textbaht", lit "฿") , ("textblank", lit "␢") , ("textbigcircle", lit "○") , ("textbrokenbar", lit "¦") , ("textbullet", lit "•") , ("textcentoldstyle", lit "¢") , ("textcopyright", lit "©") , ("textdagger", lit "†") , ("textdegree", lit "°") , ("textdollar", lit "$") , ("textdong", lit "₫") , ("textlira", lit "₤") , ("textmu", lit "μ") , ("textmusicalnote", lit "♪") , ("textonehalf", lit "½") , ("textonequarter", lit "¼") , ("textparagraph", lit "¶") , ("textpertenthousand", lit "‱") , ("textpeso", lit "₱") , ("textquotesingle", lit "'") , ("textregistered", lit "®") , ("textsection", lit "§") , ("textsterling", lit "£") , ("textthreequarters", lit "¾") , ("textthreesuperior", lit "³") , ("texttwosuperior", lit "²") , ("textyen", lit "¥") ] accentCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines) accentCommands tok = let accent = accentWith tok in M.fromList [ ("aa", lit "å") , ("AA", lit "Å") , ("ss", lit "ß") , ("o", lit "ø") , ("O", lit "Ø") , ("L", lit "Ł") , ("l", lit "ł") , ("ae", lit "æ") , ("AE", lit "Æ") , ("oe", lit "œ") , ("OE", lit "Œ") , ("H", accent '\779' Nothing) -- hungarumlaut , ("`", accent '\768' (Just '`')) -- grave , ("'", accent '\769' (Just '\'')) -- acute , ("^", accent '\770' (Just '^')) -- circ , ("~", accent '\771' (Just '~')) -- tilde , ("\"", accent '\776' Nothing) -- umlaut , (".", accent '\775' Nothing) -- dot , ("=", accent '\772' Nothing) -- macron , ("|", accent '\781' Nothing) -- vertical line above , ("b", accent '\817' Nothing) -- macron below , ("c", accent '\807' Nothing) -- cedilla , ("G", accent '\783' Nothing) -- doublegrave , ("h", accent '\777' Nothing) -- hookabove , ("d", accent '\803' Nothing) -- dotbelow , ("f", accent '\785' Nothing) -- inverted breve , ("r", accent '\778' Nothing) -- ringabove , ("t", accent '\865' Nothing) -- double inverted breve , ("U", accent '\782' Nothing) -- double vertical line above , ("v", accent '\780' Nothing) -- hacek , ("u", accent '\774' Nothing) -- breve , ("k", accent '\808' Nothing) -- ogonek , ("textogonekcentered", accent '\808' Nothing) -- ogonek , ("i", lit "ı") -- dotless i , ("j", lit "ȷ") -- dotless j , ("newtie", accent '\785' Nothing) -- inverted breve , ("textcircled", accent '\8413' Nothing) -- combining circle ] charCommands :: PandocMonad m => M.Map Text (LP m Inlines) charCommands = M.fromList [ ("ldots", lit "…") , ("vdots", lit "\8942") , ("dots", lit "…") , ("mdots", lit "…") , ("sim", lit "~") , ("sep", lit ",") , ("P", lit "¶") , ("S", lit "§") , ("$", lit "$") , ("%", lit "%") , ("&", lit "&") , ("#", lit "#") , ("_", lit "_") , ("{", lit "{") , ("}", lit "}") , ("-", lit "\x00ad") -- soft hyphen , ("qed", lit "\a0\x25FB") , ("lq", return (str "‘")) , ("rq", return (str "’")) , ("textquoteleft", return (str "‘")) , ("textquoteright", return (str "’")) , ("textquotedblleft", return (str "“")) , ("textquotedblright", return (str "”")) , ("/", pure mempty) -- italic correction , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState guard $ not inTableCell optional rawopt spaces)) , (",", lit "\8198") , ("@", pure mempty) , (" ", lit "\160") , ("ps", pure $ str "PS." <> space) , ("TeX", lit "TeX") , ("LaTeX", lit "LaTeX") , ("bar", lit "|") , ("textless", lit "<") , ("textgreater", lit ">") , ("textbackslash", lit "\\") , ("backslash", lit "\\") , ("slash", lit "/") -- fontawesome , ("faCheck", lit "\10003") , ("faClose", lit "\10007") -- hyphenat , ("bshyp", lit "\\\173") , ("fshyp", lit "/\173") , ("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 => LP m Inlines -> M.Map Text (LP m Inlines) biblatexInlineCommands tok = M.fromList -- biblatex misc [ ("RN", romanNumeralUpper) , ("Rn", romanNumeralLower) , ("mkbibquote", spanWith nullAttr . doubleQuoted <$> tok) , ("mkbibemph", spanWith nullAttr . emph <$> tok) , ("mkbibitalic", spanWith nullAttr . emph <$> tok) , ("mkbibbold", spanWith nullAttr . strong <$> tok) , ("mkbibparens", spanWith nullAttr . (\x -> str "(" <> x <> str ")") <$> tok) , ("mkbibbrackets", spanWith nullAttr . (\x -> str "[" <> x <> str "]") <$> tok) , ("autocap", spanWith nullAttr <$> tok) , ("textnormal", spanWith ("",["nodecor"],[]) <$> tok) , ("bibstring", (\x -> spanWith ("",[],[("bibstring",x)]) (str x)) . untokenize <$> braced) , ("adddot", pure (str ".")) , ("adddotspace", pure (spanWith nullAttr (str "." <> space))) , ("addabbrvspace", pure space) , ("hyphen", pure (str "-")) ] nameCommands :: PandocMonad m => M.Map Text (LP m Inlines) nameCommands = M.fromList [ ("figurename", doTerm Translations.Figure) , ("prefacename", doTerm Translations.Preface) , ("refname", doTerm Translations.References) , ("bibname", doTerm Translations.Bibliography) , ("chaptername", doTerm Translations.Chapter) , ("partname", doTerm Translations.Part) , ("contentsname", doTerm Translations.Contents) , ("listfigurename", doTerm Translations.ListOfFigures) , ("listtablename", doTerm Translations.ListOfTables) , ("indexname", doTerm Translations.Index) , ("abstractname", doTerm Translations.Abstract) , ("tablename", doTerm Translations.Table) , ("enclname", doTerm Translations.Encl) , ("ccname", doTerm Translations.Cc) , ("headtoname", doTerm Translations.To) , ("pagename", doTerm Translations.Page) , ("seename", doTerm Translations.See) , ("seealsoname", doTerm Translations.SeeAlso) , ("proofname", doTerm Translations.Proof) , ("glossaryname", doTerm Translations.Glossary) , ("lstlistingname", doTerm Translations.Listing) ] refCommands :: PandocMonad m => M.Map Text (LP m Inlines) refCommands = M.fromList [ ("label", rawInlineOr "label" dolabel) , ("ref", rawInlineOr "ref" $ doref "ref") , ("cref", rawInlineOr "cref" $ doref "ref+label") -- from cleveref.sty , ("Cref", rawInlineOr "Cref" $ doref "ref+Label") -- from cleveref.sty , ("vref", rawInlineOr "vref" $ doref "ref") -- from varioref.sty , ("eqref", rawInlineOr "eqref" $ doref "eqref") -- from amsmath.sty , ("autoref", rawInlineOr "autoref" $ doref "ref+label") -- from hyperref.sty ] acronymCommands :: PandocMonad m => M.Map Text (LP m Inlines) acronymCommands = M.fromList -- glossaries package [ ("gls", doAcronym "short") , ("Gls", doAcronym "short") , ("glsdesc", doAcronym "long") , ("Glsdesc", doAcronym "long") , ("GLSdesc", doAcronym "long") , ("acrlong", doAcronym "long") , ("Acrlong", doAcronym "long") , ("acrfull", doAcronym "full") , ("Acrfull", doAcronym "full") , ("acrshort", doAcronym "abbrv") , ("Acrshort", doAcronym "abbrv") , ("glspl", doAcronymPlural "short") , ("Glspl", doAcronymPlural "short") , ("glsdescplural", doAcronymPlural "long") , ("Glsdescplural", doAcronymPlural "long") , ("GLSdescplural", doAcronymPlural "long") -- acronyms package , ("ac", doAcronym "short") , ("acf", doAcronym "full") , ("acs", doAcronym "abbrv") , ("acl", doAcronym "long") , ("acp", doAcronymPlural "short") , ("acfp", doAcronymPlural "full") , ("acsp", doAcronymPlural "abbrv") , ("aclp", doAcronymPlural "long") , ("Ac", doAcronym "short") , ("Acf", doAcronym "full") , ("Acs", doAcronym "abbrv") , ("Acl", doAcronym "long") , ("Acp", doAcronymPlural "short") , ("Acfp", doAcronymPlural "full") , ("Acsp", doAcronymPlural "abbrv") , ("Aclp", doAcronymPlural "long") ] doAcronym :: PandocMonad m => Text -> LP m Inlines doAcronym form = do acro <- braced return . mconcat $ [spanWith ("",[],[("acronym-label", untokenize acro), ("acronym-form", "singular+" <> form)]) $ str $ untokenize acro] doAcronymPlural :: PandocMonad m => Text -> LP m Inlines doAcronymPlural form = do acro <- braced let plural = str "s" return . mconcat $ [spanWith ("",[],[("acronym-label", untokenize acro), ("acronym-form", "plural+" <> form)]) $ mconcat [str $ untokenize acro, plural]] ================================================ FILE: src/Text/Pandoc/Readers/LaTeX/Lang.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.LaTeX.Lang Copyright : Copyright (C) 2018-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Functions for parsing polyglossia and babel language specifiers to BCP47 'Lang'. -} module Text.Pandoc.Readers.LaTeX.Lang ( setDefaultLanguage , polyglossiaLangToBCP47 , babelLangToBCP47 , enquoteCommands , inlineLanguageCommands ) where import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Shared (extractSpaces) import Text.Collate.Lang (Lang(..), renderLang) import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.Translations (setTranslations) import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.Parsing (updateState, option, getState, QuoteContext(..), withQuoteContext) import Text.Pandoc.Builder (Blocks, Inlines, setMeta, str, spanWith, singleQuoted, doubleQuoted) enquote :: PandocMonad m => LP m Inlines -> Bool -> Maybe Text -> LP m Inlines enquote tok starred mblang = do skipopts let lang = mblang >>= babelLangToBCP47 let langspan = case lang of Nothing -> id Just l -> spanWith ("",[],[("lang", renderLang l)]) quoteContext <- sQuoteContext <$> getState if starred || quoteContext == InDoubleQuote then singleQuoted . langspan <$> withQuoteContext InSingleQuote tok else doubleQuoted . langspan <$> withQuoteContext InDoubleQuote tok enquoteCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines) enquoteCommands tok = M.fromList [ ("enquote*", enquote tok True Nothing) , ("enquote", enquote tok False Nothing) -- foreignquote is supposed to use native quote marks , ("foreignquote*", braced >>= enquote tok True . Just . untokenize) , ("foreignquote", braced >>= enquote tok False . Just . untokenize) -- hypehnquote uses regular quotes , ("hyphenquote*", braced >>= enquote tok True . Just . untokenize) , ("hyphenquote", braced >>= enquote tok False . Just . untokenize) ] foreignlanguage :: PandocMonad m => LP m Inlines -> LP m Inlines foreignlanguage tok = do babelLang <- untokenize <$> braced case babelLangToBCP47 babelLang of Just lang -> spanWith ("", [], [("lang", renderLang lang)]) <$> tok _ -> tok inlineLanguageCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines) inlineLanguageCommands tok = M.fromList $ ("foreignlanguage", foreignlanguage tok) : (mk <$> M.toList polyglossiaLangToBCP47) where mk (polyglossia, bcp47Func) = ("text" <> polyglossia, inlineLanguage tok bcp47Func) inlineLanguage :: PandocMonad m => LP m Inlines -> (Text -> Lang) -> LP m Inlines inlineLanguage tok bcp47Func = do o <- option "" $ T.filter (\c -> c /= '[' && c /= ']') <$> rawopt let lang = renderLang $ bcp47Func o extractSpaces (spanWith ("", [], [("lang", lang)])) <$> tok setDefaultLanguage :: PandocMonad m => LP m Blocks setDefaultLanguage = do o <- option "" $ T.filter (\c -> c /= '[' && c /= ']') <$> rawopt polylang <- untokenize <$> braced case M.lookup polylang polyglossiaLangToBCP47 of Nothing -> return mempty -- TODO mzero? warning? Just langFunc -> do let l = langFunc o setTranslations l updateState $ setMeta "lang" $ str (renderLang l) return mempty polyglossiaLangToBCP47 :: M.Map T.Text (T.Text -> Lang) polyglossiaLangToBCP47 = M.fromList [ ("arabic", \o -> case T.filter (/=' ') o of "locale=algeria" -> Lang "ar" Nothing (Just "DZ") [] [] [] "locale=mashriq" -> Lang "ar" Nothing (Just "SY") [] [] [] "locale=libya" -> Lang "ar" Nothing (Just "LY") [] [] [] "locale=morocco" -> Lang "ar" Nothing (Just "MA") [] [] [] "locale=mauritania" -> Lang "ar" Nothing (Just "MR") [] [] [] "locale=tunisia" -> Lang "ar" Nothing (Just "TN") [] [] [] _ -> Lang "ar" Nothing Nothing [] [] []) , ("german", \o -> case T.filter (/=' ') o of "spelling=old" -> Lang "de" Nothing (Just "DE") ["1901"] [] [] "variant=austrian,spelling=old" -> Lang "de" Nothing (Just "AT") ["1901"] [] [] "variant=austrian" -> Lang "de" Nothing (Just "AT") [] [] [] "variant=swiss,spelling=old" -> Lang "de" Nothing (Just "CH") ["1901"] [] [] "variant=swiss" -> Lang "de" Nothing (Just "CH") [] [] [] _ -> Lang "de" Nothing Nothing [] [] []) , ("lsorbian", \_ -> Lang "dsb" Nothing Nothing [] [] []) , ("greek", \o -> case T.filter (/=' ') o of "variant=poly" -> Lang "el" Nothing (Just "polyton") [] [] [] "variant=ancient" -> Lang "grc" Nothing Nothing [] [] [] _ -> Lang "el" Nothing Nothing [] [] []) , ("english", \o -> case T.filter (/=' ') o of "variant=australian" -> Lang "en" Nothing (Just "AU") [] [] [] "variant=canadian" -> Lang "en" Nothing (Just "CA") [] [] [] "variant=british" -> Lang "en" Nothing (Just "GB") [] [] [] "variant=newzealand" -> Lang "en" Nothing (Just "NZ") [] [] [] "variant=american" -> Lang "en" Nothing (Just "US") [] [] [] _ -> Lang "en" Nothing Nothing [] [] []) , ("usorbian", \_ -> Lang "hsb" Nothing Nothing [] [] []) , ("latin", \o -> case T.filter (/=' ') o of "variant=classic" -> Lang "la" Nothing Nothing ["x-classic"] [] [] _ -> Lang "la" Nothing Nothing [] [] []) , ("slovenian", \_ -> Lang "sl" Nothing Nothing [] [] []) , ("serbianc", \_ -> Lang "sr" (Just "Cyrl") Nothing [] [] []) , ("pinyin", \_ -> Lang "zh" (Just "Latn") Nothing ["pinyin"] [] []) , ("afrikaans", \_ -> simpleLang "af") , ("amharic", \_ -> simpleLang "am") , ("assamese", \_ -> simpleLang "as") , ("asturian", \_ -> simpleLang "ast") , ("bulgarian", \_ -> simpleLang "bg") , ("bengali", \_ -> simpleLang "bn") , ("tibetan", \_ -> simpleLang "bo") , ("breton", \_ -> simpleLang "br") , ("catalan", \_ -> simpleLang "ca") , ("welsh", \_ -> simpleLang "cy") , ("czech", \_ -> simpleLang "cs") , ("coptic", \_ -> simpleLang "cop") , ("danish", \_ -> simpleLang "da") , ("divehi", \_ -> simpleLang "dv") , ("esperanto", \_ -> simpleLang "eo") , ("spanish", \_ -> simpleLang "es") , ("estonian", \_ -> simpleLang "et") , ("basque", \_ -> simpleLang "eu") , ("farsi", \_ -> simpleLang "fa") , ("finnish", \_ -> simpleLang "fi") , ("french", \_ -> simpleLang "fr") , ("friulan", \_ -> simpleLang "fur") , ("irish", \_ -> simpleLang "ga") , ("scottish", \_ -> simpleLang "gd") , ("ethiopic", \_ -> simpleLang "gez") , ("galician", \_ -> simpleLang "gl") , ("hebrew", \_ -> simpleLang "he") , ("hindi", \_ -> simpleLang "hi") , ("croatian", \_ -> simpleLang "hr") , ("magyar", \_ -> simpleLang "hu") , ("armenian", \_ -> simpleLang "hy") , ("gujarati", \_ -> simpleLang "gu") , ("interlingua", \_ -> simpleLang "ia") , ("indonesian", \_ -> simpleLang "id") , ("icelandic", \_ -> simpleLang "is") , ("italian", \_ -> simpleLang "it") , ("japanese", \_ -> simpleLang "ja") , ("khmer", \_ -> simpleLang "km") , ("kurmanji", \_ -> simpleLang "kmr") , ("kannada", \_ -> simpleLang "kn") , ("korean", \_ -> simpleLang "ko") , ("lao", \_ -> simpleLang "lo") , ("lithuanian", \_ -> simpleLang "lt") , ("latvian", \_ -> simpleLang "lv") , ("malayalam", \_ -> simpleLang "ml") , ("mongolian", \_ -> simpleLang "mn") , ("marathi", \_ -> simpleLang "mr") , ("dutch", \_ -> simpleLang "nl") , ("nynorsk", \_ -> simpleLang "nn") , ("norsk", \_ -> simpleLang "no") , ("nko", \_ -> simpleLang "nqo") , ("occitan", \_ -> simpleLang "oc") , ("oriya", \_ -> simpleLang "or") , ("punjabi", \_ -> simpleLang "pa") , ("polish", \_ -> simpleLang "pl") , ("piedmontese", \_ -> simpleLang "pms") , ("portuguese", \_ -> simpleLang "pt") , ("romansh", \_ -> simpleLang "rm") , ("romanian", \_ -> simpleLang "ro") , ("russian", \_ -> simpleLang "ru") , ("sanskrit", \_ -> simpleLang "sa") , ("samin", \_ -> simpleLang "se") , ("slovak", \_ -> simpleLang "sk") , ("albanian", \_ -> simpleLang "sq") , ("serbian", \_ -> simpleLang "sr") , ("swedish", \_ -> simpleLang "sv") , ("syriac", \_ -> simpleLang "syr") , ("tamil", \_ -> simpleLang "ta") , ("telugu", \_ -> simpleLang "te") , ("thai", \_ -> simpleLang "th") , ("turkmen", \_ -> simpleLang "tk") , ("turkish", \_ -> simpleLang "tr") , ("ukrainian", \_ -> simpleLang "uk") , ("urdu", \_ -> simpleLang "ur") , ("vietnamese", \_ -> simpleLang "vi") ] simpleLang :: Text -> Lang simpleLang l = Lang l Nothing Nothing [] [] [] babelLangToBCP47 :: T.Text -> Maybe Lang babelLangToBCP47 s = case s of "austrian" -> Just $ Lang "de" Nothing (Just "AT") ["1901"] [] [] "naustrian" -> Just $ Lang "de" Nothing (Just "AT") [] [] [] "swissgerman" -> Just $ Lang "de" Nothing (Just "CH") ["1901"] [] [] "nswissgerman" -> Just $ Lang "de" Nothing (Just "CH") [] [] [] "german" -> Just $ Lang "de" Nothing (Just "DE") ["1901"] [] [] "ngerman" -> Just $ Lang "de" Nothing (Just "DE") [] [] [] "lowersorbian" -> Just $ Lang "dsb" Nothing Nothing [] [] [] "uppersorbian" -> Just $ Lang "hsb" Nothing Nothing [] [] [] "polytonicgreek" -> Just $ Lang "el" Nothing Nothing ["polyton"] [] [] "polutonikogreek" -> Just $ Lang "el" Nothing Nothing ["polyton"] [] [] "slovene" -> Just $ simpleLang "sl" "australian" -> Just $ Lang "en" Nothing (Just "AU") [] [] [] "canadian" -> Just $ Lang "en" Nothing (Just "CA") [] [] [] "british" -> Just $ Lang "en" Nothing (Just "GB") [] [] [] "newzealand" -> Just $ Lang "en" Nothing (Just "NZ") [] [] [] "american" -> Just $ Lang "en" Nothing (Just "US") [] [] [] "classiclatin" -> Just $ Lang "la" Nothing Nothing ["x-classic"] [] [] _ -> ($ "") <$> M.lookup s polyglossiaLangToBCP47 ================================================ FILE: src/Text/Pandoc/Readers/LaTeX/Macro.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Text.Pandoc.Readers.LaTeX.Macro ( macroDef ) where import Text.Pandoc.Extensions (Extension(..)) import Text.Pandoc.Logging (LogMessage(MacroAlreadyDefined)) import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.TeX import Text.Pandoc.Class import Text.Pandoc.Shared (safeRead) import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline, optional, space, spaces, withRaw, (<|>)) import Control.Applicative ((<|>), optional) import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import qualified Data.List.NonEmpty as NonEmpty import Data.List.NonEmpty (NonEmpty(..)) macroDef :: (PandocMonad m, Monoid a) => (Text -> a) -> LP m a macroDef constructor = do (_, s) <- withRaw (commandDef <|> environmentDef) (constructor (untokenize s) <$ guardDisabled Ext_latex_macros) <|> return mempty where commandDef = do nameMacroPairs <- newcommand <|> checkGlobal (letmacro <|> edefmacro <|> defmacro <|> newif) guardDisabled Ext_latex_macros <|> mapM_ insertMacro nameMacroPairs environmentDef = do mbenv <- newenvironment case mbenv of Nothing -> return () Just (name, macro1, macro2) -> guardDisabled Ext_latex_macros <|> do insertMacro (name, macro1) insertMacro ("end" <> name, macro2) -- @\newenvironment{envname}[n-args][default]{begin}{end}@ -- is equivalent to -- @\newcommand{\envname}[n-args][default]{begin}@ -- @\newcommand{\endenvname}@ insertMacro :: PandocMonad m => (Text, Macro) -> LP m () insertMacro (name, macro'@(Macro GlobalScope _ _ _ _)) = updateState $ \s -> s{ sMacros = NonEmpty.map (M.insert name macro') (sMacros s) } insertMacro (name, macro'@(Macro GroupScope _ _ _ _)) = updateState $ \s -> s{ sMacros = M.insert name macro' (NonEmpty.head (sMacros s)) :| NonEmpty.tail (sMacros s) } lookupMacro :: PandocMonad m => Text -> LP m Macro lookupMacro name = do macros :| _ <- sMacros <$> getState case M.lookup name macros of Just m -> return m Nothing -> fail "Macro not found" letmacro :: PandocMonad m => LP m [(Text, Macro)] letmacro = do controlSeq "let" withVerbatimMode $ do Tok _ (CtrlSeq name) _ <- anyControlSeq optional $ symbol '=' spaces -- we first parse in verbatim mode, and then expand macros, -- because we don't want \let\foo\bar to turn into -- \let\foo hello if we have previously \def\bar{hello} target <- anyControlSeq <|> singleChar case target of (Tok _ (CtrlSeq name') _) -> (do m <- lookupMacro name' pure [(name, m)]) <|> pure [(name, Macro GroupScope ExpandWhenDefined [] Nothing [target])] _ -> pure [(name, Macro GroupScope ExpandWhenDefined [] Nothing [target])] checkGlobal :: PandocMonad m => LP m [(Text, Macro)] -> LP m [(Text, Macro)] checkGlobal p = (controlSeq "global" *> (map (\(n, Macro _ expand arg optarg contents) -> (n, Macro GlobalScope expand arg optarg contents)) <$> p)) <|> p edefmacro :: PandocMonad m => LP m [(Text, Macro)] edefmacro = do scope <- (GroupScope <$ controlSeq "edef") <|> (GlobalScope <$ controlSeq "xdef") (name, contents) <- withVerbatimMode $ do Tok _ (CtrlSeq name) _ <- anyControlSeq -- we first parse in verbatim mode, and then expand macros, -- because we don't want \let\foo\bar to turn into -- \let\foo hello if we have previously \def\bar{hello} contents <- bracedOrToken return (name, contents) -- expand macros contents' <- parseFromToks (many anyTok) contents return [(name, Macro scope ExpandWhenDefined [] Nothing contents')] defmacro :: PandocMonad m => LP m [(Text, Macro)] defmacro = do -- we use withVerbatimMode, because macros are to be expanded -- at point of use, not point of definition scope <- (GroupScope <$ controlSeq "def") <|> (GlobalScope <$ controlSeq "gdef") withVerbatimMode $ do Tok _ (CtrlSeq name) _ <- anyControlSeq argspecs <- many (argspecArg <|> argspecPattern) contents <- bracedOrToken return [(name, Macro scope ExpandWhenUsed argspecs Nothing contents)] -- \newif\iffoo' defines: -- \iffoo to be \iffalse -- \footrue to be a command that defines \iffoo to be \iftrue -- \foofalse to be a command that defines \iffoo to be \iffalse newif :: PandocMonad m => LP m [(Text, Macro)] newif = do controlSeq "newif" withVerbatimMode $ do Tok pos (CtrlSeq name) _ <- anyControlSeq -- \def\iffoo\iffalse -- \def\footrue{\def\iffoo\iftrue} -- \def\foofalse{\def\iffoo\iffalse} let base = T.drop 2 name return [ (name, Macro GroupScope ExpandWhenUsed [] Nothing [Tok pos (CtrlSeq "iffalse") "\\iffalse"]) , (base <> "true", Macro GroupScope ExpandWhenUsed [] Nothing [ Tok pos (CtrlSeq "def") "\\def" , Tok pos (CtrlSeq name) ("\\" <> name) , Tok pos Symbol "{" , Tok pos (CtrlSeq "iftrue") "\\iftrue" , Tok pos Symbol "}" ]) , (base <> "false", Macro GroupScope ExpandWhenUsed [] Nothing [ Tok pos (CtrlSeq "def") "\\def" , Tok pos (CtrlSeq name) ("\\" <> name) , Tok pos Symbol "{" , Tok pos (CtrlSeq "iffalse") "\\iffalse" , Tok pos Symbol "}" ]) ] argspecArg :: PandocMonad m => LP m ArgSpec argspecArg = do Tok _ (Arg i) _ <- satisfyTok isArgTok return $ ArgNum i argspecPattern :: PandocMonad m => LP m ArgSpec argspecPattern = Pattern <$> many1 (satisfyTok (\(Tok _ toktype' txt) -> (toktype' == Symbol || toktype' == Word) && (txt /= "{" && txt /= "\\" && txt /= "}"))) newcommand :: PandocMonad m => LP m [(Text, Macro)] newcommand = do Tok pos (CtrlSeq mtype) _ <- controlSeq "newcommand" <|> controlSeq "renewcommand" <|> controlSeq "providecommand" <|> controlSeq "DeclareMathOperator" <|> controlSeq "DeclareRobustCommand" withVerbatimMode $ do Tok _ (CtrlSeq name) txt <- do optional (symbol '*') anyControlSeq <|> (symbol '{' *> spaces *> anyControlSeq <* spaces <* symbol '}') spaces numargs <- option 0 $ try bracketedNum let argspecs = map ArgNum [1..numargs] spaces optarg <- option Nothing $ Just <$> try bracketedToks spaces contents' <- bracedOrToken let contents = case mtype of "DeclareMathOperator" -> Tok pos (CtrlSeq "mathop") "\\mathop" : Tok pos Symbol "{" : Tok pos (CtrlSeq "mathrm") "\\mathrm" : Tok pos Symbol "{" : (contents' ++ [ Tok pos Symbol "}", Tok pos Symbol "}" ]) _ -> contents' let macro = Macro GroupScope ExpandWhenUsed argspecs optarg contents (do lookupMacro name case mtype of "providecommand" -> return [] "renewcommand" -> return [(name, macro)] _ -> [] <$ report (MacroAlreadyDefined txt pos)) <|> pure [(name, macro)] newenvironment :: PandocMonad m => LP m (Maybe (Text, Macro, Macro)) newenvironment = do pos <- getPosition Tok _ (CtrlSeq mtype) _ <- controlSeq "newenvironment" <|> controlSeq "renewenvironment" <|> controlSeq "provideenvironment" withVerbatimMode $ do optional $ symbol '*' spaces name <- untokenize <$> braced spaces numargs <- option 0 $ try bracketedNum spaces optarg <- option Nothing $ Just <$> try bracketedToks let argspecs = map (\i -> ArgNum i) [1..numargs] startcontents <- spaces >> bracedOrToken endcontents <- spaces >> bracedOrToken -- we need the environment to be in a group so macros defined -- inside behave correctly: let bg = Tok pos (CtrlSeq "bgroup") "\\bgroup " let eg = Tok pos (CtrlSeq "egroup") "\\egroup " let result = (name, Macro GroupScope ExpandWhenUsed argspecs optarg (bg:startcontents), Macro GroupScope ExpandWhenUsed [] Nothing (endcontents ++ [eg])) (do lookupMacro name case mtype of "provideenvironment" -> return Nothing "renewenvironment" -> return (Just result) _ -> do report $ MacroAlreadyDefined name pos return Nothing) <|> return (Just result) bracketedNum :: PandocMonad m => LP m Int bracketedNum = do ds <- untokenize <$> bracketedToks case safeRead ds of Just i -> return i _ -> return 0 ================================================ FILE: src/Text/Pandoc/Readers/LaTeX/Math.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Text.Pandoc.Readers.LaTeX.Math ( withMathMode , dollarsMath , inlineEnvironments , inlineEnvironmentNames , inlineEnvironment , mathInline , mathDisplay , theoremstyle , theoremEnvironment , newtheorem , proof ) where import Data.Maybe (fromMaybe, mapMaybe, listToMaybe) import qualified Data.List as L import Text.Pandoc.Walk (walk) import Text.Pandoc.Builder as B import qualified Data.Sequence as Seq import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.TeX import Text.Pandoc.Class import Text.Pandoc.Shared (trimMath, trimr) import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline, optional, space, spaces, withRaw, (<|>)) import Control.Applicative ((<|>), optional) import Control.Monad (guard, mzero) import qualified Data.Map as M import Data.Text (Text) withMathMode :: PandocMonad m => LP m a -> LP m a withMathMode p = do oldMathMode <- sMathMode <$> getState updateState $ \s -> s{ sMathMode = True } result <- p updateState $ \s -> s{ sMathMode = oldMathMode } return result dollarsMath :: PandocMonad m => LP m Inlines dollarsMath = do symbol '$' display <- option False (True <$ symbol '$') (do contents <- try $ untokenize <$> withMathMode (pDollarsMath 0) if display then mathDisplay contents <$ symbol '$' else return $ mathInline contents) <|> (guard display >> return (mathInline "")) -- Int is number of embedded groupings pDollarsMath :: PandocMonad m => Int -> LP m [Tok] pDollarsMath n = do tk@(Tok _ toktype t) <- anyTok case toktype of Symbol | t == "$" , n == 0 -> return [] | t == "\\" -> do tk' <- anyTok (tk :) . (tk' :) <$> pDollarsMath n | t == "{" -> (tk :) <$> pDollarsMath (n+1) | t == "}" -> if n > 0 then (tk :) <$> pDollarsMath (n-1) else mzero _ -> (tk :) <$> pDollarsMath n mathDisplay :: Text -> Inlines mathDisplay = displayMath . trimMath mathInline :: Text -> Inlines mathInline = math . trimMath mathEnvWith :: PandocMonad m => (Inlines -> a) -> Maybe Text -> Text -> LP m a mathEnvWith f innerEnv name = f . mathDisplay . inner <$> mathEnv name where inner x = case innerEnv of Nothing -> x Just y -> "\\begin{" <> y <> "}\n" <> x <> "\n\\end{" <> y <> "}" mathEnv :: PandocMonad m => Text -> LP m Text mathEnv name = withMathMode $ do optional blankline res <- manyTill anyTok (end_ name) return $ trimr $ untokenize res inlineEnvironment :: PandocMonad m => LP m Inlines inlineEnvironment = try $ do controlSeq "begin" name <- untokenize <$> braced M.findWithDefault mzero name inlineEnvironments inlineEnvironmentNames :: [Text] inlineEnvironmentNames = M.keys (inlineEnvironments :: M.Map Text (LP PandocPure Inlines)) inlineEnvironments :: PandocMonad m => M.Map Text (LP m Inlines) inlineEnvironments = M.fromList [ ("displaymath", mathEnvWith id Nothing "displaymath") , ("math", math <$> mathEnv "math") , ("equation", mathEnvWith id (Just "equation") "equation") , ("equation*", mathEnvWith id (Just "equation*") "equation*") , ("gather", mathEnvWith id (Just "gather") "gather") , ("gather*", mathEnvWith id (Just "gather*") "gather*") , ("multline", mathEnvWith id (Just "multline") "multline") , ("multline*", mathEnvWith id (Just "multline*") "multline*") , ("eqnarray", mathEnvWith id (Just "eqnarray") "eqnarray") , ("eqnarray*", mathEnvWith id (Just "eqnarray*") "eqnarray*") , ("align", mathEnvWith id (Just "align") "align") , ("align*", mathEnvWith id (Just "align*") "align*") , ("alignat", mathEnvWith id (Just "alignat") "alignat") , ("alignat*", mathEnvWith id (Just "alignat*") "alignat*") , ("flalign", mathEnvWith id (Just "flalign") "flalign") , ("flalign*", mathEnvWith id (Just "flalign*") "flalign*") -- the following are not yet handled by texmath, so we use substitutes: , ("dmath", mathEnvWith id Nothing "dmath") , ("dmath*", mathEnvWith id Nothing "dmath*") , ("dgroup", mathEnvWith id (Just "aligned") "dgroup") , ("dgroup*", mathEnvWith id (Just "aligned") "dgroup*") , ("darray", mathEnvWith id (Just "aligned") "darray") , ("darray*", mathEnvWith id (Just "aligned") "darray*") , ("subequations", mathEnvWith id Nothing "subequations") ] theoremstyle :: PandocMonad m => LP m Blocks theoremstyle = do stylename <- untokenize <$> braced let mbstyle = case stylename of "plain" -> Just PlainStyle "definition" -> Just DefinitionStyle "remark" -> Just RemarkStyle _ -> Nothing case mbstyle of Nothing -> return () Just sty -> updateState $ \s -> s{ sLastTheoremStyle = sty } return mempty newtheorem :: PandocMonad m => LP m Inlines -> LP m Blocks newtheorem inline = do number <- option True (False <$ symbol '*' <* sp) name <- untokenize <$> braced sp series <- option Nothing $ Just . untokenize <$> bracketedToks sp showName <- tokWith inline sp syncTo <- option Nothing $ Just . untokenize <$> bracketedToks sty <- sLastTheoremStyle <$> getState let spec = TheoremSpec { theoremName = showName , theoremStyle = sty , theoremSeries = series , theoremSyncTo = syncTo , theoremNumber = number , theoremLastNum = DottedNum [0] } tmap <- sTheoremMap <$> getState updateState $ \s -> s{ sTheoremMap = M.insert name spec tmap } return mempty extractLabelFromBlock :: Block -> Maybe Text extractLabelFromBlock (Para inlines) = extractLabel Nothing inlines where extractLabel = L.foldl' go go :: Maybe Text -> Inline -> Maybe Text go (Just t) _ = Just t go Nothing (Span (_, _, attrs) _) = lookup "label" attrs go Nothing _ = Nothing extractLabelFromBlock _ = Nothing theoremEnvironment :: PandocMonad m => LP m Blocks -> LP m Inlines -> Text -> LP m Blocks theoremEnvironment blocks opt name = do resetCaption tmap <- sTheoremMap <$> getState case M.lookup name tmap of Nothing -> mzero Just tspec -> do optTitle <- option mempty $ (\x -> space <> "(" <> x <> ")") <$> opt bs <- env name blocks let mblabel = listToMaybe $ mapMaybe extractLabelFromBlock (toList bs) number <- if theoremNumber tspec then do let name' = fromMaybe name $ theoremSeries tspec num <- getNextNumber (maybe (DottedNum [0]) theoremLastNum . M.lookup name' . sTheoremMap) updateState $ \s -> s{ sTheoremMap = M.adjust (\spec -> spec{ theoremLastNum = num }) name' (sTheoremMap s) } case mblabel of Just ident -> updateState $ \s -> s{ sLabels = M.insert ident (B.toList $ str (renderDottedNum num)) (sLabels s) } Nothing -> return () return $ space <> B.text (renderDottedNum num) else return mempty let titleEmph = case theoremStyle tspec of PlainStyle -> B.strong DefinitionStyle -> B.strong RemarkStyle -> B.emph let title = titleEmph (theoremName tspec <> number) <> optTitle <> "." <> space return $ divWith (fromMaybe "" mblabel, [name], []) $ addTitle title $ maybe id removeLabel mblabel $ case theoremStyle tspec of PlainStyle -> walk italicize bs _ -> bs proof :: PandocMonad m => LP m Blocks -> LP m Inlines -> LP m Blocks proof blocks opt = do title <- option (B.text "Proof") opt bs <- env "proof" blocks return $ B.divWith ("", ["proof"], []) $ addQed $ addTitle (B.emph (title <> ".")) bs addTitle :: Inlines -> Blocks -> Blocks addTitle ils bs = case B.toList bs of (Para xs : rest) -> B.fromList (Para (B.toList ils ++ (Space : xs)) : rest) _ -> B.para ils <> bs addQed :: Blocks -> Blocks addQed bs = case Seq.viewr (B.unMany bs) of s Seq.:> Para ils -> B.Many (s Seq.|> Para (ils ++ B.toList qedSign)) _ -> bs <> B.para qedSign where qedSign = B.str "\xa0\x25FB" italicize :: Block -> Block italicize x@(Para [Image{}]) = x -- see #6925 italicize x@(Plain [Image{}]) = x -- ditto italicize (Para ils) = Para [Emph ils] italicize (Plain ils) = Plain [Emph ils] italicize x = x ================================================ FILE: src/Text/Pandoc/Readers/LaTeX/Parsing.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Readers.LaTeX.Parsing Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable General parsing types and functions for LaTeX. -} module Text.Pandoc.Readers.LaTeX.Parsing ( DottedNum(..) , renderDottedNum , incrementDottedNum , TheoremSpec(..) , TheoremStyle(..) , LaTeXState(..) , defaultLaTeXState , LP , TokStream(..) , withVerbatimMode , rawLaTeXParser , applyMacros , tokenize , tokenizeSources , getInputTokens , untokenize , untoken , satisfyTok , peekTok , parseFromToks , disablingWithRaw , doMacros , doMacros' , setpos , anyControlSeq , anySymbol , isNewlineTok , isWordTok , isArgTok , infile , spaces , spaces1 , tokTypeIn , controlSeq , symbol , symbolIn , sp , whitespace , newlineTok , comment , anyTok , singleChar , tokWith , specialChars , endline , blankline , primEscape , bgroup , egroup , grouped , braced , braced' , bracedUrl , bracedOrToken , bracketed , bracketedToks , parenWrapped , dimenarg , ignore , withRaw , keyvals , verbEnv , begin_ , end_ , getRawCommand , skipopts , rawopt , overlaySpecification , getNextNumber , label , setCaption , resetCaption , env , addMeta , removeLabel ) where import Control.Applicative (many, (<|>)) import Control.Monad import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) import Data.Char (chr, isAlphaNum, isDigit, isLetter, ord) import Data.Default import Data.List (intercalate) import qualified Data.IntMap as IntMap import qualified Data.Map as M import qualified Data.Set as Set import Data.Text (Text) import Data.Maybe (fromMaybe) import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Text as T import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Error (PandocError (PandocMacroLoop,PandocShouldNeverHappenError)) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline, space, spaces, withRaw, (<|>)) import Text.Pandoc.TeX (ExpansionPoint (..), Macro (..), ArgSpec (..), Tok (..), TokType (..)) import Text.Pandoc.Shared import Text.Pandoc.Walk newtype DottedNum = DottedNum [Int] deriving (Show, Eq) renderDottedNum :: DottedNum -> T.Text renderDottedNum (DottedNum xs) = T.pack $ intercalate "." (map show xs) incrementDottedNum :: Int -> DottedNum -> DottedNum incrementDottedNum level (DottedNum ns) = DottedNum $ case reverse (take level (ns ++ repeat 0)) of (x:xs) -> reverse (x+1 : xs) [] -> [] -- shouldn't happen data TheoremStyle = PlainStyle | DefinitionStyle | RemarkStyle deriving (Show, Eq) data TheoremSpec = TheoremSpec { theoremName :: Inlines , theoremStyle :: TheoremStyle , theoremSeries :: Maybe Text , theoremSyncTo :: Maybe Text , theoremNumber :: Bool , theoremLastNum :: DottedNum } deriving (Show, Eq) data LaTeXState = LaTeXState{ sOptions :: ReaderOptions , sMeta :: Meta , sQuoteContext :: QuoteContext , sMacros :: NonEmpty (M.Map Text Macro) , sContainers :: [Text] , sLogMessages :: [LogMessage] , sIdentifiers :: Set.Set Text , sVerbatimMode :: Bool , sMathMode :: Bool , sCaption :: Maybe Caption , sInListItem :: Bool , sInTableCell :: Bool , sLastHeaderNum :: DottedNum , sLastFigureNum :: DottedNum , sLastTableNum :: DottedNum , sLastNoteNum :: Int , sFootnoteTexts :: M.Map Int Blocks , sTheoremMap :: M.Map Text TheoremSpec , sLastTheoremStyle :: TheoremStyle , sLastLabel :: Maybe Text , sLabels :: M.Map Text [Inline] , sHasChapters :: Bool , sToggles :: M.Map Text Bool , sFileContents :: M.Map Text Text , sEnableWithRaw :: Bool , sRawTokens :: IntMap.IntMap [Tok] , sLigatures :: Bool } deriving Show defaultLaTeXState :: LaTeXState defaultLaTeXState = LaTeXState{ sOptions = def , sMeta = nullMeta , sQuoteContext = NoQuote , sMacros = M.empty :| [] , sContainers = [] , sLogMessages = [] , sIdentifiers = Set.empty , sVerbatimMode = False , sMathMode = False , sCaption = Nothing , sInListItem = False , sInTableCell = False , sLastHeaderNum = DottedNum [] , sLastFigureNum = DottedNum [] , sLastTableNum = DottedNum [] , sLastNoteNum = 0 , sFootnoteTexts = M.empty , sTheoremMap = M.empty , sLastTheoremStyle = PlainStyle , sLastLabel = Nothing , sLabels = M.empty , sHasChapters = False , sToggles = M.empty , sFileContents = M.empty , sEnableWithRaw = True , sRawTokens = IntMap.empty , sLigatures = True } instance PandocMonad m => HasQuoteContext LaTeXState m where getQuoteContext = sQuoteContext <$> getState withQuoteContext context parser = do oldState <- getState let oldQuoteContext = sQuoteContext oldState setState oldState { sQuoteContext = context } result <- parser newState <- getState setState newState { sQuoteContext = oldQuoteContext } return result instance HasLogMessages LaTeXState where addLogMessage msg st = st{ sLogMessages = msg : sLogMessages st } getLogMessages st = reverse $ sLogMessages st instance HasIdentifierList LaTeXState where extractIdentifierList = sIdentifiers updateIdentifierList f st = st{ sIdentifiers = f $ sIdentifiers st } instance HasIncludeFiles LaTeXState where getIncludeFiles = sContainers addIncludeFile f s = s{ sContainers = f : sContainers s } dropLatestIncludeFile s = s { sContainers = drop 1 $ sContainers s } instance HasMacros LaTeXState where extractMacros st = NonEmpty.head $ sMacros st updateMacros f st = st{ sMacros = f (NonEmpty.head (sMacros st)) :| NonEmpty.tail (sMacros st) } instance HasReaderOptions LaTeXState where extractReaderOptions = sOptions instance HasMeta LaTeXState where setMeta field val st = st{ sMeta = setMeta field val $ sMeta st } deleteMeta field st = st{ sMeta = deleteMeta field $ sMeta st } instance Default LaTeXState where def = defaultLaTeXState -- The Boolean is True if macros have already been expanded, -- False if they need expanding. data TokStream = TokStream !Bool [Tok] deriving (Show) instance Semigroup TokStream where (TokStream exp1 ts1) <> (TokStream exp2 ts2) = TokStream (if null ts1 then exp2 else exp1) (ts1 <> ts2) instance Monoid TokStream where mempty = TokStream False mempty mappend = (<>) instance Monad m => Stream TokStream m Tok where uncons (TokStream _ []) = return Nothing uncons (TokStream _ (t:ts)) = return $ Just (t, TokStream False ts) type LP m = ParsecT TokStream LaTeXState m withVerbatimMode :: PandocMonad m => LP m a -> LP m a withVerbatimMode parser = do alreadyVerbatimMode <- sVerbatimMode <$> getState if alreadyVerbatimMode then parser else do updateState $ \st -> st{ sVerbatimMode = True } result <- parser updateState $ \st -> st{ sVerbatimMode = False } return result rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s, Show a) => [Tok] -> LP m () -> LP m a -> ParsecT Sources s m (a, Text) rawLaTeXParser toks parser valParser = do pstate <- getState let lstate = def{ sOptions = extractReaderOptions pstate } let lstate' = lstate { sMacros = extractMacros pstate :| [] } let setStartPos = case toks of Tok pos _ _ : _ -> setPosition pos _ -> return () let preparser = setStartPos >> parser let rawparser = (,) <$> withRaw valParser <*> getState res' <- lift $ runParserT (withRaw (preparser >> getPosition)) lstate "chunk" $ TokStream False toks case res' of Left _ -> mzero Right (endpos, toks') -> do res <- lift $ runParserT rawparser lstate' "chunk" $ TokStream False toks' case res of Left _ -> mzero Right ((val, raw), st) -> do updateState (updateMacros ((NonEmpty.head (sMacros st)) <>)) let rawChar = do pos <- getPosition if pos >= endpos then mzero else anyChar result <- (guardEnabled Ext_latex_macros >> (untokenize raw <$ skipMany rawChar)) <|> T.pack <$> many rawChar -- ensure we end with space if input did, see #4442 let result' = case reverse toks' of (Tok _ (CtrlSeq _) t : _) | " " `T.isSuffixOf` t , not (" " `T.isSuffixOf` result) -> result <> " " _ -> result return (val, result') applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) => Text -> ParsecT Sources s m Text applyMacros s = (guardDisabled Ext_latex_macros >> return s) <|> do let retokenize = untokenize <$> many anyTok pstate <- getState let lstate = def{ sOptions = extractReaderOptions pstate , sMacros = extractMacros pstate :| [] } res <- runParserT retokenize lstate "math" $ TokStream False (tokenize (initialPos "math") s) case res of Left e -> Prelude.fail (show e) Right s' -> return s' {- When tokenize or untokenize change, test with this QuickCheck property: > tokUntokRoundtrip :: String -> Bool > tokUntokRoundtrip s = > let t = T.pack s in untokenize (tokenize "random" t) == t -} tokenizeSources :: Sources -> [Tok] tokenizeSources = concatMap tokenizeSource . unSources where tokenizeSource (pos, t) = tokenize pos t -- Return tokens from input sources. Ensure that starting position is -- correct. getInputTokens :: PandocMonad m => ParsecT Sources s m [Tok] getInputTokens = do pos <- getPosition ss <- getInput return $ case ss of Sources [] -> [] Sources ((_,t):rest) -> tokenizeSources $ Sources ((pos,t):rest) tokenize :: SourcePos -> Text -> [Tok] tokenize = totoks False where totoks atIsLetter pos t = case T.uncons t of Nothing -> [] Just (c, rest) | c == '\n' -> Tok pos Newline "\n" : totoks atIsLetter (setSourceColumn (incSourceLine pos 1) 1) rest | isSpaceOrTab c -> let (sps, rest') = T.span isSpaceOrTab t in Tok pos Spaces sps : totoks atIsLetter (incSourceColumn pos (T.length sps)) rest' | isAlphaNum c -> let (ws, rest') = T.span isAlphaNum t in Tok pos Word ws : totoks atIsLetter (incSourceColumn pos (T.length ws)) rest' | c == '%' -> let (cs, rest') = T.break (== '\n') rest in Tok pos Comment ("%" <> cs) : totoks atIsLetter (incSourceColumn pos (1 + T.length cs)) rest' | c == '\\' -> case T.uncons rest of Nothing -> [Tok pos (CtrlSeq " ") "\\"] Just (d, rest') | isLetter' atIsLetter d -> let (ws, rest'') = T.span (isLetter' atIsLetter) rest (ss, rest''') = T.span isSpaceOrTab rest'' atIsLetter' = case ws of "makeatletter" -> True "makeatother" -> False _ -> atIsLetter in Tok pos (CtrlSeq ws) ("\\" <> ws <> ss) : totoks atIsLetter' (incSourceColumn pos (1 + T.length ws + T.length ss)) rest''' | isSpaceOrTab d || d == '\n' -> let (w1, r1) = T.span isSpaceOrTab rest (w2, (w3, r3)) = case T.uncons r1 of Just ('\n', r2) -> (T.pack "\n", T.span isSpaceOrTab r2) _ -> (mempty, (mempty, r1)) ws = "\\" <> w1 <> w2 <> w3 in case T.uncons r3 of Just ('\n', _) -> Tok pos (CtrlSeq " ") ("\\" <> w1) : totoks atIsLetter (incSourceColumn pos (T.length ws)) r1 _ -> Tok pos (CtrlSeq " ") ws : totoks atIsLetter (incSourceColumn pos (T.length ws)) r3 | otherwise -> Tok pos (CtrlSeq (T.singleton d)) (T.pack [c,d]) : totoks atIsLetter (incSourceColumn pos 2) rest' | c == '#' -> case T.uncons rest of Just ('#', t3) -> let (t1, t2) = T.span (\d -> d >= '0' && d <= '9') t3 in case safeRead t1 of Just i -> Tok pos (DeferredArg i) ("##" <> t1) : totoks atIsLetter (incSourceColumn pos (2 + T.length t1)) t2 Nothing -> Tok pos Symbol "#" : Tok (incSourceColumn pos 1) Symbol "#" : totoks atIsLetter (incSourceColumn pos 1) t3 _ -> let (t1, t2) = T.span (\d -> d >= '0' && d <= '9') rest in case safeRead t1 of Just i -> Tok pos (Arg i) ("#" <> t1) : totoks atIsLetter (incSourceColumn pos (1 + T.length t1)) t2 Nothing -> Tok pos Symbol "#" : totoks atIsLetter (incSourceColumn pos 1) rest | c == '^' -> case T.uncons rest of Just ('^', rest') -> case T.uncons rest' of Just (d, rest'') | isLowerHex d -> case T.uncons rest'' of Just (e, rest''') | isLowerHex e -> Tok pos Esc2 (T.pack ['^','^',d,e]) : totoks atIsLetter (incSourceColumn pos 4) rest''' _ -> Tok pos Esc1 (T.pack ['^','^',d]) : totoks atIsLetter (incSourceColumn pos 3) rest'' | d < '\128' -> Tok pos Esc1 (T.pack ['^','^',d]) : totoks atIsLetter (incSourceColumn pos 3) rest'' _ -> Tok pos Symbol "^" : Tok (incSourceColumn pos 1) Symbol "^" : totoks atIsLetter (incSourceColumn pos 2) rest' _ -> Tok pos Symbol "^" : totoks atIsLetter (incSourceColumn pos 1) rest | otherwise -> Tok pos Symbol (T.singleton c) : totoks atIsLetter (incSourceColumn pos 1) rest isSpaceOrTab :: Char -> Bool isSpaceOrTab ' ' = True isSpaceOrTab '\t' = True isSpaceOrTab _ = False -- First parameter is True if @ is letter isLetter' :: Bool -> Char -> Bool isLetter' True '@' = True isLetter' _ c = isLetter c isLetterOrAt :: Char -> Bool isLetterOrAt '@' = True isLetterOrAt c = isLetter c isLowerHex :: Char -> Bool isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f' untokenize :: [Tok] -> Text untokenize = foldr untokenAccum mempty untokenAccum :: Tok -> Text -> Text untokenAccum (Tok _ (CtrlSeq _) t) accum = -- insert space to prevent breaking a control sequence; see #5836 case (T.unsnoc t, T.uncons accum) of (Just (_,c), Just (d,_)) | isLetter c , isLetter d -> t <> " " <> accum _ -> t <> accum untokenAccum (Tok _ _ t) accum = t <> accum untoken :: Tok -> Text untoken t = untokenAccum t mempty parseFromToks :: PandocMonad m => LP m a -> [Tok] -> LP m a parseFromToks parser toks = do oldInput <- getInput setInput $ TokStream False toks oldpos <- getPosition case toks of Tok pos _ _ : _ -> setPosition pos _ -> return () -- we ignore existing raw tokens maps (see #9517) oldRawTokens <- sRawTokens <$> getState updateState $ \st -> st{ sRawTokens = mempty } result <- parser updateState $ \st -> st{ sRawTokens = oldRawTokens } setInput oldInput setPosition oldpos return result disablingWithRaw :: PandocMonad m => LP m a -> LP m a disablingWithRaw parser = do oldEnableWithRaw <- sEnableWithRaw <$> getState updateState $ \st -> st{ sEnableWithRaw = False } result <- parser updateState $ \st -> st{ sEnableWithRaw = oldEnableWithRaw } return result satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok satisfyTok f = do doMacros -- apply macros on remaining input stream res <- tokenPrim (T.unpack . untoken) updatePos matcher updateState $ \st -> if sEnableWithRaw st then let !newraws = IntMap.map (res:) $! sRawTokens st in st{ sRawTokens = newraws } else st return $! res where matcher t | f t = Just t | otherwise = Nothing updatePos :: SourcePos -> Tok -> TokStream -> SourcePos updatePos _spos _ (TokStream _ (Tok pos _ _ : _)) = pos updatePos spos (Tok _ _ t) _ = incSourceColumn spos (T.length t) peekTok :: PandocMonad m => LP m Tok peekTok = do doMacros lookAhead (satisfyTok (const True)) doMacros :: PandocMonad m => LP m () doMacros = do TokStream macrosExpanded toks <- getInput unless macrosExpanded $ do st <- getState unless (sVerbatimMode st) $ doMacros' 1 toks >>= setInput . TokStream True doMacros' :: PandocMonad m => Int -> [Tok] -> LP m [Tok] doMacros' n inp = case inp of Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" : Tok _ Word name : Tok _ Symbol "}" : ts -> handleMacros n spos name ts <|> return inp Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" : Tok _ Word name : Tok _ Symbol "}" : ts -> handleMacros n spos ("end" <> name) ts <|> return inp Tok _ (CtrlSeq "expandafter") _ : t : ts -> combineTok t <$> doMacros' n ts Tok spos (CtrlSeq name) _ : ts -> handleMacros n spos name ts <|> return inp _ -> return inp where combineTok (Tok spos (CtrlSeq name) x) (Tok _ Word w : ts) | T.all isLetterOrAt w = Tok spos (CtrlSeq (name <> w)) (x1 <> w <> x2) : ts where (x1, x2) = T.break isSpaceOrTab x combineTok t ts = t:ts matchTok (Tok _ toktype txt) = satisfyTok (\(Tok _ toktype' txt') -> toktype == toktype' && txt == txt') matchPattern toks = try $ mapM_ matchTok toks getargs argmap [] = return argmap getargs argmap (Pattern toks : rest) = try $ do matchPattern toks getargs argmap rest getargs argmap (ArgNum i : Pattern toks : rest) = try $ do x <- mconcat <$> manyTill (braced <|> ((:[]) <$> anyTok)) (matchPattern toks) getargs (M.insert i x argmap) rest getargs argmap (ArgNum i : rest) = do x <- try $ spaces >> bracedOrToken getargs (M.insert i x argmap) rest addTok False _args spos (Tok _ (DeferredArg i) txt) acc = Tok spos (Arg i) txt : acc addTok False args spos (Tok _ (Arg i) _) acc = case M.lookup i args of Nothing -> mzero Just xs -> foldr (addTok True args spos) acc xs -- see #4007 addTok _ _ spos (Tok _ (CtrlSeq x) txt) acc@(Tok _ Word _ : _) | not (T.null txt) , isLetter (T.last txt) = Tok spos (CtrlSeq x) (txt <> " ") : acc addTok _ _ spos t acc = setpos spos t : acc handleMacros n' spos name ts = do when (n' > 20) -- detect macro expansion loops $ throwError $ PandocMacroLoop name (macros :| _ ) <- sMacros <$> getState case M.lookup name macros of Nothing -> trySpecialMacro name ts Just (Macro _scope expansionPoint argspecs optarg newtoks) -> do let getargs' = do args <- (case expansionPoint of ExpandWhenUsed -> withVerbatimMode ExpandWhenDefined -> id) $ case optarg of Nothing -> getargs M.empty argspecs Just o -> do x <- option o bracketedToks getargs (M.singleton 1 x) $ drop 1 argspecs TokStream _ rest <- getInput return (args, rest) lstate <- getState res <- lift $ runParserT getargs' lstate "args" $ TokStream False ts case res of Left _ -> Prelude.fail $ "Could not parse arguments for " ++ T.unpack name Right (args, rest) -> do -- first boolean param is true if we're tokenizing -- an argument (in which case we don't want to -- expand #1 etc.) let result = foldr (addTok False args spos) rest newtoks case expansionPoint of ExpandWhenUsed -> doMacros' (n' + 1) result ExpandWhenDefined -> return result -- | Certain macros do low-level tex manipulations that can't -- be represented in our Macro type, so we handle them here. trySpecialMacro :: PandocMonad m => Text -> [Tok] -> LP m [Tok] trySpecialMacro "xspace" ts = do ts' <- doMacros' 1 ts case ts' of Tok pos Word t : _ | startsWithAlphaNum t -> return $ Tok pos Spaces " " : ts' _ -> return ts' trySpecialMacro "iftrue" ts = handleIf (ifParser True) ts trySpecialMacro "iffalse" ts = handleIf (ifParser False) ts trySpecialMacro "ifmmode" ts = do mathMode <- sMathMode <$> getState handleIf (ifParser mathMode) ts trySpecialMacro "ifstrequal" ts = do handleIf ifStrequalParser ts trySpecialMacro _ _ = mzero ifStrequalParser :: PandocMonad m => LP m [Tok] ifStrequalParser = do str1 <- braced <|> count 1 anyTok str2 <- braced <|> count 1 anyTok ifequal <- withVerbatimMode (braced <|> count 1 anyTok) ifnotequal <- withVerbatimMode (braced <|> count 1 anyTok) TokStream _ ts <- getInput return $ if untokenize str1 == untokenize str2 then ifequal ++ ts else ifnotequal ++ ts handleIf :: PandocMonad m => LP m [Tok] -> [Tok] -> LP m [Tok] handleIf parser ts = do res' <- lift $ runParserT parser defaultLaTeXState "tokens" $ TokStream False ts case res' of Left _ -> Prelude.fail "Could not parse conditional" Right ts' -> return ts' ifParser :: PandocMonad m => Bool -> LP m [Tok] ifParser b = do ifToks <- many (notFollowedBy (controlSeq "else" <|> controlSeq "fi") *> anyTok) elseToks <- (controlSeq "else" >> manyTill anyTok (controlSeq "fi")) <|> ([] <$ controlSeq "fi") TokStream _ rest <- getInput return $ (if b then ifToks else elseToks) ++ rest startsWithAlphaNum :: Text -> Bool startsWithAlphaNum t = case T.uncons t of Just (c, _) | isAlphaNum c -> True _ -> False setpos :: SourcePos -> Tok -> Tok setpos spos (Tok _ tt txt) = Tok spos tt txt anyControlSeq :: PandocMonad m => LP m Tok anyControlSeq = satisfyTok isCtrlSeq isCtrlSeq :: Tok -> Bool isCtrlSeq (Tok _ (CtrlSeq _) _) = True isCtrlSeq _ = False anySymbol :: PandocMonad m => LP m Tok anySymbol = satisfyTok isSymbolTok isSymbolTok :: Tok -> Bool isSymbolTok (Tok _ Symbol _) = True isSymbolTok _ = False isWordTok :: Tok -> Bool isWordTok (Tok _ Word _) = True isWordTok _ = False isArgTok :: Tok -> Bool isArgTok (Tok _ (Arg _) _) = True isArgTok _ = False infile :: PandocMonad m => SourceName -> LP m Tok infile reference = satisfyTok (\(Tok source _ _) -> (sourceName source) == reference) spaces :: PandocMonad m => LP m () spaces = skipMany (satisfyTok (tokTypeIn [Comment, Spaces, Newline])) spaces1 :: PandocMonad m => LP m () spaces1 = skipMany1 (satisfyTok (tokTypeIn [Comment, Spaces, Newline])) tokTypeIn :: [TokType] -> Tok -> Bool tokTypeIn toktypes (Tok _ tt _) = tt `elem` toktypes controlSeq :: PandocMonad m => Text -> LP m Tok controlSeq name = satisfyTok isNamed where isNamed (Tok _ (CtrlSeq n) _) = n == name isNamed _ = False symbol :: PandocMonad m => Char -> LP m Tok symbol c = satisfyTok isc where isc (Tok _ Symbol d) = case T.uncons d of Just (c',_) -> c == c' _ -> False isc _ = False symbolIn :: PandocMonad m => [Char] -> LP m Tok symbolIn cs = satisfyTok isInCs where isInCs (Tok _ Symbol d) = case T.uncons d of Just (c,_) -> c `elem` cs _ -> False isInCs _ = False sp :: PandocMonad m => LP m () sp = do optional $ skipMany (whitespace <|> comment) optional $ endline *> skipMany (whitespace <|> comment) whitespace :: PandocMonad m => LP m () whitespace = () <$ satisfyTok isSpaceTok isSpaceTok :: Tok -> Bool isSpaceTok (Tok _ Spaces _) = True isSpaceTok _ = False newlineTok :: PandocMonad m => LP m () newlineTok = () <$ satisfyTok isNewlineTok isNewlineTok :: Tok -> Bool isNewlineTok (Tok _ Newline _) = True isNewlineTok _ = False comment :: PandocMonad m => LP m () comment = () <$ satisfyTok isCommentTok isCommentTok :: Tok -> Bool isCommentTok (Tok _ Comment _) = True isCommentTok _ = False anyTok :: PandocMonad m => LP m Tok anyTok = satisfyTok (const True) singleCharTok :: PandocMonad m => LP m Tok singleCharTok = satisfyTok $ \case Tok _ Word t -> T.length t == 1 Tok _ Symbol t -> not (T.any (`Set.member` specialChars) t) _ -> False singleChar :: PandocMonad m => LP m Tok singleChar = singleCharTok <|> singleCharFromWord where singleCharFromWord = do Tok pos toktype t <- disablingWithRaw $ satisfyTok isWordTok let (t1, t2) = (T.take 1 t, T.drop 1 t) TokStream macrosExpanded inp <- getInput setInput $ TokStream macrosExpanded $ Tok pos toktype t1 : Tok (incSourceColumn pos 1) toktype t2 : inp anyTok specialChars :: Set.Set Char specialChars = Set.fromList "#$%&~_^\\{}" endline :: PandocMonad m => LP m () endline = try $ do newlineTok lookAhead anyTok notFollowedBy blankline blankline :: PandocMonad m => LP m () blankline = try $ skipMany whitespace *> newlineTok primEscape :: PandocMonad m => LP m Char primEscape = do Tok _ toktype t <- satisfyTok (tokTypeIn [Esc1, Esc2]) case toktype of Esc1 -> case T.uncons (T.drop 2 t) of Just (c, _) | c >= '\64' && c <= '\127' -> return (chr (ord c - 64)) | otherwise -> return (chr (ord c + 64)) Nothing -> Prelude.fail "Empty content of Esc1" Esc2 -> case safeRead ("0x" <> T.drop 2 t) of Just x -> return (chr x) Nothing -> Prelude.fail $ "Could not read: " ++ T.unpack t _ -> Prelude.fail "Expected an Esc1 or Esc2 token" -- should not happen bgroup :: PandocMonad m => LP m Tok bgroup = try $ do optional sp t <- symbol '{' <|> controlSeq "bgroup" <|> controlSeq "begingroup" -- Add a copy of the macro table to the top of the macro stack, -- private for this group. We inherit all the macros defined in -- the parent group. updateState $ \s -> s{ sMacros = NonEmpty.cons (NonEmpty.head (sMacros s)) (sMacros s) } return t egroup :: PandocMonad m => LP m Tok egroup = do t <- symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup" -- remove the group's macro table from the stack updateState $ \s -> s{ sMacros = fromMaybe (sMacros s) $ NonEmpty.nonEmpty (NonEmpty.tail (sMacros s)) } return t grouped :: (PandocMonad m, Monoid a) => LP m a -> LP m a grouped parser = try $ do bgroup -- first we check for an inner 'grouped', because -- {{a,b}} should be parsed the same as {a,b} try (grouped parser <* egroup) <|> (mconcat <$> manyTill parser egroup) braced' :: PandocMonad m => LP m Tok -> LP m [Tok] braced' getTok = symbol '{' *> go (1 :: Int) where go n = do t <- getTok case t of Tok _ Symbol "}" | n > 1 -> (t:) <$> go (n - 1) | otherwise -> return [] Tok _ Symbol "{" -> (t:) <$> go (n + 1) _ -> (t:) <$> go n braced :: PandocMonad m => LP m [Tok] braced = braced' anyTok -- URLs require special handling, because they can contain % -- characters. So we retonenize comments as we go... bracedUrl :: PandocMonad m => LP m [Tok] bracedUrl = braced' (retokenizeComment >> anyTok) -- For handling URLs, which allow literal % characters... retokenizeComment :: PandocMonad m => LP m () retokenizeComment = (do Tok pos Comment txt <- satisfyTok isCommentTok let updPos (Tok pos' toktype' txt') = Tok (incSourceColumn (incSourceLine pos' (sourceLine pos - 1)) (sourceColumn pos)) toktype' txt' let newtoks = map updPos $ tokenize pos $ T.tail txt TokStream macrosExpanded ts <- getInput setInput $ TokStream macrosExpanded ((Tok pos Symbol "%" : newtoks) ++ ts)) <|> return () bracedOrToken :: PandocMonad m => LP m [Tok] bracedOrToken = braced <|> ((:[]) <$> (anyControlSeq <|> singleChar)) bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a bracketed parser = try $ do symbol '[' mconcat <$> manyTill parser (symbol ']') bracketedToks :: PandocMonad m => LP m [Tok] bracketedToks = do symbol '[' concat <$> manyTill ((snd <$> withRaw (try braced)) <|> count 1 anyTok) (symbol ']') parenWrapped :: PandocMonad m => Monoid a => LP m a -> LP m a parenWrapped parser = try $ do symbol '(' mconcat <$> manyTill parser (symbol ')') dimenarg :: PandocMonad m => LP m Text dimenarg = try $ do optional sp ch <- option False $ True <$ symbol '=' minus <- option "" $ "-" <$ symbol '-' s1 <- option "" (do Tok _ _ s1 <- satisfyTok isWordTok guard (case T.uncons s1 of Just (c,_) -> isDigit c Nothing -> False) pure s1) s2 <- option "" $ try $ do symbol '.' Tok _ _ t <- satisfyTok isWordTok return ("." <> t) let s = s1 <> s2 let (num, rest) = T.span (\c -> isDigit c || c == '.') s guard $ T.length num > 0 guard $ rest `elem` ["", "pt","pc","in","bp","cm","mm","dd","cc","sp","ex","em", "mu", -- "mu" in math mode only "px" -- "px" with pdftex and luatex only ] return $ T.pack ['=' | ch] <> minus <> s ignore :: (Monoid a, PandocMonad m) => Text -> ParsecT s u m a ignore raw = do pos <- getPosition report $ SkippedContent raw pos return mempty withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok]) withRaw parser = do rawTokensMap <- sRawTokens <$> getState let key = case IntMap.lookupMax rawTokensMap of Nothing -> 0 Just (n,_) -> n + 1 -- insert empty list at key updateState $ \st -> st{ sRawTokens = IntMap.insert key [] $ sRawTokens st } result <- parser mbRevToks <- IntMap.lookup key . sRawTokens <$> getState raw <- case mbRevToks of Just revtoks -> do updateState $ \st -> st{ sRawTokens = IntMap.delete key $ sRawTokens st} return $ reverse revtoks Nothing -> throwError $ PandocShouldNeverHappenError $ "sRawTokens has nothing at key " <> T.pack (show key) return (result, raw) keyval :: PandocMonad m => LP m (Text, Text) keyval = try $ do sp key <- untokenize <$> many1 (notFollowedBy (symbol '=') >> (symbol '-' <|> symbol '_' <|> satisfyTok isWordTok)) sp val <- option mempty $ do symbol '=' sp (untokenize <$> braced) <|> (mconcat <$> many1 ( (untokenize . snd <$> withRaw braced) <|> (untokenize <$> many1 (satisfyTok (\case Tok _ Symbol "]" -> False Tok _ Symbol "," -> False Tok _ Symbol "{" -> False Tok _ Symbol "}" -> False _ -> True))))) sp optional (symbol ',') sp return (key, T.strip val) keyvals :: PandocMonad m => LP m [(Text, Text)] keyvals = try $ symbol '[' >> manyTill keyval (symbol ']') <* sp verbEnv :: PandocMonad m => Text -> LP m Text verbEnv name = withVerbatimMode $ do optional blankline res <- manyTill anyTok (end_ name) return $ stripTrailingNewline $ untokenize res -- Strip single final newline and any spaces following it. -- Input is unchanged if it doesn't end with newline + -- optional spaces. stripTrailingNewline :: Text -> Text stripTrailingNewline t = let (b, e) = T.breakOnEnd "\n" t in if T.all (== ' ') e then T.dropEnd 1 b else t begin_ :: PandocMonad m => Text -> LP m () begin_ t = try (do controlSeq "begin" spaces txt <- untokenize <$> braced guard (t == txt)) ("\\begin{" ++ T.unpack t ++ "}") end_ :: PandocMonad m => Text -> LP m () end_ t = try (do controlSeq "end" spaces txt <- untokenize <$> braced guard $ t == txt) ("\\end{" ++ T.unpack t ++ "}") getRawCommand :: PandocMonad m => Text -> Text -> LP m Text getRawCommand name txt = do (_, rawargs) <- withRaw $ case name of "write" -> do void $ many $ satisfyTok isDigitTok -- digits void braced "titleformat" -> do void braced skipopts void $ count 4 braced "def" -> void $ manyTill anyTok braced "vadjust" -> void (manyTill anyTok braced) <|> void (satisfyTok isPreTok) -- see #7531 _ | isFontSizeCommand name -> return () | name `elem` ["hfil", "hfill", "vfil", "vfill", "hfilneg", "vfilneg"] -> return () | name `elem` ["hskip", "vskip", "mskip"] -> do dimenarg skipMany $ try $ do sp satisfyTok $ \case Tok _ Word "plus" -> True Tok _ Word "minus" -> True _ -> False dimenarg | otherwise -> do skipopts option "" (try dimenarg) void $ many braced return $ txt <> untokenize rawargs isPreTok :: Tok -> Bool isPreTok (Tok _ Word "pre") = True isPreTok _ = False isDigitTok :: Tok -> Bool isDigitTok (Tok _ Word t) = T.all isDigit t isDigitTok _ = False skipopts :: PandocMonad m => LP m () skipopts = skipMany (void overlaySpecification <|> void rawopt) -- opts in angle brackets are used in beamer overlaySpecification :: PandocMonad m => LP m Text overlaySpecification = try $ do symbol '<' t <- untokenize <$> manyTill overlayTok (symbol '>') -- see issue #3368 guard $ not (T.all isLetter t) || t `elem` ["beamer","presentation", "trans", "handout","article", "second"] return $ "<" <> t <> ">" overlayTok :: PandocMonad m => LP m Tok overlayTok = satisfyTok (\case Tok _ Word _ -> True Tok _ Spaces _ -> True Tok _ Symbol c -> c `elem` ["-","+","@","|",":",","] _ -> False) rawopt :: PandocMonad m => LP m Text rawopt = try $ do sp inner <- untokenize <$> bracketedToks sp return $ "[" <> inner <> "]" isFontSizeCommand :: Text -> Bool isFontSizeCommand "tiny" = True isFontSizeCommand "scriptsize" = True isFontSizeCommand "footnotesize" = True isFontSizeCommand "small" = True isFontSizeCommand "normalsize" = True isFontSizeCommand "large" = True isFontSizeCommand "Large" = True isFontSizeCommand "LARGE" = True isFontSizeCommand "huge" = True isFontSizeCommand "Huge" = True isFontSizeCommand _ = False getNextNumber :: Monad m => (LaTeXState -> DottedNum) -> LP m DottedNum getNextNumber getCurrentNum = do st <- getState let chapnum = case sLastHeaderNum st of DottedNum (n:_) | sHasChapters st -> Just n _ -> Nothing return . DottedNum $ case getCurrentNum st of DottedNum [m,n] -> case chapnum of Just m' | m' == m -> [m, n+1] | otherwise -> [m', 1] Nothing -> [1] -- shouldn't happen DottedNum [n] -> case chapnum of Just m -> [m, 1] Nothing -> [n + 1] _ -> case chapnum of Just n -> [n, 1] Nothing -> [1] label :: PandocMonad m => LP m () label = do controlSeq "label" t <- braced updateState $ \st -> st{ sLastLabel = Just $ untokenize t } setCaption :: PandocMonad m => LP m Inlines -> LP m () setCaption inline = try $ do mbshort <- Just . toList <$> bracketed inline <|> pure Nothing ils <- tokWith inline optional $ try $ spaces *> label updateState $ \st -> st{ sCaption = Just $ Caption mbshort [Plain $ toList ils] } resetCaption :: PandocMonad m => LP m () resetCaption = updateState $ \st -> st{ sCaption = Nothing , sLastLabel = Nothing } env :: PandocMonad m => Text -> LP m a -> LP m a env name p = do -- environments are groups as far as macros are concerned, -- so we need a local copy of the macro table (see above, bgroup, egroup): updateState $ \s -> s{ sMacros = NonEmpty.cons (NonEmpty.head (sMacros s)) (sMacros s) } result <- p updateState $ \s -> s{ sMacros = fromMaybe (sMacros s) $ NonEmpty.nonEmpty (NonEmpty.tail (sMacros s)) } end_ name return result tokWith :: PandocMonad m => LP m Inlines -> LP m Inlines tokWith inlineParser = try $ spaces >> grouped inlineParser <|> (lookAhead anyControlSeq >> inlineParser) <|> singleChar' where singleChar' = do Tok _ _ t <- singleChar return $ str t addMeta :: PandocMonad m => ToMetaValue a => Text -> a -> LP m () addMeta field val = updateState $ \st -> st{ sMeta = addMetaField field val $ sMeta st } -- remove label spans to avoid duplicated identifier removeLabel :: Walkable [Inline] a => Text -> a -> a removeLabel lbl = walk go where go (Span (_,_,kvs) _ : rest) | Just lbl' <- lookup "label" kvs , lbl' == lbl = go (dropWhile isSpaceOrSoftBreak rest) go (x:xs) = x : go xs go [] = [] isSpaceOrSoftBreak Space = True isSpaceOrSoftBreak SoftBreak = True isSpaceOrSoftBreak _ = False ================================================ FILE: src/Text/Pandoc/Readers/LaTeX/SIunitx.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} module Text.Pandoc.Readers.LaTeX.SIunitx ( siunitxCommands ) where import Text.Pandoc.Builder ( space, subscript, superscript, emph, str, fromList, text, Many(Many, unMany), Inline(Superscript, Str), Inlines ) import Text.Pandoc.Readers.LaTeX.Parsing ( anyControlSeq, braced, bracketed, controlSeq, grouped, isWordTok, keyvals, satisfyTok, skipopts, spaces1, symbol, untokenize, LP ) import Text.Pandoc.TeX ( Tok(Tok), TokType(Word, CtrlSeq) ) import Text.Pandoc.Class.PandocMonad ( PandocMonad ) import Text.Pandoc.Parsing ( many1, eof, string, satisfy, skipMany, option, many, char, try, skipMany1, runParser, Parsec ) import Control.Applicative ((<|>)) import Control.Monad (void) import qualified Data.Map as M import Data.Char (isDigit) import Data.Text (Text) import qualified Data.Text as T import Data.List (intersperse) import qualified Data.Sequence as Seq import Text.Pandoc.Walk (walk) siunitxCommands :: PandocMonad m => LP m Inlines -> M.Map Text (LP m Inlines) siunitxCommands tok = M.fromList [ ("si", dosi tok) , ("unit", dosi tok) -- v3 version of si , ("SI", doSI tok) , ("qty", doSI tok) -- v3 version of SI , ("SIrange", doSIrange True tok) , ("qtyrange", doSIrange True tok) -- v3 version of SIrange , ("SIlist", doSIlist tok) , ("qtylist", doSIlist tok) -- v3 version of SIlist , ("numrange", doSIrange False tok) , ("numlist", doSInumlist) , ("num", doSInum) , ("ang", doSIang) ] dosi :: PandocMonad m => LP m Inlines -> LP m Inlines dosi tok = do options <- option [] keyvals grouped (siUnit options tok) <|> siUnit options tok -- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €" doSI :: PandocMonad m => LP m Inlines -> LP m Inlines doSI tok = do skipopts value <- doSInum valueprefix <- option "" $ bracketed tok unit <- dosi tok return . mconcat $ [valueprefix, emptyOr160 valueprefix, value, emptyOr160 unit, unit] doSInum :: PandocMonad m => LP m Inlines doSInum = skipopts *> (tonum . untokenize <$> braced) tonum :: Text -> Inlines tonum value = case runParser parseNum () "" value of Left _ -> text value Right num -> num doSInumlist :: PandocMonad m => LP m Inlines doSInumlist = do skipopts xs <- map tonum . T.splitOn ";" . untokenize <$> braced case xs of [] -> return mempty [x] -> return x _ -> return $ mconcat (intersperse (str "," <> space) (init xs)) <> text ", & " <> last xs doSIlist :: PandocMonad m => LP m Inlines -> LP m Inlines doSIlist tok = do options <- option [] keyvals nums <- map tonum . T.splitOn ";" . untokenize <$> braced unit <- grouped (siUnit options tok) <|> siUnit options tok let xs = map (<> (str "\xa0" <> unit)) nums case xs of [] -> return mempty [x] -> return x _ -> return $ mconcat (intersperse (str "," <> space) (init xs)) <> text ", & " <> last xs parseNum :: Parsec Text () Inlines parseNum = (mconcat <$> many parseNumPart) <* eof minus :: Text minus = "\x2212" hyphenToMinus :: Inline -> Inline hyphenToMinus (Str t) = Str (T.replace "-" minus t) hyphenToMinus x = x parseNumPart :: Parsec Text () Inlines parseNumPart = parseDecimalNum <|> parseComma <|> parsePlusMinus <|> parsePM <|> parseI <|> parseExp <|> parseX <|> parseSpace where parseDecimalNum, parsePlusMinus, parsePM, parseComma, parseI, parseX, parseExp, parseSpace :: Parsec Text () Inlines parseDecimalNum = try $ do pref <- option mempty $ (mempty <$ char '+') <|> (minus <$ char '-') basenum' <- many1 (satisfy (\c -> isDigit c || c == '.')) let basenum = pref <> T.pack (case basenum' of '.':_ -> '0':basenum' _ -> basenum') uncertainty <- option mempty $ T.pack <$> parseParens if T.null uncertainty then return $ str basenum else return $ str $ basenum <> "\xa0\xb1\xa0" <> let (_,ys) = T.break (=='.') basenum in case (T.length ys - 1, T.length uncertainty) of (0,_) -> uncertainty (x,y) | x > y -> "0." <> T.replicate (x - y) "0" <> T.dropWhileEnd (=='0') uncertainty | otherwise -> T.take (y - x) uncertainty <> case T.dropWhileEnd (=='0') (T.drop (y - x) uncertainty) of t | T.null t -> mempty | otherwise -> "." <> t parseComma = str "." <$ char ',' parsePlusMinus = str "\xa0\xb1\xa0" <$ try (string "+-") parsePM = str "\xa0\xb1\xa0" <$ try (string "\\pm") parseParens = char '(' *> many1 (satisfy (\c -> isDigit c || c == '.')) <* char ')' parseI = str "i" <$ char 'i' parseX = str "\xa0\xd7\xa0" <$ char 'x' parseExp = (\n -> str ("\xa0\xd7\xa0" <> "10") <> superscript n) <$> (char 'e' *> parseDecimalNum) parseSpace = mempty <$ skipMany1 (char ' ') doSIang :: PandocMonad m => LP m Inlines doSIang = do skipopts ps <- T.splitOn ";" . untokenize <$> braced let dropPlus t = case T.uncons t of Just ('+',t') -> t' _ -> t case ps ++ repeat "" of (d:m:s:_) -> return $ (if T.null d then mempty else str (dropPlus d) <> str "\xb0") <> (if T.null m then mempty else str (dropPlus m) <> str "\x2032") <> (if T.null s then mempty else str (dropPlus s) <> str "\x2033") _ -> return mempty -- converts e.g. \SIrange{100}{200}{\ms} to "100 ms--200 ms" doSIrange :: PandocMonad m => Bool -> LP m Inlines -> LP m Inlines doSIrange includeUnits tok = do skipopts startvalue <- doSInum startvalueprefix <- option "" $ bracketed tok stopvalue <- doSInum stopvalueprefix <- option "" $ bracketed tok unit <- if includeUnits then dosi tok else return mempty return . mconcat $ [startvalueprefix, emptyOr160 startvalueprefix, startvalue, emptyOr160 unit, unit, "\8211", -- An en-dash stopvalueprefix, emptyOr160 stopvalueprefix, stopvalue, emptyOr160 unit, unit] emptyOr160 :: Inlines -> Inlines emptyOr160 x = if x == mempty then x else str "\160" siUnit :: forall m. PandocMonad m => [(Text,Text)] -> LP m Inlines -> LP m Inlines siUnit options tok = mconcat . intersperse (str "\xa0") <$> many1 siUnitPart where siUnitPart :: LP m Inlines siUnitPart = try $ do skipMany (void (symbol '.') <|> void (symbol '~') <|> spaces1) x <- ((siPrefix <*> siBase) <|> (do u <- siBase <|> tok option u $ siSuffix <*> pure u)) option x (siInfix x) siInfix :: Inlines -> LP m Inlines siInfix u1 = try $ (do _ <- controlSeq "per" u2 <- siUnitPart let useSlash = lookup "per-mode" options == Just "symbol" if useSlash then return (u1 <> str "/" <> u2) else return (u1 <> str "\xa0" <> negateExponent u2)) <|> (do _ <- symbol '/' u2 <- siUnitPart return (u1 <> str "/" <> u2)) siPrefix :: LP m (Inlines -> Inlines) siPrefix = (do _ <- controlSeq "square" skipopts return (<> superscript "2")) <|> (do _ <- controlSeq "cubic" skipopts return (<> superscript "3")) <|> (do _ <- controlSeq "raisetothe" skipopts n <- walk hyphenToMinus <$> tok return (<> superscript n)) siSuffix :: LP m (Inlines -> Inlines) siSuffix = (do _ <- controlSeq "squared" skipopts return (<> superscript "2")) <|> (do _ <- controlSeq "cubed" skipopts return (<> superscript "3")) <|> (do _ <- controlSeq "tothe" skipopts n <- walk hyphenToMinus <$> tok return (<> superscript n)) <|> (symbol '^' *> (do n <- walk hyphenToMinus <$> tok return (<> superscript n))) <|> (symbol '_' *> (do n <- walk hyphenToMinus <$> tok return (<> subscript n))) negateExponent :: Inlines -> Inlines negateExponent ils = case Seq.viewr (unMany ils) of xs Seq.:> Superscript ss -> (Many xs) <> superscript (str minus <> fromList ss) _ -> ils <> superscript (str (minus <> "1")) siBase :: LP m Inlines siBase = ((try (do Tok _ (CtrlSeq name) _ <- anyControlSeq case M.lookup name siUnitModifierMap of Just il -> (il <>) <$> siBase Nothing -> case M.lookup name siUnitMap of Just il -> pure il Nothing -> fail "not a unit command")) <|> (do Tok _ Word t <- satisfyTok isWordTok return $ str t) ) siUnitModifierMap :: M.Map Text Inlines siUnitModifierMap = M.fromList [ ("atto", str "a") , ("centi", str "c") , ("deca", str "d") , ("deci", str "d") , ("deka", str "d") , ("exa", str "E") , ("femto", str "f") , ("giga", str "G") , ("hecto", str "h") , ("kilo", str "k") , ("mega", str "M") , ("micro", str "μ") , ("milli", str "m") , ("nano", str "n") , ("peta", str "P") , ("pico", str "p") , ("tera", str "T") , ("yocto", str "y") , ("yotta", str "Y") , ("zepto", str "z") , ("zetta", str "Z") ] siUnitMap :: M.Map Text Inlines siUnitMap = M.fromList [ ("fg", str "fg") , ("pg", str "pg") , ("ng", str "ng") , ("ug", str "μg") , ("mg", str "mg") , ("g", str "g") , ("kg", str "kg") , ("amu", str "u") , ("pm", str "pm") , ("nm", str "nm") , ("um", str "μm") , ("mm", str "mm") , ("cm", str "cm") , ("dm", str "dm") , ("m", str "m") , ("km", str "km") , ("as", str "as") , ("fs", str "fs") , ("ps", str "ps") , ("ns", str "ns") , ("us", str "μs") , ("ms", str "ms") , ("s", str "s") , ("fmol", str "fmol") , ("pmol", str "pmol") , ("nmol", str "nmol") , ("umol", str "μmol") , ("mmol", str "mmol") , ("mol", str "mol") , ("kmol", str "kmol") , ("pA", str "pA") , ("nA", str "nA") , ("uA", str "μA") , ("mA", str "mA") , ("A", str "A") , ("kA", str "kA") , ("ul", str "μl") , ("ml", str "ml") , ("l", str "l") , ("hl", str "hl") , ("uL", str "μL") , ("mL", str "mL") , ("L", str "L") , ("hL", str "hL") , ("mHz", str "mHz") , ("Hz", str "Hz") , ("kHz", str "kHz") , ("MHz", str "MHz") , ("GHz", str "GHz") , ("THz", str "THz") , ("mN", str "mN") , ("N", str "N") , ("kN", str "kN") , ("MN", str "MN") , ("Pa", str "Pa") , ("kPa", str "kPa") , ("MPa", str "MPa") , ("GPa", str "GPa") , ("mohm", str "mΩ") , ("kohm", str "kΩ") , ("Mohm", str "MΩ") , ("pV", str "pV") , ("nV", str "nV") , ("uV", str "μV") , ("mV", str "mV") , ("V", str "V") , ("kV", str "kV") , ("W", str "W") , ("uW", str "μW") , ("mW", str "mW") , ("kW", str "kW") , ("MW", str "MW") , ("GW", str "GW") , ("J", str "J") , ("uJ", str "μJ") , ("mJ", str "mJ") , ("kJ", str "kJ") , ("eV", str "eV") , ("meV", str "meV") , ("keV", str "keV") , ("MeV", str "MeV") , ("GeV", str "GeV") , ("TeV", str "TeV") , ("kWh", str "kWh") , ("F", str "F") , ("fF", str "fF") , ("pF", str "pF") , ("K", str "K") , ("dB", str "dB") , ("ampere", str "A") , ("angstrom", str "Å") , ("arcmin", str "′") , ("arcminute", str "′") , ("arcsecond", str "″") , ("astronomicalunit", str "au") , ("atomicmassunit", str "u") , ("bar", str "bar") , ("barn", str "b") , ("becquerel", str "Bq") , ("bel", str "B") , ("bohr", emph (str "a") <> subscript (str "0")) , ("candela", str "cd") , ("celsius", str "°C") , ("clight", emph (str "c") <> subscript (str "0")) , ("coulomb", str "C") , ("dalton", str "Da") , ("day", str "d") , ("decibel", str "db") , ("degreeCelsius",str "°C") , ("degree", str "°") , ("electronmass", emph (str "m") <> subscript (str "e")) , ("electronvolt", str "eV") , ("elementarycharge", emph (str "e")) , ("farad", str "F") , ("gram", str "g") , ("gray", str "Gy") , ("hartree", emph (str "E") <> subscript (str "h")) , ("hectare", str "ha") , ("henry", str "H") , ("hertz", str "Hz") , ("hour", str "h") , ("joule", str "J") , ("katal", str "kat") , ("kelvin", str "K") , ("kilogram", str "kg") , ("knot", str "kn") , ("liter", str "L") , ("litre", str "l") , ("lumen", str "lm") , ("lux", str "lx") , ("meter", str "m") , ("metre", str "m") , ("minute", str "min") , ("mmHg", str "mmHg") , ("mole", str "mol") , ("nauticalmile", str "M") , ("neper", str "Np") , ("newton", str "N") , ("ohm", str "Ω") , ("Pa", str "Pa") , ("pascal", str "Pa") , ("percent", str "%") , ("planckbar", emph (str "\x210f")) , ("radian", str "rad") , ("second", str "s") , ("siemens", str "S") , ("sievert", str "Sv") , ("steradian", str "sr") , ("tesla", str "T") , ("tonne", str "t") , ("volt", str "V") , ("watt", str "W") , ("weber", str "Wb") ] ================================================ FILE: src/Text/Pandoc/Readers/LaTeX/Table.hs ================================================ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} module Text.Pandoc.Readers.LaTeX.Table ( tableEnvironments ) where import Data.Functor (($>)) import Text.Pandoc.Class import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.TeX import Text.Pandoc.Builder as B import qualified Data.Map as M import Data.Text (Text) import Data.Maybe (fromMaybe) import qualified Data.Text as T import Control.Applicative ((<|>), optional, many) import Control.Monad (when, void) import Text.Pandoc.Shared (safeRead, trim) import Text.Pandoc.Logging (LogMessage(SkippedContent)) import Text.Pandoc.Walk (walkM) import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline, optional, space, spaces, withRaw, (<|>)) tableEnvironments :: PandocMonad m => LP m Blocks -> LP m Inlines -> M.Map Text (LP m Blocks) tableEnvironments block inline = M.fromList [ ("longtable", env "longtable" $ resetCaption *> simpTable block inline "longtable" False >>= addTableCaption) , ("table", env "table" $ skipopts *> resetCaption *> blocks >>= addTableCaption) , ("tabular*", env "tabular*" $ simpTable block inline "tabular*" True) , ("tabularx", env "tabularx" $ simpTable block inline "tabularx" True) , ("tabular", env "tabular" $ simpTable block inline "tabular" False) , ("supertabular", env "supertabular" $ simpTable block inline "supertabular" False) , ("supertabular*", env "supertabular*" $ simpTable block inline "supertabular*" False) ] where blocks = mconcat <$> many block hline :: PandocMonad m => LP m () hline = try $ do spaces hasParenArg <- (False <$ controlSeq "hline") <|> (False <$ controlSeq "cline") <|> (True <$ controlSeq "cmidrule") <|> -- booktabs rules: (True <$ controlSeq "toprule") <|> (True <$ controlSeq "bottomrule") <|> (True <$ controlSeq "midrule") <|> (True <$ controlSeq "endhead") <|> (True <$ controlSeq "endfirsthead") optional rawopt when hasParenArg $ void $ optional (parenWrapped (() <$ singleChar)) lbreak :: PandocMonad m => LP m Tok lbreak = (controlSeq "\\" <|> controlSeq "tabularnewline") <* optional (void rawopt) <* spaces amp :: PandocMonad m => LP m Tok amp = symbol '&' -- Split a Word into individual Symbols (for parseAligns) splitWordTok :: PandocMonad m => LP m () splitWordTok = do TokStream macrosExpanded inp <- getInput case inp of (Tok spos Word t : rest) -> setInput $ TokStream macrosExpanded $ map (Tok spos Symbol . T.singleton) (T.unpack t) <> rest _ -> return () parseAligns :: PandocMonad m => LP m [(Alignment, ColWidth, ([Tok], [Tok]))] parseAligns = try $ do let maybeBar = skipMany (try $ sp *> (() <$ symbol '|' <|> () <$ (symbol '@' >> braced))) let cAlign = AlignCenter <$ symbol 'c' let lAlign = AlignLeft <$ symbol 'l' let rAlign = AlignRight <$ symbol 'r' let parAlign = AlignLeft <$ symbol 'p' -- aligns from tabularx let xAlign = AlignLeft <$ symbol 'X' let mAlign = AlignLeft <$ symbol 'm' let bAlign = AlignLeft <$ symbol 'b' let alignChar = splitWordTok *> ( cAlign <|> lAlign <|> rAlign <|> parAlign <|> xAlign <|> mAlign <|> bAlign ) let alignPrefix = symbol '>' >> braced let alignSuffix = symbol '<' >> braced let colWidth = try $ do ts <- braced let isLinewidth (Tok _ (CtrlSeq "linewidth") _) = True isLinewidth _ = False case break isLinewidth ts of (ds, _:_) -> return $ safeRead $ trim $ untokenize ds _ -> return Nothing let alignSpec = do pref <- option [] alignPrefix spaces al <- alignChar width <- colWidth <|> option Nothing (do s <- untokenize <$> braced pos <- getPosition report $ SkippedContent s pos return Nothing) spaces suff <- option [] alignSuffix return (al, width, (pref, suff)) let starAlign = do -- '*{2}{r}' == 'rr', we just expand like a macro symbol '*' spaces ds <- trim . untokenize <$> bracedOrToken spaces spec <- bracedOrToken case safeRead ds of Just n -> do TokStream _ ts <- getInput setInput $ TokStream False (mconcat (replicate n spec) ++ ts) Nothing -> Prelude.fail $ "Could not parse " <> T.unpack ds <> " as number" bgroup spaces maybeBar aligns' <- many $ try $ spaces >> optional starAlign >> (alignSpec <* maybeBar) spaces egroup spaces return $ map toSpec aligns' where toColWidth (Just w) | w > 0 = ColWidth w toColWidth _ = ColWidthDefault toSpec (x, y, z) = (x, toColWidth y, z) -- N.B. this parser returns a Row that may have erroneous empty cells -- in it. See the note above fixTableHead for details. parseTableRow :: PandocMonad m => LP m Blocks -- ^ block parser -> LP m Inlines -- ^ inline parser -> Text -- ^ table environment name -> [([Tok], [Tok])] -- ^ pref/suffixes -> LP m Row parseTableRow block inline envname prefsufs = do notFollowedBy (spaces *> end_ envname) -- contexts that can contain & that is not colsep: let canContainAmp (Tok _ (CtrlSeq "begin") _) = True canContainAmp (Tok _ (CtrlSeq "verb") _) = True canContainAmp (Tok _ (CtrlSeq "Verb") _) = True canContainAmp _ = False -- add prefixes and suffixes in token stream: let celltoks (pref, suff) = do prefpos <- getPosition contents <- mconcat <$> many ( snd <$> withRaw ((lookAhead (controlSeq "parbox") >> void block) -- #5711 <|> (lookAhead (satisfyTok canContainAmp) >> void inline) <|> (lookAhead (controlSeq "begin") >> void block) -- #4746 <|> (lookAhead (symbol '$') >> void inline)) <|> (do notFollowedBy (() <$ amp <|> () <$ lbreak <|> end_ envname) count 1 anyTok) ) suffpos <- getPosition option [] (count 1 amp) return $ map (setpos prefpos) pref ++ contents ++ map (setpos suffpos) suff rawcells <- mapM celltoks prefsufs cells <- mapM (parseFromToks (parseTableCell block)) rawcells spaces return $ Row nullAttr cells parseTableCell :: PandocMonad m => LP m Blocks -> LP m Cell parseTableCell block = do spaces updateState $ \st -> st{ sInTableCell = True } cell' <- multicolumnCell block <|> multirowCell block <|> parseSimpleCell <|> parseEmptyCell updateState $ \st -> st{ sInTableCell = False } spaces return cell' where -- The parsing of empty cells is important in LaTeX, especially when dealing -- with multirow/multicolumn. See #6603. parseEmptyCell = spaces $> emptyCell parseSimpleCell = simpleCell <$> (plainify . mconcat <$> many block) cellAlignment :: PandocMonad m => LP m Alignment cellAlignment = skipMany (symbol '|') *> alignment <* skipMany (symbol '|') where alignment = do c <- untoken <$> singleChar <* optional braced -- ignore args return $ case c of "l" -> AlignLeft "r" -> AlignRight "c" -> AlignCenter "*" -> AlignDefault _ -> AlignDefault plainify :: Blocks -> Blocks plainify bs = case toList bs of [Para ils] -> plain (fromList ils) _ -> bs multirowCell :: PandocMonad m => LP m Blocks -> LP m Cell multirowCell block = controlSeq "multirow" >> do -- Full prototype for \multirow macro is: -- \multirow[vpos]{nrows}[bigstruts]{width}[vmove]{text} -- However, everything except `nrows` and `text` make -- sense in the context of the Pandoc AST _ <- optional $ symbol '[' *> cellAlignment <* symbol ']' -- vertical position nrows <- fmap (fromMaybe 1 . safeRead . untokenize) braced _ <- optional $ symbol '[' *> manyTill anyTok (symbol ']') -- bigstrut-related _ <- symbol '{' *> manyTill anyTok (symbol '}') -- Cell width _ <- optional $ symbol '[' *> manyTill anyTok (symbol ']') -- Length used for fine-tuning content <- symbol '{' *> (plainify . mconcat <$> many block) <* symbol '}' return $ cell AlignDefault (RowSpan nrows) (ColSpan 1) content multicolumnCell :: PandocMonad m => LP m Blocks -> LP m Cell multicolumnCell block = controlSeq "multicolumn" >> do span' <- fmap (fromMaybe 1 . safeRead . untokenize) braced alignment <- symbol '{' *> cellAlignment <* symbol '}' let singleCell = do content <- plainify . mconcat <$> many block return $ cell alignment (RowSpan 1) (ColSpan span') content -- Two possible contents: either a \multirow cell, or content. -- E.g. \multicol{1}{c}{\multirow{2}{1em}{content}} -- Note that a \multirow cell can be nested in a \multicolumn, -- but not the other way around. See #6603 let nestedCell = do (Cell _ _ (RowSpan rs) _ bs) <- multirowCell block return $ cell alignment (RowSpan rs) (ColSpan span') (fromList bs) symbol '{' *> (nestedCell <|> singleCell) <* symbol '}' -- LaTeX tables are stored with empty cells underneath multirow cells -- denoting the grid spaces taken up by them. More specifically, if a -- cell spans m rows, then it will overwrite all the cells in the -- columns it spans for (m-1) rows underneath it, requiring padding -- cells in these places. These padding cells need to be removed for -- proper table reading. See #6603. -- -- These fixTable functions do not otherwise fix up malformed -- input tables: that is left to the table builder. fixTableHead :: TableHead -> TableHead fixTableHead (TableHead attr rows) = TableHead attr rows' where rows' = fixTableRows rows fixTableBody :: TableBody -> TableBody fixTableBody (TableBody attr rhc th tb) = TableBody attr rhc th' tb' where th' = fixTableRows th tb' = fixTableRows tb fixTableRows :: [Row] -> [Row] fixTableRows = fixTableRows' $ repeat Nothing where fixTableRows' oldHang (Row attr cells : rs) = let (newHang, cells') = fixTableRow oldHang cells rs' = fixTableRows' newHang rs in Row attr cells' : rs' fixTableRows' _ [] = [] -- The overhang is represented as Just (relative cell dimensions) or -- Nothing for an empty grid space. fixTableRow :: [Maybe (ColSpan, RowSpan)] -> [Cell] -> ([Maybe (ColSpan, RowSpan)], [Cell]) fixTableRow oldHang cells -- If there's overhang, drop cells until their total width meets the -- width of the occupied grid spaces (or we run out) | (n, prefHang, restHang) <- splitHang oldHang , n > 0 = let cells' = dropToWidth getCellW n cells (restHang', cells'') = fixTableRow restHang cells' in (prefHang restHang', cells'') -- Otherwise record the overhang of a pending cell and fix the rest -- of the row | c@(Cell _ _ h w _):cells' <- cells = let h' = max 1 h w' = max 1 w oldHang' = dropToWidth getHangW w' oldHang (newHang, cells'') = fixTableRow oldHang' cells' in (toHang w' h' <> newHang, c : cells'') | otherwise = (oldHang, []) where getCellW (Cell _ _ _ w _) = w getHangW = maybe 1 fst getCS (ColSpan n) = n toHang c r | r > 1 = [Just (c, r)] | otherwise = replicate (getCS c) Nothing -- Take the prefix of the overhang list representing filled grid -- spaces. Also return the remainder and the length of this prefix. splitHang = splitHang' 0 id splitHang' !n l (Just (c, r):xs) = splitHang' (n + c) (l . (toHang c (r-1) ++)) xs splitHang' n l xs = (n, l, xs) -- Drop list items until the total width of the dropped items -- exceeds the passed width. dropToWidth _ n l | n < 1 = l dropToWidth wproj n (c:cs) = dropToWidth wproj (n - wproj c) cs dropToWidth _ _ [] = [] simpTable :: PandocMonad m => LP m Blocks -> LP m Inlines -> Text -> Bool -> LP m Blocks simpTable block inline envname hasWidthParameter = try $ do when hasWidthParameter $ () <$ tokWith inline skipopts colspecs <- parseAligns let (aligns, widths, prefsufs) = unzip3 colspecs optional $ controlSeq "caption" *> setCaption inline spaces optional label spaces optional lbreak spaces skipMany hline spaces header' <- option [] . try . fmap (:[]) $ parseTableRow block inline envname prefsufs <* lbreak <* many1 hline spaces rows <- sepEndBy (parseTableRow block inline envname prefsufs) (lbreak <* optional (skipMany hline)) spaces optional $ controlSeq "caption" *> setCaption inline spaces optional label spaces optional lbreak spaces lookAhead $ controlSeq "end" -- make sure we're at end let th = fixTableHead $ TableHead nullAttr header' let tbs = [fixTableBody $ TableBody nullAttr 0 [] rows] let tf = TableFoot nullAttr [] return $ table emptyCaption (zip aligns widths) th tbs tf addTableCaption :: PandocMonad m => Blocks -> LP m Blocks addTableCaption = walkM go where go (Table attr c spec th tb tf) = do st <- getState let capt = fromMaybe c $ sCaption st let mblabel = sLastLabel st case mblabel of Nothing -> return () Just lab -> do num <- getNextNumber sLastTableNum setState st{ sLastTableNum = num , sLabels = M.insert lab [Str (renderDottedNum num)] (sLabels st) } -- add num to caption? let attr' = case (attr, mblabel) of ((_,classes,kvs), Just ident) -> (ident,classes,kvs) _ -> attr return $ addAttrDiv attr' $ maybe id removeLabel mblabel $ Table nullAttr capt spec th tb tf go x = return x -- TODO: For now we add a Div to contain table attributes, since -- most writers don't do anything yet with attributes on Table. -- This can be removed when that changes. addAttrDiv :: Attr -> Block -> Block addAttrDiv ("",[],[]) b = b addAttrDiv attr b = Div attr [b] ================================================ FILE: src/Text/Pandoc/Readers/LaTeX.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.LaTeX Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of LaTeX to 'Pandoc' document. -} module Text.Pandoc.Readers.LaTeX ( readLaTeX, applyMacros, rawLaTeXInline, rawLaTeXBlock, inlineCommand ) where import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) import Data.Containers.ListUtils (nubOrd) import Data.Char (isDigit, isLetter, isAlphaNum, toUpper, chr) import Data.Default import Data.List (intercalate) import qualified Data.Map as M import Data.Maybe (fromMaybe, maybeToList) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import Data.Either (partitionEithers) import Skylighting (defaultSyntaxMap) import System.FilePath (addExtension, replaceExtension, takeExtension) import Text.Collate.Lang (renderLang) import Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocPure, PandocMonad (..), getResourcePath, readFileFromDirs, report, setResourcePath, getZonedTime) import Data.Time (ZonedTime(..), LocalTime(..), showGregorian) import Text.Pandoc.Error (PandocError (PandocParseError)) import Text.Pandoc.Highlighting (languagesByExtension) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline, optional, space, spaces, withRaw, (<|>)) import Text.Pandoc.TeX (Tok (..), TokType (..)) import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.Readers.LaTeX.Citation (citationCommands, cites) import Text.Pandoc.Readers.LaTeX.Math (withMathMode, dollarsMath, inlineEnvironments, inlineEnvironment, mathDisplay, mathInline, newtheorem, theoremstyle, proof, theoremEnvironment) import Text.Pandoc.Readers.LaTeX.Table (tableEnvironments) import Text.Pandoc.Readers.LaTeX.Macro (macroDef) import Text.Pandoc.Readers.LaTeX.Lang (inlineLanguageCommands, enquoteCommands, babelLangToBCP47, setDefaultLanguage) import Text.Pandoc.Readers.LaTeX.SIunitx (siunitxCommands) import Text.Pandoc.Readers.LaTeX.Inline (acronymCommands, refCommands, nameCommands, charCommands, accentCommands, miscCommands, biblatexInlineCommands, verbCommands, rawInlineOr, listingsLanguage) import Text.Pandoc.Shared import Text.Pandoc.Walk import Data.List.NonEmpty (nonEmpty) -- for debugging: -- import Text.Pandoc.Extensions (getDefaultExtensions) -- import Text.Pandoc.Class.PandocIO (runIOorExplode, PandocIO) -- import Debug.Trace (traceShowId) -- | Parse LaTeX from string and return 'Pandoc' document. readLaTeX :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options -> a -- ^ Input to parse -> m Pandoc readLaTeX opts ltx = do let sources = toSources ltx parsed <- runParserT parseLaTeX def{ sOptions = opts } "source" (TokStream False (tokenizeSources sources)) case parsed of Right result -> return result Left e -> throwError $ fromParsecError sources e parseLaTeX :: PandocMonad m => LP m Pandoc parseLaTeX = do bs <- blocks eof st <- getState let meta = sMeta st let doc' = doc bs let headerLevel (Header n _ _) = [n] headerLevel _ = [] let bottomLevel = maybe 1 minimum $ nonEmpty $ query headerLevel doc' let adjustHeaders m (Header n attr ils) = Header (n+m) attr ils adjustHeaders _ x = x let (Pandoc _ bs') = -- handle the case where you have \part or \chapter (if bottomLevel < 1 then walk (adjustHeaders (1 - bottomLevel)) else id) $ walk (resolveFootnoteMarks (sFootnoteTexts st)) $ walk (resolveRefs (sLabels st)) doc' return $ Pandoc meta bs' resolveRefs :: M.Map Text [Inline] -> Inline -> Inline resolveRefs labels x@(Link (ident,classes,kvs) _ _) = case (T.takeWhile (/='+') <$> lookup "reference-type" kvs, lookup "reference" kvs) of (Just "ref", Just lab) -> -- TODO special treatment of ref+label case M.lookup lab labels of Just txt -> Link (ident,classes,kvs) txt ("#" <> lab, "") Nothing -> x _ -> x resolveRefs _ x = x -- | Resolve footnote marks (\footnotemark) to actual notes using -- the footnote texts collected from \footnotetext commands. resolveFootnoteMarks :: M.Map Int Blocks -> Inline -> Inline resolveFootnoteMarks fnTexts (Span (_, classes, kvs) _) | "footnote-mark" `elem` classes , Just numText <- lookup "note-num" kvs , [(n, "")] <- reads (T.unpack numText) = case M.lookup n fnTexts of Just contents -> Note (toList contents) Nothing -> Str "" -- No matching footnotetext found resolveFootnoteMarks _ x = x -- testParser :: LP PandocIO a -> Text -> IO a -- testParser p t = do -- res <- runIOorExplode (runParserT p defaultLaTeXState{ -- sOptions = def{ readerExtensions = -- enableExtension Ext_raw_tex $ -- getDefaultExtensions "latex" }} "source" -- (tokenize (initialPos "source") t)) -- case res of -- Left e -> error (show e) -- Right r -> return r rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParsecT Sources s m Text rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) toks <- getInputTokens snd <$> ( rawLaTeXParser toks (makeAtLetterSection <|> macroDef (const mempty) <|> do choice (map controlSeq ["include", "input", "subfile", "usepackage"]) skipMany opt braced return mempty) blocks <|> rawLaTeXParser toks (void (environment <|> blockCommand)) (mconcat <$> many (block <|> beginOrEndCommand))) makeAtLetterSection :: PandocMonad m => LP m () makeAtLetterSection = try $ do controlSeq "makeatletter" void $ manyTill ( whitespace <|> newlineTok <|> macroDef (const ()) <|> void environment <|> void blockCommand ) (controlSeq "makeatother") -- See #4667 for motivation; sometimes people write macros -- that just evaluate to a begin or end command, which blockCommand -- won't accept. beginOrEndCommand :: PandocMonad m => LP m Blocks beginOrEndCommand = try $ do Tok _ (CtrlSeq name) txt <- anyControlSeq guard $ name == "begin" || name == "end" (envname, rawargs) <- withRaw braced if M.member (untokenize envname) (inlineEnvironments :: M.Map Text (LP PandocPure Inlines)) then mzero else return $ rawBlock "latex" (txt <> untokenize rawargs) rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParsecT Sources s m Text rawLaTeXInline = do lookAhead (try (char '\\' >> letter)) toks <- getInputTokens raw <- snd <$> ( rawLaTeXParser toks (mempty <$ (controlSeq "input" >> skipMany rawopt >> braced)) inlines <|> rawLaTeXParser toks (void inline) inlines ) finalbraces <- mconcat <$> many (try (string "{}")) -- see #5439 return $ raw <> T.pack finalbraces inlineCommand :: PandocMonad m => ParsecT Sources ParserState m Inlines inlineCommand = do lookAhead (try (char '\\' >> letter)) toks <- getInputTokens fst <$> rawLaTeXParser toks (void (inlineEnvironment <|> inlineCommand')) inlines -- inline elements: inlineGroup :: PandocMonad m => LP m Inlines inlineGroup = do ils <- grouped inline if null ils then return mempty else return $ spanWith nullAttr ils -- we need the span so we can detitlecase bibtex entries; -- we need to know when something is {C}apitalized doLHSverb :: PandocMonad m => LP m Inlines doLHSverb = codeWith ("",["haskell"],[]) . untokenize <$> manyTill (satisfyTok (not . isNewlineTok)) (symbol '|') mkImage :: PandocMonad m => [(Text, Text)] -> Text -> LP m Inlines mkImage options (T.unpack -> src) = do let replaceRelative (k,v) = case numUnit v of Just (num, "\\textwidth") -> (k, showFl (num * 100) <> "%") Just (num, "\\linewidth") -> (k, showFl (num * 100) <> "%") Just (num, "\\textheight") -> (k, showFl (num * 100) <> "%") _ -> (k, v) let kvs = map replaceRelative $ filter (\(k,_) -> k `elem` ["width", "height"]) options let attr = ("",[], kvs) let alt = maybe (str "image") str $ lookup "alt" options defaultExt <- getOption readerDefaultImageExtension let exts' = [".pdf", ".png", ".jpg", ".mps", ".jpeg", ".jbig2", ".jb2"] let exts = exts' ++ map (map toUpper) exts' let findFile s [] = return s findFile s (e:es) = do let s' = addExtension s e exists <- fileExists s' if exists then return s' else findFile s es src' <- case takeExtension src of "" | not (T.null defaultExt) -> return $ addExtension src $ T.unpack defaultExt | otherwise -> findFile src exts _ -> return src return $ imageWith attr (T.pack src') "" alt removeDoubleQuotes :: Text -> Text removeDoubleQuotes t = Data.Maybe.fromMaybe t $ T.stripPrefix "\"" t >>= T.stripSuffix "\"" doubleQuote :: PandocMonad m => LP m Inlines doubleQuote = quoted' doubleQuoted (try $ count 2 $ symbol '`') (void $ try $ count 2 $ symbol '\'') <|> quoted' doubleQuoted ((:[]) <$> symbol '“') (void $ symbol '”') -- the following is used by babel for localized quotes: <|> quoted' doubleQuoted (try $ sequence [symbol '"', symbol '`']) (void $ try $ sequence [symbol '"', symbol '\'']) singleQuote :: PandocMonad m => LP m Inlines singleQuote = quoted' singleQuoted ((:[]) <$> symbol '`') (try $ symbol '\'' >> notFollowedBy (satisfyTok startsWithLetter)) <|> quoted' singleQuoted ((:[]) <$> symbol '‘') (try $ symbol '’' >> notFollowedBy (satisfyTok startsWithLetter)) where startsWithLetter (Tok _ Word t) = case T.uncons t of Just (c, _) | isLetter c -> True _ -> False startsWithLetter _ = False quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m [Tok] -> LP m () -> LP m Inlines quoted' f starter ender = do startchs <- untokenize <$> starter smart <- extensionEnabled Ext_smart <$> getOption readerExtensions if smart then do ils <- many (notFollowedBy ender >> inline) (ender >> return (f (mconcat ils))) <|> (<> mconcat ils) <$> lit (case startchs of "``" -> "“" "`" -> "‘" cs -> cs) else lit startchs lit :: Text -> LP m Inlines lit = pure . str blockquote :: PandocMonad m => Bool -> Maybe Text -> LP m Blocks blockquote cvariant mblang = do citepar <- if cvariant then (\xs -> para (cite xs mempty)) <$> cites inline NormalCitation False else option mempty $ para <$> bracketed inline let lang = mblang >>= babelLangToBCP47 let langdiv = case lang of Nothing -> id Just l -> divWith ("",[],[("lang", renderLang l)]) _closingPunct <- option mempty $ bracketed inline -- currently ignored bs <- grouped block optional $ symbolIn (".:;?!" :: [Char]) -- currently ignored return $ blockQuote . langdiv $ (bs <> citepar) inlineCommand' :: PandocMonad m => LP m Inlines inlineCommand' = try $ do Tok _ (CtrlSeq name) cmd <- anyControlSeq guard $ name /= "begin" && name /= "end" && name /= "and" star <- if T.all isAlphaNum name then option "" ("*" <$ symbol '*' <* sp) else pure "" overlay <- option "" overlaySpecification let name' = name <> star <> overlay let names = nubOrd [name', name] -- check non-starred as fallback let raw = do guard $ isInlineCommand name || not (isBlockCommand name) rawcommand <- getRawCommand name (cmd <> star) (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand)) <|> ignore rawcommand lookupListDefault raw names inlineCommands tok :: PandocMonad m => LP m Inlines tok = tokWith inline unescapeURL :: Text -> Text unescapeURL = T.concat . go . T.splitOn "\\" where isEscapable c = T.any (== c) "#$%&~_^\\{}" go (x:xs) = x : map unescapeInterior xs go [] = [] unescapeInterior t | Just (c, _) <- T.uncons t , isEscapable c = t | otherwise = "\\" <> t inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines) inlineCommands = M.unions [ accentCommands tok , miscCommands , citationCommands inline , siunitxCommands tok , acronymCommands , refCommands , nameCommands , verbCommands , charCommands , enquoteCommands tok , inlineLanguageCommands tok , biblatexInlineCommands tok , rest ] where disableLigatures p = do oldLigatures <- sLigatures <$> getState updateState (\s -> s{ sLigatures = False }) res <- p updateState (\s -> s{ sLigatures = oldLigatures }) pure res rest = M.fromList [ ("emph", extractSpaces emph <$> tok) , ("textit", extractSpaces emph <$> tok) , ("textsl", extractSpaces emph <$> tok) , ("textsc", extractSpaces smallcaps <$> tok) , ("textsf", extractSpaces (spanWith ("",["sans-serif"],[])) <$> tok) , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok) , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok) , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok) , ("texttt", formatCode nullAttr <$> disableLigatures tok) , ("alert", skipopts >> spanWith ("",["alert"],[]) <$> tok) -- beamer , ("textsuperscript", extractSpaces superscript <$> tok) , ("textsubscript", extractSpaces subscript <$> tok) , ("textbf", extractSpaces strong <$> tok) , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok) , ("underline", underline <$> tok) , ("mbox", rawInlineOr "mbox" $ processHBox <$> tok) , ("hbox", rawInlineOr "hbox" $ processHBox <$> tok) , ("vbox", rawInlineOr "vbox" tok) , ("lettrine", rawInlineOr "lettrine" lettrine) , ("(", withMathMode (mathInline . untokenize <$> manyTill anyTok (controlSeq ")"))) , ("[", withMathMode (mathDisplay . untokenize <$> manyTill anyTok (controlSeq "]"))) , ("ensuremath", withMathMode (mathInline . untokenize <$> braced)) , ("texorpdfstring", const <$> tok <*> tok) -- old TeX commands , ("em", extractSpaces emph <$> inlines) , ("it", extractSpaces emph <$> inlines) , ("sl", extractSpaces emph <$> inlines) , ("bf", extractSpaces strong <$> inlines) , ("tt", formatCode nullAttr <$> inlines) , ("rm", inlines) , ("itshape", extractSpaces emph <$> inlines) , ("slshape", extractSpaces emph <$> inlines) , ("scshape", extractSpaces smallcaps <$> inlines) , ("bfseries", extractSpaces strong <$> inlines) , ("MakeUppercase", makeUppercase <$> tok) , ("MakeTextUppercase", makeUppercase <$> tok) -- textcase , ("uppercase", makeUppercase <$> tok) , ("MakeLowercase", makeLowercase <$> tok) , ("MakeTextLowercase", makeLowercase <$> tok) , ("lowercase", makeLowercase <$> tok) , ("thanks", skipopts >> note <$> grouped block) , ("footnote", skipopts >> footnote) , ("footnotemark", footnotemark) , ("footnotetext", footnotetext) , ("newline", pure B.linebreak) , ("passthrough", fixPassthroughEscapes <$> tok) -- \passthrough macro used by latex writer -- for listings , ("includegraphics", do options <- option [] keyvals src <- bracedFilename mkImage options . unescapeURL $ src) -- svg , ("includesvg", do options <- option [] keyvals src <- bracedFilename mkImage options . unescapeURL $ src) -- hyperref , ("url", (\url -> linkWith ("",["uri"],[]) url "" (str url)) . unescapeURL . untokenize <$> bracedUrl) , ("nolinkurl", code . unescapeURL . untokenize <$> bracedUrl) , ("href", do url <- bracedUrl sp link (unescapeURL $ untokenize url) "" <$> tok) , ("hyperlink", hyperlink) , ("hyperref", hyperref) , ("hypertarget", hypertargetInline) -- hyphenat , ("nohyphens", tok) , ("textnhtt", formatCode nullAttr <$> tok) , ("nhttfamily", formatCode nullAttr <$> tok) -- LaTeX colors , ("textcolor", coloredInline "color") , ("colorbox", coloredInline "background-color") -- etoolbox , ("newtoggle", braced >>= newToggle) , ("toggletrue", braced >>= setToggle True) , ("togglefalse", braced >>= setToggle False) , ("iftoggle", try $ ifToggle >> inline) -- include , ("input", rawInlineOr "input" $ include "input") -- soul package , ("st", extractSpaces strikeout <$> tok) , ("ul", underline <$> tok) , ("hl", extractSpaces (spanWith ("",["mark"],[])) <$> tok) -- ulem package , ("sout", extractSpaces strikeout <$> tok) , ("uline", underline <$> tok) -- plain tex stuff that should just be passed through as raw tex , ("ifdim", ifdim) -- generally only used in \date , ("today", today) -- this is used internally by pandoc but the definition is too complicated -- for pandoc to handle (see #11140): , ("pandocbounded", tok) ] bracedFilename :: PandocMonad m => LP m Text bracedFilename = removeDoubleQuotes . T.strip . untokenize . filter (not . isComment) <$> braced isComment :: Tok -> Bool isComment (Tok _ Comment _) = True isComment _ = False today :: PandocMonad m => LP m Inlines today = text . T.pack . showGregorian . localDay . zonedTimeToLocalTime <$> getZonedTime footnote :: PandocMonad m => LP m Inlines footnote = do updateState $ \st -> st{ sLastNoteNum = sLastNoteNum st + 1 } contents <- grouped block >>= walkM resolveNoteLabel return $ note contents -- | Parse \footnotemark[n]. If n is not given, increment the counter. -- Returns a span marker that will be resolved to a Note later. footnotemark :: PandocMonad m => LP m Inlines footnotemark = do mbNum <- optionalFootnoteNum noteNum <- case mbNum of Just n -> return n Nothing -> do updateState $ \st -> st{ sLastNoteNum = sLastNoteNum st + 1 } sLastNoteNum <$> getState return $ B.spanWith ("", ["footnote-mark"], [("note-num", tshow noteNum)]) mempty -- | Parse \footnotetext[n]{text}. If n is not given, use current counter. -- Stores the text in state to be resolved later. footnotetext :: PandocMonad m => LP m Inlines footnotetext = do mbNum <- optionalFootnoteNum noteNum <- case mbNum of Just n -> return n Nothing -> sLastNoteNum <$> getState contents <- grouped block >>= walkM resolveNoteLabel updateState $ \st -> st{ sFootnoteTexts = M.insert noteNum contents (sFootnoteTexts st) } return mempty -- | Parse optional footnote number argument [n] optionalFootnoteNum :: PandocMonad m => LP m (Maybe Int) optionalFootnoteNum = option Nothing $ do t <- bracketedToks case reads (T.unpack $ untokenize t) of [(n, "")] -> return $ Just n _ -> return Nothing resolveNoteLabel :: PandocMonad m => Inline -> LP m Inline resolveNoteLabel (Span (_,cls,kvs) _) | Just lab <- lookup "label" kvs = do updateState $ \st -> st{ sLabels = M.insert lab (toList . text . tshow $ sLastNoteNum st) $ sLabels st } return $ Span (lab,cls,kvs) [] resolveNoteLabel il = return il lettrine :: PandocMonad m => LP m Inlines lettrine = do optional rawopt x <- tok y <- tok return $ extractSpaces (spanWith ("",["lettrine"],[])) x <> smallcaps y ifdim :: PandocMonad m => LP m Inlines ifdim = do contents <- manyTill anyTok (controlSeq "fi") return $ rawInline "latex" $ "\\ifdim" <> untokenize contents <> "\\fi" makeUppercase :: Inlines -> Inlines makeUppercase = fromList . walk (alterStr T.toUpper) . toList makeLowercase :: Inlines -> Inlines makeLowercase = fromList . walk (alterStr T.toLower) . toList alterStr :: (Text -> Text) -> Inline -> Inline alterStr f (Str xs) = Str (f xs) alterStr _ x = x fixPassthroughEscapes :: Inlines -> Inlines fixPassthroughEscapes = walk go where go (Code attr txt) = Code attr (T.pack $ unescapePassthrough $ T.unpack txt) go x = x unescapePassthrough [] = [] unescapePassthrough ('\\':c:cs) | c `elem` ['%','{','}','\\'] = c : unescapePassthrough cs unescapePassthrough (c:cs) = c : unescapePassthrough cs hyperlink :: PandocMonad m => LP m Inlines hyperlink = try $ do src <- untokenize <$> braced lab <- tok return $ link ("#" <> src) "" lab hyperref :: PandocMonad m => LP m Inlines hyperref = try $ do url <- (("#" <>) . untokenize <$> try (sp *> bracketedToks <* sp)) <|> untokenize <$> (bracedUrl <* bracedUrl <* bracedUrl) link url "" <$> tok hypertargetBlock :: PandocMonad m => LP m Blocks hypertargetBlock = try $ do ref <- untokenize <$> braced bs <- grouped block case toList bs of [Header 1 (ident,_,_) _] | ident == ref -> return bs _ -> return $ divWith (ref, [], []) bs hypertargetInline :: PandocMonad m => LP m Inlines hypertargetInline = try $ do ref <- untokenize <$> braced ils <- grouped inline return $ spanWith (ref, [], []) ils newToggle :: (Monoid a, PandocMonad m) => [Tok] -> LP m a newToggle name = do updateState $ \st -> st{ sToggles = M.insert (untokenize name) False (sToggles st) } return mempty setToggle :: (Monoid a, PandocMonad m) => Bool -> [Tok] -> LP m a setToggle on name = do updateState $ \st -> st{ sToggles = M.adjust (const on) (untokenize name) (sToggles st) } return mempty ifToggle :: PandocMonad m => LP m () ifToggle = do name <- braced spaces yes <- withVerbatimMode braced spaces no <- withVerbatimMode braced toggles <- sToggles <$> getState TokStream _ inp <- getInput let name' = untokenize name case M.lookup name' toggles of Just True -> setInput $ TokStream False (yes ++ inp) Just False -> setInput $ TokStream False (no ++ inp) Nothing -> do pos <- getPosition report $ UndefinedToggle name' pos return () coloredInline :: PandocMonad m => Text -> LP m Inlines coloredInline stylename = do skipopts color <- braced spanWith ("",[],[("style",stylename <> ": " <> untokenize color)]) <$> tok processHBox :: Inlines -> Inlines processHBox = walk convert where convert Space = Str $ T.singleton $ chr 160 -- non-breakable space convert SoftBreak = Str $ T.singleton $ chr 160 -- non-breakable space convert LineBreak = Str "" convert x = x isBlockCommand :: Text -> Bool isBlockCommand s = s `M.member` (blockCommands :: M.Map Text (LP PandocPure Blocks)) || s `Set.member` treatAsBlock treatAsBlock :: Set.Set Text treatAsBlock = Set.fromList [ "special", "pdfannot", "pdfstringdef" , "bibliographystyle" , "maketitle", "makeindex", "makeglossary" , "addcontentsline", "addtocontents", "addtocounter" -- \ignore{} is used conventionally in literate haskell for definitions -- that are to be processed by the compiler but not printed. , "ignore" , "hyperdef" , "markboth", "markright", "markleft" , "hspace", "vspace" , "newpage" , "clearpage" , "pagebreak" , "titleformat" , "listoffigures" , "listoftables" , "write" ] isInlineCommand :: Text -> Bool isInlineCommand s = s `M.member` (inlineCommands :: M.Map Text (LP PandocPure Inlines)) || s `Set.member` treatAsInline treatAsInline :: Set.Set Text treatAsInline = Set.fromList [ "index" , "hspace" , "vspace" , "noindent" , "newpage" , "clearpage" , "pagebreak" ] lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v lookupListDefault d = (fromMaybe d .) . lookupList where lookupList l m = msum $ map (`M.lookup` m) l inline :: PandocMonad m => LP m Inlines inline = do Tok pos toktype t <- peekTok let eatOneToken = satisfyTok (const True) let symbolAsString = str t <$ eatOneToken let unescapedSymbolAsString = do eatOneToken report $ ParsingUnescaped t pos return $ str t ligatures <- sLigatures <$> getState case toktype of Comment -> mempty <$ eatOneToken Spaces -> space <$ eatOneToken Newline -> softbreak <$ endline Word -> str t <$ eatOneToken Symbol -> case t of "-" | ligatures -> eatOneToken *> option (str "-") (symbol '-' *> option (str "–") (str "—" <$ symbol '-')) "'" -> eatOneToken *> option (str "’") (str "”" <$ (guard ligatures *> symbol '\'')) "~" -> str "\160" <$ eatOneToken "`" | ligatures -> doubleQuote <|> singleQuote <|> (str "‘" <$ symbol '`') | otherwise -> str "‘" <$ symbol '`' "\"" | ligatures -> doubleQuote <|> singleQuote <|> symbolAsString "“" -> doubleQuote <|> symbolAsString "‘" -> singleQuote <|> symbolAsString "$" -> dollarsMath <|> unescapedSymbolAsString "|" -> (guardEnabled Ext_literate_haskell *> eatOneToken *> doLHSverb) <|> symbolAsString "{" -> inlineGroup "#" -> unescapedSymbolAsString "&" -> unescapedSymbolAsString "_" -> unescapedSymbolAsString "^" -> unescapedSymbolAsString "\\" -> mzero "}" -> mzero _ -> symbolAsString CtrlSeq _ -> macroDef (rawInline "latex") <|> inlineGroup <|> inlineCommand' <|> inlineEnvironment Esc1 -> str . T.singleton <$> primEscape Esc2 -> str . T.singleton <$> primEscape _ -> mzero inlines :: PandocMonad m => LP m Inlines inlines = mconcat <$> many inline opt :: PandocMonad m => LP m Inlines opt = do toks <- try (sp *> bracketedToks <* sp) -- now parse the toks as inlines st <- getState parsed <- runParserT (mconcat <$> many inline) st "bracketed option" (TokStream False toks) case parsed of Right result -> return result Left e -> throwError $ fromParsecError (toSources toks) e -- block elements: preamble :: PandocMonad m => LP m Blocks preamble = mconcat <$> many preambleBlock where preambleBlock = (mempty <$ spaces1) <|> macroDef (rawBlock "latex") <|> filecontents <|> (mempty <$ blockCommand) <|> (mempty <$ braced) <|> (do notFollowedBy (begin_ "document") anyTok return mempty) rule :: PandocMonad m => LP m Blocks rule = do skipopts width <- T.takeWhile (\c -> isDigit c || c == '.') . stringify <$> tok _thickness <- tok -- 0-width rules are used to fix spacing issues: case safeRead width of Just (0 :: Double) -> return mempty _ -> return horizontalRule paragraph :: PandocMonad m => LP m Blocks paragraph = do x <- trimInlines . mconcat <$> many1 inline if x == mempty then return mempty else return $ para x rawBlockOr :: PandocMonad m => Text -> LP m Blocks -> LP m Blocks rawBlockOr name fallback = do -- if raw_tex allowed, don't process parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions if parseRaw then rawBlock "latex" <$> getRawCommand name ("\\" <> name) else fallback doSubfile :: PandocMonad m => LP m Blocks doSubfile = do skipMany opt f <- T.unpack <$> bracedFilename oldToks <- getInput setInput $ TokStream False [] insertIncluded (ensureExtension (/= "") ".tex" f) bs <- blocks eof setInput oldToks return bs include :: (PandocMonad m, Monoid a) => Text -> LP m a include name = do let isAllowed = case name of "include" -> (== ".tex") "input" -> (/= "") _ -> const False skipMany opt fs <- map (T.unpack . removeDoubleQuotes . T.strip) . T.splitOn "," . untokenize . filter (not . isComment) <$> braced mapM_ (insertIncluded . ensureExtension isAllowed ".tex") fs return mempty usepackage :: (PandocMonad m, Monoid a) => LP m a usepackage = do skipMany opt fs <- map (T.unpack . removeDoubleQuotes . T.strip) . T.splitOn "," . untokenize . filter (not . isComment) <$> braced let parsePackage f = do TokStream _ ts <- getIncludedToks (ensureExtension (== ".sty") ".sty" f) parseFromToks (do _ <- blocks eof <|> do pos <- getPosition report $ CouldNotParseIncludeFile (T.pack f) pos) ts mapM_ parsePackage fs return mempty readFileFromTexinputs :: PandocMonad m => FilePath -> LP m (Maybe Text) readFileFromTexinputs fp = do fileContentsMap <- sFileContents <$> getState case M.lookup (T.pack fp) fileContentsMap of Just t -> return (Just t) Nothing -> do dirs <- map (\t -> if T.null t then "." else T.unpack t) . T.split (==':') . fromMaybe "" <$> lookupEnv "TEXINPUTS" readFileFromDirs dirs fp ensureExtension :: (FilePath -> Bool) -> FilePath -> FilePath -> FilePath ensureExtension isAllowed defaultExt fp = let ext = takeExtension fp in if isAllowed ext then fp else addExtension fp defaultExt getIncludedToks :: PandocMonad m => FilePath -> LP m TokStream getIncludedToks f = do pos <- getPosition containers <- getIncludeFiles <$> getState when (T.pack f `elem` containers) $ throwError $ PandocParseError $ T.pack $ "Include file loop at " ++ show pos updateState $ addIncludeFile $ T.pack f mbcontents <- readFileFromTexinputs f contents <- case mbcontents of Just s -> return s Nothing -> do report $ CouldNotLoadIncludeFile (T.pack f) pos return "" updateState dropLatestIncludeFile return $ TokStream False $ tokenize (initialPos f) contents insertIncluded :: PandocMonad m => FilePath -> LP m () insertIncluded f = do contents <- getIncludedToks f ts <- getInput setInput $ contents <> ts authors :: PandocMonad m => LP m () authors = try $ do bgroup let oneAuthor = blocksToInlines' . B.toList . mconcat <$> many1 block auths <- sepBy oneAuthor (controlSeq "and") egroup addMeta "author" (map trimInlines auths) looseItem :: PandocMonad m => LP m Blocks looseItem = do inListItem <- sInListItem <$> getState guard $ not inListItem skipopts return mempty epigraph :: PandocMonad m => LP m Blocks epigraph = do p1 <- grouped block p2 <- grouped block return $ divWith ("", ["epigraph"], []) (p1 <> p2) section :: PandocMonad m => Attr -> Int -> LP m Blocks section (ident, classes, kvs) lvl = do skipopts contents <- grouped inline lab <- option ident $ try (spaces >> controlSeq "label" >> spaces >> untokenize <$> braced) when (lvl == 0) $ updateState $ \st -> st{ sHasChapters = True } unless ("unnumbered" `elem` classes) $ do hn <- sLastHeaderNum <$> getState hasChapters <- sHasChapters <$> getState let lvl' = lvl + if hasChapters then 1 else 0 let num = incrementDottedNum lvl' hn updateState $ \st -> st{ sLastHeaderNum = num , sLabels = M.insert lab [Str (renderDottedNum num)] (sLabels st) } attr' <- registerHeader (lab, classes, kvs) contents return $ headerWith attr' lvl contents blockCommand :: PandocMonad m => LP m Blocks blockCommand = try $ do Tok _ (CtrlSeq name) txt <- anyControlSeq guard $ name /= "begin" && name /= "end" && name /= "and" star <- option "" ("*" <$ symbol '*' <* sp) let name' = name <> star let names = nubOrd [name', name] let rawDefiniteBlock = do guard $ isBlockCommand name rawcontents <- getRawCommand name (txt <> star) (guardEnabled Ext_raw_tex >> return (rawBlock "latex" rawcontents)) <|> ignore rawcontents -- heuristic: if it could be either block or inline, we -- treat it if block if we have a sequence of block -- commands followed by a newline. But we stop if we -- hit a \startXXX, since this might start a raw ConTeXt -- environment (this is important because this parser is -- used by the Markdown reader). let startCommand = try $ do Tok _ (CtrlSeq n) _ <- anyControlSeq guard $ "start" `T.isPrefixOf` n let rawMaybeBlock = try $ do guard $ not $ isInlineCommand name rawcontents <- getRawCommand name (txt <> star) curr <- (guardEnabled Ext_raw_tex >> return (rawBlock "latex" rawcontents)) <|> ignore rawcontents rest <- many $ notFollowedBy startCommand *> blockCommand lookAhead $ blankline <|> startCommand return $ curr <> mconcat rest let raw = rawDefiniteBlock <|> rawMaybeBlock lookupListDefault raw names blockCommands closing :: PandocMonad m => LP m Blocks closing = do contents <- tok st <- getState let extractInlines (MetaBlocks [Plain ys]) = ys extractInlines (MetaBlocks [Para ys ]) = ys extractInlines _ = [] let sigs = case lookupMeta "author" (sMeta st) of Just (MetaList xs) -> para $ trimInlines $ fromList $ intercalate [LineBreak] $ map extractInlines xs _ -> mempty return $ para (trimInlines contents) <> sigs parbox :: PandocMonad m => LP m Blocks parbox = try $ do skipopts braced -- size oldInTableCell <- sInTableCell <$> getState -- see #5711 updateState $ \st -> st{ sInTableCell = False } res <- grouped block updateState $ \st -> st{ sInTableCell = oldInTableCell } return res blockCommands :: PandocMonad m => M.Map Text (LP m Blocks) blockCommands = M.fromList [ ("par", mempty <$ skipopts) , ("parbox", parbox) , ("title", mempty <$ (skipopts *> (grouped inline >>= addMeta "title") <|> (grouped block >>= addMeta "title"))) , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle")) , ("author", mempty <$ (skipopts *> authors)) -- -- in letter class, temp. store address & sig as title, author , ("address", mempty <$ (skipopts *> tok >>= addMeta "address")) , ("signature", mempty <$ (skipopts *> authors)) , ("date", mempty <$ (skipopts *> tok >>= addMeta "date")) , ("newtheorem", newtheorem inline) , ("theoremstyle", theoremstyle) -- KOMA-Script metadata commands , ("extratitle", mempty <$ (skipopts *> tok >>= addMeta "extratitle")) , ("frontispiece", mempty <$ (skipopts *> tok >>= addMeta "frontispiece")) , ("titlehead", mempty <$ (skipopts *> tok >>= addMeta "titlehead")) , ("subject", mempty <$ (skipopts *> tok >>= addMeta "subject")) , ("publishers", mempty <$ (skipopts *> tok >>= addMeta "publishers")) , ("uppertitleback", mempty <$ (skipopts *> tok >>= addMeta "uppertitleback")) , ("lowertitleback", mempty <$ (skipopts *> tok >>= addMeta "lowertitleback")) , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication")) -- sectioning , ("part", section nullAttr (-1)) , ("part*", section ("",["unnumbered"],[]) (-1)) , ("chapter", section nullAttr 0) , ("chapter*", section ("",["unnumbered"],[]) 0) , ("section", section nullAttr 1) , ("section*", section ("",["unnumbered"],[]) 1) , ("subsection", section nullAttr 2) , ("subsection*", section ("",["unnumbered"],[]) 2) , ("subsubsection", section nullAttr 3) , ("subsubsection*", section ("",["unnumbered"],[]) 3) , ("paragraph", section nullAttr 4) , ("paragraph*", section ("",["unnumbered"],[]) 4) , ("subparagraph", section nullAttr 5) , ("subparagraph*", section ("",["unnumbered"],[]) 5) , ("minisec", section ("",["unnumbered","unlisted"],[]) 6) -- from KOMA -- beamer slides , ("frametitle", section nullAttr 3) , ("framesubtitle", section nullAttr 4) -- letters , ("opening", para . trimInlines <$> (skipopts *> tok)) , ("closing", skipopts *> closing) -- memoir , ("plainbreak", braced >> pure horizontalRule) , ("plainbreak*", braced >> pure horizontalRule) , ("fancybreak", braced >> pure horizontalRule) , ("fancybreak*", braced >> pure horizontalRule) , ("plainfancybreak", braced >> braced >> braced >> pure horizontalRule) , ("plainfancybreak*", braced >> braced >> braced >> pure horizontalRule) , ("pfbreak", pure horizontalRule) , ("pfbreak*", pure horizontalRule) -- , ("hrule", pure horizontalRule) , ("strut", pure mempty) , ("rule", rule) , ("item", looseItem) , ("documentclass", skipopts *> braced *> preamble) , ("centerline", para . trimInlines <$> (skipopts *> tok)) , ("caption", mempty <$ setCaption inline) , ("bibliography", mempty <$ (skipopts *> braced >>= addMeta "bibliography" . splitBibs . untokenize)) , ("addbibresource", mempty <$ (skipopts *> braced >>= addMeta "bibliography" . splitBibs . untokenize)) , ("endinput", mempty <$ skipSameFileToks) -- includes , ("lstinputlisting", inputListing) , ("inputminted", inputMinted) , ("graphicspath", graphicsPath) -- polyglossia , ("setdefaultlanguage", setDefaultLanguage) , ("setmainlanguage", setDefaultLanguage) -- hyperlink , ("hypertarget", hypertargetBlock) -- LaTeX colors , ("textcolor", coloredBlock "color") , ("colorbox", coloredBlock "background-color") -- csquotes , ("blockquote", blockquote False Nothing) , ("blockcquote", blockquote True Nothing) , ("foreignblockquote", braced >>= blockquote False . Just . untokenize) , ("foreignblockcquote", braced >>= blockquote True . Just . untokenize) , ("hyphenblockquote", braced >>= blockquote False . Just . untokenize) , ("hyphenblockcquote", braced >>= blockquote True . Just . untokenize) -- include , ("include", rawBlockOr "include" $ include "include") , ("input", rawBlockOr "input" $ include "input") , ("subfile", rawBlockOr "subfile" doSubfile) , ("usepackage", rawBlockOr "usepackage" usepackage) -- preamble , ("PackageError", mempty <$ (braced >> braced >> braced)) -- epigraph package , ("epigraph", epigraph) -- alignment , ("raggedright", pure mempty) -- etoolbox , ("newtoggle", braced >>= newToggle) , ("toggletrue", braced >>= setToggle True) , ("togglefalse", braced >>= setToggle False) , ("iftoggle", try $ ifToggle >> block) ] skipSameFileToks :: PandocMonad m => LP m () skipSameFileToks = do pos <- getPosition skipMany $ infile (sourceName pos) environments :: PandocMonad m => M.Map Text (LP m Blocks) environments = M.union (tableEnvironments block inline) $ M.fromList [ ("document", env "document" blocks <* skipMany anyTok) , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract")) , ("sloppypar", env "sloppypar" blocks) , ("letter", env "letter" letterContents) , ("minipage", divWith ("",["minipage"],[]) <$> env "minipage" (skipopts *> spaces *> optional braced *> spaces *> blocks)) , ("figure", env "figure" figure') , ("figure*", env "figure*" figure') , ("subfigure", env "subfigure" $ skipopts *> tok *> figure') , ("center", divWith ("", ["center"], []) <$> env "center" blocks) , ("quote", blockQuote <$> env "quote" blocks) , ("quotation", blockQuote <$> env "quotation" blocks) , ("verse", blockQuote <$> env "verse" blocks) , ("itemize", bulletList <$> listenv "itemize" (many item)) , ("description", definitionList <$> listenv "description" (many descItem)) , ("enumerate", orderedList') , ("alltt", alltt <$> env "alltt" blocks) , ("code", guardEnabled Ext_literate_haskell *> (codeBlockWith ("",["haskell","literate"],[]) <$> verbEnv "code")) , ("comment", mempty <$ verbEnv "comment") , ("verbatim", codeBlock <$> verbEnv "verbatim") , ("Verbatim", fancyverbEnv "Verbatim") , ("BVerbatim", fancyverbEnv "BVerbatim") , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals codeBlockWith attr <$> verbEnv "lstlisting") , ("minted", minted) , ("obeylines", obeylines) , ("tikzpicture", rawVerbEnv "tikzpicture") , ("tikzcd", rawVerbEnv "tikzcd") , ("lilypond", rawVerbEnv "lilypond") , ("ly", rawVerbEnv "ly") -- amsthm , ("proof", proof blocks opt) -- other , ("CSLReferences", braced >> braced >> env "CSLReferences" blocks) , ("otherlanguage", env "otherlanguage" otherlanguageEnv) ] otherlanguageEnv :: PandocMonad m => LP m Blocks otherlanguageEnv = do skipopts babelLang <- untokenize <$> braced case babelLangToBCP47 babelLang of Just lang -> divWith ("", [], [("lang", renderLang lang)]) <$> blocks Nothing -> blocks langEnvironment :: PandocMonad m => Text -> LP m Blocks langEnvironment name = case babelLangToBCP47 name of Just lang -> env name (divWith ("", [], [("lang", renderLang lang)]) <$> blocks) Nothing -> mzero -- fall through to raw environment filecontents :: PandocMonad m => LP m Blocks filecontents = try $ do controlSeq "begin" name <- untokenize <$> braced guard $ name == "filecontents" || name == "filecontents*" skipopts fp <- untokenize <$> braced txt <- verbEnv name updateState $ \st -> st{ sFileContents = M.insert fp txt (sFileContents st) } return mempty environment :: PandocMonad m => LP m Blocks environment = try $ do controlSeq "begin" name <- untokenize <$> braced M.findWithDefault mzero name environments <|> langEnvironment name <|> theoremEnvironment blocks opt name <|> if M.member name (inlineEnvironments :: M.Map Text (LP PandocPure Inlines)) then mzero else try (rawEnv name) <|> rawVerbEnv name rawEnv :: PandocMonad m => Text -> LP m Blocks rawEnv name = do exts <- getOption readerExtensions let parseRaw = extensionEnabled Ext_raw_tex exts rawOptions <- mconcat <$> many rawopt let beginCommand = "\\begin{" <> name <> "}" <> rawOptions pos1 <- getPosition if parseRaw then do (_, raw) <- withRaw $ env name blocks return $ rawBlock "latex" $ beginCommand <> untokenize raw else do bs <- env name blocks report $ SkippedContent beginCommand pos1 pos2 <- getPosition report $ SkippedContent ("\\end{" <> name <> "}") pos2 return $ divWith ("",[name],[]) bs rawVerbEnv :: PandocMonad m => Text -> LP m Blocks rawVerbEnv name = do pos <- getPosition (_, raw) <- withRaw $ verbEnv name let raw' = "\\begin{" <> name <> "}" <> untokenize raw exts <- getOption readerExtensions let parseRaw = extensionEnabled Ext_raw_tex exts if parseRaw then return $ rawBlock "latex" raw' else do report $ SkippedContent raw' pos return mempty fancyverbEnv :: PandocMonad m => Text -> LP m Blocks fancyverbEnv name = do options <- option [] keyvals let kvs = [ (if k == "firstnumber" then "startFrom" else k, v) | (k,v) <- options ] let classes = [ "numberLines" | lookup "numbers" options == Just "left" ] let attr = ("",classes,kvs) codeBlockWith attr <$> verbEnv name obeylines :: PandocMonad m => LP m Blocks obeylines = para . fromList . removeLeadingTrailingBreaks . walk softBreakToHard . toList <$> env "obeylines" inlines where softBreakToHard SoftBreak = LineBreak softBreakToHard x = x removeLeadingTrailingBreaks = reverse . dropWhile isLineBreak . reverse . dropWhile isLineBreak isLineBreak LineBreak = True isLineBreak _ = False minted :: PandocMonad m => LP m Blocks minted = do attr <- mintedAttr codeBlockWith attr <$> verbEnv "minted" mintedAttr :: PandocMonad m => LP m Attr mintedAttr = do options <- option [] keyvals lang <- untokenize <$> braced let kvs = [ (if k == "firstnumber" then "startFrom" else k, v) | (k,v) <- options ] let classes = [ lang | not (T.null lang) ] ++ [ "numberLines" | lookup "linenos" options == Just "true" ] return ("",classes,kvs) inputMinted :: PandocMonad m => LP m Blocks inputMinted = do pos <- getPosition attr <- mintedAttr f <- T.filter (/='"') . untokenize <$> braced mbCode <- readFileFromTexinputs (T.unpack f) rawcode <- case mbCode of Just s -> return s Nothing -> do report $ CouldNotLoadIncludeFile f pos return "" return $ B.codeBlockWith attr rawcode letterContents :: PandocMonad m => LP m Blocks letterContents = do bs <- blocks st <- getState -- add signature (author) and address (title) let addr = case lookupMeta "address" (sMeta st) of Just (MetaBlocks [Plain xs]) -> para $ trimInlines $ fromList xs _ -> mempty return $ addr <> bs -- sig added by \closing figure' :: PandocMonad m => LP m Blocks figure' = try $ do sp poshint <- option "" $ untokenize <$> bracketedToks sp resetCaption innerContent <- many $ try (Left <$> label) <|> (Right <$> block) let content = walk go $ mconcat $ snd $ partitionEithers innerContent st <- getState let caption' = fromMaybe B.emptyCaption $ sCaption st let mblabel = sLastLabel st let kvs = [("latex-placement", poshint) | not (T.null poshint)] let ident = fromMaybe "" mblabel let attr = (ident, [], kvs) case mblabel of Nothing -> pure () Just lab -> do num <- getNextNumber sLastFigureNum setState st { sLastFigureNum = num , sLabels = M.insert lab [Str (renderDottedNum num)] (sLabels st) } return $ B.figureWith attr caption' content where -- Remove the `Image` caption b.c. it's on the `Figure` go (Para [Image attr [Str "image"] target]) = Plain [Image attr [] target] go x = x coloredBlock :: PandocMonad m => Text -> LP m Blocks coloredBlock stylename = try $ do skipopts color <- braced notFollowedBy (grouped inline) let constructor = divWith ("",[],[("style",stylename <> ": " <> untokenize color)]) constructor <$> grouped block graphicsPath :: PandocMonad m => LP m Blocks graphicsPath = do ps <- map (T.unpack . untokenize) <$> (bgroup *> spaces *> manyTill (braced <* spaces) egroup) getResourcePath >>= setResourcePath . (<> ps) return mempty splitBibs :: Text -> [Inlines] splitBibs = map (str . T.pack . flip replaceExtension "bib" . T.unpack . trim) . splitTextBy (==',') alltt :: Blocks -> Blocks alltt = walk strToCode where strToCode (Str s) = Code nullAttr s strToCode Space = RawInline (Format "latex") "\\ " strToCode SoftBreak = LineBreak strToCode x = x parseListingsOptions :: [(Text, Text)] -> Attr parseListingsOptions options = let kvs = [ (if k == "firstnumber" then "startFrom" else k, v) | (k,v) <- options ] classes = [ "numberLines" | lookup "numbers" options == Just "left" ] ++ maybeToList (listingsLanguage options) in (fromMaybe "" (lookup "label" options), classes, kvs) inputListing :: PandocMonad m => LP m Blocks inputListing = do pos <- getPosition options <- option [] keyvals f <- T.filter (/='"') . untokenize <$> braced mbCode <- readFileFromTexinputs (T.unpack f) codeLines <- case mbCode of Just s -> return $ T.lines s Nothing -> do report $ CouldNotLoadIncludeFile f pos return [] let (ident,classes,kvs) = parseListingsOptions options let classes' = (case listingsLanguage options of Nothing -> (take 1 (languagesByExtension defaultSyntaxMap (T.pack $ takeExtension $ T.unpack f)) <>) Just _ -> id) classes let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead let lastline = fromMaybe (length codeLines) $ lookup "lastline" options >>= safeRead let codeContents = T.intercalate "\n" $ take (1 + lastline - firstline) $ drop (firstline - 1) codeLines return $ codeBlockWith (ident,classes',kvs) codeContents -- lists item :: PandocMonad m => LP m Blocks item = void blocks *> controlSeq "item" *> skipopts *> blocks descItem :: PandocMonad m => LP m (Inlines, [Blocks]) descItem = do optional spaces1 controlSeq "item" sp ils <- opt bs <- blocks return (ils, [bs]) listenv :: PandocMonad m => Text -> LP m a -> LP m a listenv name p = try $ do oldInListItem <- sInListItem `fmap` getState updateState $ \st -> st{ sInListItem = True } res <- env name p updateState $ \st -> st{ sInListItem = oldInListItem } return res orderedList' :: PandocMonad m => LP m Blocks orderedList' = try $ do spaces let markerSpec = do symbol '[' ts <- untokenize <$> manyTill anyTok (symbol ']') case runParser anyOrderedListMarker def "option" ts of Right r -> return r Left _ -> do pos <- getPosition report $ SkippedContent ("[" <> ts <> "]") pos return (1, DefaultStyle, DefaultDelim) (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) markerSpec spaces optional $ try $ controlSeq "setlength" *> grouped (count 1 $ controlSeq "itemindent") *> braced spaces start <- option 1 $ try $ do pos <- getPosition controlSeq "setcounter" ctr <- untokenize <$> braced guard $ "enum" `T.isPrefixOf` ctr guard $ T.all (`elem` ['i','v']) (T.drop 4 ctr) sp num <- untokenize <$> braced case safeRead num of Just i -> return (i + 1 :: Int) Nothing -> do report $ SkippedContent ("\\setcounter{" <> ctr <> "}{" <> num <> "}") pos return 1 bs <- listenv "enumerate" (many item) return $ orderedListWith (start, style, delim) bs block :: PandocMonad m => LP m Blocks block = do Tok _ toktype _ <- peekTok res <- (case toktype of Newline -> mempty <$ spaces1 Spaces -> mempty <$ spaces1 Comment -> mempty <$ spaces1 Word -> paragraph CtrlSeq "begin" -> environment CtrlSeq _ -> macroDef (rawBlock "latex") <|> blockCommand _ -> mzero) <|> paragraph <|> grouped block trace (T.take 60 $ tshow $ B.toList res) return res blocks :: PandocMonad m => LP m Blocks blocks = mconcat <$> many block ================================================ FILE: src/Text/Pandoc/Readers/Man.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Man Copyright : Copyright (C) 2018-2020 Yan Pashkovsky and John MacFarlane License : GNU GPL, version 2 or above Maintainer : Yan Pashkovsky Stability : WIP Portability : portable Conversion of man to 'Pandoc' document. -} module Text.Pandoc.Readers.Man (readMan) where import Data.Char (toLower) import Data.Default (Default) import Control.Monad (mzero, guard, void) import Control.Monad.Trans (lift) import Control.Monad.Except (throwError) import Data.Maybe (catMaybes, isJust) import Data.List (intersperse) import qualified Data.Text as T import Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad(..), report) import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Walk (query) import Text.Pandoc.Readers.Roff -- TODO explicit imports import qualified Text.Pandoc.Parsing as P import qualified Data.Foldable as Foldable import Text.Pandoc.Shared (extractSpaces) data ManState = ManState { readerOptions :: ReaderOptions , metadata :: Meta , tableCellsPlain :: Bool } deriving Show instance Default ManState where def = ManState { readerOptions = def , metadata = nullMeta , tableCellsPlain = True } type ManParser m = P.ParsecT [RoffToken] ManState m -- | Read man (troff) from an input string and return a Pandoc document. readMan :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readMan opts s = do let Sources inps = toSources s tokenz <- mconcat <$> mapM (uncurry lexRoff) inps let state = def {readerOptions = opts} :: ManState eitherdoc <- readWithMTokens parseMan state (Foldable.toList . unRoffTokens $ tokenz) either (throwError . fromParsecError (Sources inps)) return eitherdoc readWithMTokens :: PandocMonad m => ParsecT [RoffToken] ManState m a -- ^ parser -> ManState -- ^ initial state -> [RoffToken] -- ^ input -> m (Either ParseError a) readWithMTokens parser state input = runParserT parser state "source" input parseMan :: PandocMonad m => ManParser m Pandoc parseMan = do bs <- many parseBlock <* eof meta <- metadata <$> getState let (Pandoc _ blocks) = doc $ mconcat bs return $ Pandoc meta blocks parseBlock :: PandocMonad m => ManParser m Blocks parseBlock = choice [ parseList , parseDefinitionList , parseHeader , parseTable , parseTitle , parseCodeBlock , parseBlockQuote , parseNewParagraph , parsePara , skipUnknownMacro ] parseTable :: PandocMonad m => ManParser m Blocks parseTable = do updateState $ \st -> st { tableCellsPlain = True } let isTbl Tbl{} = True isTbl _ = False Tbl _opts rows pos <- msatisfy isTbl case rows of ((as,_):_) -> try (do let as' = map (columnTypeToAlignment . columnType) as guard $ all isJust as' let alignments = catMaybes as' let (headerRow', bodyRows') = case rows of (h:x:bs) | isHrule x -> (h, bs) _ -> (([],[]), rows) headerRow <- mapM parseTableCell $ snd headerRow' bodyRows <- mapM (mapM parseTableCell . snd) bodyRows' isPlainTable <- tableCellsPlain <$> getState let widths = if isPlainTable then repeat ColWidthDefault else repeat $ ColWidth (1.0 / fromIntegral (length alignments)) return $ B.table B.emptyCaption (zip alignments widths) (TableHead nullAttr $ toHeaderRow headerRow) [TableBody nullAttr 0 [] $ map toRow bodyRows] (TableFoot nullAttr [])) <|> fallback pos [] -> fallback pos where parseTableCell ts = do st <- getState let ts' = Foldable.toList $ unRoffTokens ts let plaintcell = try $ do skipMany memptyLine plain . trimInlines <$> (parseInlines <* eof) let blockstcell = try $ do skipMany memptyLine mconcat <$> many parseBlock <* eof res <- if null ts' then return $ Right mempty else lift $ readWithMTokens plaintcell st ts' case res of Left _ -> do res' <- lift $ readWithMTokens blockstcell st ts' case res' of Left _ -> Prelude.fail "Could not parse table cell" Right x -> do updateState $ \s -> s{ tableCellsPlain = False } return x Right x -> return x isHrule :: TableRow -> Bool isHrule ([cellfmt], _) = columnType cellfmt `elem` ['_','-','='] isHrule (_, [RoffTokens ss]) = case Foldable.toList ss of [TextLine [RoffStr (T.unpack -> [c])]] -> c `elem` ['_','-','='] _ -> False isHrule _ = False fallback pos = do report $ SkippedContent "TABLE" pos return $ B.para (B.text "TABLE") columnTypeToAlignment :: Char -> Maybe Alignment columnTypeToAlignment c = case toLower c of 'a' -> Just AlignLeft 'c' -> Just AlignCenter 'l' -> Just AlignLeft 'n' -> Just AlignRight 'r' -> Just AlignRight _ -> Nothing toRow = Row nullAttr . map simpleCell toHeaderRow l = [toRow l | not (null l)] parseNewParagraph :: PandocMonad m => ManParser m Blocks parseNewParagraph = do mmacro "P" <|> mmacro "PP" <|> mmacro "LP" <|> memptyLine return mempty -- -- Parser: [RoffToken] -> Pandoc -- msatisfy :: Monad m => (RoffToken -> Bool) -> P.ParsecT [RoffToken] st m RoffToken msatisfy predic = P.tokenPrim show nextPos testTok where testTok t = if predic t then Just t else Nothing nextPos _pos _x (ControlLine _ _ pos':_) = pos' nextPos pos _x _xs = P.updatePosString (P.setSourceColumn (P.setSourceLine pos $ P.sourceLine pos + 1) 1) "" mtoken :: PandocMonad m => ManParser m RoffToken mtoken = msatisfy (const True) mline :: PandocMonad m => ManParser m RoffToken mline = msatisfy isTextLine where isTextLine (TextLine _) = True isTextLine _ = False memptyLine :: PandocMonad m => ManParser m RoffToken memptyLine = msatisfy isEmptyLine where isEmptyLine EmptyLine = True isEmptyLine _ = False mmacro :: PandocMonad m => T.Text -> ManParser m RoffToken mmacro mk = msatisfy isControlLine where isControlLine (ControlLine mk' _ _) | mk == mk' = True | otherwise = False isControlLine _ = False mmacroAny :: PandocMonad m => ManParser m RoffToken mmacroAny = msatisfy isControlLine where isControlLine ControlLine{} = True isControlLine _ = False -- -- RoffToken -> Block functions -- parseTitle :: PandocMonad m => ManParser m Blocks parseTitle = do (ControlLine _ args _) <- mmacro "TH" let adjustMeta = case args of (x:y:z:a:b:_) -> setMeta "title" (linePartsToInlines x) . setMeta "section" (linePartsToInlines y) . setMeta "date" (linePartsToInlines z) . setMeta "footer" (linePartsToInlines a) . setMeta "header" (linePartsToInlines b) [x,y,z,a] -> setMeta "title" (linePartsToInlines x) . setMeta "section" (linePartsToInlines y) . setMeta "date" (linePartsToInlines z) . setMeta "footer" (linePartsToInlines a) [x,y,z] -> setMeta "title" (linePartsToInlines x) . setMeta "section" (linePartsToInlines y) . setMeta "date" (linePartsToInlines z) [x,y] -> setMeta "title" (linePartsToInlines x) . setMeta "section" (linePartsToInlines y) [x] -> setMeta "title" (linePartsToInlines x) [] -> id updateState $ \st -> st{ metadata = adjustMeta $ metadata st } return mempty linePartsToInlines :: [LinePart] -> Inlines linePartsToInlines = go False where go :: Bool -> [LinePart] -> Inlines go _ [] = mempty go mono (MacroArg _:xs) = go mono xs -- shouldn't happen go mono (RoffStr s : RoffStr t : xs) = go mono (RoffStr (s <> t):xs) go mono (RoffStr s : xs) | mono = code s <> go mono xs | otherwise = text s <> go mono xs go mono (Font fs: xs) | litals > 0 && litals >= lbolds && litals >= lmonos = extractSpaces emph (go mono (Font fs{ fontItalic = False } : map (adjustFontSpec (\s -> s{ fontItalic = False })) itals)) <> go mono italsrest | lbolds > 0 && lbolds >= lmonos = extractSpaces strong (go mono (Font fs{ fontBold = False } : map (adjustFontSpec (\s -> s{ fontBold = False })) bolds)) <> go mono boldsrest | lmonos > 0 = go True (Font fs{ fontMonospace = False } : map (adjustFontSpec (\s -> s { fontMonospace = False })) monos) <> go mono monosrest | otherwise = go mono xs where adjustFontSpec f (Font fspec) = Font (f fspec) adjustFontSpec _ x = x withFont f (Font fspec) = f fspec withFont _ _ = False litals = length itals lbolds = length bolds lmonos = length monos (itals, italsrest) = if fontItalic fs then break (withFont (not . fontItalic)) xs else ([], xs) (bolds, boldsrest) = if fontBold fs then break (withFont (not . fontBold)) xs else ([], xs) (monos, monosrest) = if fontMonospace fs then break (withFont (not . fontMonospace)) xs else ([], xs) parsePara :: PandocMonad m => ManParser m Blocks parsePara = para . trimInlines <$> parseInlines parseInlines :: PandocMonad m => ManParser m Inlines parseInlines = mconcat . intersperse B.space <$> many1 parseInline parseInline :: PandocMonad m => ManParser m Inlines parseInline = try $ do tok <- mtoken case tok of TextLine lparts -> return $ linePartsToInlines lparts ControlLine mname args pos -> handleInlineMacro mname args pos _ -> mzero handleInlineMacro :: PandocMonad m => T.Text -> [Arg] -> SourcePos -> ManParser m Inlines handleInlineMacro mname args _pos = case mname of "UR" -> parseLink args "MT" -> parseEmailLink args "B" -> parseBold args "I" -> parseItalic args "br" -> return linebreak "BI" -> parseAlternatingFonts [strong, emph] args "IB" -> parseAlternatingFonts [emph, strong] args "IR" -> parseAlternatingFonts [emph, id] args "RI" -> parseAlternatingFonts [id, emph] args "BR" -> parseAlternatingFonts [strong, id] args "RB" -> parseAlternatingFonts [id, strong] args "SY" -> return $ extractSpaces strong $ mconcat $ intersperse B.space $ map linePartsToInlines args "YS" -> return mempty "OP" -> case args of (x:ys) -> return $ B.space <> str "[" <> B.space <> mconcat (extractSpaces strong (linePartsToInlines x) : map ((B.space <>) . linePartsToInlines) ys) <> B.space <> str "]" [] -> return mempty _ -> mzero parseBold :: PandocMonad m => [Arg] -> ManParser m Inlines parseBold [] = do TextLine lparts <- mline return $ extractSpaces strong $ linePartsToInlines lparts parseBold args = return $ extractSpaces strong $ mconcat $ intersperse B.space $ map linePartsToInlines args parseItalic :: PandocMonad m => [Arg] -> ManParser m Inlines parseItalic [] = do TextLine lparts <- mline return $ extractSpaces emph $ linePartsToInlines lparts parseItalic args = return $ extractSpaces emph $ mconcat $ intersperse B.space $ map linePartsToInlines args parseAlternatingFonts :: [Inlines -> Inlines] -> [Arg] -> ManParser m Inlines parseAlternatingFonts constructors args = return $ mconcat $ zipWith (\f arg -> f (linePartsToInlines arg)) (cycle constructors) args lineInl :: PandocMonad m => ManParser m Inlines lineInl = do (TextLine fragments) <- mline return $ linePartsToInlines fragments bareIP :: PandocMonad m => ManParser m RoffToken bareIP = msatisfy isBareIP where isBareIP (ControlLine "IP" [] _) = True isBareIP _ = False endmacro :: PandocMonad m => T.Text -> ManParser m () endmacro name = void (mmacro name) <|> lookAhead (void newBlockMacro) <|> lookAhead eof where newBlockMacro = msatisfy isNewBlockMacro isNewBlockMacro (ControlLine "SH" _ _) = True isNewBlockMacro (ControlLine "SS" _ _) = True isNewBlockMacro _ = False parseCodeBlock :: PandocMonad m => ManParser m Blocks parseCodeBlock = try $ do optional bareIP optional (mmacro "in") -- some people indent their code toks <- (mmacro "nf" *> manyTill codeline (endmacro "fi")) <|> (mmacro "EX" *> manyTill codeline (endmacro "EE")) optional (mmacro "in") return $ codeBlock (T.intercalate "\n" $ catMaybes toks) where codeline = do tok <- mtoken case tok of ControlLine "PP" _ _ -> return $ Just "" -- .PP sometimes used for blank line ControlLine mname args pos -> (Just . query getText <$> handleInlineMacro mname args pos) <|> do report $ SkippedContent ("." <> mname) pos return Nothing Tbl _ _ pos -> do report $ SkippedContent "TABLE" pos return $ Just "TABLE" EmptyLine -> return $ Just "" TextLine ss | not (null ss) , all isFontToken ss -> return Nothing | otherwise -> return $ Just $ linePartsToText ss isFontToken Font{} = True isFontToken _ = False getText :: Inline -> T.Text getText (Str s) = s getText Space = " " getText (Code _ s) = s getText SoftBreak = "\n" getText LineBreak = "\n" getText _ = "" parseHeader :: PandocMonad m => ManParser m Blocks parseHeader = do ControlLine name args _ <- mmacro "SH" <|> mmacro "SS" contents <- if null args then option mempty lineInl else return $ mconcat $ intersperse B.space $ map linePartsToInlines args let lvl = if name == "SH" then 1 else 2 return $ header lvl contents parseBlockQuote :: PandocMonad m => ManParser m Blocks parseBlockQuote = blockQuote <$> ( (mmacro "RS" *> (mconcat <$> manyTill parseBlock (endmacro "RE"))) <|> parseIndentedParagraphs ) where parseIndentedParagraphs = try $ do bareIP first <- parsePara <|> parseCodeBlock rest <- many $ try (memptyLine *> (parsePara <|> parseCodeBlock)) pure (first <> mconcat rest) data ListType = Ordered ListAttributes | Bullet | Definition T.Text listTypeMatches :: Maybe ListType -> ListType -> Bool listTypeMatches Nothing _ = True listTypeMatches (Just Bullet) Bullet = True listTypeMatches (Just (Ordered (_,x,y))) (Ordered (_,x',y')) = x == x' && y == y' listTypeMatches (Just (Definition _)) (Definition _) = True listTypeMatches (Just _) _ = False listItem :: PandocMonad m => Maybe ListType -> ManParser m (ListType, Blocks) listItem mbListType = try $ do (ControlLine _ args _) <- mmacro "IP" case args of (arg1 : _) -> do let cs = linePartsToText arg1 let cs' = if not (T.any (== '.') cs || T.any (== ')') cs) then cs <> "." else cs let lt = case P.runParser anyOrderedListMarker defaultParserState "list marker" cs' of Right (start, listtype, listdelim) | cs == cs' -> Ordered (start, listtype, listdelim) | otherwise -> Ordered (start, listtype, DefaultDelim) Left _ | cs == "\183" || cs == "-" || cs == "*" || cs == "+" -> Bullet | otherwise -> Definition cs guard $ listTypeMatches mbListType lt skipMany memptyLine inls <- option mempty parseInlines skipMany memptyLine continuations <- mconcat <$> many continuation return (lt, para inls <> continuations) [] -> mzero parseList :: PandocMonad m => ManParser m Blocks parseList = try $ do x@(lt, _) <- listItem Nothing xs <- many (listItem (Just lt)) let toDefItem (Definition t, bs) = (B.text t, [bs]) toDefItem _ = mempty return $ case lt of Bullet -> bulletList $ map snd (x:xs) Ordered lattr -> orderedListWith lattr $ map snd (x:xs) Definition _ -> definitionList $ map toDefItem (x:xs) continuation :: PandocMonad m => ManParser m Blocks continuation = (mmacro "RS" *> (mconcat <$> manyTill parseBlock (endmacro "RE"))) <|> try ((memptyLine <|> bareIP) *> (parsePara <|> parseCodeBlock)) definitionListItem :: PandocMonad m => ManParser m (Inlines, [Blocks]) definitionListItem = try $ do mmacro "TP" -- args specify indent level, can ignore skipMany memptyLine term <- parseInline skipMany memptyLine moreterms <- many $ try $ do mmacro "TQ" parseInline skipMany memptyLine firstBlock <- parseBlock otherBlocks <- mconcat <$> many continuation return ( mconcat (intersperse B.linebreak (term:moreterms)) , [firstBlock <> otherBlocks]) parseDefinitionList :: PandocMonad m => ManParser m Blocks parseDefinitionList = definitionList <$> many1 definitionListItem parseLink :: PandocMonad m => [Arg] -> ManParser m Inlines parseLink args = do contents <- mconcat <$> many lineInl ControlLine _ endargs _ <- mmacro "UE" let url = case args of [] -> "" (x:_) -> linePartsToText x return $ link url "" contents <> case endargs of [] -> mempty (x:_) -> linePartsToInlines x parseEmailLink :: PandocMonad m => [Arg] -> ManParser m Inlines parseEmailLink args = do contents <- mconcat <$> many lineInl ControlLine _ endargs _ <- mmacro "ME" let url = case args of [] -> "" (x:_) -> "mailto:" <> linePartsToText x return $ link url "" contents <> case endargs of [] -> mempty (x:_) -> linePartsToInlines x skipUnknownMacro :: PandocMonad m => ManParser m Blocks skipUnknownMacro = do tok <- mmacroAny case tok of ControlLine mkind _ pos -> do report $ SkippedContent ("." <> mkind) pos return mempty _ -> Prelude.fail "the impossible happened" ================================================ FILE: src/Text/Pandoc/Readers/Markdown.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.Markdown Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of markdown-formatted plain text to 'Pandoc' document. -} module Text.Pandoc.Readers.Markdown ( readMarkdown, yamlToMeta, yamlToRefs ) where import Control.Monad import Control.Monad.Except (throwError) import qualified Data.Bifunctor as Bifunctor import Data.Char (isAlphaNum, isPunctuation, isSpace) 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 import qualified Data.Attoparsec.Text as A import Data.Text (Text) import qualified Data.Text as T import qualified Data.ByteString as BS import System.FilePath (addExtension, takeExtension, takeDirectory) import qualified System.FilePath.Windows as Windows import qualified System.FilePath.Posix as Posix import Text.DocLayout (realLength) import Text.HTML.TagSoup hiding (Row) import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad (..), report) import Text.Pandoc.Definition as Pandoc import Text.Pandoc.Emoji (emojiToInline) import Text.Pandoc.Error import Safe.Foldable (maximumBounded) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Walk (walk) import Text.Pandoc.Parsing hiding (tableCaption) import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag, isInlineTag, isTextTag) import Text.Pandoc.Readers.LaTeX (applyMacros, rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared import Text.Pandoc.URI (escapeURI, isURI, pBase64DataURI) import Text.Pandoc.XML (fromEntities) import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs, yamlMetaBlock) -- import Debug.Trace (traceShowId) type MarkdownParser m = ParsecT Sources ParserState m type F = Future ParserState -- | Read markdown from an input string and return a Pandoc document. readMarkdown :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options -> a -- ^ Input -> m Pandoc readMarkdown opts s = do parsed <- readWithM parseMarkdown def{ stateOptions = opts } (ensureFinalNewlines 3 (toSources s)) case parsed of Right result -> return result Left e -> throwError e -- | Read a YAML string and convert it to pandoc metadata. -- String scalars in the YAML are parsed as Markdown. yamlToMeta :: PandocMonad m => ReaderOptions -> Maybe FilePath -> BS.ByteString -> m Meta yamlToMeta opts mbfp bstr = do let parser = do oldPos <- getPosition setPosition $ initialPos (fromMaybe "" mbfp) meta <- yamlBsToMeta (fmap B.toMetaValue <$> parseBlocks) bstr checkNotes setPosition oldPos st <- getState let result = runF meta st reportLogMessages return result parsed <- readWithM parser def{ stateOptions = opts } ("" :: Text) case parsed of Right result -> return result Left e -> throwError e -- | Read a YAML string and extract references from the -- 'references' field, filter using an id predicate and -- parsing fields as Markdown. yamlToRefs :: PandocMonad m => (Text -> Bool) -> ReaderOptions -> Maybe FilePath -> BS.ByteString -> m [MetaValue] yamlToRefs idpred opts mbfp bstr = do let parser = do case mbfp of Nothing -> return () Just fp -> setPosition $ initialPos fp refs <- yamlBsToRefs (fmap B.toMetaValue <$> parseBlocks) idpred bstr checkNotes st <- getState let result = runF refs st reportLogMessages return result parsed <- readWithM parser def{ stateOptions = opts } ("" :: Text) case parsed of Right result -> return result Left e -> throwError e -- -- Constants and data structure definitions -- isBulletListMarker :: Char -> Bool isBulletListMarker '*' = True isBulletListMarker '+' = True isBulletListMarker '-' = True isBulletListMarker _ = False isHruleChar :: Char -> Bool isHruleChar '*' = True isHruleChar '-' = True isHruleChar '_' = True isHruleChar _ = False setextHChars :: [Char] setextHChars = "=-" -- -- auxiliary functions -- -- | Succeeds when we're in list context. inList :: PandocMonad m => MarkdownParser m () inList = do ctx <- stateParserContext <$> getState guard (ctx == ListItemState) spnl :: PandocMonad m => ParsecT Sources st m () spnl = try $ do skipSpaces optional newline skipSpaces notFollowedBy (char '\n') spnl' :: PandocMonad m => ParsecT Sources st m Text spnl' = try $ do xs <- many spaceChar ys <- option "" $ try $ (:) <$> newline <*> (many spaceChar <* notFollowedBy (char '\n')) return $ T.pack $ xs ++ ys indentSpaces :: PandocMonad m => MarkdownParser m Text indentSpaces = try $ do tabStop <- getOption readerTabStop countChar tabStop (char ' ') <|> textStr "\t" "indentation" nonindentSpaces :: PandocMonad m => MarkdownParser m Text nonindentSpaces = do n <- skipNonindentSpaces return $ T.replicate n " " -- returns number of spaces parsed skipNonindentSpaces :: PandocMonad m => MarkdownParser m Int skipNonindentSpaces = do tabStop <- getOption readerTabStop gobbleAtMostSpaces (tabStop - 1) <* notFollowedBy spaceChar litChar :: PandocMonad m => MarkdownParser m Text litChar = T.singleton <$> escapedChar' <|> characterReference <|> T.singleton <$> noneOf "\n" <|> try (newline >> notFollowedBy blankline >> return " ") litBetween :: PandocMonad m => Char -> Char -> MarkdownParser m Text litBetween op cl = try $ do char op mconcat <$> (manyTill litChar (char cl)) litCharNoSpace :: PandocMonad m => MarkdownParser m Text litCharNoSpace = T.singleton <$> escapedChar'' <|> characterReference <|> T.singleton <$> noneOf "\n \r\t" where escapedChar'' = do c <- escapedChar' pure $ case c of ' ' -> '\160' _ -> c litBetweenNoSpace :: PandocMonad m => Char -> Char -> MarkdownParser m Text litBetweenNoSpace op cl = try $ do char op mconcat <$> (manyTill litCharNoSpace (char cl)) -- | Parse a sequence of elements between square brackets, -- including between balanced pairs of square brackets. -- Skip brackets in standard inline escapes, code, raw HTML or LaTeX. inBalancedBrackets :: PandocMonad m => MarkdownParser m (F a) -> MarkdownParser m (F a) inBalancedBrackets innerParser = try $ char '[' >> withRaw (go 1) >>= parseFromString innerParser . stripBracket . snd where stripBracket t = case T.unsnoc t of Just (t', ']') -> t' _ -> t go :: PandocMonad m => Int -> MarkdownParser m () go 0 = return () go openBrackets = (() <$ (escapedChar <|> code <|> math <|> endline <|> rawHtmlInline <|> rawLaTeXInline') >> go openBrackets) <|> (do char ']' Control.Monad.when (openBrackets > 1) $ go (openBrackets - 1)) <|> (char '[' >> go (openBrackets + 1)) <|> (satisfy (/= '\n') >> go openBrackets) -- -- document structure -- rawTitleBlockLine :: PandocMonad m => MarkdownParser m Text rawTitleBlockLine = do char '%' skipSpaces first <- anyLine rest <- many $ try $ do spaceChar notFollowedBy blankline skipSpaces anyLine return $ trim $ T.unlines (first:rest) titleLine :: PandocMonad m => MarkdownParser m (F Inlines) titleLine = try $ do raw <- rawTitleBlockLine res <- parseFromString' inlines raw return $ trimInlinesF res authorsLine :: PandocMonad m => MarkdownParser m (F [Inlines]) authorsLine = try $ do raw <- rawTitleBlockLine let sep = (char ';' <* spaces) <|> newline let pAuthors = sepEndBy (trimInlinesF . mconcat <$> many (try $ notFollowedBy sep >> inline)) sep sequence <$> parseFromString' pAuthors raw dateLine :: PandocMonad m => MarkdownParser m (F Inlines) dateLine = try $ do raw <- rawTitleBlockLine res <- parseFromString' inlines raw return $ trimInlinesF res titleBlock :: PandocMonad m => MarkdownParser m () titleBlock = pandocTitleBlock <|> mmdTitleBlock pandocTitleBlock :: PandocMonad m => MarkdownParser m () pandocTitleBlock = do guardEnabled Ext_pandoc_title_block lookAhead (char '%') try $ do title <- option mempty titleLine author <- option (return []) authorsLine date <- option mempty dateLine optional blanklines let meta' = do title' <- title author' <- author date' <- date return $ (if null title' then id else B.setMeta "title" title') . (if null author' then id else B.setMeta "author" author') . (if null date' then id else B.setMeta "date" date') $ nullMeta updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' } yamlMetaBlock' :: PandocMonad m => MarkdownParser m (F Blocks) yamlMetaBlock' = do guardEnabled Ext_yaml_metadata_block newMetaF <- yamlMetaBlock (fmap B.toMetaValue <$> parseBlocks) -- Since `<>` is left-biased, existing values are not touched: updateState $ \st -> st{ stateMeta' = stateMeta' st <> newMetaF } return mempty mmdTitleBlock :: PandocMonad m => MarkdownParser m () mmdTitleBlock = do guardEnabled Ext_mmd_title_block try $ do firstPair <- kvPair False restPairs <- many (kvPair True) let kvPairs = firstPair : restPairs blanklines updateState $ \st -> st{ stateMeta' = stateMeta' st <> return (Meta $ M.fromList kvPairs) } kvPair :: PandocMonad m => Bool -> MarkdownParser m (Text, MetaValue) kvPair allowEmpty = try $ do key <- many1TillChar (alphaNum <|> oneOf "_- ") (char ':') val <- trim <$> manyTillChar anyChar (try $ newline >> lookAhead (blankline <|> nonspaceChar)) guard $ allowEmpty || not (T.null val) let key' = T.concat $ T.words $ T.toLower key let val' = MetaInlines $ B.toList $ B.text val return (key',val') parseMarkdown :: PandocMonad m => MarkdownParser m Pandoc parseMarkdown = do optional titleBlock blocks <- parseBlocks st <- getState checkNotes let doc = runF (do Pandoc _ bs <- B.doc <$> blocks meta <- stateMeta' st return $ Pandoc meta bs) st reportLogMessages return doc -- check for notes with no corresponding note references checkNotes :: PandocMonad m => MarkdownParser m () checkNotes = do st <- getState let notesUsed = stateNoteRefs st let notesDefined = M.keys (stateNotes' st) mapM_ (\n -> unless (n `Set.member` notesUsed) $ case M.lookup n (stateNotes' st) of Just (pos, _) -> report (NoteDefinedButNotUsed n pos) Nothing -> throwError $ PandocShouldNeverHappenError "note not found") notesDefined referenceKey :: PandocMonad m => MarkdownParser m (F Blocks) referenceKey = try $ do pos <- getPosition skipNonindentSpaces notFollowedBy (void cite) (_,raw) <- reference char ':' skipSpaces >> optional newline >> skipSpaces >> notFollowedBy (char '[') let sourceURL = fmap T.unwords $ many $ try $ do skipMany spaceChar notFollowedBy' referenceTitle notFollowedBy' $ guardEnabled Ext_link_attributes >> attributes notFollowedBy' $ guardEnabled Ext_mmd_link_attributes >> try (spnl <* keyValAttr) notFollowedBy' (() <$ reference) mconcat <$> many1 (notFollowedBy space *> litChar) rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths) src <- (if rebase then rebasePath pos else id) <$> (try (litBetween '<' '>') <|> sourceURL) tit <- option "" referenceTitle attr <- option nullAttr $ try $ do guardEnabled Ext_link_attributes skipSpaces >> optional newline >> skipSpaces attributes addKvs <- option [] $ guardEnabled Ext_mmd_link_attributes >> many (try $ spnl >> keyValAttr) blanklines let attr' = extractIdClass $ L.foldl' (\x f -> f x) attr addKvs target = (escapeURI $ trimr src, tit) st <- getState let oldkeys = stateKeys st let key = toKey raw case M.lookup key oldkeys of Just (t,a) | not (t == target && a == attr') -> -- We don't warn on two duplicate keys if the targets are also -- the same. This can happen naturally with --reference-location=block -- or section. See #3701. logMessage $ DuplicateLinkReference raw pos _ -> return () updateState $ \s -> s { stateKeys = M.insert key (target, attr') oldkeys } return $ return mempty referenceTitle :: PandocMonad m => MarkdownParser m Text referenceTitle = try $ do skipSpaces >> optional newline >> skipSpaces quotedTitle '"' <|> quotedTitle '\'' <|> charsInBalanced '(' ')' litChar -- A link title in quotes quotedTitle :: PandocMonad m => Char -> MarkdownParser m Text quotedTitle c = try $ do char c notFollowedBy spaces let pEnder = try $ char c >> notFollowedBy (satisfy isAlphaNum) let regChunk = many1Char (noneOf ['\\','\n','&',c]) <|> litChar let nestedChunk = (\x -> (c `T.cons` x) `T.snoc` c) <$> quotedTitle c T.unwords . T.words . T.concat <$> manyTill (nestedChunk <|> regChunk) pEnder -- | PHP Markdown Extra style abbreviation key. Currently -- we just skip them, since Pandoc doesn't have an element for -- an abbreviation. abbrevKey :: PandocMonad m => MarkdownParser m (F Blocks) abbrevKey = do guardEnabled Ext_abbreviations try $ do char '*' reference char ':' skipMany (satisfy (/= '\n')) blanklines return $ return mempty noteMarker :: PandocMonad m => MarkdownParser m Text noteMarker = string "[^" >> many1TillChar (satisfy (`notElem` ['\r','\n','\t',' ','^','[',']'])) (char ']') rawLine :: PandocMonad m => MarkdownParser m Text rawLine = try $ do notFollowedBy blankline notFollowedByDivCloser notFollowedBy' $ try $ skipNonindentSpaces >> noteMarker optional indentSpaces anyLine rawLines :: PandocMonad m => MarkdownParser m Text rawLines = do first <- anyLine rest <- many rawLine return $ T.unlines (first:rest) noteBlock :: PandocMonad m => MarkdownParser m (F Blocks) noteBlock = do guardEnabled Ext_footnotes try $ do pos <- getPosition skipNonindentSpaces ref <- noteMarker char ':' optional blankline optional indentSpaces updateState $ \st -> st{ stateInNote = True } first <- rawLines rest <- many $ try $ blanklines >> indentSpaces >> rawLines let raw = T.unlines (first:rest) <> "\n" optional blanklines parsed <- parseFromString' parseBlocks raw oldnotes <- stateNotes' <$> getState case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos Nothing -> return () updateState $ \s -> s { stateNotes' = M.insert ref (pos, parsed) oldnotes, stateInNote = False } return mempty -- -- parsing blocks -- parseBlocks :: PandocMonad m => MarkdownParser m (F Blocks) parseBlocks = mconcat <$> manyTill block eof block :: PandocMonad m => MarkdownParser m (F Blocks) block = do res <- choice [ mempty <$ blanklines , codeBlockFenced , yamlMetaBlock' -- note: bulletList needs to be before header because of -- the possibility of empty list items: - , bulletList , divHtml , divFenced , header , lhsCodeBlock , htmlBlock , table , codeBlockIndented , rawTeXBlock , lineBlock , blockQuote , hrule , orderedList , definitionList , noteBlock , referenceKey , abbrevKey , para , plain ] "block" trace (T.take 60 $ tshow $ B.toList $ runF res defaultParserState) return res -- -- header blocks -- header :: PandocMonad m => MarkdownParser m (F Blocks) header = setextHeader <|> atxHeader "header" atxChar :: PandocMonad m => MarkdownParser m Char atxChar = do exts <- getOption readerExtensions return $ if extensionEnabled Ext_literate_haskell exts then '=' else '#' atxHeader :: PandocMonad m => MarkdownParser m (F Blocks) atxHeader = try $ do level <- fmap length (atxChar >>= many1 . char) notFollowedBy $ guardEnabled Ext_fancy_lists >> (char '.' <|> char ')') -- this would be a list guardDisabled Ext_space_in_atx_header <|> notFollowedBy nonspaceChar skipSpaces (text, raw) <- withRaw $ do oldAllowLineBreaks <- stateAllowLineBreaks <$> getState updateState $ \st -> st{ stateAllowLineBreaks = False } res <- trimInlinesF . mconcat <$> many (notFollowedBy atxClosing >> inline) updateState $ \st -> st{ stateAllowLineBreaks = oldAllowLineBreaks } return res attr <- atxClosing attr' <- registerHeader attr (runF text defaultParserState) guardDisabled Ext_implicit_header_references <|> registerImplicitHeader raw attr' return $ B.headerWith attr' level <$> text atxClosing :: PandocMonad m => MarkdownParser m Attr atxClosing = try $ do attr' <- option nullAttr (guardEnabled Ext_mmd_header_identifiers >> mmdHeaderIdentifier) skipMany . char =<< atxChar skipSpaces attr <- option attr' (guardEnabled Ext_header_attributes >> attributes) blanklines return attr setextHeaderEnd :: PandocMonad m => MarkdownParser m Attr setextHeaderEnd = try $ do attr <- option nullAttr $ (guardEnabled Ext_mmd_header_identifiers >> mmdHeaderIdentifier) <|> (guardEnabled Ext_header_attributes >> attributes) blanklines return attr mmdHeaderIdentifier :: PandocMonad m => MarkdownParser m Attr mmdHeaderIdentifier = do (_, raw) <- reference let raw' = trim $ stripFirstAndLast raw let ident = T.concat $ T.words $ T.toLower raw' let attr = (ident, [], []) guardDisabled Ext_implicit_header_references <|> registerImplicitHeader raw' attr skipSpaces return attr setextHeader :: PandocMonad m => MarkdownParser m (F Blocks) setextHeader = try $ do -- This lookahead prevents us from wasting time parsing Inlines -- unless necessary -- it gives a significant performance boost. lookAhead $ anyLine >> many1 (oneOf setextHChars) >> blankline skipSpaces (text, raw) <- withRaw $ do oldAllowLineBreaks <- stateAllowLineBreaks <$> getState updateState $ \st -> st{ stateAllowLineBreaks = False } res <- trimInlinesF . mconcat <$> many (notFollowedBy setextHeaderEnd >> inline) updateState $ \st -> st{ stateAllowLineBreaks = oldAllowLineBreaks } return res attr <- setextHeaderEnd underlineChar <- oneOf setextHChars many (char underlineChar) blanklines let level = fromMaybe 0 (elemIndex underlineChar setextHChars) + 1 attr' <- registerHeader attr (runF text defaultParserState) guardDisabled Ext_implicit_header_references <|> registerImplicitHeader raw attr' return $ B.headerWith attr' level <$> text registerImplicitHeader :: PandocMonad m => Text -> Attr -> MarkdownParser m () registerImplicitHeader raw attr@(ident, _, _) | T.null raw = return () | otherwise = do let key = toKey $ "[" <> raw <> "]" updateState $ \s -> -- don't override existing headers s { stateHeaderKeys = M.insertWith (\_new old -> old) key (("#" <> ident,""), attr) (stateHeaderKeys s) } -- -- hrule block -- hrule :: PandocMonad m => ParsecT Sources st m (F Blocks) hrule = try $ do skipSpaces start <- satisfy isHruleChar count 2 (skipSpaces >> char start) skipMany (spaceChar <|> char start) newline optional blanklines return $ return B.horizontalRule -- -- code blocks -- indentedLine :: PandocMonad m => MarkdownParser m Text indentedLine = indentSpaces >> anyLineNewline blockDelimiter :: PandocMonad m => (Char -> Bool) -> Maybe Int -> ParsecT Sources ParserState m Int blockDelimiter f len = try $ do skipNonindentSpaces c <- lookAhead (satisfy f) case len of Just l -> count l (char c) >> many (char c) >> return l Nothing -> fmap ((+ 3) . length) (count 3 (char c) >> many (char c)) attributes :: PandocMonad m => MarkdownParser m Attr attributes = try $ do char '{' spnl attrs <- many (attribute <* spnl) char '}' return $ L.foldl' (\x f -> f x) nullAttr attrs attribute :: PandocMonad m => MarkdownParser m (Attr -> Attr) attribute = identifierAttr <|> classAttr <|> keyValAttr <|> specialAttr identifier :: PandocMonad m => MarkdownParser m Text identifier = do first <- letter rest <- many $ alphaNum <|> oneOf "-_:." return $ T.pack (first:rest) identifierAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) identifierAttr = try $ do char '#' result <- T.pack <$> many1 (alphaNum <|> oneOf "-_:.") -- see #7920 return $ \(_,cs,kvs) -> (result,cs,kvs) classAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) classAttr = try $ do char '.' result <- identifier return $ \(id',cs,kvs) -> (id',cs ++ [result],kvs) keyValAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) keyValAttr = try $ do key <- identifier char '=' val <- mconcat <$> enclosed (char '"') (char '"') litChar <|> mconcat <$> enclosed (char '\'') (char '\'') litChar <|> ("" <$ try (string "\"\"")) <|> ("" <$ try (string "''")) <|> manyChar (escapedChar' <|> noneOf " \t\n\r}") return $ \(id',cs,kvs) -> case key of "id" -> (val,cs,kvs) "class" -> (id',cs ++ T.words val,kvs) _ -> (id',cs,kvs ++ [(key,val)]) specialAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) specialAttr = do char '-' return $ \(id',cs,kvs) -> (id',cs ++ ["unnumbered"],kvs) rawAttribute :: PandocMonad m => MarkdownParser m Text rawAttribute = do char '{' skipMany spaceChar char '=' format <- many1Char $ satisfy (\c -> isAlphaNum c || c `elem` ['-', '_']) skipMany spaceChar char '}' return format codeBlockFenced :: PandocMonad m => MarkdownParser m (F Blocks) codeBlockFenced = try $ do indentchars <- nonindentSpaces let indentLevel = T.length indentchars c <- (guardEnabled Ext_fenced_code_blocks >> lookAhead (char '~')) <|> (guardEnabled Ext_backtick_code_blocks >> lookAhead (char '`')) size <- blockDelimiter (== c) Nothing skipMany spaceChar rawattr <- (Left <$> (guardEnabled Ext_raw_attribute >> try rawAttribute)) <|> (Right <$> do let pLangId = many1Char . satisfy $ \x -> x `notElem` ['`', '{', '}'] && not (isSpace x) mbLanguageId <- optionMaybe (toLanguageId <$> pLangId) skipMany spaceChar mbAttr <- optionMaybe (guardEnabled Ext_fenced_code_attributes *> try attributes) return $ case mbAttr of Nothing -> ("", maybeToList mbLanguageId, []) Just (elementId, classes, attrs) -> (elementId, (maybe id (:) mbLanguageId) classes, attrs)) blankline contents <- T.intercalate "\n" <$> manyTill (gobbleAtMostSpaces indentLevel >> anyLine) (try $ do blockDelimiter (== c) (Just size) blanklines) return $ return $ case rawattr of Left syn -> B.rawBlock syn contents Right attr -> B.codeBlockWith attr contents -- correctly handle github language identifiers toLanguageId :: Text -> Text toLanguageId = T.toLower . go where go "c++" = "cpp" go "objective-c" = "objectivec" go x = x codeBlockIndented :: PandocMonad m => MarkdownParser m (F Blocks) codeBlockIndented = do contents <- many1 (indentedLine <|> try (do b <- blanklines l <- indentedLine return $ b <> l)) optional blanklines classes <- getOption readerIndentedCodeClasses return $ return $ B.codeBlockWith ("", classes, []) $ stripTrailingNewlines $ T.concat contents lhsCodeBlock :: PandocMonad m => MarkdownParser m (F Blocks) lhsCodeBlock = do guardEnabled Ext_literate_haskell (return . B.codeBlockWith ("",["haskell","literate"],[]) <$> (lhsCodeBlockBird <|> lhsCodeBlockLaTeX)) <|> (return . B.codeBlockWith ("",["haskell"],[]) <$> lhsCodeBlockInverseBird) lhsCodeBlockLaTeX :: PandocMonad m => MarkdownParser m Text lhsCodeBlockLaTeX = try $ do string "\\begin{code}" manyTill spaceChar newline contents <- many1TillChar anyChar (try $ string "\\end{code}") blanklines return $ stripTrailingNewlines contents lhsCodeBlockBird :: PandocMonad m => MarkdownParser m Text lhsCodeBlockBird = lhsCodeBlockBirdWith '>' lhsCodeBlockInverseBird :: PandocMonad m => MarkdownParser m Text lhsCodeBlockInverseBird = lhsCodeBlockBirdWith '<' lhsCodeBlockBirdWith :: PandocMonad m => Char -> MarkdownParser m Text lhsCodeBlockBirdWith c = try $ do pos <- getPosition when (sourceColumn pos /= 1) $ Prelude.fail "Not in first column" lns <- many1 $ birdTrackLine c -- if (as is normal) there is always a space after >, drop it let lns' = if all (\ln -> T.null ln || T.take 1 ln == " ") lns then map (T.drop 1) lns else lns blanklines return $ T.intercalate "\n" lns' birdTrackLine :: PandocMonad m => Char -> ParsecT Sources st m Text birdTrackLine c = try $ do char c -- allow html tags on left margin: when (c == '<') $ notFollowedBy letter anyLine -- -- block quotes -- emailBlockQuoteStart :: PandocMonad m => MarkdownParser m Char emailBlockQuoteStart = try $ skipNonindentSpaces >> char '>' <* optional (char ' ') emailBlockQuote :: PandocMonad m => MarkdownParser m [Text] emailBlockQuote = try $ do emailBlockQuoteStart let emailLine = manyChar $ nonEndline <|> try (endline >> notFollowedBy emailBlockQuoteStart >> return '\n') let emailSep = try (newline >> emailBlockQuoteStart) first <- emailLine rest <- many $ try $ emailSep >> emailLine let raw = first:rest newline <|> (eof >> return '\n') optional blanklines return raw 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 $ 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 -- bulletListStart :: PandocMonad m => MarkdownParser m () bulletListStart = try $ do optional newline -- if preceded by a Plain block in a list context skipNonindentSpaces notFollowedBy' (() <$ hrule) -- because hrules start out just like lists satisfy isBulletListMarker gobbleSpaces 1 <|> () <$ lookAhead newline try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar) <|> return () orderedListStart :: PandocMonad m => Maybe (ListNumberStyle, ListNumberDelim) -> MarkdownParser m (Int, ListNumberStyle, ListNumberDelim) orderedListStart mbstydelim = try $ do optional newline -- if preceded by a Plain block in a list context skipNonindentSpaces notFollowedBy $ string "p." >> spaceChar >> digit -- page number (do guardDisabled Ext_fancy_lists start <- many1Char digit >>= safeRead char '.' gobbleSpaces 1 <|> () <$ lookAhead newline optional $ try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar) return (start, DefaultStyle, DefaultDelim)) <|> (do (num, style, delim) <- maybe anyOrderedListMarker (\(sty,delim) -> (\start -> (start,sty,delim)) <$> orderedListMarker sty delim) mbstydelim gobbleSpaces 1 <|> () <$ lookAhead newline -- if it could be an abbreviated first name, -- insist on more than one space when (delim == Period && (style == UpperAlpha || (style == UpperRoman && num `elem` [1, 5, 10, 50, 100, 500, 1000]))) $ () <$ lookAhead (newline <|> spaceChar) optional $ try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar) return (num, style, delim)) listStart :: PandocMonad m => MarkdownParser m () listStart = bulletListStart <|> Control.Monad.void (orderedListStart Nothing) <|> defListStart listLine :: PandocMonad m => Int -> MarkdownParser m Text listLine continuationIndent = try $ do notFollowedBy' (do gobbleSpaces continuationIndent skipMany spaceChar listStart) notFollowedByHtmlCloser notFollowedByDivCloser optional (() <$ gobbleSpaces continuationIndent) anyLine -- parse raw text for one list item, excluding start marker and continuations rawListItem :: PandocMonad m => Bool -- four space rule -> MarkdownParser m a -> MarkdownParser m (Text, Int) rawListItem fourSpaceRule start = try $ do pos1 <- getPosition start pos2 <- getPosition let continuationIndent = if fourSpaceRule then 4 else sourceColumn pos2 - sourceColumn pos1 first <- anyLine rest <- many (do notFollowedBy listStart notFollowedBy (() <$ codeBlockFenced) notFollowedBy blankline listLine continuationIndent) blanks <- manyChar blankline let result = T.unlines (first:rest) <> blanks return (result, continuationIndent) -- continuation of a list item - indented and separated by blankline -- or (in compact lists) endline. -- note: nested lists are parsed as continuations listContinuation :: PandocMonad m => Int -> MarkdownParser m Text listContinuation continuationIndent = try $ do x <- try $ do notFollowedBy blankline notFollowedByHtmlCloser notFollowedByDivCloser gobbleSpaces continuationIndent anyLineNewline xs <- many $ try $ do notFollowedBy blankline notFollowedByHtmlCloser notFollowedByDivCloser gobbleSpaces continuationIndent <|> notFollowedBy' listStart anyLineNewline blanks <- manyChar blankline return $ T.concat (x:xs) <> blanks -- Variant of blanklines that doesn't require blank lines -- before a fence or eof. blanklines' :: PandocMonad m => MarkdownParser m Text blanklines' = blanklines <|> try checkDivCloser where checkDivCloser = do guardEnabled Ext_fenced_divs divLevel <- stateFencedDivLevel <$> getState guard (divLevel >= 1) lookAhead divFenceEnd return "" notFollowedByDivCloser :: PandocMonad m => MarkdownParser m () notFollowedByDivCloser = guardDisabled Ext_fenced_divs <|> do divLevel <- stateFencedDivLevel <$> getState guard (divLevel < 1) <|> notFollowedBy divFenceEnd notFollowedByHtmlCloser :: PandocMonad m => MarkdownParser m () notFollowedByHtmlCloser = do inHtmlBlock <- stateInHtmlBlock <$> getState case inHtmlBlock of Just t -> notFollowedBy' $ htmlTag (~== TagClose t) Nothing -> return () listItem :: PandocMonad m => Bool -- four-space rule -> MarkdownParser m a -> MarkdownParser m (F Blocks) listItem fourSpaceRule start = try $ do -- parsing with ListItemState forces markers at beginning of lines to -- count as list item markers, even if not separated by blank space. -- see definition of "endline" state <- getState let oldContext = stateParserContext state setState $ state {stateParserContext = ListItemState} (first, continuationIndent) <- rawListItem fourSpaceRule start continuations <- many (listContinuation continuationIndent) -- parse the extracted block, which may contain various block elements: let raw = T.concat (first:continuations) contents <- parseFromString' parseBlocks raw updateState (\st -> st {stateParserContext = oldContext}) exts <- getOption readerExtensions return $ B.fromList . taskListItemFromAscii exts . B.toList <$> contents orderedList :: PandocMonad m => MarkdownParser m (F Blocks) orderedList = try $ do (start, style, delim) <- lookAhead (orderedListStart Nothing) unless (style `elem` [DefaultStyle, Decimal, Example] && delim `elem` [DefaultDelim, Period]) $ guardEnabled Ext_fancy_lists when (style == Example) $ guardEnabled Ext_example_lists fourSpaceRule <- (True <$ guardEnabled Ext_four_space_rule) <|> return (style == Example) items <- fmap sequence $ many1 $ listItem fourSpaceRule (orderedListStart (Just (style, delim))) start' <- if style == Example then return start else (start <$ guardEnabled Ext_startnum) <|> return 1 return $ B.orderedListWith (start', style, delim) <$> fmap compactify items bulletList :: PandocMonad m => MarkdownParser m (F Blocks) bulletList = do fourSpaceRule <- (True <$ guardEnabled Ext_four_space_rule) <|> return False items <- fmap sequence $ many1 $ listItem fourSpaceRule bulletListStart return $ B.bulletList <$> fmap compactify items -- definition lists defListStart :: PandocMonad m => MarkdownParser m () defListStart = do nonindentSpaces char ':' <|> char '~' gobbleSpaces 1 <|> () <$ lookAhead newline try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar) <|> return () definitionListItem :: PandocMonad m => MarkdownParser m (F (Inlines, [Blocks])) definitionListItem = try $ do rawLine' <- anyLine term <- parseFromString' (trimInlinesF <$> inlines) rawLine' isTight <- (False <$ blanklines) <|> pure True fourSpaceRule <- (True <$ guardEnabled Ext_four_space_rule) <|> pure False contents <- many1 $ listItem fourSpaceRule defListStart optional blanklines return $ liftM2 (,) term ((if isTight then fmap (fmap (fmap paraToPlain)) else id) (sequence contents)) paraToPlain :: Block -> Block paraToPlain (Para ils) = Plain ils paraToPlain x = x definitionList :: PandocMonad m => MarkdownParser m (F Blocks) definitionList = try $ do guardEnabled Ext_definition_lists lookAhead (anyLine >> optional (blankline >> notFollowedBy (Control.Monad.void table)) >> -- don't capture table caption as def list! defListStart) items <- fmap sequence $ many1 definitionListItem return $ B.definitionList <$> items -- -- paragraph block -- para :: PandocMonad m => MarkdownParser m (F Blocks) para = try $ do exts <- getOption readerExtensions result <- trimInlinesF <$> inlines1 let figureOr constr inlns = case B.toList inlns of [Image attr figCaption (src, tit)] | extensionEnabled Ext_implicit_figures exts , not (null figCaption) -> do implicitFigure attr (B.fromList figCaption) src tit _ -> constr inlns option (figureOr B.plain <$> result) $ try $ do newline (mempty <$ blanklines) <|> (guardDisabled Ext_blank_before_blockquote <* lookAhead blockQuote) <|> (guardEnabled Ext_backtick_code_blocks <* lookAhead codeBlockFenced) <|> (guardDisabled Ext_blank_before_header <* lookAhead header) <|> (guardEnabled Ext_lists_without_preceding_blankline -- Avoid creating a paragraph in a nested list. <* notFollowedBy' (inList <* listStart)) <|> do guardEnabled Ext_native_divs inHtmlBlock <- stateInHtmlBlock <$> getState case inHtmlBlock of Just "div" -> () <$ lookAhead (htmlTag (~== TagClose ("div" :: Text))) _ -> mzero <|> do guardEnabled Ext_fenced_divs divLevel <- stateFencedDivLevel <$> getState if divLevel > 0 then lookAhead divFenceEnd else mzero return $ figureOr B.para <$> result plain :: PandocMonad m => MarkdownParser m (F Blocks) plain = fmap B.plain . trimInlinesF <$> inlines1 implicitFigure :: Attr -> Inlines -> Text -> Text -> Blocks implicitFigure (ident, classes, attribs) capt url title = let alt = case "alt" `lookup` attribs of Just alt' -> B.text alt' _ -> capt attribs' = filter ((/= "latex-placement") . fst) (filter ((/= "alt") . fst) attribs) figattribs = case lookup "latex-placement" attribs of Just p -> [("latex-placement", p)] _ -> mempty figattr = (ident, mempty, figattribs) caption = B.simpleCaption $ B.plain capt figbody = B.plain $ B.imageWith ("", classes, attribs') url title alt in B.figureWith figattr caption figbody -- -- raw html -- htmlElement :: PandocMonad m => MarkdownParser m Text htmlElement = rawVerbatimBlock <|> strictHtmlBlock <|> fmap snd (htmlTag isBlockTag) htmlBlock :: PandocMonad m => MarkdownParser m (F Blocks) htmlBlock = do guardEnabled Ext_raw_html try (do (TagOpen _ attrs) <- lookAhead $ fst <$> htmlTag isBlockTag return . B.rawBlock "html" <$> rawVerbatimBlock <|> (do guardEnabled Ext_markdown_attribute oldMarkdownAttribute <- stateMarkdownAttribute <$> getState markdownAttribute <- case lookup "markdown" attrs of Just "0" -> False <$ updateState (\st -> st{ stateMarkdownAttribute = False }) Just _ -> True <$ updateState (\st -> st{ stateMarkdownAttribute = True }) Nothing -> return oldMarkdownAttribute res <- if markdownAttribute then rawHtmlBlocks else htmlBlock' updateState $ \st -> st{ stateMarkdownAttribute = oldMarkdownAttribute } return res) <|> (guardEnabled Ext_markdown_in_html_blocks >> rawHtmlBlocks)) <|> htmlBlock' htmlBlock' :: PandocMonad m => MarkdownParser m (F Blocks) htmlBlock' = try $ do first <- htmlElement skipMany spaceChar optional blanklines return $ if T.null first then mempty else return $ B.rawBlock "html" first strictHtmlBlock :: PandocMonad m => MarkdownParser m Text strictHtmlBlock = htmlInBalanced (not . isInlineTag) rawVerbatimBlock :: PandocMonad m => MarkdownParser m Text rawVerbatimBlock = htmlInBalanced isVerbTag where isVerbTag (TagOpen "pre" _) = True isVerbTag (TagOpen "style" _) = True isVerbTag (TagOpen "script" _) = True isVerbTag (TagOpen "textarea" _) = True isVerbTag _ = False rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks) rawTeXBlock = do guardEnabled Ext_raw_tex result <- (B.rawBlock "tex" . trim . T.concat <$> many1 ((<>) <$> rawConTeXtEnvironment <*> spnl')) <|> (B.rawBlock "tex" . trim . T.concat <$> many1 ((<>) <$> rawLaTeXBlock <*> spnl')) return $ case B.toList result of [RawBlock _ cs] | T.all (`elem` [' ','\t','\n']) cs -> return mempty -- don't create a raw block for suppressed macro defs _ -> return result rawHtmlBlocks :: PandocMonad m => MarkdownParser m (F Blocks) rawHtmlBlocks = do (TagOpen tagtype _, raw) <- htmlTag isBlockTag let selfClosing = "/>" `T.isSuffixOf` raw -- we don't want '
    text' to be a code block: skipMany spaceChar tabStop <- getOption readerTabStop indentlevel <- option 0 $ do blankline sum <$> many ( (1 <$ char ' ') <|> (tabStop <$ char '\t') ) -- try to find closing tag -- we set stateInHtmlBlock so that closing tags that can be either block or -- inline will not be parsed as inline tags oldInHtmlBlock <- stateInHtmlBlock <$> getState updateState $ \st -> st{ stateInHtmlBlock = Just tagtype } let closer = htmlTag (~== TagClose tagtype) let block' = try $ do gobbleAtMostSpaces indentlevel notFollowedBy' closer block contents <- if selfClosing then return mempty else mconcat <$> many block' result <- try (do gobbleAtMostSpaces indentlevel (_, rawcloser) <- closer return (return (B.rawBlock "html" $ stripMarkdownAttribute raw) <> contents <> return (B.rawBlock "html" rawcloser))) <|> return (return (B.rawBlock "html" raw) <> contents) updateState $ \st -> st{ stateInHtmlBlock = oldInHtmlBlock } return result -- remove markdown="1" attribute stripMarkdownAttribute :: Text -> Text stripMarkdownAttribute s = renderTags' $ map filterAttrib $ parseTags s where filterAttrib (TagOpen t as) = TagOpen t [(k,v) | (k,v) <- as, k /= "markdown"] filterAttrib x = x -- -- line block -- lineBlock :: PandocMonad m => MarkdownParser m (F Blocks) lineBlock = do guardEnabled Ext_line_blocks try $ do lines' <- lineBlockLines >>= mapM (parseFromString' (trimInlinesF <$> inlines)) return $ B.lineBlock <$> sequence lines' -- -- Tables -- -- Parse a dashed line with optional trailing spaces; return its length -- and the length including trailing space. dashedLine :: PandocMonad m => Char -> ParsecT Sources st m (Int, Int) dashedLine ch = do dashes <- many1 (char ch) sp <- many spaceChar let lengthDashes = length dashes lengthSp = length sp return (lengthDashes, lengthDashes + lengthSp) -- Parse a table header with dashed lines of '-' preceded by -- one (or zero) line of text. simpleTableHeader :: PandocMonad m => Bool -- ^ Headerless table -> MarkdownParser m (F [[Blocks]], [Alignment], [Int]) simpleTableHeader headless = try $ do rawContent <- if headless then return "" else anyLine initSp <- nonindentSpaces dashes <- many1 (dashedLine '-') newline let (lengths, lines') = unzip dashes let indices = scanl (+) (T.length initSp) lines' -- If no header, calculate alignment on basis of first row of text rawHeads <- fmap (drop 1 . splitTextByIndices (init indices)) $ if headless then lookAhead anyLine else return rawContent let aligns = zipWith alignType (map (: []) rawHeads) lengths let rawHeads' = if headless then [] else rawHeads heads <- fmap sequence $ mapM (parseFromString' (mconcat <$> many plain).trim) rawHeads' return (fmap (:[]) heads, aligns, indices) -- Returns an alignment type for a table, based on a list of strings -- (the rows of the column header) and a number (the length of the -- dashed line under the rows. alignType :: [Text] -> Int -> Alignment alignType [] _ = AlignDefault alignType strLst len = let nonempties = filter (not . T.null) $ map trimr strLst (leftSpace, rightSpace) = case sortOn T.length nonempties of (x:_) -> (T.head x `elem` [' ', '\t'], realLength x < len) [] -> (False, False) in case (leftSpace, rightSpace) of (True, False) -> AlignRight (False, True) -> AlignLeft (True, True) -> AlignCenter (False, False) -> AlignDefault -- Parse a table footer - dashed lines followed by blank line. tableFooter :: PandocMonad m => MarkdownParser m Text tableFooter = try $ skipNonindentSpaces >> many1 (dashedLine '-') >> blanklines' -- Parse a table separator - dashed line. tableSep :: PandocMonad m => MarkdownParser m Char tableSep = try $ skipNonindentSpaces >> many1 (dashedLine '-') >> char '\n' -- Parse a raw line and split it into chunks by indices. rawTableLine :: PandocMonad m => [Int] -> MarkdownParser m [Text] rawTableLine indices = do notFollowedBy' (blanklines' <|> tableFooter) line <- anyLine return $ map trim $ drop 1 $ splitTextByIndices (init indices) line -- Parse a table line and return a list of lists of blocks (columns). tableLine :: PandocMonad m => [Int] -> MarkdownParser m (F [Blocks]) tableLine indices = rawTableLine indices >>= fmap sequence . mapM (parseFromString' (mconcat <$> many plain)) -- Parse a multiline table row and return a list of blocks (columns). multilineRow :: PandocMonad m => [Int] -> MarkdownParser m (F [Blocks]) multilineRow indices = do colLines <- many1 (rawTableLine indices) let cols = map T.unlines $ transpose colLines fmap sequence $ mapM (parseFromString' (mconcat <$> many plain)) cols -- Parses a table caption: inlines beginning with 'Table:' -- and followed by blank lines. tableCaption :: PandocMonad m => MarkdownParser m (F Inlines, Attr) tableCaption = do guardEnabled Ext_table_captions try $ do skipNonindentSpaces (string ":" <* notFollowedBy (satisfy isPunctuation)) <|> (oneOf ['T','t'] >> string "able:") let attributes' = guardEnabled Ext_table_attributes *> attributes ils <- trimInlinesF . mconcat <$> many (notFollowedBy (attributes' *> blanklines) *> inline) attr <- option nullAttr attributes' blanklines pure (ils, attr) -- Parse a simple table with '---' header and one line per row. simpleTable :: PandocMonad m => Bool -- ^ Headerless table -> MarkdownParser m (F TableComponents) simpleTable headless = do tableComponents <- tableWith' NormalizeHeader (simpleTableHeader headless) tableLine (return ()) (if headless then tableFooter else tableFooter <|> blanklines') -- All columns in simple tables have default widths. let useDefaultColumnWidths tc = let cs' = map (Bifunctor.second (const ColWidthDefault)) $ tableColSpecs tc in tc {tableColSpecs = cs'} return $ useDefaultColumnWidths <$> tableComponents -- Parse a multiline table: starts with row of '-' on top, then header -- (which may be multiline), then the rows, -- which may be multiline, separated by blank lines, and -- ending with a footer (dashed line followed by blank line). multilineTable :: PandocMonad m => Bool -- ^ Headerless table -> MarkdownParser m (F TableComponents) multilineTable headless = tableWith' NormalizeHeader (multilineTableHeader headless) multilineRow blanklines tableFooter multilineTableHeader :: PandocMonad m => Bool -- ^ Headerless table -> MarkdownParser m (F [[Blocks]], [Alignment], [Int]) multilineTableHeader headless = try $ do unless headless $ tableSep >> notFollowedBy blankline rawContent <- if headless then return $ repeat "" else many1 $ notFollowedBy tableSep >> anyLine initSp <- nonindentSpaces dashes <- many1 (dashedLine '-') newline let (lengths, lines') = unzip dashes let indices = scanl (+) (T.length initSp) lines' -- compensate for the fact that intercolumn spaces are -- not included in the last index: let indices' = case reverse indices of [] -> [] (x:xs) -> reverse (x+1:xs) rawHeadsList <- if headless then map (:[]) . drop 1 . splitTextByIndices (init indices') <$> lookAhead anyLine else return $ transpose $ map (drop 1 . splitTextByIndices (init indices')) rawContent let aligns = zipWith alignType rawHeadsList lengths let rawHeads = if headless then [] else map (T.unlines . map trim) rawHeadsList heads <- fmap sequence $ mapM (parseFromString' (mconcat <$> many plain).trim) rawHeads return (fmap (:[]) heads, aligns, indices') -- Parse a grid table: starts with row of '-' on top, then header -- (which may be grid), then the rows, -- which may be grid, separated by blank lines, and -- ending with a footer (dashed line followed by blank line). gridTable :: PandocMonad m => MarkdownParser m (F TableComponents) gridTable = gridTableWith' NormalizeHeader parseBlocks pipeBreak :: PandocMonad m => MarkdownParser m ([Alignment], [Int]) pipeBreak = try $ do nonindentSpaces openPipe <- (True <$ char '|') <|> return False first <- pipeTableHeaderPart rest <- many $ sepPipe *> pipeTableHeaderPart closePipe <- (True <$ char '|') <|> return False -- at least one pipe needed for a one-column table: guard $ not (null rest && not (openPipe || closePipe)) blankline return $ unzip (first:rest) pipeTable :: PandocMonad m => MarkdownParser m (F TableComponents) pipeTable = try $ do nonindentSpaces lookAhead nonspaceChar (heads,(aligns, seplengths)) <- (,) <$> pipeTableRow <*> pipeBreak let cellContents = parseFromString' pipeTableCell . trim let numcols = length aligns let heads' = take numcols heads lines' <- many pipeTableRow let lines'' = map (take numcols) lines' let lineWidths = map (sum . map realLength) (heads' : lines'') columns <- getOption readerColumns -- add numcols + 1 for the pipes themselves let widths = if maximumBounded (sum seplengths : lineWidths) + (numcols + 1) > columns then map (\len -> fromIntegral len / fromIntegral (sum seplengths)) seplengths else replicate (length aligns) 0.0 (headCells :: F [Blocks]) <- sequence <$> mapM cellContents heads' (rows :: F [[Blocks]]) <- sequence <$> mapM (fmap sequence . mapM cellContents) lines'' return $ toTableComponents' NormalizeHeader aligns widths <$> fmap (:[]) headCells <*> rows sepPipe :: PandocMonad m => MarkdownParser m () sepPipe = try $ do char '|' <|> char '+' notFollowedBy blankline -- parse a row, returning raw cell contents pipeTableRow :: PandocMonad m => MarkdownParser m [Text] pipeTableRow = try $ do scanForPipe skipMany spaceChar openPipe <- (True <$ char '|') <|> return False -- split into cells let chunk = void (code <|> math <|> rawHtmlInline <|> escapedChar <|> rawLaTeXInline') <|> void (noneOf "|\n\r") cells <- (snd <$> withRaw (many chunk)) `sepBy1` char '|' closePipe <- (True <$ char '|') <|> return False -- at least one pipe needed for a one-column table: guard $ not (length cells == 1 && not (openPipe || closePipe)) blankline return cells pipeTableCell :: PandocMonad m => MarkdownParser m (F Blocks) pipeTableCell = (do result <- inlines1 return $ B.plain <$> result) <|> return mempty pipeTableHeaderPart :: PandocMonad m => ParsecT Sources st m (Alignment, Int) pipeTableHeaderPart = try $ do skipMany spaceChar left <- optionMaybe (char ':') pipe <- many1 (char '-') right <- optionMaybe (char ':') skipMany spaceChar let len = length pipe + maybe 0 (const 1) left + maybe 0 (const 1) right return (case (left,right) of (Nothing,Nothing) -> AlignDefault (Just _,Nothing) -> AlignLeft (Nothing,Just _) -> AlignRight (Just _,Just _) -> AlignCenter, len) -- Succeed only if current line contains a pipe. scanForPipe :: PandocMonad m => ParsecT Sources st m () scanForPipe = do Sources inps <- getInput let ln = case inps of [] -> "" ((_,t):(_,t'):_) | T.null t -> t' ((_,t):_) -> t case T.break (\c -> c == '\n' || c == '|') ln of (_, T.uncons -> Just ('|', _)) -> return () _ -> mzero table :: PandocMonad m => MarkdownParser m (F Blocks) table = try $ do (frontCaption, frontAttr) <- option (Nothing, nullAttr) (Bifunctor.first Just <$> tableCaption) tableComponents <- (guardEnabled Ext_pipe_tables >> try (scanForPipe >> pipeTable)) <|> (guardEnabled Ext_multiline_tables >> try (multilineTable False)) <|> (guardEnabled Ext_simple_tables >> try (simpleTable True <|> simpleTable False)) <|> (guardEnabled Ext_multiline_tables >> try (multilineTable True)) <|> (guardEnabled Ext_grid_tables >> try gridTable) "table" optional blanklines (caption, attr) <- case frontCaption of Nothing -> option (return mempty, nullAttr) tableCaption Just c -> return (c, frontAttr) return $ do caption' <- caption (TableComponents _attr _capt colspecs th tb tf) <- tableComponents return $ B.tableWith attr (B.simpleCaption $ B.plain caption') colspecs th tb tf -- -- inline -- inlines :: PandocMonad m => MarkdownParser m (F Inlines) inlines = mconcat <$> many inline inlines1 :: PandocMonad m => MarkdownParser m (F Inlines) inlines1 = mconcat <$> many1 inline inline :: PandocMonad m => MarkdownParser m (F Inlines) inline = do c <- lookAhead anyChar ((case c of ' ' -> whitespace '\t' -> whitespace '\n' -> endline '`' -> code '_' -> strongOrEmph '*' -> strongOrEmph '^' -> inlineNote <|> superscript '[' -> note <|> cite <|> bracketedSpan <|> wikilink B.linkWith <|> link '!' -> image '$' -> math '~' -> strikeout <|> subscript '=' -> mark '<' -> autoLink <|> spanHtml <|> rawHtmlInline <|> ltSign '\\' -> math <|> escapedNewline <|> escapedChar <|> rawLaTeXInline' '@' -> cite <|> exampleRef '"' -> smart '\'' -> smart '\8216' -> smart '\145' -> smart '\8220' -> smart '\147' -> smart '-' -> cite <|> smart '.' -> smart '&' -> return . B.singleton <$> charRef ':' -> emoji _ -> mzero) <|> bareURL <|> str <|> symbol) "inline" escapedChar' :: PandocMonad m => MarkdownParser m Char escapedChar' = try $ do char '\\' (guardEnabled Ext_all_symbols_escapable >> satisfy (\c -> c /= '\n' && c /= '\r' && not (isAlphaNum c))) <|> (guardEnabled Ext_angle_brackets_escapable >> oneOf "\\`*_{}[]()>#+-.!~\"<>") <|> oneOf "\\`*_{}[]()>#+-.!" escapedNewline :: PandocMonad m => MarkdownParser m (F Inlines) escapedNewline = do guardEnabled Ext_escaped_line_breaks try $ do char '\\' lookAhead (char '\n') -- don't consume the newline (see #3730) return $ return B.linebreak escapedChar :: PandocMonad m => MarkdownParser m (F Inlines) escapedChar = do result <- escapedChar' case result of ' ' -> return $ return $ B.str "\160" -- "\ " is a nonbreaking space _ -> return $ return $ B.str $ T.singleton result ltSign :: PandocMonad m => MarkdownParser m (F Inlines) ltSign = do guardDisabled Ext_raw_html <|> (notFollowedByHtmlCloser >> notFollowedBy' (htmlTag isBlockTag)) char '<' return $ return $ B.str "<" -- Note that if the citations extension is enabled, example refs will be -- parsed as citations, and handled by a clause in the parser for citations, -- since we won't know whether we have an example ref until the -- whole document has been parsed. But we need this parser -- here in case citations is disabled. exampleRef :: PandocMonad m => MarkdownParser m (F Inlines) exampleRef = do guardEnabled Ext_example_lists try $ do char '@' lab <- mconcat . map T.pack <$> many (many1 alphaNum <|> try (do c <- char '_' <|> char '-' cs <- many1 alphaNum return (c:cs))) return $ do st <- askF return $ case M.lookup lab (stateExamples st) of Just n -> B.str $ tshow n Nothing -> B.str $ "@" <> lab symbol :: PandocMonad m => MarkdownParser m (F Inlines) symbol = do result <- noneOf "<\\\n\t " <|> try (do lookAhead $ char '\\' notFollowedBy' (() <$ rawTeXBlock) char '\\') return $ return $ B.str $! T.singleton result -- parses inline code, between n `s and n `s code :: PandocMonad m => MarkdownParser m (F Inlines) code = try $ do starts <- many1 (char '`') skipSpaces result <- trim . T.concat <$> manyTill ( many1Char (noneOf "`\n") <|> many1Char (char '`') <|> (char '\n' >> notFollowedBy (inList >> listStart) >> notFollowedBy' blankline >> return " ")) (try $ skipSpaces >> count (length starts) (char '`') >> notFollowedBy (char '`')) rawattr <- (Left <$> (guardEnabled Ext_raw_attribute >> try rawAttribute)) <|> (Right <$> option ("",[],[]) (guardEnabled Ext_inline_code_attributes >> try attributes)) return $ return $ case rawattr of Left syn -> B.rawInline syn $! result Right attr -> B.codeWith attr $! result math :: PandocMonad m => MarkdownParser m (F Inlines) math = (return . B.displayMath <$> (mathDisplay >>= applyMacros)) <|> (return . B.math <$> (mathInline >>= applyMacros)) <+?> (guardEnabled Ext_smart *> (return <$> apostrophe) <* notFollowedBy (space <|> satisfy isPunctuation)) -- Parses material enclosed in *s, **s, _s, or __s. -- Designed to avoid backtracking. enclosure :: PandocMonad m => Char -> MarkdownParser m (F Inlines) enclosure c = do -- we can't start an enclosure with _ if after a string and -- the intraword_underscores extension is enabled: guardDisabled Ext_intraword_underscores <|> guard (c == '*') <|> (guard =<< notAfterString) cs <- many1Char (char c) (return (B.str cs) <>) <$> whitespace <|> case T.length cs of 3 -> three c 2 -> two c mempty 1 -> one c mempty _ -> return (return $ B.str cs) ender :: PandocMonad m => Char -> Int -> MarkdownParser m () ender c n = try $ do count n (char c) guard (c == '*') <|> guardDisabled Ext_intraword_underscores <|> notFollowedBy alphaNum -- Parse inlines til you hit one c or a sequence of two cs. -- If one c, emit emph and then parse two. -- If two cs, emit strong and then parse one. -- Otherwise, emit ccc then the results. three :: PandocMonad m => Char -> MarkdownParser m (F Inlines) three c = do contents <- mconcat <$> many (notFollowedBy (ender c 1) >> inline) (ender c 3 >> updateLastStrPos >> return (B.strong . B.emph <$> contents)) <|> (ender c 2 >> updateLastStrPos >> one c (B.strong <$> contents)) <|> (ender c 1 >> updateLastStrPos >> two c (B.emph <$> contents)) <|> return (return (B.str $ T.pack [c,c,c]) <> contents) -- Parse inlines til you hit two c's, and emit strong. -- If you never do hit two cs, emit ** plus inlines parsed. two :: PandocMonad m => Char -> F Inlines -> MarkdownParser m (F Inlines) two c prefix' = do contents <- mconcat <$> many (try $ notFollowedBy (ender c 2) >> inline) (ender c 2 >> updateLastStrPos >> return (B.strong <$> (prefix' <> contents))) <|> return (return (B.str $ T.pack [c,c]) <> (prefix' <> contents)) -- Parse inlines til you hit a c, and emit emph. -- If you never hit a c, emit * plus inlines parsed. one :: PandocMonad m => Char -> F Inlines -> MarkdownParser m (F Inlines) one c prefix' = do contents <- mconcat <$> many ( (notFollowedBy (ender c 1) >> inline) <|> try (string [c,c] >> notFollowedBy (ender c 1) >> two c mempty) ) (ender c 1 >> updateLastStrPos >> return (B.emph <$> (prefix' <> contents))) <|> return (return (B.str $ T.singleton c) <> (prefix' <> contents)) strongOrEmph :: PandocMonad m => MarkdownParser m (F Inlines) strongOrEmph = enclosure '*' <|> enclosure '_' -- | Parses a list of inlines between start and end delimiters. inlinesBetween :: PandocMonad m => (Show b) => MarkdownParser m a -> MarkdownParser m b -> MarkdownParser m (F Inlines) inlinesBetween start end = trimInlinesF . mconcat <$> try (start >> many1Till inner end) where inner = innerSpace <|> (notFollowedBy' (() <$ whitespace) >> inline) innerSpace = try $ whitespace <* notFollowedBy' end strikeout :: PandocMonad m => MarkdownParser m (F Inlines) strikeout = fmap B.strikeout <$> (guardEnabled Ext_strikeout >> inlinesBetween strikeStart strikeEnd) where strikeStart = string "~~" >> lookAhead nonspaceChar >> notFollowedBy (char '~') strikeEnd = try $ string "~~" mark :: PandocMonad m => MarkdownParser m (F Inlines) mark = fmap (B.spanWith ("",["mark"],[])) <$> (guardEnabled Ext_mark >> inlinesBetween markStart markEnd) where markStart = string "==" >> lookAhead nonspaceChar >> notFollowedBy (char '=') markEnd = try $ string "==" superscript :: PandocMonad m => MarkdownParser m (F Inlines) superscript = do fmap B.superscript <$> (regularSuperscript <|> mmdShortSuperscript) where regularSuperscript = do guardEnabled Ext_superscript litBetweenNoSpace '^' '^' >>= parseFromString inlines mmdShortSuperscript = try $ do guardEnabled Ext_short_subsuperscripts char '^' result <- T.pack <$> many1 alphaNum return $ return $ B.str result subscript :: PandocMonad m => MarkdownParser m (F Inlines) subscript = do fmap B.subscript <$> (regularSubscript <|> mmdShortSubscript) where regularSubscript = do guardEnabled Ext_subscript litBetweenNoSpace '~' '~' >>= parseFromString inlines mmdShortSubscript = try $ do guardEnabled Ext_short_subsuperscripts char '~' result <- T.pack <$> many1 alphaNum return $ return $ B.str result whitespace :: PandocMonad m => MarkdownParser m (F Inlines) whitespace = spaceChar >> return <$> (lb <|> regsp) "whitespace" where lb = spaceChar >> skipMany spaceChar >> option B.space (endline >> return B.linebreak) regsp = skipMany spaceChar >> return B.space nonEndline :: PandocMonad m => ParsecT Sources st m Char nonEndline = satisfy (/='\n') str :: PandocMonad m => MarkdownParser m (F Inlines) str = do !result <- mconcat <$> many1 ( T.pack <$> (many1 alphaNum) <|> "." <$ try (char '.' <* notFollowedBy (char '.')) ) updateLastStrPos (do guardEnabled Ext_smart abbrevs <- getOption readerAbbreviations if result `Set.member` abbrevs then try (do ils <- whitespace notFollowedBy (() <$ cite <|> () <$ note) -- ?? lookAhead alphaNum -- replace space after with nonbreaking space -- if softbreak, move before abbrev if possible (#4635) return $ do ils' <- ils case B.toList ils' of [Space] -> return $! (B.str result <> B.str "\160") _ -> return $! (B.str result <> ils')) <|> return (return $! B.str result) else return (return $! B.str result)) <|> return (return $! B.str result) -- an endline character that can be treated as a space, not a structural break endline :: PandocMonad m => MarkdownParser m (F Inlines) endline = try $ do newline notFollowedBy blankline getState >>= guard . stateAllowLineBreaks -- parse potential list-starts differently if in a list: notFollowedBy (inList >> listStart) guardDisabled Ext_lists_without_preceding_blankline <|> notFollowedBy listStart guardEnabled Ext_blank_before_blockquote <|> notFollowedBy emailBlockQuoteStart guardEnabled Ext_blank_before_header <|> (notFollowedBy . char =<< atxChar) -- atx header guardDisabled Ext_backtick_code_blocks <|> notFollowedBy (() <$ (lookAhead (char '`') >> codeBlockFenced)) notFollowedByHtmlCloser notFollowedByDivCloser (eof >> return mempty) <|> (guardEnabled Ext_hard_line_breaks >> return (return B.linebreak)) <|> (guardEnabled Ext_ignore_line_breaks >> return mempty) <|> (skipMany spaceChar >> return (return B.softbreak)) -- -- links -- -- a reference label for a link reference :: PandocMonad m => MarkdownParser m (F Inlines, Text) reference = do guardDisabled Ext_footnotes <|> notFollowedBy' noteMarker withRaw $ trimInlinesF <$> inBalancedBrackets inlines -- source for a link, with optional title source :: PandocMonad m => MarkdownParser m (Text, Text) source = do char '(' skipSpaces let parenthesizedChars = do result <- charsInBalanced '(' ')' litChar return $ "(" <> result <> ")" let linkTitle' = try $ spnl >> linkTitle let urlChunk = do try parenthesizedChars <|> (notFollowedBy (oneOf "\n\r )") >> litChar) <|> (lookAhead (oneOf "\n\r") >> notFollowedBy linkTitle' >> litChar) <|> try (many1Char spaceChar <* notFollowedBy (oneOf "\"')")) let sourceURL = T.unwords . T.words . T.concat <$> many urlChunk src <- try (litBetween '<' '>') <|> try base64DataURI <|> sourceURL tit <- option "" linkTitle' skipSpaces char ')' return (escapeURI $ trimr src, tit) base64DataURI :: PandocMonad m => ParsecT Sources s m Text base64DataURI = do Sources ((pos, txt):rest) <- getInput let r = A.parse (fst <$> A.match pBase64DataURI) txt case r of A.Done remaining consumed -> do let pos' = incSourceColumn pos (T.length consumed) setInput $ Sources ((pos', remaining):rest) return consumed _ -> mzero linkTitle :: PandocMonad m => MarkdownParser m Text linkTitle = quotedTitle '"' <|> quotedTitle '\'' wikilink :: PandocMonad m => (Attr -> Text -> Text -> Inlines -> Inlines) -> MarkdownParser m (F Inlines) wikilink constructor = do let attr = (mempty, ["wikilink"], mempty) titleAfter <- (True <$ guardEnabled Ext_wikilinks_title_after_pipe) <|> (False <$ guardEnabled Ext_wikilinks_title_before_pipe) try $ do string "[[" *> notFollowedBy' (char '[') raw <- many1TillChar anyChar (try $ string "]]") let (title, url) = case T.break (== '|') raw of (before, "") -> (before, before) (before, after) | titleAfter -> (T.drop 1 after, before) | otherwise -> (before, T.drop 1 after) guard $ T.all (`notElem` ['\n','\r','\f','\t']) url return . pure . constructor attr url "" $ B.text $ fromEntities title link :: PandocMonad m => MarkdownParser m (F Inlines) link = try $ do st <- getState guard $ stateAllowLinks st setState $ st{ stateAllowLinks = False } (lab,raw) <- reference setState $ st{ stateAllowLinks = True } regLink B.linkWith lab <|> referenceLink B.linkWith (lab,raw) bracketedSpan :: PandocMonad m => MarkdownParser m (F Inlines) bracketedSpan = do guardEnabled Ext_bracketed_spans try $ do (lab,_) <- reference attr <- attributes return $ wrapSpan attr <$> lab -- | Given an @Attr@ value, this returns a function to wrap the contents -- of a span. Handles special classes (@smallcaps@, @ul@, @underline@) -- and uses the respective constructors to handle them. wrapSpan :: Attr -> Inlines -> Inlines wrapSpan (ident, classes, kvs) = let (initConst, kvs') = case lookup "style" kvs of Just s | isSmallCapsFontVariant s -> let kvsNoStyle = [(k, v) | (k, v) <- kvs, k /= "style"] in (Just B.smallcaps, kvsNoStyle) _ -> (Nothing, kvs) (mConstr, remainingClasses) = foldr go (initConst, []) classes wrapInConstr c = maybe c (c .) go cls (accConstr, other) = case cls of "smallcaps" -> (Just $ wrapInConstr B.smallcaps accConstr, other) "ul" -> (Just $ wrapInConstr B.underline accConstr, other) "underline" -> (Just $ wrapInConstr B.underline accConstr, other) _ -> (accConstr, cls:other) in case (ident, remainingClasses, kvs') of ("", [], []) -> fromMaybe (B.spanWith nullAttr) mConstr attr -> wrapInConstr (B.spanWith attr) mConstr isSmallCapsFontVariant :: Text -> Bool isSmallCapsFontVariant s = T.toLower (T.filter (`notElem` [' ', '\t', ';']) s) == "font-variant:small-caps" regLink :: PandocMonad m => (Attr -> Text -> Text -> Inlines -> Inlines) -> F Inlines -> MarkdownParser m (F Inlines) regLink constructor lab = try $ do (!src, !tit) <- source rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths) pos <- getPosition let !src' = if rebase then rebasePath pos src else src !attr <- option nullAttr $ guardEnabled Ext_link_attributes >> attributes return $ constructor attr src' tit <$> lab -- a link like [this][ref] or [this][] or [this] referenceLink :: PandocMonad m => (Attr -> Text -> Text -> Inlines -> Inlines) -> (F Inlines, Text) -> MarkdownParser m (F Inlines) referenceLink constructor (lab, raw) = do sp <- (True <$ lookAhead (char ' ')) <|> return False (_,!raw') <- option (mempty, "") $ lookAhead (try (do guardEnabled Ext_citations guardDisabled Ext_spaced_reference_links <|> spnl normalCite return (mempty, ""))) <|> try ((guardDisabled Ext_spaced_reference_links <|> spnl) >> reference) when (raw' == "") $ guardEnabled Ext_shortcut_reference_links !attr <- option nullAttr $ guardEnabled Ext_link_attributes >> attributes let !labIsRef = raw' == "" || raw' == "[]" let (exclam, rawsuffix) = case T.uncons raw of Just ('!', rest) -> (True, rest) _ -> (False, raw) let !key = toKey $ if labIsRef then rawsuffix else raw' parsedRaw <- parseFromString' inlines raw' fallback <- parseFromString' inlines $ if exclam then rawsuffix else dropBrackets rawsuffix implicitHeaderRefs <- option False $ True <$ guardEnabled Ext_implicit_header_references let makeFallback = do parsedRaw' <- parsedRaw fallback' <- fallback return $ (if exclam then "!" <> fallback' else B.str "[" <> fallback' <> B.str "]") <> (if sp && not (T.null raw) then B.space else mempty) <> parsedRaw' return $ do keys <- asksF stateKeys case M.lookup key keys of Nothing -> if implicitHeaderRefs then do headerKeys <- asksF stateHeaderKeys case M.lookup key headerKeys of Just ((src, tit), _) -> constructor attr src tit <$> lab Nothing -> makeFallback else makeFallback Just ((src,tit), defattr) -> constructor (combineAttr attr defattr) src tit <$> lab dropBrackets :: Text -> Text dropBrackets = dropRB . dropLB where dropRB (T.unsnoc -> Just (xs,']')) = xs dropRB xs = xs dropLB (T.uncons -> Just ('[',xs)) = xs dropLB xs = xs bareURL :: PandocMonad m => MarkdownParser m (F Inlines) bareURL = do guardEnabled Ext_autolink_bare_uris getState >>= guard . stateAllowLinks try $ do (cls, (orig, src)) <- (("uri",) <$> uri) <|> (("email",) <$> emailAddress) notFollowedBy $ try $ spaces >> htmlTag (~== TagClose ("a" :: Text)) return $ return $ B.linkWith ("",[cls],[]) src "" (B.str orig) autoLink :: PandocMonad m => MarkdownParser m (F Inlines) autoLink = try $ do getState >>= guard . stateAllowLinks char '<' (cls, (orig, src)) <- (("uri",) <$> uri) <|> (("email",) <$> emailAddress) -- in rare cases, something may remain after the uri parser -- is finished, because the uri parser tries to avoid parsing -- final punctuation. for example: in ``, -- the URI parser will stop before the dashes. extra <- fromEntities <$> manyTillChar nonspaceChar (char '>') attr <- option ("", [cls], []) $ try $ guardEnabled Ext_link_attributes >> attributes return $ return $ B.linkWith attr (src <> escapeURI extra) "" (B.str $ orig <> extra) -- | Rebase a relative path, by adding the (relative) directory -- of the containing source position. Absolute links and URLs -- are untouched. rebasePath :: SourcePos -> Text -> Text rebasePath pos path = do let fp = sourceName pos isFragment = T.take 1 path == "#" path' = T.unpack path isAbsolutePath = Posix.isAbsolute path' || Windows.isAbsolute path' in if T.null path || isFragment || isAbsolutePath || isURI path then path else case takeDirectory fp of "" -> path "." -> path d -> T.pack d <> "/" <> path image :: PandocMonad m => MarkdownParser m (F Inlines) image = try $ do char '!' wikilink B.imageWith <|> do (lab,raw) <- reference defaultExt <- getOption readerDefaultImageExtension let constructor attr' src | "data:" `T.isPrefixOf` src = B.imageWith attr' src -- see #9118 | otherwise = case takeExtension (T.unpack src) of "" -> B.imageWith attr' (T.pack $ addExtension (T.unpack src) $ T.unpack defaultExt) _ -> B.imageWith attr' src regLink constructor lab <|> referenceLink constructor (lab, "!" <> raw) note :: PandocMonad m => MarkdownParser m (F Inlines) note = try $ do guardEnabled Ext_footnotes ref <- noteMarker updateState $ \st -> st{ stateNoteRefs = Set.insert ref (stateNoteRefs st) , stateNoteNumber = stateNoteNumber st + 1 } noteNum <- stateNoteNumber <$> getState return $ do notes <- asksF stateNotes' case M.lookup ref notes of Nothing -> return $ B.str $ "[^" <> ref <> "]" Just (_pos, contents) -> do st <- askF -- process the note in a context that doesn't resolve -- notes, to avoid infinite looping with notes inside -- notes: let contents' = runF contents st{ stateNotes' = M.empty } let addCitationNoteNum c@Citation{} = c{ citationNoteNum = noteNum } let adjustCite (Cite cs ils) = Cite (map addCitationNoteNum cs) ils adjustCite x = x return $ B.note $ walk adjustCite contents' inlineNote :: PandocMonad m => MarkdownParser m (F Inlines) inlineNote = do guardEnabled Ext_inline_notes try $ do char '^' updateState $ \st -> st{ stateInNote = True , stateNoteNumber = stateNoteNumber st + 1 } contents <- inBalancedBrackets inlines notFollowedBy (char '(' <|> char '[' <|> ('{' <$ attributes)) -- ^[link](foo)^ is superscript updateState $ \st -> st{ stateInNote = False } return $ B.note . B.para <$> contents rawLaTeXInline' :: PandocMonad m => MarkdownParser m (F Inlines) rawLaTeXInline' = do guardEnabled Ext_raw_tex notFollowedBy' rawConTeXtEnvironment !s <- rawLaTeXInline return $ return $ B.rawInline "tex" s -- "tex" because it might be context rawConTeXtEnvironment :: PandocMonad m => ParsecT Sources st m Text rawConTeXtEnvironment = try $ do string "\\start" completion <- inBrackets (letter <|> digit <|> spaceChar) <|> many1Char letter !contents <- manyTill (rawConTeXtEnvironment <|> countChar 1 anyChar) (try $ string "\\stop" >> textStr completion) return $! "\\start" <> completion <> T.concat contents <> "\\stop" <> completion inBrackets :: PandocMonad m => ParsecT Sources st m Char -> ParsecT Sources st m Text inBrackets parser = do char '[' contents <- manyChar parser char ']' return $! "[" <> contents <> "]" spanHtml :: PandocMonad m => MarkdownParser m (F Inlines) spanHtml = do guardEnabled Ext_native_spans try $ do (TagOpen _ attrs, _) <- htmlTag (~== TagOpen ("span" :: Text) []) contents <- mconcat <$> manyTill inline (htmlTag (~== TagClose ("span" :: Text))) let ident = fromMaybe "" $ lookup "id" attrs let classes = maybe [] T.words $ lookup "class" attrs let keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"] return $ wrapSpan (ident, classes, keyvals) <$> contents divHtml :: PandocMonad m => MarkdownParser m (F Blocks) divHtml = do guardEnabled Ext_native_divs try $ do openpos <- getPosition (TagOpen _ attrs, _) <- htmlTag (~== TagOpen ("div" :: Text) []) -- we set stateInHtmlBlock so that closing tags that can be either block -- or inline will not be parsed as inline tags oldInHtmlBlock <- stateInHtmlBlock <$> getState updateState $ \st -> st{ stateInHtmlBlock = Just "div" } optional blanklines contents <- mconcat <$> many (notFollowedBy' (htmlTag (~== TagClose ("div" :: Text))) >> block) void (htmlTag (~== TagClose ("div" :: Text))) <|> (getPosition >>= report . UnclosedDiv openpos) let ident = fromMaybe "" $ lookup "id" attrs let classes = maybe [] T.words $ lookup "class" attrs let keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"] updateState $ \st -> st{ stateInHtmlBlock = oldInHtmlBlock } return $ B.divWith (ident, classes, keyvals) <$> contents divFenced :: PandocMonad m => MarkdownParser m (F Blocks) divFenced = do guardEnabled Ext_fenced_divs try $ do openpos <- getPosition string ":::" skipMany (char ':') skipMany spaceChar attribs <- attributes <|> ((\x -> ("",[x],[])) <$> many1Char nonspaceChar) skipMany spaceChar skipMany (char ':') blankline updateState $ \st -> st{ stateFencedDivLevel = stateFencedDivLevel st + 1 } bs <- mconcat <$> many (notFollowedBy divFenceEnd >> block) divFenceEnd <|> (getPosition >>= report . UnclosedDiv openpos) updateState $ \st -> st{ stateFencedDivLevel = stateFencedDivLevel st - 1 } return $ B.divWith attribs <$> bs divFenceEnd :: PandocMonad m => MarkdownParser m () divFenceEnd = try $ do string ":::" skipMany (char ':') blanklines return () rawHtmlInline :: PandocMonad m => MarkdownParser m (F Inlines) rawHtmlInline = do guardEnabled Ext_raw_html inHtmlBlock <- stateInHtmlBlock <$> getState let isCloseBlockTag t = case inHtmlBlock of Just t' -> t ~== TagClose t' Nothing -> False mdInHtml <- option False $ ( guardEnabled Ext_markdown_in_html_blocks <|> guardEnabled Ext_markdown_attribute ) >> return True (_,result) <- htmlTag $ if mdInHtml then (\x -> isInlineTag x && not (isCloseBlockTag x)) else not . isTextTag return $ return $ B.rawInline "html" result -- Emoji emoji :: PandocMonad m => MarkdownParser m (F Inlines) emoji = do guardEnabled Ext_emoji try $ do char ':' emojikey <- many1Char (alphaNum <|> oneOf "_+-") char ':' case emojiToInline emojikey of Just i -> return (return $ B.singleton i) Nothing -> mzero -- Citations cite :: PandocMonad m => MarkdownParser m (F Inlines) cite = do guardEnabled Ext_citations -- We only use stateNoteNumber for assigning citationNoteNum, -- so we just assume that all citations produce notes. -- citationNoteNum doesn't affect non-note styles. inNote <- stateInNote <$> getState unless inNote $ updateState $ \st -> st{ stateNoteNumber = stateNoteNumber st + 1 } textualCite <|> do (cs, raw) <- withRaw normalCite return $ flip B.cite (B.text raw) <$> cs textualCite :: PandocMonad m => MarkdownParser m (F Inlines) textualCite = try $ do (suppressAuthor, key) <- citeKey True -- If this is a reference to an earlier example list item, -- then don't parse it as a citation. If the example list -- item comes later, we'll parse it here and figure out in -- the runF stage if it's a citation. But it helps with -- issue #6836 to filter out known example list references -- at this stage, so that we don't increment stateNoteNumber. getState >>= guard . isNothing . M.lookup key . stateExamples noteNum <- stateNoteNumber <$> getState let first = Citation{ citationId = key , citationPrefix = [] , citationSuffix = [] , citationMode = if suppressAuthor then SuppressAuthor else AuthorInText , citationNoteNum = noteNum , citationHash = 0 } (do -- parse [braced] material after author-in-text cite (cs, raw) <- withRaw $ (fmap (first:) <$> try (spnl *> normalCite)) <|> bareloc first let (spaces',raw') = T.span isSpace raw spc | T.null spaces' = mempty | otherwise = B.space lab <- parseFromString' inlines $ dropBrackets raw' fallback <- referenceLink B.linkWith (lab,raw') -- undo any incrementing of stateNoteNumber from last step: updateState $ \st -> st{ stateNoteNumber = noteNum } return $ do fallback' <- fallback cs' <- cs return $ case B.toList fallback' of Link{}:_ -> B.cite [first] (B.str $ "@" <> key) <> spc <> fallback' _ -> B.cite cs' (B.text $ "@" <> key <> " " <> raw)) <|> -- no braced material return (do st <- askF return $ case M.lookup key (stateExamples st) of Just n -> B.str $ tshow n _ -> B.cite [first] $ B.str $ "@" <> key) bareloc :: PandocMonad m => Citation -> MarkdownParser m (F [Citation]) bareloc c = try $ do spnl char '[' notFollowedBy $ char '^' suff <- suffix rest <- option (return []) $ try $ char ';' >> spnl >> citeList spnl char ']' notFollowedBy $ oneOf "[({" return $ do suff' <- suff rest' <- rest return $ c{ citationSuffix = B.toList suff' } : rest' normalCite :: PandocMonad m => MarkdownParser m (F [Citation]) normalCite = try $ do char '[' spnl citations <- citeList spnl char ']' -- not a link or a bracketed span notFollowedBy (try (void source) <|> (guardEnabled Ext_bracketed_spans *> void attributes) <|> void reference) return citations suffix :: PandocMonad m => MarkdownParser m (F Inlines) suffix = try $ do hasSpace <- option False (notFollowedBy nonspaceChar >> return True) spnl ils <- many (notFollowedBy (oneOf ";]") >> inline) let rest = trimInlinesF (mconcat ils) return $ if hasSpace && not (null ils) then (B.space <>) <$> rest else rest prefix :: PandocMonad m => MarkdownParser m (F Inlines) prefix = trimInlinesF . mconcat <$> manyTill (notFollowedBy (char ';') >> inline) (char ']' <|> lookAhead (try $ do optional (try (char ';' >> spnl)) citeKey True return ']')) citeList :: PandocMonad m => MarkdownParser m (F [Citation]) citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl) citation :: PandocMonad m => MarkdownParser m (F Citation) citation = try $ do pref <- prefix (suppress_author, key) <- citeKey True suff <- suffix noteNum <- stateNoteNumber <$> getState return $ do x <- pref y <- suff return Citation{ citationId = key , citationPrefix = B.toList x , citationSuffix = B.toList y , citationMode = if suppress_author then SuppressAuthor else NormalCitation , citationNoteNum = noteNum , citationHash = 0 } smart :: PandocMonad m => MarkdownParser m (F Inlines) smart = do guardEnabled Ext_smart doubleQuoted <|> singleQuoted <|> (return <$> doubleCloseQuote) <|> (return <$> apostrophe) <|> (return <$> dash) <|> (return <$> ellipses) singleQuoted :: PandocMonad m => MarkdownParser m (F Inlines) singleQuoted = do singleQuoteStart (try (withQuoteContext InSingleQuote $ fmap B.singleQuoted . trimInlinesF . mconcat <$> many1Till inline singleQuoteEnd)) <|> (return (return (B.str "\8217"))) -- doubleQuoted will handle regular double-quoted sections, as well -- as dialogues with an open double-quote without a close double-quote -- in the same paragraph. doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines) doubleQuoted = do doubleQuoteStart (try (withQuoteContext InDoubleQuote $ fmap B.doubleQuoted . trimInlinesF . mconcat <$> many1Till inline doubleQuoteEnd)) <|> (return (return (B.str "\8220"))) ================================================ FILE: src/Text/Pandoc/Readers/Mdoc/Lex.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} {- | Module : Text.Pandoc.Readers.Mdoc.Lex Copyright : Copyright (C) 2024 Evan Silberman License : GNU GPL, version 2 or above Maintainer : Evan Silberman Stability : WIP Portability : portable Tokenizer for mdoc -} module Text.Pandoc.Readers.Mdoc.Lex ( MdocToken(..) , MdocTokens(..) , DelimSide(..) , lexMdoc , toString ) where import Control.Monad (void, guard, when) import Control.Monad.Except (throwError) import Text.Pandoc.Class.PandocMonad (PandocMonad(..)) import Data.Char (isAlphaNum) import Data.Maybe (isJust) import qualified Data.Text as T import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.Roff.Escape import Text.Pandoc.Readers.Mdoc.Macros import qualified Data.Sequence as Seq -- As a higher level language with a wealth of semantic macros, mdoc -- discourages authors from falling back to low-level roff features like font -- selection, custom macros, defined strings, etc. Pandoc's mdoc reader is -- accordingly implemented as a high-level interpreter of mdoc's semantic macros -- and almost no raw roff requests are supported. -- -- tbl(7) and eqn(7) macros are rare but not completely unseen in mdoc manuals. -- they are not yet implemented. most use of tbl macros in mdoc could probably -- be replaced with .Bl -column data DelimSide = Open | Middle | Close deriving (Show, Eq) -- | Tokens for Mdoc documents data MdocToken = Str T.Text SourcePos -- ^ The contents of a text line | Macro T.Text SourcePos -- ^ A macro to be processed | Lit T.Text SourcePos -- ^ Literal text on a control line | Blank SourcePos -- ^ A blank line | Delim DelimSide T.Text SourcePos -- ^ A delimiter character | Eol -- ^ The end of a control line deriving Show toString :: MdocToken -> T.Text toString (Str x _) = x toString (Macro x _) = x toString (Lit x _) = x toString (Delim _ x _) = x toString Blank{} = mempty toString Eol = mempty newtype MdocTokens = MdocTokens { unMdocTokens :: Seq.Seq MdocToken } deriving (Show, Semigroup, Monoid) singleTok :: MdocToken -> MdocTokens singleTok t = MdocTokens (Seq.singleton t) type Lexer m = ParsecT Sources () m instance RoffLikeLexer MdocTokens where -- This is a bit confusing. We're lexing to MdocTokens, but for escaping -- purposes we just want Texts. type Token MdocTokens = T.Text -- We don't need a state type State MdocTokens = () -- We don't support predefined string expansion expandString = return () escString = return mempty -- what token type the unescaped text gets wrapped in is decided by other -- parts of the lexer. emit = id -- All escapes are resolved in the lexer and we never need to emit anything, -- vs. the roff lexer which has to push the backlashes to the output while -- in copy mode. backslash = (mempty <* char '\\') <|> (mempty <* string "\\E") -- We don't support macro definition and we don't output anything for \A checkDefined = const mempty -- We don't support copy mode and \E is treated as backslash escE = return mempty -- We don't support low-level font selection escFont = escIgnore 'f' [escapeArg, countChar 1 (satisfy (/='\n'))] eofline :: (Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m MdocToken eofline = do void newline <|> eof return Eol lexComment :: PandocMonad m => Lexer m MdocTokens lexComment = do try $ string ".\\\"" skipMany $ noneOf "\n" eofline return mempty argText :: PandocMonad m => Lexer m T.Text argText = do beg <- escape <|> regularText end <- mconcat <$> many (escape <|> regularText <|> quoteChar) return $ beg <> end spaceTabChar :: PandocMonad m => Lexer m T.Text spaceTabChar = T.singleton <$> spaceChar quotedArg :: PandocMonad m => Lexer m T.Text quotedArg = do quoteChar t <- mconcat <$> many (try innerQuote <|> escape <|> regularText <|> spaceTabChar) quoteChar notFollowedBy quoteChar return t where innerQuote = do string "\"\"" return "\"" anyText :: PandocMonad m => Lexer m T.Text anyText = escape <|> regularText <|> quoteChar <|> spaceTabChar regularText :: PandocMonad m => Lexer m T.Text regularText = many1Char $ noneOf "\n\r\t \\\"" quoteChar :: PandocMonad m => Lexer m T.Text quoteChar = T.singleton <$> char '"' mdocToken :: PandocMonad m => Lexer m MdocTokens mdocToken = lexComment <|> lexControlLine <|> lexTextLine lexMacroName :: PandocMonad m => Lexer m T.Text lexMacroName = many1Char (satisfy isMacroChar) where isMacroChar '%' = True isMacroChar x = isAlphaNum x lexMacro :: PandocMonad m => Lexer m MdocToken lexMacro = do pos <- getPosition name <- lexMacroName eof <|> void (lookAhead (spaceChar <|> newline)) skipSpaces return $ Macro name pos lexCallableMacro :: PandocMonad m => Lexer m MdocToken lexCallableMacro = do pos <- getPosition q <- optionMaybe quoteChar name <- lexMacroName when (isJust q) (void quoteChar) eof <|> void (lookAhead (spaceChar <|> newline)) skipSpaces guard $ isCallableMacro name return $ Macro name pos lexDelim :: (PandocMonad m) => Lexer m MdocToken lexDelim = do pos <- getPosition q <- optionMaybe quoteChar t <- Delim Open <$> oneOfStrings ["(", "["] <|> Delim Close <$> oneOfStrings [".", ",", ":", ";", ")", "]", "?", "!"] <|> Delim Middle <$> textStr "|" when (isJust q) (void quoteChar) eof <|> void (lookAhead (spaceChar <|> newline)) skipSpaces return $ t pos lexLit :: PandocMonad m => Lexer m MdocToken lexLit = do pos <- getPosition t <- argText <|> quotedArg skipSpaces return $ Lit t pos lexTextLine :: PandocMonad m => Lexer m MdocTokens lexTextLine = do pos <- getPosition guard $ sourceColumn pos == 1 t <- mconcat <$> many anyText eofline if T.null $ T.strip t then return $ singleTok $ Blank pos else return $ singleTok $ Str t pos lexControlLine :: PandocMonad m => Lexer m MdocTokens lexControlLine = do pos <- getPosition guard $ sourceColumn pos == 1 char '.' eofline *> mempty <|> do m@(Macro name _) <- lexMacro -- .Ns macros at the start of a line are ignored. We'd have to look behind -- to keep track of the "start of the line" in the parser, so we'll drop -- those macros in lexing. let start | name == "Ns" = [] | otherwise = [m] let parsed = isParsedMacro name (wds, e) <- manyUntil (l parsed) eofline return $ MdocTokens $ Seq.fromList $ start <> wds <> [e] where l True = try lexDelim <|> try lexCallableMacro <|> lexLit l False = try lexDelim <|> lexLit -- | Tokenize a string as a sequence of mdoc tokens. lexMdoc :: PandocMonad m => SourcePos -> T.Text -> m MdocTokens lexMdoc pos txt = do eithertokens <- readWithM (do setPosition pos mconcat <$> manyTill mdocToken eof) def txt case eithertokens of Left e -> throwError e Right tokenz -> return tokenz ================================================ FILE: src/Text/Pandoc/Readers/Mdoc/Macros.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Mdoc.Macros Copyright : © 2024 Evan Silberman License : GNU GPL, version 2 or above Maintainer : Evan Silberman Stability : WIP Portability : portable -} module Text.Pandoc.Readers.Mdoc.Macros (isParsedMacro, isCallableMacro) where import Data.Set (member, fromList, Set) import Data.Text isParsedMacro :: Text -> Bool isParsedMacro a = member a parsedMacros isCallableMacro :: Text -> Bool isCallableMacro a = member a callableMacros parsedMacros :: Set Text parsedMacros = fromList [ "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", "Bc", "Bo", "Bq", "Brc", "Bro", "Brq", "Bsx", "Bx", "Cd", "Cm", "D1", "Dc", "Dl", "Do", "Dq", "Dv", "Dx", "Ec", "Em", "En", "Eo", "Er", "Es", "Ev", "Fa", "Fc", "Fl", "Fn", "Fr", "Ft", "Fx", "Ic", "In", "It", "Li", "Lk", "Ms", "Mt", "Nm", "No", "Ns", "Nx", "Oc", "Oo", "Op", "Ot", "Ox", "Pa", "Pc", "Pf", "Po", "Pq", "Qc", "Ql", "Qo", "Qq", "Sc", "Sh", "So", "Sq", "Ss", "St", "Sx", "Sy", "Ta", "Tn", "Ux", "Va", "Vt", "Xc", "Xo", "Xr"] callableMacros :: Set Text callableMacros = fromList [ "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", "Bc", "Bo", "Bq", "Brc", "Bro", "Brq", "Bsx", "Bx", "Cd", "Cm", "Dc", "Do", "Dq", "Dv", "Dx", "Ec", "Em", "En", "Eo", "Er", "Es", "Ev", "Fa", "Fc", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx", "Ic", "In", "Li", "Lk", "Ms", "Mt", "Nm", "No", "Ns", "Nx", "Oc", "Oo", "Op", "Ot", "Ox", "Pa", "Pc", "Pf", "Po", "Pq", "Qc", "Ql", "Qo", "Qq", "Sc", "So", "Sq", "St", "Sx", "Sy", "Ta", "Tn", "Ux", "Va", "Vt", "Xc", "Xo", "Xr"] ================================================ FILE: src/Text/Pandoc/Readers/Mdoc/Standards.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Mdoc.Standards Copyright : © 2024 Evan Silberman License : GNU GPL, version 2 or above Maintainer : Evan Silberman Stability : WIP Portability : portable -} module Text.Pandoc.Readers.Mdoc.Standards (standard) where import Data.Map (fromList, Map) import qualified Data.Map as M import Data.Text standard :: Text -> Maybe Text standard = flip M.lookup standards standards :: Map Text Text standards = fromList [ ("-p1003.1-88", "IEEE Std 1003.1-1988 (“POSIX.1”)"), ("-p1003.1-90", "IEEE Std 1003.1-1990 (“POSIX.1”)"), ("-p1003.1-96", "ISO/IEC 9945-1:1996 (“POSIX.1”)"), ("-p1003.1-2001", "IEEE Std 1003.1-2001 (“POSIX.1”)"), ("-p1003.1-2004", "IEEE Std 1003.1-2004 (“POSIX.1”)"), ("-p1003.1-2008", "IEEE Std 1003.1-2008 (“POSIX.1”)"), ("-p1003.1-2024", "IEEE Std 1003.1-2024 (“POSIX.1”)"), ("-p1003.1", "IEEE Std 1003.1 (“POSIX.1”)"), ("-p1003.1b", "IEEE Std 1003.1b (“POSIX.1b”)"), ("-p1003.1b-93", "IEEE Std 1003.1b-1993 (“POSIX.1b”)"), ("-p1003.1c-95", "IEEE Std 1003.1c-1995 (“POSIX.1c”)"), ("-p1003.1g-2000", "IEEE Std 1003.1g-2000 (“POSIX.1g”)"), ("-p1003.1i-95", "IEEE Std 1003.1i-1995 (“POSIX.1i”)"), ("-p1003.2", "IEEE Std 1003.2 (“POSIX.2”)"), ("-p1003.2-92", "IEEE Std 1003.2-1992 (“POSIX.2”)"), ("-p1003.2a-92", "IEEE Std 1003.2a-1992 (“POSIX.2”)"), ("-isoC", "ISO/IEC 9899:1990 (“ISO C90”)"), ("-isoC-90", "ISO/IEC 9899:1990 (“ISO C90”)"), ("-isoC-amd1", "ISO/IEC 9899/AMD1:1995 (“ISO C90, Amendment 1”)"), ("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (“ISO C90, Technical Corrigendum 1”)"), ("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (“ISO C90, Technical Corrigendum 2”)"), ("-isoC-99", "ISO/IEC 9899:1999 (“ISO C99”)"), ("-isoC-2011", "ISO/IEC 9899:2011 (“ISO C11”)"), ("-isoC-2023", "ISO/IEC 9899:2024 (“ISO C23”)"), ("-iso9945-1-90", "ISO/IEC 9945-1:1990 (“POSIX.1”)"), ("-iso9945-1-96", "ISO/IEC 9945-1:1996 (“POSIX.1”)"), ("-iso9945-2-93", "ISO/IEC 9945-2:1993 (“POSIX.2”)"), ("-ansiC", "ANSI X3.159-1989 (“ANSI C89”)"), ("-ansiC-89", "ANSI X3.159-1989 (“ANSI C89”)"), ("-ieee754", "IEEE Std 754-1985"), ("-iso8802-3", "ISO 8802-3: 1989"), ("-iso8601", "ISO 8601"), ("-ieee1275-94", "IEEE Std 1275-1994 (“Open Firmware”)"), ("-xpg3", "X/Open Portability Guide Issue 3 (“XPG3”)"), ("-xpg4", "X/Open Portability Guide Issue 4 (“XPG4”)"), ("-xpg4.2", "X/Open Portability Guide Issue 4, Version 2 (“XPG4.2”)"), ("-xbd5", "X/Open Base Definitions Issue 5 (“XBD5”)"), ("-xcu5", "X/Open Commands and Utilities Issue 5 (“XCU5”)"), ("-xsh5", "X/Open System Interfaces and Headers Issue 5 (“XSH5”)"), ("-xns5", "X/Open Networking Services Issue 5 (“XNS5”)"), ("-xns5.2", "X/Open Networking Services Issue 5.2 (“XNS5.2”)"), ("-xcurses4.2", "X/Open Curses Issue 4, Version 2 (“XCURSES4.2”)"), ("-susv1", "Version 1 of the Single UNIX Specification (“SUSv1”)"), ("-susv2", "Version 2 of the Single UNIX Specification (“SUSv2”)"), ("-susv3", "Version 3 of the Single UNIX Specification (“SUSv3”)"), ("-susv4", "Version 4 of the Single UNIX Specification (“SUSv4”)"), ("-svid4", "System V Interface Definition, Fourth Edition (“SVID4”)") ] ================================================ FILE: src/Text/Pandoc/Readers/Mdoc.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.Mdoc Copyright : © 2024 Evan Silberman License : GNU GPL, version 2 or above Maintainer : Evan Silberman Stability : WIP Portability : portable Conversion of mdoc to 'Pandoc' document. -} module Text.Pandoc.Readers.Mdoc (readMdoc) where import Data.Char (isAsciiLower, toUpper) import Data.Default (Default) import Data.Either (fromRight) import Data.Functor (($>)) import Data.Maybe (catMaybes) import Control.Monad (mplus, guard, void, when, unless) import Control.Monad.Except (throwError) #if MIN_VERSION_base(4,19,0) import Data.List (intersperse, unsnoc) #else import Data.List (intersperse) #endif import qualified Data.Map.Strict as M import qualified Data.Text as T import Text.Pandoc.Definition (Pandoc(Pandoc), Meta) import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad(..)) import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (uncons) import Text.Pandoc.Logging import Text.Pandoc.Readers.Mdoc.Lex import Text.Pandoc.Readers.Mdoc.Standards import Text.Parsec (modifyState) import qualified Text.Pandoc.Parsing as P import qualified Data.Foldable as Foldable import Text.Pandoc.Shared (stringify) #if !MIN_VERSION_base(4,19,0) unsnoc :: [a] -> Maybe ([a], a) unsnoc = foldr (\x -> Just . maybe ([], x) (\(~(a, b)) -> (x : a, b))) Nothing #endif {- As a general principle, if mandoc -T lint issues a WARNING admonition or worse about a construct, I consider it fair game for this reader to do something different than what mandoc does with it, including bailing out instead of recovering. -} data MdocSection = ShName | ShSynopsis | ShAuthors | ShSeeAlso | ShOther deriving (Show, Eq) -- Declaration order is important: this is the order fields of a reference -- are printed by mandoc data ReferenceField = Author | ArticleTitle | BookTitle | Publisher | Journal | TechReportTitle | IssueNumber | VolumeNumber | Url | Pages | Institution | PubLocation | PubDate | Optional deriving (Show, Eq, Ord, Enum) -- mandoc allows specifying multiple of _any_ reference field, and just -- prints them all out in document order, even though authors are the only -- field where this is the documented behavior. There's no lint warning -- about this either. I'd prefer to do last-one-wins for the non-author -- fields, which would presumably make it easier to transform the -- bibliographic data into something else, but for now all I'm doing is -- printing the references out the same way mandoc does. type MdocReference = M.Map ReferenceField [T.Text] data MdocState = MdocState { readerOptions :: ReaderOptions , metadata :: Meta , tableCellsPlain :: Bool , spacingMode :: Bool , authorNameSplit :: Bool , inLineEnclosure :: Bool , progName :: Maybe T.Text , currentSection :: MdocSection , currentReference :: MdocReference , logMessages :: [LogMessage] } deriving (Show) instance Default MdocState where def = MdocState { readerOptions = def , metadata = B.nullMeta , tableCellsPlain = True , spacingMode = True , authorNameSplit = False , inLineEnclosure = False , currentSection = ShOther , currentReference = M.empty , progName = Nothing , logMessages = [] } instance HasLogMessages MdocState where addLogMessage msg st = st{ logMessages = msg : logMessages st } getLogMessages st = reverse $ logMessages st type MdocParser m = P.ParsecT [MdocToken] MdocState m -- | Read mdoc from an input string and return a Pandoc document. readMdoc :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readMdoc opts s = do let Sources inps = toSources s tokenz <- mconcat <$> mapM (uncurry lexMdoc) inps let state = def {readerOptions = opts} :: MdocState eitherdoc <- readWithMTokens parseMdoc state (Foldable.toList . unMdocTokens $ tokenz) either (throwError . fromParsecError (Sources inps)) return eitherdoc readWithMTokens :: PandocMonad m => ParsecT [MdocToken] MdocState m a -- ^ parser -> MdocState -- ^ initial state -> [MdocToken] -- ^ input -> m (Either ParseError a) readWithMTokens parser state input = runParserT parser state "source" input parseMdoc :: PandocMonad m => MdocParser m Pandoc parseMdoc = do optional parsePrologue bs <- many parseBlock <* eof meta <- metadata <$> getState let (Pandoc _ blocks) = B.doc $ mconcat bs reportLogMessages return $ Pandoc meta blocks msatisfy :: Monad m => (MdocToken -> Bool) -> P.ParsecT [MdocToken] st m MdocToken msatisfy predic = P.tokenPrim show nextPos testTok where testTok t = if predic t then Just t else Nothing nextPos _ _ (Macro _ pos':_) = pos' nextPos _ _ (Lit _ pos':_) = pos' nextPos _ _ (Str _ pos':_) = pos' nextPos _ _ (Delim _ _ pos':_) = pos' nextPos _ _ (Blank pos':_) = pos' nextPos a _ (Eol{}:x:xs) = nextPos a x xs nextPos pos _ [Eol] = pos nextPos pos _ [] = pos macro :: PandocMonad m => T.Text -> MdocParser m MdocToken macro name = msatisfy t where t (Macro n _) = n == name t _ = False anyMacro :: PandocMonad m => MdocParser m MdocToken anyMacro = msatisfy t where t (Macro _ _) = True t _ = False emptyMacro :: PandocMonad m => T.Text -> MdocParser m MdocToken emptyMacro n = macro n <* eol delim :: PandocMonad m => DelimSide -> MdocParser m MdocToken delim side = msatisfy t where t (Delim s _ _) = side == s t _ = False str :: PandocMonad m => MdocParser m MdocToken str = msatisfy t where t Str{} = True t _ = False lit :: PandocMonad m => MdocParser m MdocToken lit = msatisfy t where t Lit{} = True t _ = False arg :: PandocMonad m => MdocParser m MdocToken arg = msatisfy t where t Lit{} = True t Macro{} = True t _ = False literal :: PandocMonad m => T.Text -> MdocParser m MdocToken literal n = msatisfy t where t (Lit n' _) = n == n' t _ = False blank :: PandocMonad m => MdocParser m MdocToken blank = msatisfy t where t Blank{} = True t _ = False eol :: PandocMonad m => MdocParser m () eol = void $ msatisfy t where t Eol{} = True t _ = False newControlContext :: MdocToken -> Bool newControlContext Eol{} = True newControlContext Macro{} = True newControlContext Str{} = True newControlContext Blank{} = True newControlContext Lit{} = False newControlContext Delim{} = False inlineContextEnd :: PandocMonad m => MdocParser m () inlineContextEnd = eof <|> (void . lookAhead $ msatisfy newControlContext) sectionEnd :: PandocMonad m => MdocParser m () sectionEnd = eof <|> (void . lookAhead $ macro "Sh") argsToInlines :: PandocMonad m => MdocParser m Inlines argsToInlines = do ls <- manyTill arg eol let strs = map (B.str . toString) ls spacify strs parsePrologue :: PandocMonad m => MdocParser m () parsePrologue = do macro "Dd" date <- argsToInlines macro "Dt" (Lit title _) <- lit (Lit section _) <- lit arch <- optionMaybe (toString <$> lit) eol emptyMacro "Os" let adjust = B.setMeta "title" (B.str title) . B.setMeta "date" date . B.setMeta "section" (B.str section) . maybe id (B.setMeta "architecture" . B.str) arch modifyState $ \s -> s{metadata = adjust $ metadata s} shToSectionMode :: T.Text -> MdocSection shToSectionMode "NAME" = ShName shToSectionMode "SYNOPSIS" = ShSynopsis shToSectionMode "AUTHORS" = ShAuthors shToSectionMode "SEE ALSO" = ShSeeAlso shToSectionMode _ = ShOther parseHeader :: PandocMonad m => MdocParser m Blocks parseHeader = do (Macro m _) <- lookAhead $ macro "Sh" <|> macro "Ss" txt <- lineEnclosure m id let lvl = if m == "Sh" then 1 else 2 when (lvl == 1) $ modifyState $ \s -> s{currentSection = (shToSectionMode . stringify) txt} return $ B.header lvl txt parseNameSection :: PandocMonad m => MdocParser m Blocks parseNameSection = do sec <- currentSection <$> getState guard $ sec == ShName nms <- mconcat . intersperse B.space <$> many nameNm macro "Nd" desc <- argsToInlines return $ B.para $ nms <> B.space <> "—" <> B.space <> desc where nameNm = do macro "Nm" nms <- many1 aNm eol return $ mconcat $ intersperse B.space nms comma = msatisfy $ \case (Delim _ "," _) -> True _ -> False aNm = do nm <- toString <$> lit c <- option mempty (toString <$> comma) modifyState $ \s -> s{progName = mplus (progName s) (Just nm)} return $ B.code nm <> B.str c parseSynopsisSection :: PandocMonad m => MdocParser m Blocks parseSynopsisSection = do sec <- currentSection <$> getState guard $ sec == ShSynopsis parseSynopsis sectionEnd parseMiniSynopsis :: PandocMonad m => MdocParser m Blocks parseMiniSynopsis = do macro "nr" literal "nS" literal "1" eol parseSynopsis (sectionEnd <|> end) where end = do macro "nr" literal "nS" literal "0" eol return () parseSynopsis :: PandocMonad m => MdocParser m () -> MdocParser m Blocks parseSynopsis end = do bs <- manyTill synopsisBlock end return $ mconcat bs where synopsisGroup p = B.lineBlock <$> many1 p <* optional (emptyMacro "Pp") synopsisBlock = synopsisGroup parseInvocation <|> synopsisGroup (parseCd <* optional eol) <|> synopsisGroup (parseIn <* optional eol) <|> synopsisGroup (parseFd <* optional eol) <|> synopsisGroup (parseVt <* optional eol) <|> try parseSignature <|> parseWeirdSignature <|> parseRegularBlock parseInvocation = do nm <- parseNm optional eol rest <- many synopsisInline spacify (nm:rest) parseSignature = do ft <- parseFt <* optional eol sig <- (parseFn <|> parseFo) <* optional eol return $ B.lineBlock [ft, sig <> ";"] -- e.g. OpenBSD MB_CUR_MAX(3), mild abuse of notation for Ft parseWeirdSignature = do ft <- parseFt <* optional eol rest <- many synopsisInline line <- spacify (ft:rest) return $ B.lineBlock [line] synopsisInline = parseSmToggle <|> parseStrs <|> (controlLine >>= spacify) "synopsis inlines" safeEol = do amNested <- inLineEnclosure <$> getState unless amNested $ optional eol controlLine = many1 ((choice otherInlineMacros <|> litsAndDelimsToInlines) <* safeEol) parseSeeAlsoSection :: PandocMonad m => MdocParser m Blocks parseSeeAlsoSection = do sec <- currentSection <$> getState guard $ sec == ShSeeAlso blocks <- many1Till parseSeeAlsoBlock sectionEnd return $ mconcat blocks where parseSeeAlsoBlock = parseRegularBlock <|> (B.para <$> parseRs) -- roff(7) says "In text lines, whitespace is preserved within a line." I -- considered following this rule but it really cuts against the grain of what -- Pandoc writers want to work with, for no clear benefit. This isn't wholly -- inconsistent with mandoc, because it makes no effort to render multiple -- consecutive spaces from the source document in HTML. Hence I call B.text -- instead of B.str parseStr :: PandocMonad m => MdocParser m Inlines parseStr = do (Str txt _) <- str return $ B.text txt -- It's unclear whether consecutive text lines ought to be affected by the -- spacing mode. mdoc(7) claims that: -- -- > By default, spacing is on. When switched off, no white space is -- > inserted between macro arguments and between the output generated from -- > adjacent macros, but text lines still get normal spacing between words -- > **and sentences.** -- -- (emphasis added) -- This implied to me that while spacing is off, consecutive text lines -- would have spacing between them as normal. In fact, in mandoc's -- implementation, they do not: -- -- text -- .Sm off -- text. -- text -- .Sm on -- text -- -- renders as -- -- text text.text text -- -- (The "." is in there since the allusion in the documentation to -- sentences made me wonder if that made a difference; it doesn't.) -- -- I've chosen to adopt my interpretation of the documented behavior, rather -- than mandoc's implementation. Multiple consecutive strs within a block get -- spaces between them and then packed up together, and text lines are not -- affected by the spacing mode. -- -- Reported at https://inbox.vuxu.org/mandoc-discuss/369KFE6SHMXSE.3PS4387AYEFB5@silby.fyi/T/ parseStrs :: PandocMonad m => MdocParser m Inlines parseStrs = do txt <- many1 parseStr return $ mconcat $ intersperse B.space txt parseDelim :: PandocMonad m => DelimSide -> MdocParser m Inlines parseDelim pos = do (Delim _ txt _) <- delim pos return $ B.str txt litsToText :: PandocMonad m => MdocParser m [T.Text] litsToText = do ls <- many1 lit return $ map toString ls litsToInlines :: PandocMonad m => MdocParser m Inlines litsToInlines = do ls <- many1 lit let strs = map (B.str . toString) ls spacify strs litsAndDelimsToInlines :: PandocMonad m => MdocParser m Inlines litsAndDelimsToInlines = do (o, ls, c) <- delimitedArgs $ many lit guard $ not (null o && null ls && null c) strs <- spacify $ map (B.str . toString) ls return $ o <> strs <> c openingDelimiters :: PandocMonad m => MdocParser m Inlines openingDelimiters = do openDelim <- mconcat <$> many (parseDelim Open) omids <- pipes addSpace <- spacingMode <$> getState let omid | null omids = mempty | addSpace = omids <> B.space | otherwise = omids return $ openDelim <> omid pipes :: PandocMonad m => MdocParser m Inlines pipes = many (parseDelim Middle) >>= spacify closingDelimiters :: PandocMonad m => MdocParser m Inlines closingDelimiters = do cmids <- pipes addSpace <- spacingMode <$> getState let cmid | null cmids = mempty | addSpace = B.space <> cmids | otherwise = cmids closeDelim <- mconcat <$> many (parseDelim Close) return $ cmid <> closeDelim delimitedArgs :: PandocMonad m => MdocParser m x -> MdocParser m (Inlines, x, Inlines) delimitedArgs p = do openDelim <- openingDelimiters inlines <- p closeDelim <- closingDelimiters return (openDelim, inlines, closeDelim) simpleInline :: PandocMonad m => T.Text -> (Inlines -> Inlines) -> MdocParser m Inlines simpleInline nm xform = do macro nm segs <- manyTill segment inlineContextEnd spacify segs where segment = do (openDelim, inlines, closeDelim) <- delimitedArgs $ option mempty litsToInlines return $ openDelim <> xform inlines <> closeDelim codeLikeInline' :: PandocMonad m => T.Text -> T.Text -> MdocParser m Inlines codeLikeInline' nm cl = simpleInline nm (eliminateEmpty (B.codeWith (cls cl) . stringify)) codeLikeInline :: PandocMonad m => T.Text -> MdocParser m Inlines codeLikeInline nm = codeLikeInline' nm nm spanLikeInline :: PandocMonad m => T.Text -> MdocParser m Inlines spanLikeInline nm = simpleInline nm (eliminateEmpty (B.spanWith (cls nm))) -- One-line enclosures need a little bit of state so that we don't parse -- the closing delimiters that follow nested one-line or multiline -- enclosures; the closing delimiters are meant to go after the close of -- the outermost enclosure. Hence we respect and set inLineEnclosure. lineEnclosure :: PandocMonad m => T.Text -> (Inlines -> Inlines) -> MdocParser m Inlines lineEnclosure nm xform = do macro nm amNested <- inLineEnclosure <$> getState modifyState $ \s -> s{inLineEnclosure = True} first <- openingDelimiters further <- (manyTill (parseInlineMacro <|> (try (litsAndDelimsToInlines <* notFollowedBy eol)) <|> litsToInlines <|> openingDelimiters) lineEnclosureContextEnd) further' <- spacify further finally <- if amNested then mempty else closingDelimiters <* optional eol modifyState $ \s -> s{inLineEnclosure = amNested} return $ first <> xform further' <> finally where lineEnclosureContextEnd = try $ void (lookAhead (macro "Ta")) <|> lookAhead (many (macro "Ns" <|> delim Close) *> eol) -- The Ns, Ap, and Sm macros affect the automatic insertion of spaces between -- macro arguments that occurs by default. We parse these macros to RawInlines -- that we then eliminate in foldNoSpaces. If any of these macros end up -- in the final AST returned by readMdoc, it's a bug. noSpace :: Inlines noSpace = B.rawInline "mdoc" "Ns" apMacro :: Inlines apMacro = B.rawInline "mdoc" "Ap" smOff :: Inlines smOff = B.rawInline "mdoc" "Sm off" smOn :: Inlines smOn = B.rawInline "mdoc" "Sm on" -- Accumulator for eliminating of Ns, Ap, and Sm macros from a list of 'Inlines' data SpacifyState = SpacifyState { accum :: [Inlines], -- already-folded 'Inlines' prev :: Inlines, -- content we might be appending further content to ns :: Bool, -- True when we've read an Ns and are waiting to concatenate content to prev sm :: Bool -- True when spacing mode is on } instance Default SpacifyState where def = SpacifyState [] mempty False True -- Given a list of 'Inlines'es, concatenate consecutive elements that shouldn't -- have a 'Space' inserted between them based on changes to the spacing mode, -- Ap macros, and Ns macros. foldNoSpaces :: [Inlines] -> [Inlines] foldNoSpaces xs = (finalize . foldl go def) xs where go :: SpacifyState -> Inlines -> SpacifyState go s x | ns s && x == noSpace = s | x == apMacro = s{prev = prev s <> "'", ns = True} | x == noSpace = s{ns = True} | x == smOn = s{sm = True} | sm s && x == smOff = s{accum = accum s <> [prev s], prev = mempty, sm = False} | ns s = s{prev = prev s <> x, ns = False} | not (sm s) = s{prev = prev s <> x} | null (prev s) = s{prev = x} | otherwise = s{accum = accum s <> [prev s], prev = x} finalize s | null (prev s) = accum s | otherwise = accum s <> [prev s] -- Add any necessary spaces between individual 'Inlines' in a list. -- Respects the spacing mode status. This should more or less -- always get applied to any list of 'Inlines' before doing anything -- else with it. spacify :: PandocMonad m => [Inlines] -> MdocParser m Inlines spacify x = do mode <- spacingMode <$> getState return (go mode x) where go True = mconcat . intersperse B.space . foldNoSpaces go False = mconcat . foldNoSpaces -- Compatibility note: mandoc permits, and doesn't warn on, "vertical" macros -- (Pp, Bl/El, Bd/Ed) inside of "horizontal" block partial-explicit quotations -- like Do/Dc. However there are no OpenBSD manual pages that employ such markup -- and it doesn't look right when rendered. We don't attempt to consume anything -- but pandoc inlines inside of these multiline enclosures. multilineEnclosure :: PandocMonad m => T.Text -> T.Text -> (Inlines -> Inlines) -> MdocParser m Inlines multilineEnclosure op cl xform = do macro op amNested <- inLineEnclosure <$> getState -- we're now "protected" from any outer enclosure or .It modifyState $ \s -> s{inLineEnclosure = False} openDelim <- mconcat <$> many (parseDelim Open) optional eol contents <- parseInlines (macro cl show cl) closeDelim <- if amNested then mempty else mconcat <$> many (parseDelim Close) <* optional eol modifyState $ \s -> s{inLineEnclosure = amNested} return $ openDelim <> xform contents <> closeDelim parseEo :: PandocMonad m => MdocParser m Inlines parseEo = do macro "Eo" odel <- del optional eol inner <- parseInlines macro "Ec" cdel <- del optional eol return $ odel <> inner <> cdel where del = B.str . toString <$> (arg <|> delim Open <|> delim Middle <|> delim Close) eliminateEmpty :: (Inlines -> Inlines) -> Inlines -> Inlines eliminateEmpty x y = if null y then mempty else x y cls :: T.Text -> B.Attr cls x = (mempty, [x], mempty) -- mandoc -T html formats Sy with a tag, since it's not really -- semantically , but Strong is our best option in Pandoc parseSy :: PandocMonad m => MdocParser m Inlines parseSy = simpleInline "Sy" (eliminateEmpty B.strong) parseEm :: PandocMonad m => MdocParser m Inlines parseEm = simpleInline "Em" (eliminateEmpty B.emph) parseNo :: PandocMonad m => MdocParser m Inlines parseNo = simpleInline "No" (eliminateEmpty id) -- Deprecated, mandoc doesn't style this at all parseTn :: PandocMonad m => MdocParser m Inlines parseTn = simpleInline "Tn" (eliminateEmpty id) parseLi :: PandocMonad m => MdocParser m Inlines parseLi = codeLikeInline "Li" parseEv :: PandocMonad m => MdocParser m Inlines parseEv = codeLikeInline "Ev" parseDv :: PandocMonad m => MdocParser m Inlines parseDv = codeLikeInline "Dv" parseAd :: PandocMonad m => MdocParser m Inlines parseAd = spanLikeInline "Ad" parseVa :: PandocMonad m => MdocParser m Inlines parseVa = codeLikeInline' "Va" "variable" parseVt :: PandocMonad m => MdocParser m Inlines parseVt = codeLikeInline' "Vt" "variable" parseAn :: PandocMonad m => MdocParser m Inlines parseAn = try anSplit <|> anRegular where anSplit = do macro "An" mode <- literal "-split" $> True <|> literal "-nosplit" $> False modifyState $ \s -> s{authorNameSplit = mode} return mempty anRegular = do an <- spanLikeInline "An" spl <- authorNameSplit <$> getState return $ (if spl then B.linebreak else mempty) <> an parseMs :: PandocMonad m => MdocParser m Inlines parseMs = spanLikeInline "Ms" -- TODO implement internal reference links parseSx :: PandocMonad m => MdocParser m Inlines parseSx = spanLikeInline "Sx" -- I'm not sure why mandoc inserts a ~ when Mt is missing an argument, -- but it does, and it doesn't issue a warning, so that quirk is -- retained. parseMt :: PandocMonad m => MdocParser m Inlines parseMt = simpleInline "Mt" mailto where mailto x | null x = B.link ("mailto:~") "" "~" | otherwise = B.link ("mailto:" <> stringify x) "" x parsePa :: PandocMonad m => MdocParser m Inlines parsePa = simpleInline "Pa" p where p x | null x = B.spanWith (cls "Pa") "~" | otherwise = B.spanWith (cls "Pa") x -- There's a number of unique-looking cases for Fl parsing so I am just -- handling them very explicitly instead of trying to generalize anything -- enough to handle it. Could conceivably be better. parseFl :: PandocMonad m => MdocParser m Inlines parseFl = do macro "Fl" start <- option mempty (emptyWithDelim <|> flfl <|> emptyWithMacro <|> emptyEmpty) segs <- manyTill segment inlineContextEnd spacify ([start] <> segs) where emptyWithDelim = do lookAhead $ many1 (delim Middle <|> delim Close) ds <- closingDelimiters return $ fl "-" <> ds flfl = do lookAhead (macro "Fl") x:xs <- B.toList <$> parseFl let xx = B.codeWith (cls "Fl") $ "-" <> stringify x return $ xx <> B.fromList xs emptyWithMacro = do lookAhead anyMacro rest <- parseInline return $ fl "-" <> rest emptyEmpty = lookAhead eol $> fl "-" segment = do (openDelim, inlines, closeDelim) <- delimitedArgs $ option mempty litsToText inner <- (spacify . (map fl) . flags) inlines return $ openDelim <> inner <> closeDelim fl = B.codeWith (cls "Fl") flags [] = ["-"] flags xs = map ("-" <>) xs parseAr :: PandocMonad m => MdocParser m Inlines parseAr = simpleInline "Ar" ar where ar x | null x = B.codeWith (cls "variable") "file ..." | otherwise = B.codeWith (cls "variable") $ stringify x parseCm :: PandocMonad m => MdocParser m Inlines parseCm = codeLikeInline "Cm" parseIc :: PandocMonad m => MdocParser m Inlines parseIc = codeLikeInline "Ic" parseEr :: PandocMonad m => MdocParser m Inlines parseEr = codeLikeInline "Er" parseCd :: PandocMonad m => MdocParser m Inlines parseCd = codeLikeInline "Cd" parseQl :: PandocMonad m => MdocParser m Inlines parseQl = lineEnclosure "Ql" $ B.codeWith (cls "Ql") . stringify parseDq :: PandocMonad m => MdocParser m Inlines parseDq = lineEnclosure "Dq" B.doubleQuoted parseDo :: PandocMonad m => MdocParser m Inlines parseDo = multilineEnclosure "Do" "Dc" B.doubleQuoted parseSq :: PandocMonad m => MdocParser m Inlines parseSq = lineEnclosure "Sq" B.singleQuoted parseSo :: PandocMonad m => MdocParser m Inlines parseSo = multilineEnclosure "So" "Sc" B.singleQuoted parseQq :: PandocMonad m => MdocParser m Inlines parseQq = lineEnclosure "Qq" $ \x -> "\"" <> x <> "\"" parseQo :: PandocMonad m => MdocParser m Inlines parseQo = multilineEnclosure "Qo" "Qc" $ \x -> "\"" <> x <> "\"" parsePq :: PandocMonad m => MdocParser m Inlines parsePq = lineEnclosure "Pq" $ \x -> "(" <> x <> ")" parsePo :: PandocMonad m => MdocParser m Inlines parsePo = multilineEnclosure "Po" "Pc" $ \x -> "(" <> x <> ")" parseBq :: PandocMonad m => MdocParser m Inlines parseBq = lineEnclosure "Bq" $ \x -> "[" <> x <> "]" parseBo :: PandocMonad m => MdocParser m Inlines parseBo = multilineEnclosure "Bo" "Bc" $ \x -> "[" <> x <> "]" -- For our purposes this probably behaves identically to Bq -- in most circumstances but I might need to do something -- special with it in SYNOPSIS parseOp :: PandocMonad m => MdocParser m Inlines parseOp = lineEnclosure "Op" $ \x -> "[" <> x <> "]" parseOo :: PandocMonad m => MdocParser m Inlines parseOo = multilineEnclosure "Oo" "Oc" $ \x -> "[" <> x <> "]" parseBrq :: PandocMonad m => MdocParser m Inlines parseBrq = lineEnclosure "Brq" $ \x -> "{" <> x <> "}" parseBro :: PandocMonad m => MdocParser m Inlines parseBro = multilineEnclosure "Bro" "Brc" $ \x -> "{" <> x <> "}" parseAq :: PandocMonad m => MdocParser m Inlines parseAq = lineEnclosure "Aq" $ \x -> "⟨" <> x <> "⟩" parseAo :: PandocMonad m => MdocParser m Inlines parseAo = multilineEnclosure "Ao" "Ac" $ \x -> "⟨" <> x <> "⟩" parseDl :: PandocMonad m => MdocParser m Blocks parseDl = do inner <- lineEnclosure "Dl" id return $ B.codeBlock (stringify inner) parseD1 :: PandocMonad m => MdocParser m Blocks parseD1 = do inner <- lineEnclosure "D1" id return $ B.divWith (cls "display") $ B.plain inner parseNm :: PandocMonad m => MdocParser m Inlines parseNm = do macro "Nm" mnm <- (progName <$> getState) (op, rg, cl) <- delimitedArgs $ option mempty litsToInlines return $ case (mnm, rg) of (Just nm, x) | null x -> op <> ok nm <> cl (_, x) -> op <> (ok . stringify) x <> cl where ok = B.codeWith (cls "Nm") parseXr :: PandocMonad m => MdocParser m Inlines parseXr = do macro "Xr" (open, (name, section), close) <- delimitedArgs f let ref = name <> "(" <> section <> ")" return $ open <> B.spanWith (cls "Xr") (B.str ref) <> close where f = do n <- lit "Xr manual name" s <- lit "Xr manual section" return (toString n, toString s) parseIn :: PandocMonad m => MdocParser m Inlines parseIn = do macro "In" openClose <- closingDelimiters openOpen <- openingDelimiters header <- toString <$> lit close <- closingDelimiters return $ open openClose openOpen <> B.codeWith (cls "In") ("<" <> header <> ">") <> close where open a b | null a = b | null b = a | otherwise = a <> B.space <> b parseFd :: PandocMonad m => MdocParser m Inlines parseFd = codeLikeInline "Fd" parseFt :: PandocMonad m => MdocParser m Inlines parseFt = codeLikeInline' "Ft" "variable" -- The output here is comparable to mandoc's HTML output, which doesn't tag -- the commas/parentheses. Is this questionable from a pandoc POV? formatFunction :: T.Text -> [Inlines] -> Inlines formatFunction nm args = B.codeWith (cls "Fn") nm <> "(" <> args' <> ")" where args' = mconcat $ intersperse (", ") args parseFn :: PandocMonad m => MdocParser m Inlines parseFn = do macro "Fn" (op, (nm, args), cl) <- delimitedArgs f return $ op <> formatFunction nm (fmap (B.codeWith (cls "variable")) args) <> cl where f = do nm <- toString <$> lit args <- option [] litsToText return (nm, args) parseFa :: PandocMonad m => MdocParser m Inlines parseFa = codeLikeInline' "Fa" "variable" parseFo :: PandocMonad m => MdocParser m Inlines parseFo = do macro "Fo" nm <- toString <$> lit eol args <- many (parseFa <* eol) macro "Fc" return $ formatFunction nm args parseLk :: PandocMonad m => MdocParser m Inlines parseLk = do macro "Lk" openClose <- closingDelimiters openOpen <- openingDelimiters url <- toString <$> lit inner <- many segment >>= spacify close <- closingDelimiters let label | null inner = B.str url | otherwise = inner return $ open openClose openOpen <> B.link url "" label <> close where open a b | null a = b | null b = a | otherwise = a <> B.space <> b end = msatisfy newControlContext segment = do a <- openingDelimiters m <- option mempty litsToInlines z <- try (closingDelimiters <* notFollowedBy end) <|> option mempty pipes guard $ not $ all null [a, m, z] return $ a <> m <> z -- This is a raw roff request but it appears sometimes in mdoc -- manuals and is easy enough to handle parsebr :: PandocMonad m => MdocParser m Inlines parsebr = emptyMacro "br" >> return B.linebreak parseNs :: PandocMonad m => MdocParser m Inlines parseNs = macro "Ns" >> return noSpace -- Per mdoc(7), Pf prefix macro [argument ...] is equivalent to -- No \&prefix Ns macro [argument ...] and because of the way -- spacify works, the easiest thing to do is just push an Ns onto -- the input parsePf :: PandocMonad m => MdocParser m Inlines parsePf = do macro "Pf" t <- toString <$> anyToken rest <- getInput pos <- getPosition setInput $ (Macro "Ns" pos):rest return $ B.str t parseAp :: PandocMonad m => MdocParser m Inlines parseAp = macro "Ap" >> return apMacro parseEx :: PandocMonad m => MdocParser m Inlines parseEx = do macro "Ex" literal "-std" args <- fmap toString <$> many lit pn <- progName <$> getState eol return $ "The" <> B.space <> utils pn args <> B.space <> "0 on success, and >0 if an error occurs." where nm = B.codeWith (cls "Nm") sing = "utility exits" plur = "utilities exit" utils (Just x) [] = nm x <> B.space <> sing utils _ [x] = nm x <> B.space <> sing utils _ [x,y] = nm x <> B.space <> "and" <> B.space <> nm y <> B.space <> plur utils pn xs = case (pn, unsnoc xs) of (Nothing, Nothing) -> sing (_, Just (hd, end)) -> mconcat ((intersperse (", ") . fmap nm) hd) <> ", and " <> nm end <> B.space <> plur (Just p, Nothing) -> nm p <> B.space <> sing parseRv :: (PandocMonad m) => MdocParser m Inlines parseRv = do macro "Rv" literal "-std" args <- fmap toString <$> many lit pn <- progName <$> getState eol return $ go pn args where nm a = B.codeWith (cls "Fn") a <> "()" nothing = "Upon successful completion, the value 0 is returned;" sing = "function returns" plur = "functions return" success = "the value 0 if successful;" errno = "otherwise the value -1 is returned and the global variable" <> B.codeWith (cls "variable") "errno" <> "is set to indicate the error." message conj = "The" <> B.space <> conj <> B.space <> success <> B.space <> errno go (Just x) [] = message (nm x <> B.space <> sing) go _ [x] = message (nm x <> B.space <> sing) go _ [x, y] = message (nm x <> B.space <> "and" <> B.space <> nm y <> B.space <> plur) go pn xs = case (pn, unsnoc xs) of (Nothing, Nothing) -> nothing <> B.space <> errno (_, Just (hd, end)) -> message (mconcat ((intersperse (", ") . fmap nm) hd) <> ", and " <> nm end <> B.space <> plur) (Just p, Nothing) -> message (nm p <> B.space <> sing) parseSt :: PandocMonad m => MdocParser m Inlines parseSt = do macro "St" (Lit std pos) <- lit case standard std of Nothing -> do logMessage $ SkippedContent ("unrecognized argument to St: " <> std) pos return mempty Just t -> return $ B.text t -- TODO incorporate well-known library description and linker options -- from mandoc lib.in expected in FreeBSD LIBRARY section, at minimum. parseLb :: PandocMonad m => MdocParser m Inlines parseLb = do macro "Lb" library <- toString <$> lit return $ "library" <> B.space <> B.doubleQuoted (B.str library) unixVersion :: PandocMonad m => T.Text -> T.Text -> MdocParser m Inlines unixVersion m s = do macro m (o, v, c) <- delimitedArgs (option mempty (toString <$> lit)) return $ o <> B.str s <> f v <> c where f v | T.null v = mempty | otherwise = B.space <> B.str v parseAt :: PandocMonad m => MdocParser m Inlines parseAt = do macro "At" (o, v, c) <- delimitedArgs (optionMaybe (toString <$> lit)) let v' = maybe "AT&T UNIX" attVer v return $ o <> B.text v' <> c where isVersion x = x `elem` ["1", "2", "3", "4", "5", "6", "7"] isRelease x = x `elem` ["1", "2", "3", "4"] attVer (T.stripPrefix "v" -> Just ver) | isVersion ver = "Version " <> ver <> " AT&T UNIX" attVer "32v" = "Version 7 AT&T UNIX/32V" attVer "III" = "AT&T System III UNIX" attVer (T.stripPrefix "V." -> Just release) | isRelease release = "AT&T System V Release " <> release <> " UNIX" attVer "V" = "AT&T System V UNIX" attVer x = "AT&T UNIX " <> x parseBsx :: PandocMonad m => MdocParser m Inlines parseBsx = unixVersion "Bsx" "BSD/OS" parseBx :: PandocMonad m => MdocParser m Inlines parseBx = do macro "Bx" (o, v, c) <- delimitedArgs zeroToTwoLits return $ o <> bsd v <> c where zeroToTwoLits = do toks <- try (count 2 lit) <|> count 1 lit <|> count 0 lit return $ toString <$> toks bsd [] = B.str "BSD" bsd [x] = B.str $ x <> "BSD" bsd (x:y:_) = B.str (x <> "BSD" <> "-" <> T.toTitle y) parseDx :: PandocMonad m => MdocParser m Inlines parseDx = unixVersion "Dx" "DragonFly" parseFx :: PandocMonad m => MdocParser m Inlines parseFx = unixVersion "Fx" "FreeBSD" -- This dance to capitalize a letter at the end of a NetBSD -- version matches what mandoc does to the argument of .Nx. -- See mandoc mdoc_validate.c r1.350 -- Curiously, there's little easy-to-find evidence of what -- these lettered releases actually are, other than -- references in man page history sections to 0.9A etc. parseNx :: PandocMonad m => MdocParser m Inlines parseNx = do macro "Nx" (o, v, c) <- delimitedArgs (option mempty (toString <$> lit)) return $ o <> "NetBSD" <> f v <> c where f v | T.null v = mempty | otherwise = B.space <> B.str (fromRight v $ readWith earlyNetBSDVersion () v) earlyNetBSDVersion = do major <- oneOf "01" dot <- char '.' minor <- digit ltr <- satisfy isAsciiLower return $ T.pack [major, dot, minor, toUpper ltr] parseOx :: PandocMonad m => MdocParser m Inlines parseOx = unixVersion "Ox" "OpenBSD" parseUx :: PandocMonad m => MdocParser m Inlines parseUx = macro "Ux" >> return (B.str "UNIX") parseInlineMacro :: PandocMonad m => MdocParser m Inlines parseInlineMacro = choice (synopsisTopicMacros <> otherInlineMacros) "inline macro" -- These macros always start a new line in SYNOPSIS synopsisTopicMacros :: PandocMonad m => [MdocParser m Inlines] synopsisTopicMacros = [parseNm, parseCd, parseFd, parseFn, parseFo, parseIn, parseVt, parseFt] otherInlineMacros :: PandocMonad m => [MdocParser m Inlines] otherInlineMacros = [ parseSy, parseEm, parseLk, parseLi, parseEv, parseDv, parseMt, parsePa, parseFl, parseCm, parseIc, parseEr, parseAd, parseVa, parseAn, parseMs, parseSx, parseAr, parseFa, parseNo, parseTn, parseXr, parseQl, parseOp, parseSq, parseDq, parseQq, parsePq, parseBq, parseBrq, parseAq, parseEo, parseSo, parseDo, parseQo, parsePo, parseBo, parseBro, parseAo, parseOo, parseBf, parseRsInline, parseEx, parseRv, parseSt, parseLb, parseAt, parseBsx, parseBx, parseDx, parseFx, parseNx, parseOx, parseUx, parsebr, parseAp, parsePf, parseNs, skipUnsupportedInlines ] parseInline :: PandocMonad m => MdocParser m Inlines parseInline = parseStrs <|> (controlLine >>= spacify) "text lines or inline macros" where safeEol = do amNested <- inLineEnclosure <$> getState unless amNested $ optional eol controlLine = many1 ((parseInlineMacro <|> litsAndDelimsToInlines) <* safeEol) parseInlines :: PandocMonad m => MdocParser m Inlines parseInlines = many1 (parseSmToggle <|> parseInline) >>= spacify -- Lp is a deprecated synonym for Pp parsePara :: PandocMonad m => MdocParser m Blocks parsePara = B.para . B.trimInlines <$> parseInlines <* optional (emptyMacro "Pp" <|> emptyMacro "Lp") -- Indented display blocks are visually similar to block quotes -- but rarely carry those semantics. I'm just putting things in -- divs. Centered is discouraged and rarely seen. parseDisplay :: PandocMonad m => MdocParser m Blocks parseDisplay = do literal "-filled" <|> literal "-ragged" <|> literal "-centered" many $ (literal "-offset" *> lit) <|> (literal "-compact") eol B.divWith (cls "display") . mconcat <$> many parseRegularBlock -- This is something of a best-effort interpretation of the -unfilled -- display block type. The main difference with mandoc is probably -- that newlines inside of multiline enclosures won't be preserved. parseUnfilled :: PandocMonad m => MdocParser m Blocks parseUnfilled = do literal "-unfilled" many $ (literal "-offset" *> lit) <|> (literal "-compact") eol lns <- many $ Just <$> parseStrPreserveSpace <|> Nothing <$ parseSmToggle <|> Just <$> parseInline <|> Just "" <$ emptyMacro "Pp" return $ B.lineBlock (catMaybes lns) where parseStrPreserveSpace = (B.str . toString) <$> str <|> (blank *> mempty) parseCodeBlock :: PandocMonad m => MdocParser m Blocks parseCodeBlock = do literal "-literal" many $ (literal "-offset" *> lit) <|> (literal "-compact") eol lns <- many $ Just . toString <$> (str <|> blank) <|> Nothing <$ parseSmToggle <|> Just . stringify <$> parseInline <|> Just "" <$ emptyMacro "Pp" return $ B.codeBlock (T.unlines (catMaybes lns)) parseBd :: PandocMonad m => MdocParser m Blocks parseBd = do macro "Bd" blk <- parseCodeBlock <|> parseDisplay <|> parseUnfilled emptyMacro "Ed" return blk -- This is a bit of a best effort version. Hypothetically multiple blocks -- could occur inside a Bf and this should be a stateful thing but I don't -- know if that's observed in the wild. parseBf :: PandocMonad m => MdocParser m Inlines parseBf = do macro "Bf" xform <- B.strong <$ (literal "Sy" <|> literal "-symbolic") <|> B.emph <$ (literal "Em" <|> literal "-emphasis") <|> code <$ (literal "Li" <|> literal "-literal") eol ins <- parseInlines emptyMacro "Ef" return $ xform ins where code = B.code . stringify skipListArgument :: (PandocMonad m) => MdocParser m () skipListArgument = void $ choice [ literal "-width" *> lit, literal "-offset" *> lit, literal "-compact" ] parseItemList :: PandocMonad m => MdocParser m Blocks parseItemList = do f <- (choice (map literal ["-bullet", "-dash", "-hyphen", "-item"]) $> B.bulletList) <|> literal "-enum" $> B.orderedList many skipListArgument eol items <- many bulletItem return $ f items where bulletItem = do emptyMacro "It" mconcat <$> many parseRegularBlock -- Despite some ambiguous documentation to the contrary the Xo/Xc macros -- only seem genuinely useful in an .It head, and it's not clear what if -- anything it means to use them somewhere else in a contemporary mdoc manual. -- See https://inbox.vuxu.org/mandoc-discuss/2UKLZW0DL8BSM.2IIO9W4HSUSRR@silby.fyi/T/ -- for more blathering. parseDefinitionList :: PandocMonad m => MdocParser m Blocks parseDefinitionList = do headParser <- (choice . map literal) ["-hang", "-inset", "-ohang", "-tag"] $> parsedHead <|> literal "-diag" $> diagHead many skipListArgument eol items <- many (parseSmToggle *> mempty <|> dlItem headParser) return $ B.definitionList items where parsedHead = try xoListHead <|> eolListHead eolListHead = do modifyState $ \s -> s{inLineEnclosure = True} inner <- parseInlines eol modifyState $ \s -> s{inLineEnclosure = False} return inner diagHead = argsToInlines dlItem hed = do -- Some manuals have an evidently useless .Pp before .It -- e.g. OpenBSD ld(1), just deal with it. many ((void . emptyMacro) "Pp" <|> skipUnsupportedMacro "Tg") macro "It" dt <- hed dd <- mconcat <$> many parseRegularBlock return (dt, [dd]) xoListHead = do before <- option mempty parseInline macro "Xo" optional eol after <- many1Till parseInlines (emptyMacro "Xc") spacify (before:after) -- TODO support implicit rows: -- If the first line of the body of a -column list is not an It macro line, -- It contexts spanning one input line each are implied until an It macro -- line is encountered -- and support literal tabs parseColumnList :: PandocMonad m => MdocParser m Blocks parseColumnList = do literal "-column" many skipListArgument many $ arg <|> delim Open <|> delim Middle <|> delim Close eol rows <- many listRow return $ B.simpleTable [] rows where listRow = do optional (emptyMacro "Pp") macro "It" fmap B.plain <$> sepBy (parseInlines <|> pure mempty) (macro "Ta" <* optional eol) parseBl :: PandocMonad m => MdocParser m Blocks parseBl = do macro "Bl" blk <- parseItemList <|> parseDefinitionList <|> parseColumnList emptyMacro "El" return blk referenceField :: PandocMonad m => T.Text -> ReferenceField -> MdocParser m () referenceField m field = do macro m reference <- currentReference <$> getState contents <- stringify <$> litsAndDelimsToInlines eol modifyState $ \s -> s{currentReference = M.insertWith (++) field [contents] reference} return () parsePercentA :: PandocMonad m => MdocParser m () parsePercentA = referenceField "%A" Author parsePercentB :: PandocMonad m => MdocParser m () parsePercentB = referenceField "%B" BookTitle parsePercentC :: PandocMonad m => MdocParser m () parsePercentC = referenceField "%C" PubLocation parsePercentD :: PandocMonad m => MdocParser m () parsePercentD = referenceField "%D" PubDate parsePercentI :: PandocMonad m => MdocParser m () parsePercentI = referenceField "%I" Publisher parsePercentJ :: PandocMonad m => MdocParser m () parsePercentJ = referenceField "%J" Journal parsePercentN :: PandocMonad m => MdocParser m () parsePercentN = referenceField "%N" IssueNumber parsePercentO :: PandocMonad m => MdocParser m () parsePercentO = referenceField "%O" Optional parsePercentP :: PandocMonad m => MdocParser m () parsePercentP = referenceField "%P" Pages parsePercentQ :: PandocMonad m => MdocParser m () parsePercentQ = referenceField "%Q" Institution parsePercentR :: PandocMonad m => MdocParser m () parsePercentR = referenceField "%R" TechReportTitle parsePercentT :: PandocMonad m => MdocParser m () parsePercentT = referenceField "%T" ArticleTitle parsePercentU :: PandocMonad m => MdocParser m () parsePercentU = referenceField "%U" Url parsePercentV :: PandocMonad m => MdocParser m () parsePercentV = referenceField "%V" VolumeNumber parseReferenceField :: PandocMonad m => MdocParser m () parseReferenceField = choice [ parsePercentA, parsePercentB, parsePercentC, parsePercentD, parsePercentI, parsePercentJ, parsePercentN, parsePercentO, parsePercentP, parsePercentQ, parsePercentR, parsePercentT, parsePercentU, parsePercentV ] parseRsInline :: PandocMonad m => MdocParser m Inlines parseRsInline = do sec <- currentSection <$> getState guard $ sec /= ShSeeAlso parseRs parseRs :: PandocMonad m => MdocParser m Inlines parseRs = do emptyMacro "Rs" modifyState $ \s -> s{currentReference = M.empty} many1 parseReferenceField emptyMacro "Re" ref <- currentReference <$> getState -- TODO formatting fields correctly return $ B.text $ (M.foldl f mempty ref) <> "." where join v = T.concat (intersperse ", " v) f a v | T.null a = join v | otherwise = a <> ", " <> join v -- mandoc's roff(7) says "Blank text lines, which may include whitespace, -- are only permitted within literal contexts." mandoc -T lint warns about -- blank lines and inserts a roff `sp` request, which is handled -- differently depending on the output format. My read is that mandoc -- considers the handling of a blank line in non-literal context in mdoc(7) -- to be undefined. The Mdoc reader thus ignores blank input lines outside -- of -literal and -unfilled displays. skipBlanks :: PandocMonad m => MdocParser m Blocks skipBlanks = many1 blank *> mempty -- By default, mdoc is in "spacing mode", where horizontal space is added -- between macro contents. The Sm macro turns it off and on. When we encounter -- the Sm macro, we both modify the parser state and we emit a sentinel value -- that spacify/foldNoSpaces uses to handle cases where spacing mode gets -- turned off and on within a stretch of inlines. parseSmToggle :: PandocMonad m => MdocParser m Inlines parseSmToggle = do macro "Sm" cur <- spacingMode <$> getState mode <- optionMaybe (literal "on" $> True <|> literal "off" $> False) eol let newMode = update mode cur modifyState $ \s -> s{spacingMode = newMode} return $ if newMode then smOn else smOff where update = \case Nothing -> not Just x -> const x skipUnsupportedMacro :: PandocMonad m => T.Text -> MdocParser m () skipUnsupportedMacro nm = do (Macro _ pos) <- macro nm manyTill anyToken eol logMessage $ SkippedContent ("unsupported macro: " <> nm) pos skipUnsupportedInlines :: PandocMonad m => MdocParser m Inlines skipUnsupportedInlines = choice [ skipUnsupportedMacro "Tg", skipUnsupportedMacro "Bk", skipUnsupportedMacro "Ek" ] *> mempty skipUnknownMacro :: PandocMonad m => MdocParser m Blocks skipUnknownMacro = do pos <- getPosition m <- anyMacro manyTill anyToken eol logMessage $ SkippedContent ("unsupported macro: " <> toString m) pos return mempty parseRegularBlock :: PandocMonad m => MdocParser m Blocks parseRegularBlock = choice [ parseDl , parseD1 , parsePara , emptyMacro "Pp" *> mempty , parseBd , parseBl , skipBlanks ] parseBlock :: (PandocMonad m) => MdocParser m Blocks parseBlock = choice [ parseHeader , parseNameSection , parseSynopsisSection , parseSeeAlsoSection , parseMiniSynopsis , parseRegularBlock , skipUnknownMacro ] ================================================ FILE: src/Text/Pandoc/Readers/MediaWiki.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.MediaWiki Copyright : Copyright (C) 2012-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of mediawiki text to 'Pandoc' document. -} {- TODO: _ correctly handle tables within tables _ parse templates(?) and built-in magic words -} module Text.Pandoc.Readers.MediaWiki ( readMediaWiki ) where import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isDigit, isLetter, isSpace) import qualified Data.Foldable as F import Data.List (intersperse) import Data.Maybe (fromMaybe, maybeToList) import Data.Sequence (ViewL (..), viewl, (<|)) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import Text.HTML.TagSoup 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, 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. readMediaWiki :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readMediaWiki opts s = do let sources = toSources s parsed <- readWithM parseMediaWiki MWState{ mwOptions = opts , mwMaxNestingLevel = 4 , mwNextLinkNumber = 1 , mwCategoryLinks = [] , mwIdentifierList = Set.empty , mwLogMessages = [] , mwInTT = False , mwAllowNewlines = True , mwMeta = nullMeta } sources case parsed of Right result -> return result Left e -> throwError e data MWState = MWState { mwOptions :: ReaderOptions , mwMaxNestingLevel :: Int , mwNextLinkNumber :: Int , mwCategoryLinks :: [Inlines] , mwIdentifierList :: Set.Set Text , mwLogMessages :: [LogMessage] , mwInTT :: Bool , mwAllowNewlines :: Bool , mwMeta :: Meta } type MWParser m = ParsecT Sources MWState m instance HasReaderOptions MWState where extractReaderOptions = mwOptions instance HasIdentifierList MWState where extractIdentifierList = mwIdentifierList updateIdentifierList f st = st{ mwIdentifierList = f $ mwIdentifierList st } instance HasLogMessages MWState where addLogMessage m s = s{ mwLogMessages = m : mwLogMessages s } getLogMessages = reverse . mwLogMessages -- -- auxiliary functions -- specialChars :: [Char] specialChars = "'[]<=&*{}|\":\\_" spaceChars :: [Char] spaceChars = " \n\t" sym :: PandocMonad m => Text -> MWParser m () sym s = () <$ try (string $ T.unpack s) newBlockTags :: [Text] newBlockTags = ["haskell","syntaxhighlight","source","gallery","references"] isBlockTag' :: Tag Text -> Bool 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' (TagOpen t _) = isInlineTagName t isInlineTag' (TagClose t) = isInlineTagName t isInlineTag' _ = False 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 inlinesInTags :: PandocMonad m => Text -> MWParser m Inlines inlinesInTags tag = try $ do (_,raw) <- htmlTag (~== TagOpen tag []) if T.any (== '/') raw -- self-closing tag then return mempty else trimInlines . mconcat <$> manyTill inline (htmlTag (~== TagClose tag)) blocksInTags :: PandocMonad m => Text -> MWParser m Blocks blocksInTags tag = try $ do (_,raw) <- htmlTag (~== TagOpen tag []) let closer = if tag == "li" then htmlTag (~== TagClose ("li" :: Text)) <|> lookAhead ( htmlTag (~== TagOpen ("li" :: Text) []) <|> htmlTag (~== TagClose ("ol" :: Text)) <|> htmlTag (~== TagClose ("ul" :: Text))) else htmlTag (~== TagClose tag) if T.any (== '/') raw -- self-closing tag then return mempty else mconcat <$> manyTill block closer textInTags :: PandocMonad m => Text -> MWParser m Text textInTags tag = try $ do (_,raw) <- htmlTag (~== TagOpen tag []) if T.any (== '/') raw -- self-closing tag then return "" else T.pack <$> manyTill anyChar (htmlTag (~== TagClose tag)) -- -- main parser -- parseMediaWiki :: PandocMonad m => MWParser m Pandoc parseMediaWiki = do bs <- mconcat <$> many block 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 $ Pandoc meta (B.toList bs <> B.toList categories) -- -- block parsers -- block :: PandocMonad m => MWParser m Blocks block = do res <- mempty <$ skipMany1 blankline <|> table <|> header <|> hrule <|> orderedList <|> bulletList <|> definitionList <|> mempty <$ try (spaces *> htmlComment) <|> preformatted <|> blockTag <|> (B.rawBlock "mediawiki" <$> template) <|> para trace (T.take 60 $ tshow $ B.toList res) return res para :: PandocMonad m => MWParser m Blocks para = do contents <- trimInlines . mconcat <$> many1 inline if F.all (==Space) contents then return mempty else case B.toList contents of -- For the MediaWiki format all images are considered figures [Image attr figureCaption (src, title)] -> return $ B.simpleFigureWith attr (B.fromList figureCaption) src title _ -> return $ B.para contents table :: PandocMonad m => MWParser m Blocks table = do tableStart styles <- option [] $ parseAttrs <* skipMany spaceChar <* optional (char '|') skipMany spaceChar optional $ template >> skipMany spaceChar optional blanklines let tableWidth = case lookup "width" styles of Just w -> fromMaybe 1.0 $ parseWidth w Nothing -> 1.0 caption <- option mempty tableCaption optional newline optional rowsep hasheader <- option False $ True <$ lookAhead (skipSpaces *> char '!') (cellwidths,hdr) <- unzip <$> tableRow let widths = map (tableWidth *) (concat cellwidths) let restwidth = tableWidth - sum widths let zerocols = length $ filter (==0.0) widths let defaultwidth = if zerocols == 0 || zerocols == length widths then ColWidthDefault else ColWidth $ restwidth / fromIntegral zerocols let widths' = map (\w -> if w > 0 then ColWidth w else defaultwidth) widths let cellspecs = zip (calculateAlignments hdr) widths' rows' <- many $ try $ rowsep *> (map snd <$> tableRow) optional blanklines tableEnd let (headers,rows) = if hasheader then (hdr, rows') else ([], hdr:rows') let toRow = Row nullAttr toHeaderRow l = [toRow l | not (null l)] return $ B.table (B.simpleCaption $ B.plain caption) cellspecs (TableHead nullAttr $ toHeaderRow headers) [TableBody nullAttr 0 [] $ map toRow rows] (TableFoot nullAttr []) calculateAlignments :: [Cell] -> [Alignment] calculateAlignments = concatMap cellAligns where cellAligns :: Cell -> [Alignment] cellAligns (Cell _ align _ (ColSpan colspan) _) = replicate colspan align parseAttrs :: PandocMonad m => MWParser m [(Text,Text)] parseAttrs = many1 parseAttr parseAttr :: PandocMonad m => MWParser m (Text, Text) parseAttr = try $ do skipMany spaceChar kFirst <- letter kRest <- many (alphaNum <|> oneOf "_-:.") let k = T.pack (kFirst : kRest) skipMany spaceChar char '=' skipMany spaceChar v <- (char '"' >> manyTillChar (satisfy (/='\n')) (char '"')) <|> many1Char (satisfy $ \c -> not (isSpace c) && c /= '|') return (k,v) tableStart :: PandocMonad m => MWParser m () tableStart = try $ guardColumnOne *> skipSpaces *> sym "{|" tableEnd :: PandocMonad m => MWParser m () tableEnd = try $ guardColumnOne *> skipSpaces *> sym "|}" rowsep :: PandocMonad m => MWParser m () rowsep = try $ guardColumnOne *> skipSpaces *> sym "|-" <* many (char '-') <* optional parseAttrs <* skipSpaces <* skipMany htmlComment <* blanklines cellsep :: PandocMonad m => MWParser m [(Text,Text)] cellsep = try $ do col <- sourceColumn <$> getPosition skipMany spaceChar c <- oneOf "|!" when (col > 1) $ void $ char c notFollowedBy (oneOf "-}") attribs <- option [] (parseAttrs <* skipMany spaceChar <* char '|') skipMany spaceChar pure attribs tableCaption :: PandocMonad m => MWParser m Inlines tableCaption = try $ do optional rowsep guardColumnOne skipSpaces sym "|+" optional (try $ parseAttrs *> skipSpaces *> char '|' *> blanklines) trimInlines . mconcat <$> many (notFollowedBy (void cellsep <|> rowsep) *> inline) tableRow :: PandocMonad m => MWParser m [([Double], Cell)] tableRow = try $ skipMany htmlComment *> many tableCell -- multiple widths because cell might have colspan tableCell :: PandocMonad m => MWParser m ([Double], Cell) tableCell = try $ do attribs <- cellsep pos' <- getPosition ls <- T.concat <$> many (notFollowedBy (void cellsep <|> rowsep <|> tableEnd) *> ((snd <$> withRaw table) <|> countChar 1 anyChar)) bs <- parseFromString (do setPosition pos' mconcat <$> many block) ls let align = case lookup "align" attribs of Just "left" -> AlignLeft Just "right" -> AlignRight Just "center" -> AlignCenter _ -> AlignDefault let rowspan = RowSpan . fromMaybe 1 $ safeRead =<< lookup "rowspan" attribs let colspan = ColSpan . fromMaybe 1 $ safeRead =<< lookup "colspan" attribs let ColSpan rawcolspan = colspan let handledAttribs = ["align", "colspan", "rowspan"] attribs' = [ (k, v) | (k, v) <- attribs , k `notElem` handledAttribs ] let widths = case lookup "width" attribs of Just xs -> maybe (replicate rawcolspan 0.0) (\w -> replicate rawcolspan (w / fromIntegral rawcolspan)) (parseWidth xs) Nothing -> replicate rawcolspan 0.0 return (widths, B.cellWith (toAttr attribs') align rowspan colspan bs) parseWidth :: Text -> Maybe Double parseWidth s = case T.unsnoc s of Just (ds, '%') | T.all isDigit ds -> safeRead $ "0." <> ds _ -> Nothing template :: PandocMonad m => MWParser m Text template = try $ do string "{{" notFollowedBy (char '{') lookAhead $ letter <|> digit <|> char ':' let chunk = template <|> variable <|> many1Char (noneOf "{}") <|> countChar 1 anyChar contents <- manyTill chunk (try $ string "}}") return $ "{{" <> T.concat contents <> "}}" blockTag :: PandocMonad m => MWParser m Blocks blockTag = do (tag, _) <- lookAhead $ htmlTag isBlockTag' case tag of TagOpen "blockquote" _ -> B.blockQuote <$> blocksInTags "blockquote" TagOpen "pre" _ -> B.codeBlock . trimCode <$> textInTags "pre" TagOpen "syntaxhighlight" attrs -> syntaxhighlight "syntaxhighlight" attrs TagOpen "source" attrs -> syntaxhighlight "source" attrs TagOpen "haskell" _ -> B.codeBlockWith ("",["haskell"],[]) . trimCode <$> textInTags "haskell" TagOpen "gallery" _ -> blocksInTags "gallery" TagOpen "p" _ -> mempty <$ htmlTag (~== tag) TagClose "p" -> mempty <$ htmlTag (~== tag) _ -> B.rawBlock "html" . snd <$> htmlTag (~== tag) trimCode :: Text -> Text trimCode t = case T.uncons t of Just ('\n', xs) -> stripTrailingNewlines xs _ -> stripTrailingNewlines t syntaxhighlight :: PandocMonad m => Text -> [Attribute Text] -> MWParser m Blocks syntaxhighlight tag attrs = try $ do let mblang = lookup "lang" attrs let mbstart = lookup "start" attrs let mbline = lookup "line" attrs let classes = maybeToList mblang ++ maybe [] (const ["numberLines"]) mbline let kvs = maybe [] (\x -> [("startFrom",x)]) mbstart contents <- textInTags tag return $ B.codeBlockWith ("",classes,kvs) $ trimCode contents hrule :: PandocMonad m => MWParser m Blocks hrule = B.horizontalRule <$ try (string "----" *> many (char '-') *> newline) guardColumnOne :: PandocMonad m => MWParser m () guardColumnOne = getPosition >>= \pos -> guard (sourceColumn pos == 1) preformatted :: PandocMonad m => MWParser m Blocks preformatted = try $ do guardColumnOne char ' ' let endline' = B.linebreak <$ try (newline <* char ' ') let whitespace' = B.str <$> many1Char ('\160' <$ spaceChar) let spToNbsp ' ' = '\160' spToNbsp x = x let nowiki' = mconcat . intersperse B.linebreak . map B.str . T.lines . fromEntities . T.map spToNbsp <$> try (htmlTag (~== TagOpen ("nowiki" :: Text) []) *> manyTillChar anyChar (htmlTag (~== TagClose ("nowiki" :: Text)))) let inline' = whitespace' <|> endline' <|> nowiki' <|> try (notFollowedBy newline *> inline) contents <- mconcat <$> many1 inline' let spacesStr (Str xs) = T.all isSpace xs spacesStr _ = False if F.all spacesStr contents then return mempty else return $ B.para $ encode contents encode :: Inlines -> Inlines encode = formatCode nullAttr header :: PandocMonad m => MWParser m Blocks header = try $ do guardColumnOne lev <- length <$> many1 (char '=') guard $ lev <= 6 contents <- trimInlines . mconcat <$> manyTill inline (count lev $ char '=') opts <- mwOptions <$> getState attr <- (if isEnabled Ext_gfm_auto_identifiers opts then id else modifyIdentifier) <$> registerHeader nullAttr contents return $ B.headerWith attr lev contents -- See #4731: modifyIdentifier :: Attr -> Attr modifyIdentifier (ident,cl,kv) = (ident',cl,kv) where ident' = T.map (\c -> if c == '-' then '_' else c) ident bulletList :: PandocMonad m => MWParser m Blocks bulletList = B.bulletList <$> ( many1 (listItem '*') <|> (htmlTag (~== TagOpen ("ul" :: Text) []) *> spaces *> many (listItem '*' <|> li) <* optional (htmlTag (~== TagClose ("ul" :: Text)))) ) orderedList :: PandocMonad m => MWParser m Blocks orderedList = (B.orderedList <$> many1 (listItem '#')) <|> try (do (tag,_) <- htmlTag (~== TagOpen ("ol" :: Text) []) spaces items <- many (listItem '#' <|> li) optional (htmlTag (~== TagClose ("ol" :: Text))) let start = fromMaybe 1 $ safeRead $ fromAttrib "start" tag return $ B.orderedListWith (start, DefaultStyle, DefaultDelim) items) definitionList :: PandocMonad m => MWParser m Blocks definitionList = B.definitionList <$> many1 defListItem defListItem :: PandocMonad m => MWParser m (Inlines, [Blocks]) defListItem = try $ do terms <- mconcat . intersperse B.linebreak <$> many defListTerm -- we allow dd with no dt, or dt with no dd defs <- if null terms then notFollowedBy (try $ skipMany1 (char ':') >> string "") *> many1 (listItem ':') else many (listItem ':') return (terms, defs) defListTerm :: PandocMonad m => MWParser m Inlines defListTerm = do guardColumnOne char ';' skipMany spaceChar trimInlines . mconcat <$> many (notFollowedBy (oneOf ":\r\n") *> inline) <* optional newline listStart :: PandocMonad m => Char -> MWParser m () listStart c = char c *> notFollowedBy listStartChar listStartChar :: PandocMonad m => MWParser m Char listStartChar = oneOf "*#;:" anyListStart :: PandocMonad m => MWParser m Char anyListStart = guardColumnOne >> oneOf "*#:;" li :: PandocMonad m => MWParser m Blocks li = lookAhead (htmlTag (~== TagOpen ("li" :: Text) [])) *> (firstParaToPlain <$> blocksInTags "li") <* spaces listItem :: PandocMonad m => Char -> MWParser m Blocks listItem c = try $ do guardColumnOne <|> guard (c == ':') -- def can start on same line as term extras <- many (try $ char c <* lookAhead listStartChar) if null extras then listItem' c else do skipMany spaceChar pos' <- getPosition first <- T.concat <$> manyTill listChunk newline rest <- many (try $ string extras *> lookAhead listStartChar *> (T.concat <$> manyTill listChunk newline)) contents <- parseFromString (do setPosition pos' many1 $ listItem' c) (T.unlines (first : rest)) case c of '*' -> return $ B.bulletList contents '#' -> return $ B.orderedList contents ':' -> return $ B.definitionList [(mempty, contents)] _ -> mzero -- The point of this is to handle stuff like -- * {{cite book -- | blah -- | blah -- }} -- * next list item -- which seems to be valid mediawiki. -- Also multiline math: see #9293. listChunk :: PandocMonad m => MWParser m Text listChunk = template <|> (snd <$> withRaw math) <|> countChar 1 anyChar listItem' :: PandocMonad m => Char -> MWParser m Blocks listItem' c = try $ do listStart c skipMany spaceChar pos' <- getPosition first <- T.concat <$> manyTill listChunk newline rest <- many (try $ char c *> lookAhead listStartChar *> (T.concat <$> manyTill listChunk newline)) parseFromString (do setPosition pos' firstParaToPlain . mconcat <$> many1 block) $ T.unlines $ first : rest firstParaToPlain :: Blocks -> Blocks firstParaToPlain contents = case viewl (B.unMany contents) of Para xs :< ys -> B.Many $ Plain xs <| ys _ -> contents -- -- inline parsers -- inline :: PandocMonad m => MWParser m Inlines inline = whitespace <|> url <|> str <|> doubleQuotes <|> strong <|> emph <|> behaviorSwitch <|> image <|> internalLink <|> externalLink <|> math <|> inlineTag <|> B.singleton <$> charRef <|> inlineHtml <|> (B.rawInline "mediawiki" <$> variable) <|> (B.rawInline "mediawiki" <$> template) <|> special str :: PandocMonad m => MWParser m Inlines str = B.str <$> many1Char (noneOf $ specialChars ++ spaceChars) math :: PandocMonad m => MWParser m Inlines math = (B.displayMath . trim <$> try (many1 (char ':') >> textInTags "math")) <|> (B.math . trim <$> textInTags "math") <|> (B.displayMath . trim <$> try (dmStart *> manyTillChar anyChar dmEnd)) <|> (B.math . trim <$> try (mStart *> manyTillChar (satisfy (/='\n')) mEnd)) where dmStart = string "\\[" dmEnd = try (string "\\]") mStart = string "\\(" mEnd = try (string "\\)") variable :: PandocMonad m => MWParser m Text variable = try $ do string "{{{" contents <- manyTillChar anyChar (try $ string "}}}") return $ "{{{" <> contents <> "}}}" singleParaToPlain :: Blocks -> Blocks singleParaToPlain bs = case B.toList bs of [Para ils] -> B.fromList [Plain ils] _ -> bs inlineTag :: PandocMonad m => MWParser m Inlines inlineTag = do (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 (_,raw) <- htmlTag (~== tag) if T.any (== '/') raw then return mempty else B.text . fromEntities <$> manyTillChar anyChar (htmlTag (~== TagClose ("nowiki" :: Text))) TagOpen "br" _ -> B.linebreak <$ (htmlTag (~== TagOpen ("br" :: Text) []) -- will get /> too *> optional blankline) TagOpen "strike" _ -> B.strikeout <$> inlinesInTags "strike" 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 updateState $ \st -> st{ mwInTT = True } result <- encode <$> inlinesInTags "tt" updateState $ \st -> st{ mwInTT = inTT } return result TagOpen "hask" _ -> B.codeWith ("",["haskell"],[]) <$> textInTags "hask" _ -> B.rawInline "html" . snd <$> htmlTag (~== tag) special :: PandocMonad m => MWParser m Inlines 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' whitespace :: PandocMonad m => MWParser m Inlines whitespace = B.space <$ (skipMany1 spaceChar <|> htmlComment) <|> B.softbreak <$ endline endline :: PandocMonad m => MWParser m () endline = do getState >>= guard . mwAllowNewlines () <$ try (newline <* notFollowedBy spaceChar <* notFollowedBy newline <* notFollowedBy' hrule <* notFollowedBy tableStart <* notFollowedBy' header <* notFollowedBy anyListStart) imageIdentifier :: PandocMonad m => MWParser m () imageIdentifier = try $ do ident <- T.pack <$> many1Till letter (char ':') guard $ T.toLower ident `elem` ["file", "image", "archivo", "datei", "fichier", "bild"] image :: PandocMonad m => MWParser m Inlines image = try $ do sym "[[" imageIdentifier fname <- addUnderscores <$> many1Char (noneOf "|]") _ <- many imageOption dims <- try (char '|' *> sepBy (manyChar digit) (char 'x') <* string "px") <|> return [] _ <- many imageOption let kvs = case dims of [w] -> [("width", w)] [w, h] -> [("width", w), ("height", h)] _ -> [] let attr = ("", [], kvs) caption <- (B.str fname <$ sym "]]") <|> try (char '|' *> (mconcat <$> manyTill inline (sym "]]"))) return $ B.imageWith attr fname (stringify caption) caption imageOption :: PandocMonad m => MWParser m Text imageOption = try $ char '|' *> opt where opt = try (oneOfStrings [ "border", "thumbnail", "frameless" , "thumb", "upright", "left", "right" , "center", "none", "baseline", "sub" , "super", "top", "text-top", "middle" , "bottom", "text-bottom" ]) <|> try (textStr "frame") <|> try (oneOfStrings ["link=","alt=","page=","class="] <* many (noneOf "|]")) addUnderscores :: Text -> Text addUnderscores = T.intercalate "_" . splitTextBy sep . T.strip where sep c = isSpace c || c == '_' internalLink :: PandocMonad m => MWParser m Inlines internalLink = try $ do sym "[[" pagename <- T.unwords . T.words <$> manyChar (noneOf "|]") label <- option (B.text pagename) $ char '|' *> ( (mconcat <$> many1 (notFollowedBy (char ']') *> inline)) -- the "pipe trick" -- [[Help:Contents|] -> "Contents" <|> return (B.text $ T.drop 1 $ T.dropWhile (/=':') pagename) ) sym "]]" -- see #8525: linktrail <- B.text <$> manyChar (satisfy (\c -> isLetter c && not (isCJK c))) let link = B.linkWith (mempty, ["wikilink"], mempty) (addUnderscores pagename) (stringify label) (label <> linktrail) if "Category:" `T.isPrefixOf` pagename then do updateState $ \st -> st{ mwCategoryLinks = link : mwCategoryLinks st } return mempty else return link externalLink :: PandocMonad m => MWParser m Inlines externalLink = try $ do char '[' (_, src) <- uri lab <- try (trimInlines . mconcat <$> (skipMany1 spaceChar *> manyTill inline (char ']'))) <|> do char ']' num <- mwNextLinkNumber <$> getState updateState $ \st -> st{ mwNextLinkNumber = num + 1 } return $ B.str $ tshow num return $ B.link src "" lab url :: PandocMonad m => MWParser m Inlines url = do (orig, src) <- uri return $ B.link src "" (B.str orig) -- | Parses a list of inlines between start and end delimiters. inlinesBetween :: (PandocMonad m, Show b) => MWParser m a -> MWParser m b -> MWParser m Inlines inlinesBetween start end = trimInlines . mconcat <$> try (start >> many1Till inline end) emph :: PandocMonad m => MWParser m Inlines emph = B.emph <$> inlinesBetween start end where start = sym "''" end = try $ notFollowedBy' (() <$ strong) >> sym "''" strong :: PandocMonad m => MWParser m Inlines strong = B.strong <$> inlinesBetween start end where start = sym "'''" end = sym "'''" doubleQuotes :: PandocMonad m => MWParser m Inlines doubleQuotes = do guardEnabled Ext_smart inTT <- mwInTT <$> getState guard (not inTT) 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 <> "__" ================================================ FILE: src/Text/Pandoc/Readers/Metadata.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Readers.Metadata Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Parse YAML/JSON metadata to 'Pandoc' 'Meta'. -} module Text.Pandoc.Readers.Metadata ( yamlBsToMeta, yamlBsToRefs, yamlMetaBlock, yamlMap ) where import Control.Monad.Except (throwError) import qualified Data.ByteString as B import qualified Data.Map as M import qualified Data.Vector as V import Data.Text (Text) import qualified Data.Text as T import qualified Data.Yaml as Yaml import qualified Data.Yaml.Internal as Yaml import qualified Text.Libyaml as Y import Data.Aeson (Value(..), Object, Result(..), fromJSON, (.:?), withObject, FromJSON) import Data.Aeson.Types (formatRelativePath, parse) import Text.Pandoc.Shared (tshow, blocksToInlines) import Text.Pandoc.Class (PandocMonad (..), report) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Logging (LogMessage(YamlWarning)) import Text.Pandoc.Parsing hiding (tableWith, parse) import qualified Text.Pandoc.UTF8 as UTF8 import System.IO.Unsafe (unsafePerformIO) yamlBsToMeta :: (PandocMonad m, HasLastStrPosition st) => ParsecT Sources st m (Future st MetaValue) -> B.ByteString -> ParsecT Sources st m (Future st Meta) yamlBsToMeta pMetaValue bstr = do pos <- getPosition case decodeAllWithWarnings bstr of Right (warnings, xs) -> do mapM_ (\(Yaml.DuplicateKey jpath) -> report (YamlWarning pos $ "Duplicate key: " <> T.pack (formatRelativePath jpath))) warnings case xs of (Object o : _) -> fmap Meta <$> yamlMap pMetaValue o [Null] -> return . return $ mempty [] -> return . return $ mempty _ -> Prelude.fail "expected YAML object" Left err' -> do let msg = T.pack $ "Error parsing YAML metadata at " <> show pos <> ":\n" <> Yaml.prettyPrintParseException err' throwError $ PandocParseError $ if "did not find expected key" `T.isInfixOf` msg then msg <> "\nConsider enclosing the entire field in 'single quotes'" else msg decodeAllWithWarnings :: FromJSON a => B.ByteString -> (Either Yaml.ParseException ([Yaml.Warning], [a])) decodeAllWithWarnings = either Left (\(ws,res) -> case res of Left s -> Left (Yaml.AesonException s) Right v -> Right (ws, v)) . unsafePerformIO . Yaml.decodeAllHelper . Y.decode -- Returns filtered list of references. yamlBsToRefs :: (PandocMonad m, HasLastStrPosition st) => ParsecT Sources st m (Future st MetaValue) -> (Text -> Bool) -- ^ Filter for id -> B.ByteString -> ParsecT Sources st m (Future st [MetaValue]) yamlBsToRefs pMetaValue idpred bstr = case Yaml.decodeAllEither' bstr of Right (Object m : _) -> do case parse (withObject "metadata" (.:? "references")) (Object m) of Success (Just refs) -> sequence <$> mapM (yamlToMetaValue pMetaValue) (filter hasSelectedId refs) _ -> return $ return [] Right (Array v : _) -> do let refs = filter hasSelectedId $ V.toList v sequence <$> mapM (yamlToMetaValue pMetaValue) (filter hasSelectedId refs) Right _ -> return . return $ [] Left err' -> throwError $ PandocParseError $ T.pack $ Yaml.prettyPrintParseException err' where isSelected (String t) = idpred t isSelected _ = False hasSelectedId (Object o) = case parse (withObject "ref" (.:? "id")) (Object o) of Success (Just id') -> isSelected id' _ -> False hasSelectedId _ = False normalizeMetaValue :: (PandocMonad m, HasLastStrPosition st) => ParsecT Sources st m (Future st MetaValue) -> Text -> ParsecT Sources st m (Future st MetaValue) normalizeMetaValue pMetaValue x = -- Note: a standard quoted or unquoted YAML value will -- not end in a newline, but a "block" set off with -- `|` or `>` will. if "\n" `T.isSuffixOf` (T.dropWhileEnd isSpaceChar x) -- see #6823 then parseFromString' pMetaValue (x <> "\n\n") else try (parseFromString' asInlines x') -- see #8358 <|> -- see #8465 parseFromString' asInlines (x' <> "\n\n") where x' = T.dropWhile isSpaceOrNlChar x asInlines = fmap b2i <$> pMetaValue b2i (MetaBlocks bs) = MetaInlines (blocksToInlines bs) b2i y = y isSpaceChar ' ' = True isSpaceChar '\t' = True isSpaceChar _ = False isSpaceOrNlChar '\r' = True isSpaceOrNlChar '\n' = True isSpaceOrNlChar c = isSpaceChar c yamlToMetaValue :: (PandocMonad m, HasLastStrPosition st) => ParsecT Sources st m (Future st MetaValue) -> Value -> ParsecT Sources st m (Future st MetaValue) yamlToMetaValue pMetaValue v = case v of String t -> normalizeMetaValue pMetaValue t Bool b -> return $ return $ MetaBool b Number d -> normalizeMetaValue pMetaValue $ case fromJSON v of Success (x :: Int) -> tshow x _ -> tshow d Null -> return $ return $ MetaString "" Array{} -> do case fromJSON v of Error err' -> throwError $ PandocParseError $ T.pack err' Success xs -> fmap MetaList . sequence <$> mapM (yamlToMetaValue pMetaValue) xs Object o -> fmap MetaMap <$> yamlMap pMetaValue o yamlMap :: (PandocMonad m, HasLastStrPosition st) => ParsecT Sources st m (Future st MetaValue) -> Object -> ParsecT Sources st m (Future st (M.Map Text MetaValue)) yamlMap pMetaValue o = do case fromJSON (Object o) of Error err' -> throwError $ PandocParseError $ T.pack err' Success (m' :: M.Map Text Value) -> do let kvs = filter (not . ignorable . fst) $ M.toList m' fmap M.fromList . sequence <$> mapM toMeta kvs where ignorable t = "_" `T.isSuffixOf` t toMeta (k, v) = do fv <- yamlToMetaValue pMetaValue v return $ do v' <- fv return (k, v') -- | Parse a YAML metadata block using the supplied 'MetaValue' parser. yamlMetaBlock :: (HasLastStrPosition st, PandocMonad m) => ParsecT Sources st m (Future st MetaValue) -> ParsecT Sources st m (Future st Meta) yamlMetaBlock parser = try $ do pos <- getPosition string "---" blankline notFollowedBy blankline -- if --- is followed by a blank it's an HRULE rawYamlLines <- manyTill anyLine stopLine -- by including --- and ..., we allow yaml blocks with just comments: let rawYaml = T.unlines ("---" : (rawYamlLines ++ ["..."])) optional blanklines oldPos <- getPosition setPosition pos res <- yamlBsToMeta parser $ UTF8.fromText rawYaml setPosition oldPos pure res stopLine :: Monad m => ParsecT Sources st m () stopLine = try $ (string "---" <|> string "...") >> blankline >> return () ================================================ FILE: src/Text/Pandoc/Readers/Muse.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Muse Copyright : Copyright (C) 2017-2020 Alexander Krotov License : GNU GPL, version 2 or above Maintainer : Alexander Krotov Stability : alpha Portability : portable Conversion of Muse text to 'Pandoc' document. -} {- TODO: - tag -} module Text.Pandoc.Readers.Muse (readMuse) where import Control.Monad import Control.Monad.Reader import Control.Monad.Except (throwError) import Data.Bifunctor import Data.Default import Data.List (transpose) import qualified Data.Map as M import qualified Data.Set as Set import Data.Maybe (fromMaybe, isNothing, maybeToList) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Builder (Blocks, Inlines, underline) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Shared (trimr, tshow) -- | Read Muse from an input string and return a Pandoc document. readMuse :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readMuse opts s = do let sources = toSources s res <- flip runReaderT def $ runParserT parseMuse def{ museOptions = opts } (initialSourceName sources) sources case res of Left e -> throwError $ fromParsecError sources e Right d -> return d type F = Future MuseState data MuseState = MuseState { museMeta :: F Meta -- ^ Document metadata , museOptions :: ReaderOptions , museIdentifierList :: Set.Set Text , museLastSpacePos :: Maybe SourcePos -- ^ Position after last space or newline parsed , museLastStrPos :: Maybe SourcePos -- ^ Position after last str parsed , museLogMessages :: [LogMessage] , museNotes :: M.Map Text (SourcePos, F Blocks) } instance Default MuseState where def = MuseState { museMeta = return nullMeta , museOptions = def , museIdentifierList = Set.empty , museLastStrPos = Nothing , museLastSpacePos = Nothing , museLogMessages = [] , museNotes = M.empty } data MuseEnv = MuseEnv { museInLink :: Bool -- ^ True when parsing a link description to avoid nested links , museInPara :: Bool -- ^ True when parsing paragraph is not allowed } instance Default MuseEnv where def = MuseEnv { museInLink = False , museInPara = False } type MuseParser m = ParsecT Sources MuseState (ReaderT MuseEnv m) instance HasReaderOptions MuseState where extractReaderOptions = museOptions instance HasIdentifierList MuseState where extractIdentifierList = museIdentifierList updateIdentifierList f st = st{ museIdentifierList = f $ museIdentifierList st } instance HasLastStrPosition MuseState where setLastStrPos pos st = st{ museLastStrPos = pos } getLastStrPos st = museLastStrPos st instance HasLogMessages MuseState where addLogMessage m s = s{ museLogMessages = m : museLogMessages s } getLogMessages = reverse . museLogMessages updateLastSpacePos :: Monad m => MuseParser m () updateLastSpacePos = getPosition >>= \pos -> updateState $ \s -> s { museLastSpacePos = Just pos } -- | Parse Muse document parseMuse :: PandocMonad m => MuseParser m Pandoc parseMuse = do many directive blocks <- (:) <$> parseBlocks <*> many parseSection eof st <- getState runF (Pandoc <$> museMeta st <*> fmap B.toList (mconcat blocks)) st <$ reportLogMessages -- * Utility functions -- | Trim up to one newline from the beginning of the string. lchop :: Text -> Text lchop s = case T.uncons s of Just ('\n', xs) -> xs _ -> s -- | Trim up to one newline from the end of the string. rchop :: Text -> Text rchop s = case T.unsnoc s of Just (xs, '\n') -> xs _ -> s unindent :: Text -> Text unindent = rchop . T.intercalate "\n" . dropSpacePrefix . T.splitOn "\n" . lchop dropSpacePrefix :: [Text] -> [Text] dropSpacePrefix lns = T.drop maxIndent <$> lns where isSpaceChar c = c == ' ' || c == '\t' maxIndent = length $ takeWhile (isSpaceChar . T.head) $ takeWhile same $ T.transpose lns same t = case T.uncons t of Just (c, cs) -> T.all (== c) cs Nothing -> True atStart :: PandocMonad m => MuseParser m () atStart = do pos <- getPosition st <- getState guard $ museLastStrPos st /= Just pos noSpaceBefore :: PandocMonad m => MuseParser m () noSpaceBefore = do pos <- getPosition st <- getState guard $ museLastSpacePos st /= Just pos firstColumn :: PandocMonad m => MuseParser m () firstColumn = getPosition >>= \pos -> guard (sourceColumn pos == 1) -- * Parsers -- | Parse end-of-line, which can be either a newline or end-of-file. eol :: (Stream s m Char, UpdateSourcePos s Char) => ParsecT s st m () eol = void newline <|> eof getIndent :: PandocMonad m => MuseParser m Int getIndent = subtract 1 . sourceColumn <$ many spaceChar <*> getPosition -- ** HTML parsers openTag :: PandocMonad m => Text -> MuseParser m [(Text, Text)] openTag tag = try $ char '<' *> textStr tag *> manyTill attr (char '>') where attr = try $ (,) <$ many1 spaceChar <*> many1Char (noneOf "=\n") <* string "=\"" <*> manyTillChar (noneOf "\"") (char '"') closeTag :: PandocMonad m => Text -> MuseParser m () closeTag tag = try $ string " textStr tag *> void (char '>') -- | Convert HTML attributes to Pandoc 'Attr' htmlAttrToPandoc :: [(Text, Text)] -> Attr htmlAttrToPandoc attrs = (ident, classes, keyvals) where ident = fromMaybe "" $ lookup "id" attrs classes = maybe [] T.words $ lookup "class" attrs keyvals = [(k,v) | (k,v) <- attrs, k /= "id", k /= "class"] parseHtmlContent :: PandocMonad m => Text -- ^ Tag name -> MuseParser m (Attr, F Blocks) parseHtmlContent tag = try $ getIndent >>= \indent -> (,) <$> fmap htmlAttrToPandoc (openTag tag) <* manyTill spaceChar eol <*> allowPara (parseBlocksTill (try $ indentWith indent *> closeTag tag)) <* manyTill spaceChar eol -- closing tag must be followed by optional whitespace and newline -- ** Directive parsers -- While not documented, Emacs Muse allows "-" in directive name parseDirectiveKey :: PandocMonad m => MuseParser m Text parseDirectiveKey = char '#' *> manyChar (letter <|> char '-') parseEmacsDirective :: PandocMonad m => MuseParser m (Text, F Inlines) parseEmacsDirective = (,) <$> parseDirectiveKey <* spaceChar <*> (trimInlinesF . mconcat <$> manyTill inline' eol) parseAmuseDirective :: PandocMonad m => MuseParser m (Text, F Inlines) parseAmuseDirective = (,) <$> parseDirectiveKey <* many1 spaceChar <*> (trimInlinesF . mconcat <$> many1Till inline endOfDirective) <* many blankline where endOfDirective = lookAhead $ eof <|> try (newline *> (void blankline <|> void parseDirectiveKey)) directive :: PandocMonad m => MuseParser m () directive = do ext <- getOption readerExtensions (key, value) <- if extensionEnabled Ext_amuse ext then parseAmuseDirective else parseEmacsDirective updateState $ \st -> st { museMeta = B.setMeta (translateKey key) <$> value <*> museMeta st } where translateKey "cover" = "cover-image" translateKey x = x -- ** Block parsers allowPara :: MonadReader MuseEnv m => m a -> m a allowPara p = local (\s -> s { museInPara = False }) p -- | Parse section contents until EOF or next header parseBlocks :: PandocMonad m => MuseParser m (F Blocks) parseBlocks = try (parseEnd <|> nextSection <|> listStart <|> blockStart <|> paraStart) where nextSection = mempty <$ lookAhead headingStart parseEnd = mempty <$ eof blockStart = (B.<>) <$> (blockElements <|> emacsNoteBlock) <*> allowPara parseBlocks listStart = uncurry (B.<>) <$> allowPara (anyListUntil parseBlocks <|> amuseNoteBlockUntil parseBlocks) paraStart = do indent <- length <$> many spaceChar uncurry (B.<>) . first (p indent) <$> paraUntil parseBlocks where p indent = if indent >= 2 && indent < 6 then fmap B.blockQuote else id -- | Parse section that starts with a header parseSection :: PandocMonad m => MuseParser m (F Blocks) parseSection = ((B.<>) <$> emacsHeading <*> parseBlocks) <|> (uncurry (B.<>) <$> amuseHeadingUntil parseBlocks) parseBlocksTill :: PandocMonad m => MuseParser m a -> MuseParser m (F Blocks) parseBlocksTill end = continuation where parseEnd = mempty <$ end blockStart = (B.<>) <$> blockElements <*> allowPara continuation listStart = uncurry (B.<>) <$> allowPara (anyListUntil (parseEnd <|> continuation)) paraStart = uncurry (B.<>) <$> paraUntil (parseEnd <|> continuation) continuation = try $ parseEnd <|> listStart <|> blockStart <|> paraStart listItemContentsUntil :: PandocMonad m => Int -> MuseParser m a -> MuseParser m a -> MuseParser m (F Blocks, a) listItemContentsUntil col pre end = p where p = try listStart <|> try blockStart <|> try paraStart parsePre = (mempty,) <$> pre parseEnd = (mempty,) <$> end paraStart = do (f, (r, e)) <- paraUntil (parsePre <|> continuation <|> parseEnd) return (f B.<> r, e) blockStart = first <$> ((B.<>) <$> blockElements) <*> allowPara (parsePre <|> continuation <|> parseEnd) listStart = do (f, (r, e)) <- allowPara $ anyListUntil (parsePre <|> continuation <|> parseEnd) return (f B.<> r, e) continuation = try $ do blank <- optionMaybe blankline skipMany blankline indentWith col local (\s -> s { museInPara = museInPara s && isNothing blank }) p parseBlock :: PandocMonad m => MuseParser m (F Blocks) parseBlock = do res <- blockElements <|> para trace (T.take 60 $ tshow $ B.toList $ runF res def) return res where para = fst <$> paraUntil (try (eof <|> void (lookAhead blockElements))) blockElements :: PandocMonad m => MuseParser m (F Blocks) blockElements = (mempty <$ blankline) <|> comment <|> separator <|> pagebreak <|> example <|> exampleTag <|> literalTag <|> centerTag <|> rightTag <|> quoteTag <|> divTag <|> biblioTag <|> playTag <|> verseTag <|> lineBlock <|> museGridTable <|> table <|> commentTag -- | Parse a line comment, starting with @;@ in the first column. comment :: PandocMonad m => MuseParser m (F Blocks) comment = try $ mempty <$ firstColumn <* char ';' <* optional (spaceChar *> many (noneOf "\n")) <* eol -- | Parse a horizontal rule, consisting of 4 or more @\'-\'@ characters. separator :: PandocMonad m => MuseParser m (F Blocks) separator = try $ pure B.horizontalRule <$ string "----" <* many (char '-') <* many spaceChar <* eol -- | Parse a page break pagebreak :: PandocMonad m => MuseParser m (F Blocks) pagebreak = try $ pure (B.divWith ("", [], [("style", "page-break-before: always;")]) mempty) <$ count 6 spaceChar <* many spaceChar <* string "* * * * *" <* manyTill spaceChar eol headingStart :: PandocMonad m => MuseParser m (Text, Int) headingStart = try $ (,) <$> option "" (try (parseAnchor <* manyTill spaceChar eol)) <* firstColumn <*> fmap length (many1 $ char '*') <* spaceChar -- | Parse a single-line heading. emacsHeading :: PandocMonad m => MuseParser m (F Blocks) emacsHeading = try $ do guardDisabled Ext_amuse (anchorId, level) <- headingStart content <- trimInlinesF . mconcat <$> manyTill inline eol attr <- registerHeader (anchorId, [], []) (runF content def) return $ B.headerWith attr level <$> content -- | Parse a multi-line heading. -- It is a Text::Amuse extension, Emacs Muse does not allow heading to span multiple lines. amuseHeadingUntil :: PandocMonad m => MuseParser m a -- ^ Terminator parser -> MuseParser m (F Blocks, a) amuseHeadingUntil end = try $ do guardEnabled Ext_amuse (anchorId, level) <- headingStart (content, e) <- paraContentsUntil end attr <- registerHeader (anchorId, [], []) (runF content def) return (B.headerWith attr level <$> content, e) -- | Parse an example between @{{{@ and @}}}@. -- It is an Amusewiki extension influenced by Creole wiki, as described in @Text::Amuse@ documentation. example :: PandocMonad m => MuseParser m (F Blocks) example = try $ pure . B.codeBlock <$ string "{{{" <* many spaceChar <*> (unindent <$> manyTillChar anyChar (string "}}}")) -- | Parse an @\@ tag. exampleTag :: PandocMonad m => MuseParser m (F Blocks) exampleTag = try $ fmap pure $ B.codeBlockWith <$ many spaceChar <*> (htmlAttrToPandoc <$> openTag "example") <*> (unindent <$> manyTillChar anyChar (closeTag "example")) <* manyTill spaceChar eol -- | Parse a @\@ tag as a raw block. -- For 'RawInline' @\@ parser, see 'inlineLiteralTag'. literalTag :: PandocMonad m => MuseParser m (F Blocks) literalTag = try $ fmap pure $ B.rawBlock <$ many spaceChar <*> (fromMaybe "html" . lookup "style" <$> openTag "literal") -- FIXME: Emacs Muse inserts without style into all output formats, but we assume HTML <* manyTill spaceChar eol <*> (unindent <$> manyTillChar anyChar (closeTag "literal")) <* manyTill spaceChar eol -- | Parse @\
    @ tag. -- Currently it is ignored as Pandoc cannot represent centered blocks. centerTag :: PandocMonad m => MuseParser m (F Blocks) centerTag = snd <$> parseHtmlContent "center" -- | Parse @\@ tag. -- Currently it is ignored as Pandoc cannot represent centered blocks. rightTag :: PandocMonad m => MuseParser m (F Blocks) rightTag = snd <$> parseHtmlContent "right" -- | Parse @\@ tag. quoteTag :: PandocMonad m => MuseParser m (F Blocks) quoteTag = fmap B.blockQuote . snd <$> parseHtmlContent "quote" -- | Parse @\
    @ tag. -- @\
    @ tag is supported by Emacs Muse, but not Amusewiki 2.025. divTag :: PandocMonad m => MuseParser m (F Blocks) divTag = do (attrs, content) <- parseHtmlContent "div" return $ B.divWith attrs <$> content -- | Parse @\@ tag, the result is the same as @\
    @. -- @\@ tag is supported only in Text::Amuse mode. biblioTag :: PandocMonad m => MuseParser m (F Blocks) biblioTag = fmap (B.divWith ("", ["biblio"], [])) . snd <$ guardEnabled Ext_amuse <*> parseHtmlContent "biblio" -- | Parse @\@ tag, the result is the same as @\
    @. -- @\@ tag is supported only in Text::Amuse mode. playTag :: PandocMonad m => MuseParser m (F Blocks) playTag = do guardEnabled Ext_amuse fmap (B.divWith ("", ["play"], [])) . snd <$> parseHtmlContent "play" verseLine :: PandocMonad m => MuseParser m (F Inlines) verseLine = (<>) <$> fmap pure (option mempty (B.str <$> many1Char ('\160' <$ char ' '))) <*> fmap (trimInlinesF . mconcat) (manyTill inline' eol) -- | Parse @\@ tag. verseTag :: PandocMonad m => MuseParser m (F Blocks) verseTag = try $ getIndent >>= \indent -> fmap B.lineBlock . sequence <$ openTag "verse" <* manyTill spaceChar eol <*> manyTill (indentWith indent *> verseLine) (try $ indentWith indent *> closeTag "verse") <* manyTill spaceChar eol -- | Parse @\@ tag. commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = try $ mempty <$ many spaceChar <* openTag "comment" <* manyTill anyChar (closeTag "comment") <* manyTill spaceChar eol -- | Parse paragraph contents. paraContentsUntil :: PandocMonad m => MuseParser m a -- ^ Terminator parser -> MuseParser m (F Inlines, a) paraContentsUntil end = first (trimInlinesF . mconcat) <$> manyUntil inline (try (manyTill spaceChar eol *> local (\s -> s { museInPara = True}) end)) -- | Parse a paragraph. paraUntil :: PandocMonad m => MuseParser m a -- ^ Terminator parser -> MuseParser m (F Blocks, a) paraUntil end = do inPara <- asks museInPara guard $ not inPara first (fmap B.para) <$> paraContentsUntil end noteMarker' :: PandocMonad m => Char -> Char -> MuseParser m Text noteMarker' l r = try $ (\x y -> T.pack $ l:x:y ++ [r]) <$ char l <*> oneOf "123456789" <*> manyTill digit (char r) noteMarker :: PandocMonad m => MuseParser m Text noteMarker = noteMarker' '[' ']' <|> noteMarker' '{' '}' addNote :: PandocMonad m => Text -> SourcePos -> F Blocks -> MuseParser m () addNote ref pos content = do oldnotes <- museNotes <$> getState when (M.member ref oldnotes) (logMessage $ DuplicateNoteReference ref pos) updateState $ \s -> s{ museNotes = M.insert ref (pos, content) oldnotes } -- Amusewiki version of note -- Parsing is similar to list item, except that note marker is used instead of list marker amuseNoteBlockUntil :: PandocMonad m => MuseParser m a -> MuseParser m (F Blocks, a) amuseNoteBlockUntil end = try $ do guardEnabled Ext_amuse ref <- noteMarker pos <- getPosition void spaceChar <|> lookAhead eol (content, e) <- allowPara $ listItemContentsUntil (sourceColumn pos) (Prelude.fail "x") end addNote ref pos content return (mempty, e) -- Emacs version of note -- Notes are allowed only at the end of text, no indentation is required. emacsNoteBlock :: PandocMonad m => MuseParser m (F Blocks) emacsNoteBlock = try $ do guardDisabled Ext_amuse ref <- noteMarker pos <- getPosition content <- fmap mconcat blocksTillNote addNote ref pos content return mempty where blocksTillNote = many1Till parseBlock (eof <|> () <$ lookAhead noteMarker) -- -- Verse markup -- -- | Parse a line block indicated by @\'>\'@ characters. lineBlock :: PandocMonad m => MuseParser m (F Blocks) lineBlock = try $ getIndent >>= \indent -> fmap B.lineBlock . sequence <$> (blankVerseLine <|> nonblankVerseLine) `sepBy1'` try (indentWith indent) where blankVerseLine = try $ mempty <$ char '>' <* blankline nonblankVerseLine = try (string "> ") *> verseLine -- *** List parsers bulletListItemsUntil :: PandocMonad m => Int -- ^ Indentation -> MuseParser m a -- ^ Terminator parser -> MuseParser m ([F Blocks], a) bulletListItemsUntil indent end = try $ do char '-' void spaceChar <|> lookAhead eol (x, (xs, e)) <- allowPara $ listItemContentsUntil (indent + 2) (try (optional blankline *> indentWith indent *> bulletListItemsUntil indent end)) (([],) <$> end) return (x:xs, e) -- | Parse a bullet list. bulletListUntil :: PandocMonad m => MuseParser m a -> MuseParser m (F Blocks, a) bulletListUntil end = try $ do indent <- getIndent guard $ indent /= 0 first (fmap B.bulletList . sequence) <$> bulletListItemsUntil indent end museOrderedListMarker :: PandocMonad m => ListNumberStyle -> MuseParser m Int museOrderedListMarker style = snd <$> p <* char '.' where p = case style of Decimal -> decimal UpperRoman -> upperRoman LowerRoman -> lowerRoman UpperAlpha -> upperAlpha LowerAlpha -> lowerAlpha _ -> Prelude.fail "Unhandled case" orderedListItemsUntil :: PandocMonad m => Int -> ListNumberStyle -> MuseParser m a -> MuseParser m ([F Blocks], a) orderedListItemsUntil indent style end = continuation where continuation = try $ do pos <- getPosition void spaceChar <|> lookAhead eol (x, (xs, e)) <- allowPara $ listItemContentsUntil (sourceColumn pos) (try (optional blankline *> indentWith indent *> museOrderedListMarker style *> continuation)) (([],) <$> end) return (x:xs, e) -- | Parse an ordered list. orderedListUntil :: PandocMonad m => MuseParser m a -> MuseParser m (F Blocks, a) orderedListUntil end = try $ do indent <- getIndent guard $ indent /= 0 (style, start) <- decimal <|> lowerRoman <|> upperRoman <|> lowerAlpha <|> upperAlpha char '.' first (fmap (B.orderedListWith (start, style, Period)) . sequence) <$> orderedListItemsUntil indent style end descriptionsUntil :: PandocMonad m => Int -> MuseParser m a -> MuseParser m ([F Blocks], a) descriptionsUntil indent end = do void spaceChar <|> lookAhead eol (x, (xs, e)) <- allowPara $ listItemContentsUntil indent (try (optional blankline *> indentWith indent *> manyTill spaceChar (string "::") *> descriptionsUntil indent end)) (([],) <$> end) return (x:xs, e) definitionListItemsUntil :: PandocMonad m => Int -> MuseParser m a -> MuseParser m ([F (Inlines, [Blocks])], a) definitionListItemsUntil indent end = continuation where continuation = try $ do pos <- getPosition term <- trimInlinesF . mconcat <$> manyTill inline' (try $ string "::") (x, (xs, e)) <- descriptionsUntil (sourceColumn pos) (try (optional blankline *> indentWith indent *> continuation) <|> (([],) <$> end)) let xx = (,) <$> term <*> sequence x return (xx:xs, e) -- | Parse a definition list. definitionListUntil :: PandocMonad m => MuseParser m a -- ^ Terminator parser -> MuseParser m (F Blocks, a) definitionListUntil end = try $ do indent <- getIndent guardDisabled Ext_amuse <|> guard (indent /= 0) -- Initial space is required by Amusewiki, but not Emacs Muse first (fmap B.definitionList . sequence) <$> definitionListItemsUntil indent end anyListUntil :: PandocMonad m => MuseParser m a -- ^ Terminator parser -> MuseParser m (F Blocks, a) anyListUntil end = bulletListUntil end <|> orderedListUntil end <|> definitionListUntil end -- *** Table parsers -- | Internal Muse table representation. data MuseTable = MuseTable { museTableCaption :: Inlines , museTableHeaders :: [[Blocks]] , museTableRows :: [[Blocks]] , museTableFooters :: [[Blocks]] } data MuseTableElement = MuseHeaderRow [Blocks] | MuseBodyRow [Blocks] | MuseFooterRow [Blocks] | MuseCaption Inlines museToPandocTable :: MuseTable -> Blocks museToPandocTable (MuseTable caption headers body footers) = B.table (B.simpleCaption $ B.plain caption) attrs (TableHead nullAttr $ toHeaderRow headRow) [TableBody nullAttr 0 [] $ map toRow $ rows ++ body ++ footers] (TableFoot nullAttr []) where attrs = (AlignDefault, ColWidthDefault) <$ transpose (headers ++ body ++ footers) (headRow, rows) = case headers of (r:rs) -> (r, rs) [] -> ([], []) toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] museAppendElement :: MuseTableElement -> MuseTable -> MuseTable museAppendElement element tbl = case element of MuseHeaderRow row -> tbl{ museTableHeaders = row : museTableHeaders tbl } MuseBodyRow row -> tbl{ museTableRows = row : museTableRows tbl } MuseFooterRow row -> tbl{ museTableFooters = row : museTableFooters tbl } MuseCaption inlines -> tbl{ museTableCaption = inlines } tableElements :: PandocMonad m => MuseParser m (F [MuseTableElement]) tableElements = sequence <$> many1 tableParseElement elementsToTable :: [MuseTableElement] -> MuseTable elementsToTable = foldr museAppendElement emptyTable where emptyTable = MuseTable mempty mempty mempty mempty museGridPart :: PandocMonad m => MuseParser m Int museGridPart = try $ length <$> many1 (char '-') <* char '+' museGridTableHeader :: PandocMonad m => MuseParser m [Int] museGridTableHeader = try $ char '+' *> many1 museGridPart <* manyTill spaceChar eol museGridTableRow :: PandocMonad m => Int -> [Int] -> MuseParser m (F [Blocks]) museGridTableRow indent indices = try $ do lns <- many1 $ try (indentWith indent *> museGridTableRawLine indices) let cols = map (T.unlines . map trimr) $ transpose lns indentWith indent *> museGridTableHeader sequence <$> mapM (parseFromString' parseBlocks) cols museGridTableRawLine :: PandocMonad m => [Int] -> MuseParser m [Text] museGridTableRawLine indices = char '|' *> forM indices (\n -> countChar n anyChar <* char '|') <* manyTill spaceChar eol museGridTable :: PandocMonad m => MuseParser m (F Blocks) museGridTable = try $ do indent <- getIndent indices <- museGridTableHeader fmap rowsToTable . sequence <$> many1 (museGridTableRow indent indices) where rowsToTable rows = B.table B.emptyCaption attrs (TableHead nullAttr []) [TableBody nullAttr 0 [] $ map toRow rows] (TableFoot nullAttr []) where attrs = (AlignDefault, ColWidthDefault) <$ transpose rows toRow = Row nullAttr . map B.simpleCell -- | Parse a table. table :: PandocMonad m => MuseParser m (F Blocks) table = try $ fmap (museToPandocTable . elementsToTable) <$> tableElements tableParseElement :: PandocMonad m => MuseParser m (F MuseTableElement) tableParseElement = tableParseHeader <|> tableParseBody <|> tableParseFooter <|> tableParseCaption tableParseRow :: PandocMonad m => Int -- ^ Number of separator characters -> MuseParser m (F [Blocks]) tableParseRow n = try $ sequence <$> tableCells where tableCells = (:) <$> tableCell sep <*> (tableCells <|> fmap pure (tableCell eol)) tableCell p = try $ fmap B.plain . trimInlinesF . mconcat <$> manyTill inline' p sep = try $ many1 spaceChar *> count n (char '|') *> lookAhead (void (many1 spaceChar) <|> void eol) -- | Parse a table header row. tableParseHeader :: PandocMonad m => MuseParser m (F MuseTableElement) tableParseHeader = fmap MuseHeaderRow <$> tableParseRow 2 -- | Parse a table body row. tableParseBody :: PandocMonad m => MuseParser m (F MuseTableElement) tableParseBody = fmap MuseBodyRow <$> tableParseRow 1 -- | Parse a table footer row. tableParseFooter :: PandocMonad m => MuseParser m (F MuseTableElement) tableParseFooter = fmap MuseFooterRow <$> tableParseRow 3 -- | Parse table caption. tableParseCaption :: PandocMonad m => MuseParser m (F MuseTableElement) tableParseCaption = try $ fmap MuseCaption . trimInlinesF . mconcat <$ many spaceChar <* string "|+" <*> many1Till inline (try $ string "+|" *> eol) -- ** Inline parsers inline' :: PandocMonad m => MuseParser m (F Inlines) inline' = whitespace <|> br <|> anchor <|> footnote <|> strongEmph <|> strong <|> strongTag <|> emph <|> emphTag <|> underlined <|> superscriptTag <|> subscriptTag <|> strikeoutTag <|> verbatimTag <|> classTag <|> inlineRtl <|> inlineLtr <|> nbsp <|> linkOrImage <|> code <|> codeTag <|> mathTag <|> inlineLiteralTag <|> str <|> asterisks <|> symbol "inline" inline :: PandocMonad m => MuseParser m (F Inlines) inline = endline <|> inline' -- | Parse a soft break. endline :: PandocMonad m => MuseParser m (F Inlines) endline = try $ pure B.softbreak <$ newline <* notFollowedBy blankline <* updateLastSpacePos parseAnchor :: PandocMonad m => MuseParser m Text parseAnchor = try $ T.cons <$ firstColumn <* char '#' <*> letter <*> manyChar (letter <|> digit <|> char '-') anchor :: PandocMonad m => MuseParser m (F Inlines) anchor = try $ do anchorId <- parseAnchor skipMany spaceChar <|> void newline return $ return $ B.spanWith (anchorId, [], []) mempty -- | Parse a footnote reference. footnote :: PandocMonad m => MuseParser m (F Inlines) footnote = try $ do inLink <- asks museInLink guard $ not inLink ref <- noteMarker return $ do notes <- asksF museNotes case M.lookup ref notes of Nothing -> return $ B.str ref Just (_pos, contents) -> do st <- askF let contents' = runF contents st { museNotes = M.delete ref (museNotes st) } return $ B.note contents' whitespace :: PandocMonad m => MuseParser m (F Inlines) whitespace = try $ pure B.space <$ skipMany1 spaceChar <* updateLastSpacePos -- | Parse @\
    @ tag. br :: PandocMonad m => MuseParser m (F Inlines) br = try $ pure B.linebreak <$ string "
    " emphasisBetween :: (PandocMonad m, Show a) => MuseParser m a -> MuseParser m (F Inlines) emphasisBetween p = try $ trimInlinesF . mconcat <$ atStart <* p <* notFollowedBy space <*> many1Till inline (try $ noSpaceBefore *> p <* notFollowedBy alphaNum) -- | Parse an inline tag, such as @\@ and @\@. inlineTag :: PandocMonad m => Text -- ^ Tag name -> MuseParser m (F Inlines) inlineTag tag = try $ mconcat <$ openTag tag <*> manyTill inline (closeTag tag) -- | Parse strong emphasis inline markup, indicated by @***@. strongEmph :: PandocMonad m => MuseParser m (F Inlines) strongEmph = fmap (B.strong . B.emph) <$> emphasisBetween (string "***" <* notFollowedBy (char '*')) -- | Parse strong inline markup, indicated by @**@. strong :: PandocMonad m => MuseParser m (F Inlines) strong = fmap B.strong <$> emphasisBetween (string "**" <* notFollowedBy (char '*')) -- | Parse emphasis inline markup, indicated by @*@. emph :: PandocMonad m => MuseParser m (F Inlines) emph = fmap B.emph <$> emphasisBetween (char '*' <* notFollowedBy (char '*')) -- | Parse underline inline markup, indicated by @_@. -- Supported only in Emacs Muse mode, not Text::Amuse. underlined :: PandocMonad m => MuseParser m (F Inlines) underlined = fmap underline <$ guardDisabled Ext_amuse -- Supported only by Emacs Muse <*> emphasisBetween (char '_') -- | Parse @\@ tag. strongTag :: PandocMonad m => MuseParser m (F Inlines) strongTag = fmap B.strong <$> inlineTag "strong" -- | Parse @\@ tag. emphTag :: PandocMonad m => MuseParser m (F Inlines) emphTag = fmap B.emph <$> inlineTag "em" -- | Parse @\@ tag. superscriptTag :: PandocMonad m => MuseParser m (F Inlines) superscriptTag = fmap B.superscript <$> inlineTag "sup" -- | Parse @\@ tag. subscriptTag :: PandocMonad m => MuseParser m (F Inlines) subscriptTag = fmap B.subscript <$> inlineTag "sub" -- | Parse @\@ tag. strikeoutTag :: PandocMonad m => MuseParser m (F Inlines) strikeoutTag = fmap B.strikeout <$> inlineTag "del" -- | Parse @\@ tag. verbatimTag :: PandocMonad m => MuseParser m (F Inlines) verbatimTag = return . B.text <$ openTag "verbatim" <*> manyTillChar anyChar (closeTag "verbatim") -- | Parse @\@ tag. classTag :: PandocMonad m => MuseParser m (F Inlines) classTag = do classes <- maybe [] T.words . lookup "name" <$> openTag "class" fmap (B.spanWith ("", classes, [])) . mconcat <$> manyTill inline (closeTag "class") -- | Parse @\<\<\>>@ text. inlineRtl :: PandocMonad m => MuseParser m (F Inlines) inlineRtl = try $ fmap (B.spanWith ("", [], [("dir", "rtl")])) . mconcat <$ string "<<<" <*> manyTill inline (string ">>>") -- | Parse @\<\<\>>@ text. inlineLtr :: PandocMonad m => MuseParser m (F Inlines) inlineLtr = try $ fmap (B.spanWith ("", [], [("dir", "ltr")])) . mconcat <$ string ">>>" <*> manyTill inline (string "<<<") -- | Parse "~~" as nonbreaking space. nbsp :: PandocMonad m => MuseParser m (F Inlines) nbsp = try $ pure (B.str "\160") <$ string "~~" -- | Parse code markup, indicated by @\'=\'@ characters. code :: PandocMonad m => MuseParser m (F Inlines) code = try $ fmap pure $ B.code . uncurry (<>) <$ atStart <* char '=' <* notFollowedBy (spaceChar <|> newline) <*> manyUntilChar (noneOf "\n\r" <|> (newline <* notFollowedBy newline)) (try $ fmap T.singleton $ noneOf " \t\n\r=" <* char '=') <* notFollowedBy alphaNum -- | Parse @\@ tag. codeTag :: PandocMonad m => MuseParser m (F Inlines) codeTag = fmap pure $ B.codeWith <$> (htmlAttrToPandoc <$> openTag "code") <*> manyTillChar anyChar (closeTag "code") -- | Parse @\@ tag. -- @\@ tag is an Emacs Muse extension enabled by @(require 'muse-latex2png)@ mathTag :: PandocMonad m => MuseParser m (F Inlines) mathTag = return . B.math <$ openTag "math" <*> manyTillChar anyChar (closeTag "math") -- | Parse inline @\@ tag as a raw inline. inlineLiteralTag :: PandocMonad m => MuseParser m (F Inlines) inlineLiteralTag = try $ fmap pure $ B.rawInline <$> (fromMaybe "html" . lookup "style" <$> openTag "literal") -- FIXME: Emacs Muse inserts without style into all output formats, but we assume HTML <*> manyTillChar anyChar (closeTag "literal") str :: PandocMonad m => MuseParser m (F Inlines) str = return . B.str <$> many1Char alphaNum <* updateLastStrPos -- | Consume asterisks that were not used as emphasis opening. -- This prevents series of asterisks from being split into -- literal asterisk and emphasis opening. asterisks :: PandocMonad m => MuseParser m (F Inlines) asterisks = pure . B.str <$> many1Char (char '*') symbol :: PandocMonad m => MuseParser m (F Inlines) symbol = pure . B.str . T.singleton <$> nonspaceChar -- | Parse a link or image. linkOrImage :: PandocMonad m => MuseParser m (F Inlines) linkOrImage = try $ link "URL:" <|> image <|> link "" linkContent :: PandocMonad m => MuseParser m (F Inlines) linkContent = trimInlinesF . mconcat <$ char '[' <*> manyTill inline (char ']') -- | Parse a link starting with (possibly null) prefix link :: PandocMonad m => Text -> MuseParser m (F Inlines) link prefix = try $ do inLink <- asks museInLink guard $ not inLink textStr $ "[[" <> prefix url <- manyTillChar anyChar $ char ']' content <- option (pure $ B.str url) (local (\s -> s { museInLink = True }) linkContent) char ']' return $ B.link url "" <$> content image :: PandocMonad m => MuseParser m (F Inlines) image = try $ do string "[[" (url, (ext, width, align)) <- manyUntilChar (noneOf "]") (imageExtensionAndOptions <* char ']') content <- option mempty linkContent char ']' let widthAttr = case align of Just 'f' -> [("width", fromMaybe "100" width <> "%"), ("height", "75%")] _ -> maybeToList (("width",) . (<> "%") <$> width) let alignClass = case align of Just 'r' -> ["align-right"] Just 'l' -> ["align-left"] Just 'f' -> [] _ -> [] return $ B.imageWith ("", alignClass, widthAttr) (url <> ext) mempty <$> content where -- Taken from muse-image-regexp defined in Emacs Muse file lisp/muse-regexps.el imageExtensions = [".eps", ".gif", ".jpg", ".jpeg", ".pbm", ".png", ".tiff", ".xbm", ".xpm"] imageExtension = choice (try . textStr <$> imageExtensions) imageExtensionAndOptions = do ext <- imageExtension (width, align) <- option (Nothing, Nothing) imageAttrs return (ext, width, align) imageAttrs = (,) <$ many1 spaceChar <*> optionMaybe (many1Char digit) <* many spaceChar <*> optionMaybe (oneOf "rlf") ================================================ FILE: src/Text/Pandoc/Readers/Native.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Native Copyright : Copyright (C) 2011-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of a string representation of a pandoc type (@Pandoc@, @[Block]@, @Block@, @[Inline]@, or @Inline@) to a @Pandoc@ document. -} module Text.Pandoc.Readers.Native ( readNative ) where import Text.Pandoc.Definition import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Shared (safeRead) import Control.Monad.Except (throwError) import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Error import Text.Pandoc.Sources (ToSources(..), sourcesToText) -- | Read native formatted text and return a Pandoc document. -- The input may be a full pandoc document, a block list, a block, -- an inline list, or an inline. Thus, for example, -- -- > Str "hi" -- -- will be treated as if it were -- -- > Pandoc nullMeta [Plain [Str "hi"]] -- readNative :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readNative _ s = let t = sourcesToText . toSources $ s in case maybe (Pandoc nullMeta <$> readBlocks t) Right (safeRead t) of Right doc -> return doc Left _ -> throwError $ PandocParseError "couldn't read native" readBlocks :: Text -> Either PandocError [Block] readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead s) readBlock :: Text -> Either PandocError Block readBlock s = maybe (Plain <$> readInlines s) Right (safeRead s) readInlines :: Text -> Either PandocError [Inline] readInlines s = maybe ((:[]) <$> readInline s) Right (safeRead s) readInline :: Text -> Either PandocError Inline readInline s = maybe (Left . PandocParseError $ "Could not read: " <> s) Right (safeRead s) ================================================ FILE: src/Text/Pandoc/Readers/ODT/Arrows/State.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.Readers.ODT.Arrows.State Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable An arrow that transports a state. It is in essence a more powerful version of the standard state monad. As it is such a simple extension, there are other version out there that do exactly the same. The implementation is duplicated, though, to add some useful features. Most of these might be implemented without access to innards, but it's much faster and easier to implement this way. -} module Text.Pandoc.Readers.ODT.Arrows.State ( ArrowState(..) , withState , modifyState , ignoringState , fromState , extractFromState , tryModifyState , withSubStateF , withSubStateF' , foldS , iterateS , iterateSL , iterateS' ) where import qualified Data.List as L import Control.Arrow import qualified Control.Category as Cat import Control.Monad import Text.Pandoc.Readers.ODT.Arrows.Utils import Text.Pandoc.Readers.ODT.Generic.Fallible newtype ArrowState state a b = ArrowState { runArrowState :: (state, a) -> (state, b) } -- | Constructor withState :: (state -> a -> (state, b)) -> ArrowState state a b withState = ArrowState . uncurry -- | Constructor modifyState :: (state -> state ) -> ArrowState state a a modifyState = ArrowState . first -- | Constructor ignoringState :: ( a -> b ) -> ArrowState state a b ignoringState = ArrowState . second -- | Constructor fromState :: (state -> (state, b)) -> ArrowState state a b fromState = ArrowState . (.fst) -- | Constructor extractFromState :: (state -> b ) -> ArrowState state x b extractFromState f = ArrowState $ \(state,_) -> (state, f state) -- | Constructor tryModifyState :: (state -> Either f state) -> ArrowState state a (Either f a) tryModifyState f = ArrowState $ \(state,a) -> (state,).Left ||| (,Right a) $ f state instance Cat.Category (ArrowState s) where id = ArrowState id arrow2 . arrow1 = ArrowState $ runArrowState arrow2 . runArrowState arrow1 instance Arrow (ArrowState state) where arr = ignoringState first a = ArrowState $ \(s,(aF,aS)) -> second (,aS) $ runArrowState a (s,aF) second a = ArrowState $ \(s,(aF,aS)) -> second (aF,) $ runArrowState a (s,aS) instance ArrowChoice (ArrowState state) where left a = ArrowState $ \(s,e) -> case e of Left l -> second Left $ runArrowState a (s,l) Right r -> (s, Right r) right a = ArrowState $ \(s,e) -> case e of Left l -> (s, Left l) Right r -> second Right $ runArrowState a (s,r) instance ArrowApply (ArrowState state) where app = ArrowState $ \(s, (f,b)) -> runArrowState f (s,b) -- | Switches the type of the state temporarily. -- Drops the intermediate result state, behaving like a fallible -- identity arrow, save for side effects in the state. withSubStateF :: ArrowState s x (Either f s') -> ArrowState s' s (Either f s ) -> ArrowState s x (Either f x ) withSubStateF unlift a = keepingTheValue (withSubStateF' unlift a) >>^ spreadChoice >>^ fmap fst -- | Switches the type of the state temporarily. -- Returns the resulting sub-state. withSubStateF' :: ArrowState s x (Either f s') -> ArrowState s' s (Either f s ) -> ArrowState s x (Either f s') withSubStateF' unlift a = ArrowState go where go p@(s,_) = tryRunning unlift ( tryRunning a (second Right) ) p where tryRunning a' b v = case runArrowState a' v of (_ , Left f) -> (s, Left f) (x , Right y) -> b (y,x) -- | Fold a state arrow through something 'Foldable'. Collect the results -- in a 'Monoid'. -- Intermediate form of a fold between one with "only" a 'Monoid' -- and one with any function. foldS :: (Foldable f, Monoid m) => ArrowState s x m -> ArrowState s (f x) m foldS a = ArrowState $ \(s,f) -> foldr a' (s,mempty) f where a' x (s',m) = second (mappend m) $ runArrowState a (s',x) -- | Fold a state arrow through something 'Foldable'. Collect the results in a -- 'MonadPlus'. iterateS :: (Foldable f, MonadPlus m) => ArrowState s x y -> ArrowState s (f x) (m y) iterateS a = ArrowState $ \(s,f) -> foldr a' (s,mzero) f where a' x (s',m) = second (mplus m.return) $ runArrowState a (s',x) -- | Fold a state arrow through something 'Foldable'. Collect the results in a -- 'MonadPlus'. iterateSL :: (Foldable f, MonadPlus m) => ArrowState s x y -> ArrowState s (f x) (m y) iterateSL a = ArrowState $ \(s,f) -> L.foldl' a' (s,mzero) f where a' (s',m) x = second (mplus m.return) $ runArrowState a (s',x) -- | Fold a fallible state arrow through something 'Foldable'. -- Collect the results in a 'MonadPlus'. -- If the iteration fails, the state will be reset to the initial one. iterateS' :: (Foldable f, MonadPlus m) => ArrowState s x (Either e y ) -> ArrowState s (f x) (Either e (m y)) iterateS' a = ArrowState $ \(s,f) -> foldr (a' s) (s,Right mzero) f where a' s x (s',Right m) = case runArrowState a (s',x) of (s'',Right m') -> (s'',Right $ mplus m $ return m') (_ ,Left e ) -> (s ,Left e ) a' _ _ e = e ================================================ FILE: src/Text/Pandoc/Readers/ODT/Arrows/Utils.hs ================================================ {- | Module : Text.Pandoc.Readers.ODT.Arrows.Utils Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable Utility functions for Arrows (Kleisli monads). Some general notes on notation: * "^" is meant to stand for a pure function that is lifted into an arrow based on its usage for that purpose in "Control.Arrow". * "?" is meant to stand for the usage of a 'FallibleArrow' or a pure function with an equivalent return value. * "_" stands for the dropping of a value. -} -- We export everything module Text.Pandoc.Readers.ODT.Arrows.Utils ( and2 , and3 , and4 , and5 , and6 , liftA2 , liftA3 , liftA4 , liftA5 , liftA6 , liftA , duplicate , (>>%) , keepingTheValue , (^|||) , (|||^) , (^|||^) , (^&&&) , (&&&^) , choiceToMaybe , maybeToChoice , returnV , FallibleArrow , liftAsSuccess , (>>?) , (>>?^) , (>>?^?) , (^>>?) , (>>?!) , (>>?%) , (>>?%?) , ifFailedDo ) where import Prelude hiding (Applicative(..)) import Control.Arrow import Control.Monad (join) import Text.Pandoc.Readers.ODT.Generic.Fallible import Text.Pandoc.Readers.ODT.Generic.Utils and2 :: (Arrow a) => a b c -> a b c' -> a b (c,c') and2 = (&&&) and3 :: (Arrow a) => a b c0->a b c1->a b c2 -> a b (c0,c1,c2 ) and4 :: (Arrow a) => a b c0->a b c1->a b c2->a b c3 -> a b (c0,c1,c2,c3 ) and5 :: (Arrow a) => a b c0->a b c1->a b c2->a b c3->a b c4 -> a b (c0,c1,c2,c3,c4 ) and6 :: (Arrow a) => a b c0->a b c1->a b c2->a b c3->a b c4->a b c5 -> a b (c0,c1,c2,c3,c4,c5 ) and3 a b c = and2 a b &&& c >>^ \((z,y ) , x) -> (z,y,x ) and4 a b c d = and3 a b c &&& d >>^ \((z,y,x ) , w) -> (z,y,x,w ) and5 a b c d e = and4 a b c d &&& e >>^ \((z,y,x,w ) , v) -> (z,y,x,w,v ) and6 a b c d e f = and5 a b c d e &&& f >>^ \((z,y,x,w,v ) , u) -> (z,y,x,w,v,u ) liftA2 :: (Arrow a) => (x -> y -> z) -> a b x -> a b y -> a b z liftA2 f a b = a &&& b >>^ uncurry f liftA3 :: (Arrow a) => (z->y->x -> r) -> a b z->a b y->a b x -> a b r liftA4 :: (Arrow a) => (z->y->x->w -> r) -> a b z->a b y->a b x->a b w -> a b r liftA5 :: (Arrow a) => (z->y->x->w->v -> r) -> a b z->a b y->a b x->a b w->a b v -> a b r liftA6 :: (Arrow a) => (z->y->x->w->v->u -> r) -> a b z->a b y->a b x->a b w->a b v->a b u -> a b r liftA3 fun a b c = and3 a b c >>^ uncurry3 fun liftA4 fun a b c d = and4 a b c d >>^ uncurry4 fun liftA5 fun a b c d e = and5 a b c d e >>^ uncurry5 fun liftA6 fun a b c d e f = and6 a b c d e f >>^ uncurry6 fun liftA :: (Arrow a) => (y -> z) -> a b y -> a b z liftA fun a = a >>^ fun -- | Duplicate a value to subsequently feed it into different arrows. -- Can almost always be replaced with '(&&&)', 'keepingTheValue', -- or even '(|||)'. -- Equivalent to -- > returnA &&& returnA duplicate :: (Arrow a) => a b (b,b) duplicate = arr $ join (,) -- | Applies a function to the uncurried result-pair of an arrow-application. -- (The %-symbol was chosen to evoke an association with pairs.) (>>%) :: (Arrow a) => a x (b,c) -> (b -> c -> d) -> a x d a >>% f = a >>^ uncurry f infixr 2 >>% -- | Duplicate a value and apply an arrow to the second instance. -- Equivalent to -- > \a -> duplicate >>> second a -- or -- > \a -> returnA &&& a keepingTheValue :: (Arrow a) => a b c -> a b (b,c) keepingTheValue a = returnA &&& a ( ^||| ) :: (ArrowChoice a) => (b -> d) -> a c d -> a (Either b c) d ( |||^ ) :: (ArrowChoice a) => a b d -> (c -> d) -> a (Either b c) d ( ^|||^ ) :: (ArrowChoice a) => (b -> d) -> (c -> d) -> a (Either b c) d l ^||| r = arr l ||| r l |||^ r = l ||| arr r l ^|||^ r = arr l ||| arr r infixr 2 ^||| , |||^, ^|||^ ( ^&&& ) :: (Arrow a) => (b -> c) -> a b c' -> a b (c,c') ( &&&^ ) :: (Arrow a) => a b c -> (b -> c') -> a b (c,c') l ^&&& r = arr l &&& r l &&&^ r = l &&& arr r infixr 3 ^&&&, &&&^ -- | Converts @Right a@ into @Just a@ and @Left _@ into @Nothing@. choiceToMaybe :: (ArrowChoice a) => a (Either l r) (Maybe r) choiceToMaybe = arr eitherToMaybe -- | Converts @Nothing@ into @Left ()@ and @Just a@ into @Right a@. maybeToChoice :: (ArrowChoice a) => a (Maybe b) (Fallible b) maybeToChoice = arr maybeToEither -- | Lifts a constant value into an arrow returnV :: (Arrow a) => c -> a x c returnV = arr.const -- | Defines Left as failure, Right as success type FallibleArrow a input failure success = a input (Either failure success) -- liftAsSuccess :: (ArrowChoice a) => a x success -> FallibleArrow a x failure success liftAsSuccess a = a >>^ Right -- | Execute the second arrow if the first succeeds (>>?) :: (ArrowChoice a) => FallibleArrow a x failure success -> FallibleArrow a success failure success' -> FallibleArrow a x failure success' a >>? b = a >>> Left ^||| b -- | Execute the lifted second arrow if the first succeeds (>>?^) :: (ArrowChoice a) => FallibleArrow a x failure success -> (success -> success') -> FallibleArrow a x failure success' a >>?^ f = a >>^ Left ^|||^ Right . f -- | Execute the lifted second arrow if the first succeeds (>>?^?) :: (ArrowChoice a) => FallibleArrow a x failure success -> (success -> Either failure success') -> FallibleArrow a x failure success' a >>?^? b = a >>> Left ^|||^ b -- | Execute the second arrow if the lifted first arrow succeeds (^>>?) :: (ArrowChoice a) => (x -> Either failure success) -> FallibleArrow a success failure success' -> FallibleArrow a x failure success' a ^>>? b = a ^>> Left ^||| b -- | Execute the second, non-fallible arrow if the first arrow succeeds (>>?!) :: (ArrowChoice a) => FallibleArrow a x failure success -> a success success' -> FallibleArrow a x failure success' a >>?! f = a >>> right f --- (>>?%) :: (ArrowChoice a) => FallibleArrow a x f (b,b') -> (b -> b' -> c) -> FallibleArrow a x f c a >>?% f = a >>?^ uncurry f --- (>>?%?) :: (ArrowChoice a) => FallibleArrow a x f (b,b') -> (b -> b' -> Either f c) -> FallibleArrow a x f c a >>?%? f = a >>?^? uncurry f infixr 1 >>?, >>?^, >>?^? infixr 1 ^>>?, >>?! infixr 1 >>?%, >>?%? -- | An arrow version of a short-circuit (<|>) ifFailedDo :: (ArrowChoice a) => FallibleArrow a x f y -> FallibleArrow a x f y -> FallibleArrow a x f y ifFailedDo a b = keepingTheValue a >>> repackage ^>> (b |||^ Right) where repackage (x , Left _) = Left x repackage (_ , Right y) = Right y infixr 1 `ifFailedDo` ================================================ FILE: src/Text/Pandoc/Readers/ODT/Base.hs ================================================ {- | Module : Text.Pandoc.Readers.ODT.Base Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable Core types of the odt reader. -} module Text.Pandoc.Readers.ODT.Base ( ODTConverterState , XMLReader , XMLReaderSafe ) where import Text.Pandoc.Readers.ODT.Generic.XMLConverter import Text.Pandoc.Readers.ODT.Namespaces type ODTConverterState s = XMLConverterState Namespace s type XMLReader s a b = FallibleXMLConverter Namespace s a b type XMLReaderSafe s a b = XMLConverter Namespace s a b ================================================ FILE: src/Text/Pandoc/Readers/ODT/ContentReader.hs ================================================ {-# LANGUAGE Arrows #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.ODT.ContentReader Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable The core of the odt reader that converts odt features into Pandoc types. -} module Text.Pandoc.Readers.ODT.ContentReader ( readerState , read_body ) where import Prelude hiding (Applicative(..)) import Control.Applicative hiding (liftA, liftA2, liftA3) import Control.Arrow import Control.Monad ((<=<)) import qualified Data.ByteString.Lazy as B import Data.Foldable (fold) import Data.List (find) import qualified Data.Map as M import qualified Data.Text as T import Data.Maybe import Data.Monoid (Alt (..)) import Text.TeXMath (readMathML, writeTeX) import qualified Text.Pandoc.XML.Light as XML import Text.Pandoc.Builder hiding (underline) import Text.Pandoc.MediaBag (MediaBag, insertMedia) import Text.Pandoc.Shared import Text.Pandoc.Extensions (extensionsFromList, Extension(..)) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Readers.Docx.Combine (combineBlocks) import Text.Pandoc.Readers.ODT.Base import Text.Pandoc.Readers.ODT.Namespaces import Text.Pandoc.Readers.ODT.StyleReader import Text.Pandoc.Readers.ODT.Arrows.State (foldS) import Text.Pandoc.Readers.ODT.Arrows.Utils import Text.Pandoc.Readers.ODT.Generic.Fallible import Text.Pandoc.Readers.ODT.Generic.Utils import Text.Pandoc.Readers.ODT.Generic.XMLConverter import Network.URI (parseRelativeReference, URI(uriPath)) import qualified Data.Set as Set -------------------------------------------------------------------------------- -- State -------------------------------------------------------------------------------- type Anchor = T.Text type Media = [(FilePath, B.ByteString)] data ReaderState = ReaderState { -- | A collection of styles read somewhere else. -- It is only queried here, not modified. styleSet :: Styles -- | A stack of the styles of parent elements. -- Used to look up inherited style properties. , styleTrace :: [Style] -- | Keeps track of the current depth in nested lists , currentListLevel :: ListLevel -- | Keeps track of the previous list start counters, -- so whenever a new list continues numbering, -- we know what number to start from. -- If list does not continue numbering, the counter -- is being reset. , listContinuationStartCounters :: M.Map ListLevel Int -- | Lists may provide their own style, but they don't have -- to. If they do not, the style of a parent list may be used -- or even a default list style from the paragraph style. -- This value keeps track of the closest list style there -- currently is. , currentListStyle :: Maybe ListStyle -- | A map from internal anchor names to "pretty" ones. -- The mapping is a purely cosmetic one. , bookmarkAnchors :: M.Map Anchor Anchor -- | A map of files / binary data from the archive , envMedia :: Media -- | Hold binary resources used in the document , odtMediaBag :: MediaBag } deriving ( Show ) readerState :: Styles -> Media -> ReaderState readerState styles media = ReaderState styles [] 0 M.empty Nothing M.empty media mempty -- pushStyle' :: Style -> ReaderState -> ReaderState pushStyle' style state = state { styleTrace = style : styleTrace state } -- popStyle' :: ReaderState -> ReaderState popStyle' state = case styleTrace state of _:trace -> state { styleTrace = trace } _ -> state -- modifyListLevel :: (ListLevel -> ListLevel) -> (ReaderState -> ReaderState) modifyListLevel f state = state { currentListLevel = f (currentListLevel state) } -- modifyListContinuationStartCounter :: ListLevel -> Int -> (ReaderState -> ReaderState) modifyListContinuationStartCounter listLevel count state = state { listContinuationStartCounters = M.insert listLevel count (listContinuationStartCounters state) } -- shiftListLevel :: ListLevel -> (ReaderState -> ReaderState) shiftListLevel diff = modifyListLevel (+ diff) -- swapCurrentListStyle :: Maybe ListStyle -> ReaderState -> (ReaderState, Maybe ListStyle) swapCurrentListStyle mListStyle state = ( state { currentListStyle = mListStyle } , currentListStyle state ) -- lookupPrettyAnchor :: Anchor -> ReaderState -> Maybe Anchor lookupPrettyAnchor anchor ReaderState{..} = M.lookup anchor bookmarkAnchors -- putPrettyAnchor :: Anchor -> Anchor -> ReaderState -> ReaderState putPrettyAnchor ugly pretty state@ReaderState{..} = state { bookmarkAnchors = M.insert ugly pretty bookmarkAnchors } -- usedAnchors :: ReaderState -> [Anchor] usedAnchors ReaderState{..} = M.elems bookmarkAnchors getMediaBag :: ReaderState -> MediaBag getMediaBag ReaderState{..} = odtMediaBag getMediaEnv :: ReaderState -> Media getMediaEnv ReaderState{..} = envMedia insertMedia' :: (FilePath, B.ByteString) -> ReaderState -> ReaderState insertMedia' (fp, bs) state@ReaderState{..} = state { odtMediaBag = insertMedia fp Nothing bs odtMediaBag } -------------------------------------------------------------------------------- -- Reader type and associated tools -------------------------------------------------------------------------------- type ODTReader a b = XMLReader ReaderState a b type ODTReaderSafe a b = XMLReaderSafe ReaderState a b -- | Extract something from the styles fromStyles :: (a -> Styles -> b) -> ODTReaderSafe a b fromStyles f = keepingTheValue (getExtraState >>^ styleSet) >>% f -- getStyleByName :: ODTReader StyleName Style getStyleByName = fromStyles lookupStyle >>^ maybeToChoice -- findStyleFamily :: ODTReader Style StyleFamily findStyleFamily = fromStyles getStyleFamily >>^ maybeToChoice -- lookupListStyle :: ODTReader StyleName ListStyle lookupListStyle = fromStyles lookupListStyleByName >>^ maybeToChoice -- switchCurrentListStyle :: ODTReaderSafe (Maybe ListStyle) (Maybe ListStyle) switchCurrentListStyle = keepingTheValue getExtraState >>% swapCurrentListStyle >>> first setExtraState >>^ snd -- pushStyle :: ODTReaderSafe Style Style pushStyle = keepingTheValue ( ( keepingTheValue getExtraState >>% pushStyle' ) >>> setExtraState ) >>^ fst -- popStyle :: ODTReaderSafe x x popStyle = keepingTheValue ( getExtraState >>> arr popStyle' >>> setExtraState ) >>^ fst -- getCurrentListLevel :: ODTReaderSafe a ListLevel getCurrentListLevel = getExtraState >>^ currentListLevel -- getListContinuationStartCounters :: ODTReaderSafe a (M.Map ListLevel Int) getListContinuationStartCounters = getExtraState >>^ listContinuationStartCounters -- getPreviousListStartCounter :: ODTReaderSafe ListLevel Int getPreviousListStartCounter = proc listLevel -> do counts <- getListContinuationStartCounters -< () returnA -< M.findWithDefault 0 listLevel counts -- updateMediaWithResource :: ODTReaderSafe (FilePath, B.ByteString) (FilePath, B.ByteString) updateMediaWithResource = keepingTheValue ( (keepingTheValue getExtraState >>% insertMedia' ) >>> setExtraState ) >>^ fst lookupResource :: ODTReaderSafe FilePath (FilePath, B.ByteString) lookupResource = proc target -> do state <- getExtraState -< () case lookup target (getMediaEnv state) of Just bs -> returnV (target, bs) -<< () Nothing -> returnV ("", B.empty) -< () type AnchorPrefix = T.Text -- | An adaptation of 'uniqueIdent' from "Text.Pandoc.Shared" that generates a -- unique identifier but without assuming that the id should be for a header. -- Second argument is a list of already used identifiers. uniqueIdentFrom :: AnchorPrefix -> [Anchor] -> Anchor uniqueIdentFrom baseIdent usedIdents = let numIdent n = baseIdent <> "-" <> T.pack (show n) in if baseIdent `elem` usedIdents then maybe baseIdent numIdent $ find (\x -> numIdent x `notElem` usedIdents) ([1..60000] :: [Int]) -- if we have more than 60,000, allow repeats else baseIdent -- | First argument: basis for a new "pretty" anchor if none exists yet -- Second argument: a key ("ugly" anchor) -- Returns: saved "pretty" anchor or created new one getPrettyAnchor :: ODTReaderSafe (AnchorPrefix, Anchor) Anchor getPrettyAnchor = proc (baseIdent, uglyAnchor) -> do state <- getExtraState -< () case lookupPrettyAnchor uglyAnchor state of Just prettyAnchor -> returnA -< prettyAnchor Nothing -> do let newPretty = uniqueIdentFrom baseIdent (usedAnchors state) modifyExtraState (putPrettyAnchor uglyAnchor newPretty) -<< newPretty -- | Input: basis for a new header anchor -- Output: saved new anchor getHeaderAnchor :: ODTReaderSafe Inlines Anchor getHeaderAnchor = proc title -> do state <- getExtraState -< () let exts = extensionsFromList [Ext_auto_identifiers] let anchor = uniqueIdent exts (toList title) (Set.fromList $ usedAnchors state) modifyExtraState (putPrettyAnchor anchor anchor) -<< anchor -------------------------------------------------------------------------------- -- Working with styles -------------------------------------------------------------------------------- -- readStyleByName :: ODTReader a (StyleName, Style) readStyleByName = findAttr NsText "style-name" >>? keepingTheValue getStyleByName >>^ liftE where liftE :: (StyleName, Fallible Style) -> Fallible (StyleName, Style) liftE (name, Right v) = Right (name, v) liftE (_, Left v) = Left v -- isStyleToTrace :: ODTReader Style Bool isStyleToTrace = findStyleFamily >>?^ (==FaText) -- withNewStyle :: ODTReaderSafe x Inlines -> ODTReaderSafe x Inlines withNewStyle a = proc x -> do fStyle <- readStyleByName -< () case fStyle of Right (styleName, _) | isCodeStyle styleName -> do inlines <- a -< x arr inlineCode -<< inlines Right (_, style) -> do mFamily <- arr styleFamily -< style fTextProps <- arr ( maybeToChoice . textProperties . styleProperties ) -< style case fTextProps of Right textProps -> do state <- getExtraState -< () let triple = (state, textProps, mFamily) modifier <- arr modifierFromStyleDiff -< triple fShouldTrace <- isStyleToTrace -< style case fShouldTrace of Right shouldTrace -> if shouldTrace then do pushStyle -< style inlines <- a -< x popStyle -< () arr modifier -<< inlines else -- In case anything goes wrong a -< x Left _ -> a -< x Left _ -> a -< x Left _ -> a -< x where isCodeStyle :: StyleName -> Bool isCodeStyle "Source_Text" = True isCodeStyle "Source_20_Text" = True isCodeStyle _ = False inlineCode :: Inlines -> Inlines inlineCode = code . T.concat . map stringify . toList type PropertyTriple = (ReaderState, TextProperties, Maybe StyleFamily) type InlineModifier = Inlines -> Inlines -- | Given data about the local style changes, calculates how to modify -- an instance of 'Inlines' modifierFromStyleDiff :: PropertyTriple -> InlineModifier modifierFromStyleDiff propertyTriple = composition $ getVPosModifier propertyTriple : map (first ($ propertyTriple) >>> ifThen_else ignore) [ (hasEmphChanged , emph ) , (hasChanged isStrong , strong ) , (hasChanged strikethrough , strikeout ) ] where ifThen_else else' (if',then') = if if' then then' else else' ignore = id :: InlineModifier getVPosModifier :: PropertyTriple -> InlineModifier getVPosModifier triple@(_,textProps,_) = let getVPos = Just . verticalPosition in case lookupPreviousValueM getVPos triple of Nothing -> ignore Just oldVPos -> getVPosModifier' (oldVPos, verticalPosition textProps) getVPosModifier' (oldVPos , newVPos ) | oldVPos == newVPos = ignore getVPosModifier' ( _ , VPosSub ) = subscript getVPosModifier' ( _ , VPosSuper ) = superscript getVPosModifier' ( _ , _ ) = ignore hasEmphChanged :: PropertyTriple -> Bool hasEmphChanged = swing any [ hasChanged isEmphasised , hasChangedM pitch , hasChanged underline ] hasChanged property triple@(_, property -> newProperty, _) = (/= Just newProperty) (lookupPreviousValue property triple) hasChangedM property triple@(_, textProps,_) = fromMaybe False $ (/=) <$> property textProps <*> lookupPreviousValueM property triple lookupPreviousValue f = lookupPreviousStyleValue (fmap f . textProperties) lookupPreviousValueM f = lookupPreviousStyleValue (f <=< textProperties) lookupPreviousStyleValue f (ReaderState{..},_,mFamily) = findBy f (extendedStylePropertyChain styleTrace styleSet) <|> (f . lookupDefaultStyle' styleSet =<< mFamily) type ParaModifier = Blocks -> Blocks _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_MM_ :: Int _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_PERCENT_ :: Int _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_MM_ = 5 _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_PERCENT_ = 5 -- | Returns either 'id' or 'blockQuote' depending if any of the StyleProperties -- are indented at quote level. getParaModifier :: ListLevel -> [StyleProperties] -> ParaModifier getParaModifier listLevel props | listLevel > 0 = id -- see #9505, list paragraphs need indentation | any isBlockQuote props = blockQuote | otherwise = id where isBlockQuote SProps {..} | Just paraProps <- paraProperties , isQuoteWidth (margin_left paraProps) = True | otherwise = False isQuoteWidth mMargin | LengthValueMM margin <- mMargin , margin > _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_MM_ = True | PercentValue margin <- mMargin , margin > _MINIMUM_INDENTATION_FOR_BLOCKQUOTES_IN_PERCENT_ = True | otherwise = False -- constructPara :: ODTReaderSafe a Blocks -> ODTReaderSafe a Blocks constructPara reader = proc blocks -> do fStyle <- readStyleByName -< blocks case fStyle of Left _ -> reader -< blocks Right (styleName, _) | isTableCaptionStyle styleName -> do blocks' <- reader -< blocks arr tableCaptionP -< blocks' Right (_, style) -> do props <- fromStyles extendedStylePropertyChain -< [style] listLevel <- getCurrentListLevel -< () let modifier = getParaModifier listLevel props blocks' <- reader -< blocks arr modifier -<< blocks' where isTableCaptionStyle :: StyleName -> Bool isTableCaptionStyle "Table" = True isTableCaptionStyle _ = False tableCaptionP b = divWith ("", ["caption"], []) b type ListConstructor = [Blocks] -> Blocks getListConstructor :: ListLevelStyle -> Int -> ListConstructor getListConstructor ListLevelStyle{..} startNum = case listLevelType of LltBullet -> bulletList LltImage -> bulletList LltNumbered -> let listNumberStyle = toListNumberStyle listItemFormat listNumberDelim = toListNumberDelim listItemPrefix listItemSuffix in orderedListWith (startNum, listNumberStyle, listNumberDelim) where toListNumberStyle LinfNone = DefaultStyle toListNumberStyle LinfNumber = Decimal toListNumberStyle LinfRomanLC = LowerRoman toListNumberStyle LinfRomanUC = UpperRoman toListNumberStyle LinfAlphaLC = LowerAlpha toListNumberStyle LinfAlphaUC = UpperAlpha toListNumberStyle (LinfString _) = Example toListNumberDelim Nothing (Just ".") = Period toListNumberDelim (Just "" ) (Just ".") = Period toListNumberDelim Nothing (Just ")") = OneParen toListNumberDelim (Just "" ) (Just ")") = OneParen toListNumberDelim (Just "(") (Just ")") = TwoParens toListNumberDelim _ _ = DefaultDelim -- | Determines which style to use for a list, which level to use of that -- style, and which type of list to create as a result of this information. -- Then prepares the state for eventual child lists and constructs the list from -- the results. -- Two main cases are handled: The list may provide its own style or it may -- rely on a parent list's style. I the former case the current style in the -- state must be switched before and after the call to the child converter -- while in the latter the child converter can be called directly. -- If anything goes wrong, a default ordered-list-constructor is used. constructList :: ODTReaderSafe x [Blocks] -> ODTReaderSafe x Blocks constructList reader = proc x -> do modifyExtraState (shiftListLevel 1) -< () listLevel <- getCurrentListLevel -< () listContinuationStartCounter <- getPreviousListStartCounter -< listLevel fStyleName <- findAttr NsText "style-name" -< () fContNumbering <- findAttr NsText "continue-numbering" -< () listItemCount <- reader >>^ length -< x let continueNumbering = case fContNumbering of Right "true" -> True _ -> False let startNumForListLevelStyle = listStartingNumber continueNumbering listContinuationStartCounter let defaultOrderedListConstructor = constructOrderedList (startNumForListLevelStyle Nothing) listLevel listItemCount case fStyleName of Right styleName -> do fListStyle <- lookupListStyle -< styleName case fListStyle of Right listStyle -> do fListLevelStyle <- arr (uncurry getListLevelStyle) -< (listLevel, listStyle) case fListLevelStyle of Just listLevelStyle -> do let startNum = startNumForListLevelStyle $ Just listLevelStyle oldListStyle <- switchCurrentListStyle -< Just listStyle blocks <- constructListWith listLevelStyle startNum listLevel listItemCount -<< x switchCurrentListStyle -< oldListStyle returnA -< blocks Nothing -> defaultOrderedListConstructor -<< x Left _ -> defaultOrderedListConstructor -<< x Left _ -> do state <- getExtraState -< () mListStyle <- arr currentListStyle -< state case mListStyle of Just listStyle -> do fListLevelStyle <- arr (uncurry getListLevelStyle) -< (listLevel, listStyle) case fListLevelStyle of Just listLevelStyle -> do let startNum = startNumForListLevelStyle $ Just listLevelStyle constructListWith listLevelStyle startNum listLevel listItemCount -<< x Nothing -> defaultOrderedListConstructor -<< x Nothing -> defaultOrderedListConstructor -<< x where listStartingNumber continueNumbering listContinuationStartCounter mListLevelStyle | continueNumbering = listContinuationStartCounter | isJust mListLevelStyle = listItemStart (fromJust mListLevelStyle) | otherwise = 1 constructOrderedList startNum listLevel listItemCount = reader >>> modifyExtraState (shiftListLevel (-1)) >>> modifyExtraState (modifyListContinuationStartCounter listLevel (startNum + listItemCount)) >>^ orderedListWith (startNum, DefaultStyle, DefaultDelim) constructListWith listLevelStyle startNum listLevel listItemCount = reader >>> getListConstructor listLevelStyle startNum ^>> modifyExtraState (shiftListLevel (-1)) >>> modifyExtraState (modifyListContinuationStartCounter listLevel (startNum + listItemCount)) -------------------------------------------------------------------------------- -- Readers -------------------------------------------------------------------------------- type ElementMatcher result = (Namespace, ElementName, ODTReader result result) type InlineMatcher = ElementMatcher Inlines type BlockMatcher = ElementMatcher Blocks newtype FirstMatch a = FirstMatch (Alt Maybe a) deriving (Foldable, Monoid, Semigroup) firstMatch :: a -> FirstMatch a firstMatch = FirstMatch . Alt . Just newtype CombiningBlocks = CombiningBlocks { unCombiningBlocks :: Blocks } instance Semigroup CombiningBlocks where CombiningBlocks l <> CombiningBlocks r = CombiningBlocks (combineBlocks l r) instance Monoid CombiningBlocks where mempty = CombiningBlocks mempty -- matchingElement :: (Monoid e) => Namespace -> ElementName -> ODTReaderSafe e e -> ElementMatcher e matchingElement ns name reader = (ns, name, asResultAccumulator reader) where asResultAccumulator :: (ArrowChoice a, Monoid m) => a m m -> a m (Fallible m) asResultAccumulator a = liftAsSuccess $ keepingTheValue a >>% mappend -- matchChildContent' :: (Monoid result) => [ElementMatcher result] -> ODTReaderSafe a result matchChildContent' ls = returnV mempty >>> matchContent' ls -- matchSmushedChildBlocks' :: [ElementMatcher CombiningBlocks] -> ODTReaderSafe a Blocks matchSmushedChildBlocks' ls = liftA unCombiningBlocks $ returnV mempty >>> matchContent' ls -- matchChildContent :: (Monoid result) => [ElementMatcher result] -> ODTReaderSafe (result, XML.Content) result -> ODTReaderSafe a result matchChildContent ls fallback = returnV mempty >>> matchContent ls fallback -------------------------------------------- -- Matchers -------------------------------------------- ---------------------- -- Basics ---------------------- -- -- | Open Document allows several consecutive spaces if they are marked up read_plain_text :: ODTReaderSafe (Inlines, XML.Content) Inlines read_plain_text = fst ^&&& read_plain_text' >>% recover where -- fallible version read_plain_text' :: ODTReader (Inlines, XML.Content) Inlines read_plain_text' = ( second ( arr extractText ) >>^ spreadChoice >>?! second text ) >>?% mappend -- extractText :: XML.Content -> Fallible T.Text extractText (XML.Text cData) = succeedWith (XML.cdData cData) extractText _ = failEmpty read_text_seq :: InlineMatcher read_text_seq = matchingElement NsText "sequence" $ matchChildContent [] read_plain_text -- specifically. I honor that, although the current implementation of 'mappend' -- for 'Inlines' in "Text.Pandoc.Builder" will collapse them again. -- The rational is to be prepared for future modifications. read_spaces :: InlineMatcher read_spaces = matchingElement NsText "s" ( readAttrWithDefault NsText "c" 1 -- how many spaces? >>^ fromList.(`replicate` Space) ) -- read_line_break :: InlineMatcher read_line_break = matchingElement NsText "line-break" $ returnV linebreak -- read_tab :: InlineMatcher read_tab = matchingElement NsText "tab" $ returnV space -- read_span :: InlineMatcher read_span = matchingElement NsText "span" $ withNewStyle $ matchChildContent [ read_span , read_spaces , read_line_break , read_tab , read_link , read_frame , read_note , read_citation , read_bookmark , read_bookmark_start , read_reference_start , read_bookmark_ref , read_reference_ref ] read_plain_text -- read_paragraph :: ElementMatcher CombiningBlocks read_paragraph = matchingElement NsText "p" $ liftA CombiningBlocks $ proc blocks -> do fStyle <- readStyleByName -< blocks case fStyle of Right style | isPreformattedStyle style -> do liftA (codeBlock . stringify) $ matchParagraphContent -< blocks _ -> constructPara $ liftA para $ withNewStyle matchParagraphContent -< blocks where isPreformattedStyle :: (StyleName, Style) -> Bool isPreformattedStyle ("Preformatted_20_Text", _) = True isPreformattedStyle (_, Style { styleParentName = Just "Preformatted_20_Text" }) = True isPreformattedStyle _ = False matchParagraphContent :: ODTReaderSafe a Inlines matchParagraphContent = matchChildContent [ read_span , read_spaces , read_line_break , read_tab , read_link , read_note , read_citation , read_bookmark , read_bookmark_start , read_reference_start , read_bookmark_ref , read_reference_ref , read_frame , read_text_seq ] read_plain_text ---------------------- -- Headers ---------------------- -- read_header :: ElementMatcher CombiningBlocks read_header = matchingElement NsText "h" $ proc blocks -> do level <- ( readAttrWithDefault NsText "outline-level" 1 ) -< blocks children <- ( matchChildContent [ read_span , read_spaces , read_line_break , read_tab , read_link , read_note , read_citation , read_bookmark , read_bookmark_start , read_reference_start , read_bookmark_ref , read_reference_ref , read_frame ] read_plain_text ) -< blocks anchor <- getHeaderAnchor -< children let idAttr = (anchor, [], []) -- no classes, no key-value pairs arr (CombiningBlocks . uncurry3 headerWith) -< (idAttr, level, children) ---------------------- -- Lists ---------------------- -- read_list :: ElementMatcher CombiningBlocks read_list = matchingElement NsText "list" $ liftA CombiningBlocks $ constructList $ matchChildContent' [ read_list_item , read_list_header ] -- read_list_item :: ElementMatcher [Blocks] read_list_item = read_list_element "list-item" read_list_header :: ElementMatcher [Blocks] read_list_header = read_list_element "list-header" read_list_element :: ElementName -> ElementMatcher [Blocks] read_list_element listElement = matchingElement NsText listElement $ liftA (compactify.(:[])) ( matchSmushedChildBlocks' [ read_paragraph , read_header , read_list , read_section ] ) ---------------------- -- Sections ---------------------- read_section :: ElementMatcher CombiningBlocks read_section = matchingElement NsText "section" $ liftA (CombiningBlocks . divWith nullAttr) $ matchSmushedChildBlocks' [ read_paragraph , read_header , read_list , read_table , read_section ] ---------------------- -- Links ---------------------- read_link :: InlineMatcher read_link = matchingElement NsText "a" $ liftA3 link ( findAttrTextWithDefault NsXLink "href" "" >>> arr fixRelativeLink ) ( findAttrTextWithDefault NsOffice "title" "" ) ( matchChildContent [ read_span , read_note , read_citation , read_bookmark , read_bookmark_start , read_reference_start , read_bookmark_ref , read_reference_ref ] read_plain_text ) fixRelativeLink :: T.Text -> T.Text fixRelativeLink uri = case parseRelativeReference (T.unpack uri) of Nothing -> uri Just u -> case uriPath u of '.':'.':'/':xs -> tshow $ u{ uriPath = xs } _ -> uri ------------------------- -- Footnotes ------------------------- read_note :: InlineMatcher read_note = matchingElement NsText "note" $ liftA note $ matchChildContent' [ read_note_body ] read_note_body :: BlockMatcher read_note_body = matchingElement NsText "note-body" $ matchSmushedChildBlocks' [ read_paragraph ] ------------------------- -- Citations ------------------------- read_citation :: InlineMatcher read_citation = matchingElement NsText "bibliography-mark" $ liftA2 cite ( liftA2 makeCitation ( findAttrTextWithDefault NsText "identifier" "" ) ( readAttrWithDefault NsText "number" 0 ) ) ( matchChildContent [] read_plain_text ) where makeCitation :: T.Text -> Int -> [Citation] makeCitation citeId num = [Citation citeId [] [] NormalCitation num 0] ---------------------- -- Tables ---------------------- -- read_table :: ElementMatcher CombiningBlocks read_table = matchingElement NsTable "table" $ liftA (CombiningBlocks . table') $ (matchChildContent' [read_table_header]) &&& (matchChildContent' [read_table_row]) -- | 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 th = TableHead nullAttr $ map toRow headers tb = TableBody nullAttr 0 [] $ map toRow rows tf = TableFoot nullAttr [] -- read_table_header :: ElementMatcher [[Cell]] read_table_header = matchingElement NsTable "table-header-rows" $ matchChildContent' [ read_table_row ] -- read_table_row :: ElementMatcher [[Cell]] read_table_row = matchingElement NsTable "table-row" $ liftA (:[]) $ matchChildContent' [ read_table_cell ] -- read_table_cell :: ElementMatcher [Cell] read_table_cell = matchingElement NsTable "table-cell" $ liftA3 cell' (readAttrWithDefault NsTable "number-rows-spanned" 1 >>^ RowSpan) (readAttrWithDefault NsTable "number-columns-spanned" 1 >>^ ColSpan) $ matchSmushedChildBlocks' [ read_paragraph , read_list ] where cell' rowSpan colSpan blocks = map (cell AlignDefault rowSpan colSpan) $ compactify [blocks] ---------------------- -- Frames ---------------------- -- read_frame :: InlineMatcher read_frame = matchingElement NsDraw "frame" $ filterChildrenName' NsDraw (`elem` ["image", "object", "text-box"]) >>> foldS read_frame_child >>> arr fold read_frame_child :: ODTReaderSafe XML.Element (FirstMatch Inlines) read_frame_child = proc child -> case elName child of "image" -> read_frame_img -< child "object" -> read_frame_mathml -< child "text-box" -> read_frame_text_box -< child _ -> returnV mempty -< () read_frame_img :: ODTReaderSafe XML.Element (FirstMatch Inlines) read_frame_img = proc img -> do src <- executeIn (findAttr' NsXLink "href") -< img case fold src of "" -> returnV mempty -< () src' -> do let exts = extensionsFromList [Ext_auto_identifiers] src'' = fixRelativeLink src' resource <- lookupResource -< T.unpack src'' _ <- updateMediaWithResource -< resource w <- findAttrText' NsSVG "width" -< () h <- findAttrText' NsSVG "height" -< () titleNodes <- matchChildContent' [ read_frame_title ] -< () alt <- matchChildContent [] read_plain_text -< () arr (firstMatch . uncurry4 imageWith) -< (image_attributes w h, src'', inlineListToIdentifier exts (toList titleNodes), alt) read_frame_title :: InlineMatcher read_frame_title = matchingElement NsSVG "title" (matchChildContent [] read_plain_text) image_attributes :: Maybe T.Text -> Maybe T.Text -> Attr image_attributes x y = ( "", [], dim "width" x ++ dim "height" y) where dim _ (Just "") = [] dim name (Just v) = [(name, v)] dim _ Nothing = [] read_frame_mathml :: ODTReaderSafe XML.Element (FirstMatch Inlines) read_frame_mathml = proc obj -> do src <- executeIn (findAttr' NsXLink "href") -< obj case fold src of "" -> returnV mempty -< () src' -> do let path = T.unpack $ fromMaybe src' (T.stripPrefix "./" src') <> "/content.xml" (_, mathml) <- lookupResource -< path case readMathML (UTF8.toText $ B.toStrict mathml) of Left _ -> returnV mempty -< () Right exps -> arr (firstMatch . displayMath . writeTeX) -< exps read_frame_text_box :: ODTReaderSafe XML.Element (FirstMatch Inlines) read_frame_text_box = proc box -> do paragraphs <- executeIn (matchSmushedChildBlocks' [ read_paragraph ]) -< box arr read_img_with_caption -< toList paragraphs read_img_with_caption :: [Block] -> FirstMatch Inlines read_img_with_caption (Para [Image attr alt (src,title)] : _) = firstMatch $ singleton (Image attr alt (src, "fig:" <> title)) -- no text, default caption read_img_with_caption (Para (Image attr _ (src,title) : txt) : _) = firstMatch $ singleton (Image attr txt (src, "fig:" <> title) ) -- override caption with the text that follows read_img_with_caption ( Para (_ : xs) : ys) = read_img_with_caption (Para xs : ys) read_img_with_caption _ = mempty ---------------------- -- Internal links ---------------------- _ANCHOR_PREFIX_ :: T.Text _ANCHOR_PREFIX_ = "anchor" -- readAnchorAttr :: ODTReader a Anchor readAnchorAttr = findAttrText NsText "name" -- | Beware: may fail findAnchorName :: ODTReader AnchorPrefix Anchor findAnchorName = ( keepingTheValue readAnchorAttr >>^ spreadChoice ) >>?! getPrettyAnchor -- maybeAddAnchorFrom :: ODTReader Inlines AnchorPrefix -> ODTReaderSafe Inlines Inlines maybeAddAnchorFrom anchorReader = keepingTheValue (anchorReader >>? findAnchorName >>?^ toAnchorElem) >>> proc (inlines, fAnchorElem) -> do case fAnchorElem of Right anchorElem -> returnA -< anchorElem Left _ -> returnA -< inlines where toAnchorElem :: Anchor -> Inlines toAnchorElem anchorID = spanWith (anchorID, [], []) mempty -- no classes, no key-value pairs -- read_bookmark :: InlineMatcher read_bookmark = matchingElement NsText "bookmark" $ maybeAddAnchorFrom (liftAsSuccess $ returnV _ANCHOR_PREFIX_) -- read_bookmark_start :: InlineMatcher read_bookmark_start = matchingElement NsText "bookmark-start" $ maybeAddAnchorFrom (liftAsSuccess $ returnV _ANCHOR_PREFIX_) -- read_reference_start :: InlineMatcher read_reference_start = matchingElement NsText "reference-mark-start" $ maybeAddAnchorFrom readAnchorAttr -- | Beware: may fail findAnchorRef :: ODTReader a Anchor findAnchorRef = ( findAttrText NsText "ref-name" >>?^ (_ANCHOR_PREFIX_,) ) >>?! getPrettyAnchor -- maybeInAnchorRef :: ODTReaderSafe Inlines Inlines maybeInAnchorRef = proc inlines -> do fRef <- findAnchorRef -< () case fRef of Right anchor -> arr (toAnchorRef anchor) -<< inlines Left _ -> returnA -< inlines where toAnchorRef :: Anchor -> Inlines -> Inlines toAnchorRef anchor = link ("#" <> anchor) "" -- no title -- read_bookmark_ref :: InlineMatcher read_bookmark_ref = matchingElement NsText "bookmark-ref" $ maybeInAnchorRef <<< matchChildContent [] read_plain_text -- read_reference_ref :: InlineMatcher read_reference_ref = matchingElement NsText "reference-ref" $ maybeInAnchorRef <<< matchChildContent [] read_plain_text ---------------------- -- Entry point ---------------------- read_text :: ODTReaderSafe a Pandoc read_text = matchSmushedChildBlocks' [ read_header , read_paragraph , read_list , read_section , read_table ] >>^ doc post_process :: Pandoc -> Pandoc post_process (Pandoc m blocks) = Pandoc m (post_process' blocks) post_process' :: [Block] -> [Block] post_process' (Table attr _ specs th tb tf : Div ("", ["caption"], _) blks : xs) = Table attr (Caption Nothing blks) specs th tb tf : post_process' xs post_process' bs = bs read_body :: ODTReader a (Pandoc, MediaBag) read_body = executeInSub NsOffice "body" $ executeInSub NsOffice "text" $ liftAsSuccess $ proc inlines -> do txt <- read_text -< inlines state <- getExtraState -< () returnA -< (post_process txt, getMediaBag state) ================================================ FILE: src/Text/Pandoc/Readers/ODT/Generic/Fallible.hs ================================================ {- | Module : Text.Pandoc.Readers.ODT.Generic.Fallible Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable Data types and utilities representing failure. Most of it is based on the "Either" type in its usual configuration (left represents failure). In most cases, the failure type is implied or required to be a "Monoid". The choice of "Either" instead of a custom type makes it easier to write compatible instances of "ArrowChoice". -} -- We export everything module Text.Pandoc.Readers.ODT.Generic.Fallible ( Failure , Fallible , maybeToEither , eitherToMaybe , recover , failWith , failEmpty , succeedWith , collapseEither , chooseMax , chooseMaxWith , ChoiceVector(..) , SuccessList(..) ) where -- | Default for now. Will probably become a class at some point. type Failure = () type Fallible a = Either Failure a -- maybeToEither :: Maybe a -> Fallible a maybeToEither (Just a) = Right a maybeToEither Nothing = Left () -- eitherToMaybe :: Either _l a -> Maybe a eitherToMaybe (Left _) = Nothing eitherToMaybe (Right a) = Just a -- | > recover a === either (const a) id recover :: a -> Either _f a -> a recover a (Left _) = a recover _ (Right a) = a -- | I would love to use 'fail'. Alas, 'Monad.fail'... failWith :: failure -> Either failure _x failWith f = Left f -- failEmpty :: (Monoid failure) => Either failure _x failEmpty = failWith mempty -- succeedWith :: a -> Either _x a succeedWith = Right -- collapseEither :: Either failure (Either failure x) -> Either failure x collapseEither (Left f ) = Left f collapseEither (Right (Left f)) = Left f collapseEither (Right (Right x)) = Right x -- | If either of the values represents a non-error, the result is a -- (possibly combined) non-error. If both values represent an error, an error -- is returned. chooseMax :: (Monoid a, Monoid b) => Either a b -> Either a b -> Either a b chooseMax = chooseMaxWith mappend -- | If either of the values represents a non-error, the result is a -- (possibly combined) non-error. If both values represent an error, an error -- is returned. chooseMaxWith :: (Monoid a) => (b -> b -> b) -> Either a b -> Either a b -> Either a b chooseMaxWith (><) (Right a) (Right b) = Right $ a >< b chooseMaxWith _ (Left a) (Left b) = Left $ a `mappend` b chooseMaxWith _ (Right a) _ = Right a chooseMaxWith _ _ (Right b) = Right b -- | Class of containers that can escalate contained 'Either's. -- The word "Vector" is meant in the sense of a disease transmitter. class ChoiceVector v where spreadChoice :: v (Either f a) -> Either f (v a) instance ChoiceVector ((,) a) where spreadChoice (_, Left f) = Left f spreadChoice (x, Right y) = Right (x,y) -- Wasn't there a newtype somewhere with the elements flipped? -- | Wrapper for a list. While the normal list instance of 'ChoiceVector' -- fails whenever it can, this type will never fail. newtype SuccessList a = SuccessList { collectNonFailing :: [a] } deriving ( Eq, Ord, Show ) instance ChoiceVector SuccessList where spreadChoice = Right . SuccessList . foldr unTagRight [] . collectNonFailing where unTagRight (Right x) = (x:) unTagRight _ = id ================================================ FILE: src/Text/Pandoc/Readers/ODT/Generic/Namespaces.hs ================================================ {- | Module : Text.Pandoc.Readers.ODT.Generic.Namespaces Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable A class containing a set of namespace identifiers. Used to convert between typesafe Haskell namespace identifiers and unsafe "real world" namespaces. -} module Text.Pandoc.Readers.ODT.Generic.Namespaces ( NameSpaceIRI , NameSpaceIRIs , NameSpaceID(..) ) where import qualified Data.Map as M import Data.Text (Text) -- type NameSpaceIRI = Text -- type NameSpaceIRIs nsID = M.Map nsID NameSpaceIRI -- class (Eq nsID, Ord nsID) => NameSpaceID nsID where -- | Given a IRI, possibly update the map and return the id of the namespace. -- May fail if the namespace is unknown and the application does not -- allow unknown namespaces. getNamespaceID :: NameSpaceIRI -> NameSpaceIRIs nsID -> Maybe (NameSpaceIRIs nsID, nsID) -- | Given a namespace id, lookup its IRI. May be overridden for performance. getIRI :: nsID -> NameSpaceIRIs nsID -> Maybe NameSpaceIRI -- | The root element of an XML document has a namespace, too, and the -- "XML.Light-parser" is eager to remove the corresponding namespace -- attribute. -- As a result, at least this root namespace must be provided. getInitialIRImap :: NameSpaceIRIs nsID getIRI = M.lookup getInitialIRImap = M.empty ================================================ FILE: src/Text/Pandoc/Readers/ODT/Generic/SetMap.hs ================================================ {- | Module : Text.Pandoc.Readers.ODT.Generic.SetMap Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable A map of values to sets of values. -} module Text.Pandoc.Readers.ODT.Generic.SetMap ( SetMap , empty , fromList , insert , union3 ) where import qualified Data.Map as M import qualified Data.Set as S type SetMap k v = M.Map k (S.Set v) empty :: SetMap k v empty = M.empty fromList :: (Ord k, Ord v) => [(k,v)] -> SetMap k v fromList = foldr (uncurry insert) empty insert :: (Ord k, Ord v) => k -> v -> SetMap k v -> SetMap k v insert key value setMap = M.insertWith S.union key (S.singleton value) setMap union3 :: (Ord k) => SetMap k v -> SetMap k v -> SetMap k v -> SetMap k v union3 sm1 sm2 sm3 = sm1 `M.union` sm2 `M.union` sm3 ================================================ FILE: src/Text/Pandoc/Readers/ODT/Generic/Utils.hs ================================================ {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Reader.ODT.Generic.Utils Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable General utility functions for the odt reader. -} module Text.Pandoc.Readers.ODT.Generic.Utils ( uncurry3 , uncurry4 , uncurry5 , uncurry6 , swap , reverseComposition , tryToRead , Lookupable(..) , readLookupable , readPercent , findBy , swing , composition ) where import Control.Category (Category, (<<<), (>>>)) import qualified Control.Category as Cat (id) import Data.Char (isSpace) import qualified Data.Foldable as F (Foldable, foldr) import Data.Maybe import Data.Text (Text) import qualified Data.Text as T -- | Equivalent to -- > foldr (.) id -- where '(.)' are 'id' are the ones from "Control.Category" -- and 'foldr' is the one from "Data.Foldable". -- The noun-form was chosen to be consistent with 'sum', 'product' etc -- based on the discussion at -- -- (that I was not part of) composition :: (Category cat, F.Foldable f) => f (cat a a) -> cat a a composition = F.foldr (<<<) Cat.id -- | Equivalent to -- > foldr (flip (.)) id -- where '(.)' are 'id' are the ones from "Control.Category" -- and 'foldr' is the one from "Data.Foldable". -- A reversed version of 'composition'. reverseComposition :: (Category cat, F.Foldable f) => f (cat a a) -> cat a a reverseComposition = F.foldr (>>>) Cat.id -- | This function often makes it possible to switch values with the functions -- that are applied to them. -- -- Examples: -- > swing map :: [a -> b] -> a -> [b] -- > swing any :: [a -> Bool] -> a -> Bool -- > swing foldr :: b -> a -> [a -> b -> b] -> b -- > swing scanr :: c -> a -> [a -> c -> c] -> c -- > swing zipWith :: [a -> b -> c] -> a -> [b] -> [c] -- > swing find :: [a -> Bool] -> a -> Maybe (a -> Bool) -- -- Stolen from swing :: (((a -> b) -> b) -> c -> d) -> c -> a -> d swing = flip.(.flip id) -- swing f c a = f ($ a) c -- | Alternative to 'read'/'reads'. The former of these throws errors -- (nobody wants that) while the latter returns "to much" for simple purposes. -- This function instead applies 'reads' and returns the first match (if any) -- in a 'Maybe'. tryToRead :: (Read r) => Text -> Maybe r tryToRead = (reads . T.unpack) >>> listToMaybe >>> fmap fst -- | A version of 'reads' that requires a '%' sign after the number readPercent :: ReadS Int readPercent s = [ (i,s') | (i , r ) <- reads s , ("%" , s') <- lex r ] -- | Data that can be looked up. -- This is mostly a utility to read data with kind *. class Lookupable a where lookupTable :: [(Text, a)] -- | Very similar to a simple 'lookup' in the 'lookupTable', but with a lexer. readLookupable :: (Lookupable a) => Text -> Maybe a readLookupable s = lookup (T.takeWhile (not . isSpace) $ T.dropWhile isSpace s) lookupTable uncurry3 :: (a->b->c -> z) -> (a,b,c ) -> z uncurry4 :: (a->b->c->d -> z) -> (a,b,c,d ) -> z uncurry5 :: (a->b->c->d->e -> z) -> (a,b,c,d,e ) -> z uncurry6 :: (a->b->c->d->e->f -> z) -> (a,b,c,d,e,f ) -> z uncurry3 fun (a,b,c ) = fun a b c uncurry4 fun (a,b,c,d ) = fun a b c d uncurry5 fun (a,b,c,d,e ) = fun a b c d e uncurry6 fun (a,b,c,d,e,f ) = fun a b c d e f swap :: (a,b) -> (b,a) swap (a,b) = (b,a) -- | A version of "Data.List.find" that uses a converter to a Maybe instance. -- The returned value is the first which the converter returns in a 'Just' -- wrapper. findBy :: (a -> Maybe b) -> [a] -> Maybe b findBy _ [] = Nothing findBy f ((f -> Just x):_ ) = Just x findBy f ( _:xs) = findBy f xs ================================================ FILE: src/Text/Pandoc/Readers/ODT/Generic/XMLConverter.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE PatternGuards #-} {- | Module : Text.Pandoc.Readers.ODT.Generic.XMLConverter Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable A generalized XML parser based on stateful arrows. It might be sufficient to define this reader as a comonad, but there is not a lot of use in trying. -} module Text.Pandoc.Readers.ODT.Generic.XMLConverter ( ElementName , XMLConverterState , XMLConverter , FallibleXMLConverter , runConverter' , getExtraState , setExtraState , modifyExtraState , producingExtraState , findChild' , filterChildrenName' , isSet' , isSetWithDefault , elName , searchAttr , lookupAttr , lookupAttr' , lookupDefaultingAttr , findAttr' , findAttrText' , findAttr , findAttrText , findAttrTextWithDefault , readAttr , readAttr' , readAttrWithDefault , getAttr , executeIn , executeInSub , withEveryL , tryAll , matchContent' , matchContent ) where import Prelude hiding (Applicative(..)) import Control.Applicative hiding ( liftA, liftA2 ) import Control.Monad ( MonadPlus ) import Control.Arrow import Data.Bool ( bool ) import Data.Either ( rights ) import qualified Data.Map as M import Data.Text (Text) import Data.Default import Data.Maybe import qualified Data.List as L import qualified Data.List.NonEmpty as NonEmpty import Data.List.NonEmpty (NonEmpty(..)) import qualified Text.Pandoc.XML.Light as XML import Text.Pandoc.Readers.ODT.Arrows.State import Text.Pandoc.Readers.ODT.Arrows.Utils import Text.Pandoc.Readers.ODT.Generic.Namespaces import Text.Pandoc.Readers.ODT.Generic.Utils import Text.Pandoc.Readers.ODT.Generic.Fallible -------------------------------------------------------------------------------- -- Basis types for readability -------------------------------------------------------------------------------- -- type ElementName = Text type AttributeName = Text type AttributeValue = Text type TextAttributeValue = Text -- type NameSpacePrefix = Text -- type NameSpacePrefixes nsID = M.Map nsID NameSpacePrefix -------------------------------------------------------------------------------- -- Main converter state -------------------------------------------------------------------------------- -- GADT so some of the NameSpaceID restrictions can be deduced data XMLConverterState nsID extraState where XMLConverterState :: NameSpaceID nsID => { -- | A stack of parent elements. The top element is the current one. -- Arguably, a real Zipper would be better. But that is an -- optimization that can be made at a later time, e.g. when -- replacing Text.XML.Light. parentElements :: NonEmpty XML.Element -- | A map from internal namespace IDs to the namespace prefixes -- used in XML elements , namespacePrefixes :: NameSpacePrefixes nsID -- | A map from internal namespace IDs to namespace IRIs -- (Only necessary for matching namespace IDs and prefixes) , namespaceIRIs :: NameSpaceIRIs nsID -- | A place to put "something else". This feature is used heavily -- to keep the main code cleaner. More specifically, the main reader -- is divided into different stages. Each stage lifts something up -- here, which the next stage can then use. This could of course be -- generalized to a state-tree or used for the namespace IRIs. The -- border between states and values is an imaginary one, after all. -- But the separation as it is seems to be enough for now. , moreState :: extraState } -> XMLConverterState nsID extraState -- createStartState :: (NameSpaceID nsID) => XML.Element -> extraState -> XMLConverterState nsID extraState createStartState element extraState = XMLConverterState { parentElements = element :| [] , namespacePrefixes = M.empty , namespaceIRIs = getInitialIRImap , moreState = extraState } -- | Functor over extra state instance Functor (XMLConverterState nsID) where fmap f ( XMLConverterState parents prefixes iRIs extraState ) = XMLConverterState parents prefixes iRIs (f extraState) -- replaceExtraState :: extraState -> XMLConverterState nsID _x -> XMLConverterState nsID extraState replaceExtraState x s = fmap (const x) s -- currentElement :: XMLConverterState nsID extraState -> XML.Element currentElement state = NonEmpty.head (parentElements state) -- | Replace the current position by another, modifying the extra state -- in the process swapStack' :: XMLConverterState nsID extraState -> NonEmpty XML.Element -> ( XMLConverterState nsID extraState , NonEmpty XML.Element ) swapStack' state stack = ( state { parentElements = stack } , parentElements state ) -- pushElement :: XML.Element -> XMLConverterState nsID extraState -> XMLConverterState nsID extraState pushElement e state = state { parentElements = NonEmpty.cons e (parentElements state) } -- | Pop the top element from the call stack, unless it is the last one. popElement :: XMLConverterState nsID extraState -> Maybe (XMLConverterState nsID extraState) popElement state | _ :| (e:es) <- parentElements state = Just $ state { parentElements = e :| es } | otherwise = Nothing -------------------------------------------------------------------------------- -- Main type -------------------------------------------------------------------------------- -- It might be a good idea to pack the converters in a GADT -- Downside: data instead of type -- Upside: 'Failure' could be made a parameter as well. -- type XMLConverter nsID extraState input output = ArrowState (XMLConverterState nsID extraState ) input output type FallibleXMLConverter nsID extraState input output = XMLConverter nsID extraState input (Fallible output) -- runConverter :: XMLConverter nsID extraState input output -> XMLConverterState nsID extraState -> input -> output runConverter converter state input = snd $ runArrowState converter (state,input) runConverter' :: (NameSpaceID nsID) => FallibleXMLConverter nsID extraState () success -> extraState -> XML.Element -> Fallible success runConverter' converter extraState element = runConverter (readNSattributes >>? converter) (createStartState element extraState) () -- getCurrentElement :: XMLConverter nsID extraState x XML.Element getCurrentElement = extractFromState currentElement -- getExtraState :: XMLConverter nsID extraState x extraState getExtraState = extractFromState moreState -- setExtraState :: XMLConverter nsID extraState extraState extraState setExtraState = withState $ \state extra -> (replaceExtraState extra state , extra) -- | Lifts a function to the extra state. modifyExtraState :: (extraState -> extraState) -> XMLConverter nsID extraState x x modifyExtraState = modifyState.fmap -- | First sets the extra state to the new value. Then modifies the original -- extra state with a converter that uses the new state. Finally, the -- intermediate state is dropped and the extra state is lifted into the -- state as it was at the beginning of the function. -- As a result, exactly the extra state and nothing else is changed. -- The resulting converter even behaves like an identity converter on the -- value level. -- -- (The -ing form is meant to be mnemonic in a sequence of arrows as in -- convertingExtraState () converter >>> doOtherStuff) -- convertingExtraState :: extraState' -> FallibleXMLConverter nsID extraState' extraState extraState -> FallibleXMLConverter nsID extraState x x convertingExtraState v a = withSubStateF setVAsExtraState modifyWithA where setVAsExtraState = liftAsSuccess $ extractFromState id >>^ replaceExtraState v modifyWithA = keepingTheValue (moreState ^>> a) >>^ spreadChoice >>?% flip replaceExtraState -- | First sets the extra state to the new value. Then produces a new -- extra state with a converter that uses the new state. Finally, the -- intermediate state is dropped and the extra state is lifted into the -- state as it was at the beginning of the function. -- As a result, exactly the extra state and nothing else is changed. -- The resulting converter even behaves like an identity converter on the -- value level. -- -- Equivalent to -- -- > \v x a -> convertingExtraState v (returnV x >>> a) -- -- (The -ing form is meant to be mnemonic in a sequence of arrows as in -- producingExtraState () () producer >>> doOtherStuff) -- producingExtraState :: extraState' -> a -> FallibleXMLConverter nsID extraState' a extraState -> FallibleXMLConverter nsID extraState x x producingExtraState v x a = convertingExtraState v (returnV x >>> a) -------------------------------------------------------------------------------- -- Work in namespaces -------------------------------------------------------------------------------- -- | Arrow version of 'getIRI' lookupNSiri :: (NameSpaceID nsID) => nsID -> XMLConverter nsID extraState x (Maybe NameSpaceIRI) lookupNSiri nsID = extractFromState $ \state -> getIRI nsID $ namespaceIRIs state -- lookupNSprefix :: (NameSpaceID nsID) => nsID -> XMLConverter nsID extraState x (Maybe NameSpacePrefix) lookupNSprefix nsID = extractFromState $ \state -> M.lookup nsID $ namespacePrefixes state -- | Extracts namespace attributes from the current element and tries to -- update the current mapping accordingly readNSattributes :: (NameSpaceID nsID) => FallibleXMLConverter nsID extraState x () readNSattributes = fromState $ \state -> maybe (state, failEmpty ) ( , succeedWith ()) (extractNSAttrs state ) where extractNSAttrs :: (NameSpaceID nsID) => XMLConverterState nsID extraState -> Maybe (XMLConverterState nsID extraState) extractNSAttrs startState = L.foldl' (\state d -> state >>= addNS d) (Just startState) nsAttribs where nsAttribs = mapMaybe readNSattr (XML.elAttribs element) element = currentElement startState readNSattr (XML.Attr (XML.QName name _ (Just "xmlns")) iri) = Just (name, iri) readNSattr _ = Nothing addNS (prefix, iri) state = fmap updateState $ getNamespaceID iri $ namespaceIRIs state where updateState (iris,nsID) = state { namespaceIRIs = iris , namespacePrefixes = M.insert nsID prefix $ namespacePrefixes state } -------------------------------------------------------------------------------- -- Common namespace accessors -------------------------------------------------------------------------------- -- | Given a namespace id and an element name, creates a 'XML.QName' for -- internal use qualifyName :: (NameSpaceID nsID) => nsID -> ElementName -> XMLConverter nsID extraState x XML.QName qualifyName nsID name = lookupNSiri nsID &&& lookupNSprefix nsID >>% XML.QName name -- | Checks if a given element matches both a specified namespace id -- and a predicate elemNameMatches :: (NameSpaceID nsID) => nsID -> (ElementName -> Bool) -> XMLConverter nsID extraState XML.Element Bool elemNameMatches nsID f = keepingTheValue (lookupNSiri nsID) >>% hasMatchingName where hasMatchingName e iri = let name = XML.elName e in f (XML.qName name) && XML.qURI name == iri -- | Checks if a given element matches both a specified namespace id -- and a specified element name elemNameIs :: (NameSpaceID nsID) => nsID -> ElementName -> XMLConverter nsID extraState XML.Element Bool elemNameIs nsID name = elemNameMatches nsID (== name) -------------------------------------------------------------------------------- -- General content -------------------------------------------------------------------------------- elName :: XML.Element -> ElementName elName = XML.qName . XML.elName -- elContent :: XMLConverter nsID extraState x [XML.Content] elContent = getCurrentElement >>^ XML.elContent -------------------------------------------------------------------------------- -- Children -------------------------------------------------------------------------------- -- -- findChildren :: (NameSpaceID nsID) => nsID -> ElementName -> XMLConverter nsID extraState x [XML.Element] findChildren nsID name = qualifyName nsID name &&& getCurrentElement >>% XML.findChildren -- findChild' :: (NameSpaceID nsID) => nsID -> ElementName -> XMLConverter nsID extraState x (Maybe XML.Element) findChild' nsID name = qualifyName nsID name &&& getCurrentElement >>% XML.findChild -- findChild :: (NameSpaceID nsID) => nsID -> ElementName -> FallibleXMLConverter nsID extraState x XML.Element findChild nsID name = findChild' nsID name >>> maybeToChoice filterChildrenName' :: (NameSpaceID nsID) => nsID -> (ElementName -> Bool) -> XMLConverter nsID extraState x [XML.Element] filterChildrenName' nsID f = getCurrentElement >>> arr XML.elChildren >>> iterateS (keepingTheValue (elemNameMatches nsID f)) >>> arr (map fst . filter snd) -------------------------------------------------------------------------------- -- Attributes -------------------------------------------------------------------------------- -- isSet' :: (NameSpaceID nsID) => nsID -> AttributeName -> XMLConverter nsID extraState x (Maybe Bool) isSet' nsID attrName = findAttr' nsID attrName >>^ (>>= stringToBool') isSetWithDefault :: (NameSpaceID nsID) => nsID -> AttributeName -> Bool -> XMLConverter nsID extraState x Bool isSetWithDefault nsID attrName def' = isSet' nsID attrName >>^ fromMaybe def' -- | Lookup value in a dictionary, fail if no attribute found or value -- not in dictionary searchAttrIn :: (NameSpaceID nsID) => nsID -> AttributeName -> [(AttributeValue,a)] -> FallibleXMLConverter nsID extraState x a searchAttrIn nsID attrName dict = findAttr nsID attrName >>?^? maybeToChoice.(`lookup` dict ) -- | Lookup value in a dictionary. If attribute or value not found, -- return default value searchAttr :: (NameSpaceID nsID) => nsID -> AttributeName -> a -> [(AttributeValue,a)] -> XMLConverter nsID extraState x a searchAttr nsID attrName defV dict = searchAttrIn nsID attrName dict >>> const defV ^|||^ id -- | Read a 'Lookupable' attribute. Fail if no match. lookupAttr :: (NameSpaceID nsID, Lookupable a) => nsID -> AttributeName -> FallibleXMLConverter nsID extraState x a lookupAttr nsID attrName = lookupAttr' nsID attrName >>^ maybeToChoice -- | Read a 'Lookupable' attribute. Return the result as a 'Maybe'. lookupAttr' :: (NameSpaceID nsID, Lookupable a) => nsID -> AttributeName -> XMLConverter nsID extraState x (Maybe a) lookupAttr' nsID attrName = findAttr' nsID attrName >>^ (>>= readLookupable) -- | Read a 'Lookupable' attribute with explicit default lookupAttrWithDefault :: (NameSpaceID nsID, Lookupable a) => nsID -> AttributeName -> a -> XMLConverter nsID extraState x a lookupAttrWithDefault nsID attrName deflt = lookupAttr' nsID attrName >>^ fromMaybe deflt -- | Read a 'Lookupable' attribute with implicit default lookupDefaultingAttr :: (NameSpaceID nsID, Lookupable a, Default a) => nsID -> AttributeName -> XMLConverter nsID extraState x a lookupDefaultingAttr nsID attrName = lookupAttrWithDefault nsID attrName def -- | Return value as a (Maybe Text) findAttr' :: (NameSpaceID nsID) => nsID -> AttributeName -> XMLConverter nsID extraState x (Maybe AttributeValue) findAttr' nsID attrName = qualifyName nsID attrName &&& getCurrentElement >>% XML.findAttr -- | Return value as a (Maybe Text) findAttrText' :: (NameSpaceID nsID) => nsID -> AttributeName -> XMLConverter nsID extraState x (Maybe TextAttributeValue) findAttrText' nsID attrName = qualifyName nsID attrName &&& getCurrentElement >>% XML.findAttr -- | Return value as string or fail findAttr :: (NameSpaceID nsID) => nsID -> AttributeName -> FallibleXMLConverter nsID extraState x AttributeValue findAttr nsID attrName = findAttr' nsID attrName >>> maybeToChoice -- | Return value as text or fail findAttrText :: (NameSpaceID nsID) => nsID -> AttributeName -> FallibleXMLConverter nsID extraState x TextAttributeValue findAttrText nsID attrName = findAttr' nsID attrName >>> maybeToChoice -- | Return value as string or return provided default value findAttrTextWithDefault :: (NameSpaceID nsID) => nsID -> AttributeName -> TextAttributeValue -> XMLConverter nsID extraState x TextAttributeValue findAttrTextWithDefault nsID attrName deflt = findAttr' nsID attrName >>^ fromMaybe deflt -- | Read and return value or fail readAttr :: (NameSpaceID nsID, Read attrValue) => nsID -> AttributeName -> FallibleXMLConverter nsID extraState x attrValue readAttr nsID attrName = readAttr' nsID attrName >>> maybeToChoice -- | Read and return value or return Nothing readAttr' :: (NameSpaceID nsID, Read attrValue) => nsID -> AttributeName -> XMLConverter nsID extraState x (Maybe attrValue) readAttr' nsID attrName = findAttr' nsID attrName >>^ (>>= tryToRead) -- | Read and return value or return provided default value readAttrWithDefault :: (NameSpaceID nsID, Read attrValue) => nsID -> AttributeName -> attrValue -> XMLConverter nsID extraState x attrValue readAttrWithDefault nsID attrName deflt = findAttr' nsID attrName >>^ (>>= tryToRead) >>^ fromMaybe deflt -- | Read and return value or return default value from 'Default' instance getAttr :: (NameSpaceID nsID, Read attrValue, Default attrValue) => nsID -> AttributeName -> XMLConverter nsID extraState x attrValue getAttr nsID attrName = readAttrWithDefault nsID attrName def -------------------------------------------------------------------------------- -- Movements -------------------------------------------------------------------------------- -- jumpThere :: XMLConverter nsID extraState XML.Element XML.Element jumpThere = withState (\state element -> ( pushElement element state , element ) ) -- swapStack :: XMLConverter nsID extraState (NonEmpty XML.Element) (NonEmpty XML.Element) swapStack = withState swapStack' -- jumpBack :: FallibleXMLConverter nsID extraState _x _x jumpBack = tryModifyState (popElement >>> maybeToChoice) -- | Support function for "procedural" converters: jump to an element, execute -- a converter, jump back. -- This version is safer than 'executeThere', because it does not rely on the -- internal stack. As a result, the converter can not move around in arbitrary -- ways. The downside is of course that some of the environment is not -- accessible to the converter. switchingTheStack :: XMLConverter nsID moreState a b -> XMLConverter nsID moreState (a, XML.Element) b switchingTheStack a = second ( (:| []) ^>> swapStack ) >>> first a >>> second swapStack >>^ fst -- | Support function for "procedural" converters: jumps to an element, executes -- a converter, jumps back. -- Make sure that the converter is well-behaved; that is it should -- return to the exact position it started from in /every possible path/ of -- execution, even if it "fails". If it does not, you may encounter -- strange bugs. If you are not sure about the behaviour or want to use -- shortcuts, you can often use 'switchingTheStack' instead. executeThere :: FallibleXMLConverter nsID moreState a b -> FallibleXMLConverter nsID moreState (a, XML.Element) b executeThere a = second jumpThere >>> fst ^>> a >>> jumpBack -- >>? jumpBack would not ensure the jump. >>^ collapseEither -- | Do something in a specific element, then come back executeIn :: XMLConverter nsID extraState XML.Element s -> XMLConverter nsID extraState XML.Element s executeIn a = duplicate >>> switchingTheStack a -- | Do something in a sub-element, then come back executeInSub :: (NameSpaceID nsID) => nsID -> ElementName -> FallibleXMLConverter nsID extraState f s -> FallibleXMLConverter nsID extraState f s executeInSub nsID name a = keepingTheValue (findChild nsID name) >>> ignoringState liftFailure >>? switchingTheStack a where liftFailure (_, Left f) = Left f liftFailure (x, Right e) = Right (x, e) -------------------------------------------------------------------------------- -- Iterating over children -------------------------------------------------------------------------------- -- Helper converter to prepare different types of iterations. -- It lifts the children (of a certain type) of the current element -- into the value level and pairs each one with the current input value. prepareIteration :: (NameSpaceID nsID) => nsID -> ElementName -> XMLConverter nsID extraState b [(b, XML.Element)] prepareIteration nsID name = keepingTheValue (findChildren nsID name) >>% distributeValue -- withEveryL :: (NameSpaceID nsID) => nsID -> ElementName -> FallibleXMLConverter nsID extraState a b -> FallibleXMLConverter nsID extraState a [b] withEveryL = withEvery -- | Applies a converter to every child element of a specific type. -- Collects results in a 'MonadPlus'. -- Fails completely if any conversion fails. withEvery :: (NameSpaceID nsID, MonadPlus m) => nsID -> ElementName -> FallibleXMLConverter nsID extraState a b -> FallibleXMLConverter nsID extraState a (m b) withEvery nsID name a = prepareIteration nsID name >>> iterateS' (switchingTheStack a) -- | Applies a converter to every child element of a specific type. -- Collects all successful results in a list. tryAll :: (NameSpaceID nsID) => nsID -> ElementName -> FallibleXMLConverter nsID extraState b a -> XMLConverter nsID extraState b [a] tryAll nsID name a = prepareIteration nsID name >>> iterateS (switchingTheStack a) >>^ rights -------------------------------------------------------------------------------- -- Matching children -------------------------------------------------------------------------------- type IdXMLConverter nsID moreState x = XMLConverter nsID moreState x x type MaybeCConverter nsID moreState x = Maybe (IdXMLConverter nsID moreState (x, XML.Content)) -- Chainable converter that helps deciding which converter to actually use. type ContentMatchConverter nsID extraState x = IdXMLConverter nsID extraState (MaybeCConverter nsID extraState x, XML.Content) -- Helper function: The @c@ is actually a converter that is to be selected by -- matching XML content to the first two parameters. -- The fold used to match elements however is very simple, so to use it, -- this function wraps the converter in another converter that unifies -- the accumulator. Think of a lot of converters with the resulting type -- chained together. The accumulator not only transports the element -- unchanged to the next matcher, it also does the actual selecting by -- combining the intermediate results with '(<|>)'. makeMatcherC :: (NameSpaceID nsID) => nsID -> ElementName -> FallibleXMLConverter nsID extraState a a -> ContentMatchConverter nsID extraState a makeMatcherC nsID name c = ( second ( contentToElem >>> returnV Nothing ||| ( elemNameIs nsID name >>^ bool Nothing (Just cWithJump) ) ) >>% (<|>) ) &&&^ snd where cWithJump = ( fst ^&&& ( second contentToElem >>> spreadChoice ^>>? executeThere c ) >>% recover) &&&^ snd contentToElem :: FallibleXMLConverter nsID extraState XML.Content XML.Element contentToElem = arr $ \case XML.Elem e' -> succeedWith e' _ -> failEmpty -- Creates and chains a bunch of matchers prepareMatchersC :: (NameSpaceID nsID) => [(nsID, ElementName, FallibleXMLConverter nsID extraState x x)] -> ContentMatchConverter nsID extraState x --prepareMatchersC = foldSs . (map $ uncurry3 makeMatcherC) prepareMatchersC = reverseComposition . map (uncurry3 makeMatcherC) -- | Takes a list of element-data - converter groups and -- * Finds all content of the current element -- * Matches each group to each piece of content in order -- (at most one group per piece of content) -- * Filters non-matched content -- * Chains all found converters in content-order -- * Applies the chain to the input element matchContent' :: (NameSpaceID nsID) => [(nsID, ElementName, FallibleXMLConverter nsID extraState a a)] -> XMLConverter nsID extraState a a matchContent' lookups = matchContent lookups (arr fst) -- | Takes a list of element-data - converter groups and -- * Finds all content of the current element -- * Matches each group to each piece of content in order -- (at most one group per piece of content) -- * Adds a default converter for all non-matched content -- * Chains all found converters in content-order -- * Applies the chain to the input element matchContent :: (NameSpaceID nsID) => [(nsID, ElementName, FallibleXMLConverter nsID extraState a a)] -> XMLConverter nsID extraState (a,XML.Content) a -> XMLConverter nsID extraState a a matchContent lookups fallback = let matcher = prepareMatchersC lookups in keepingTheValue ( elContent >>> map (Nothing,) ^>> iterateSL matcher >>^ map swallowOrFallback -- >>> foldSs >>> reverseComposition ) >>> swap ^>> app where -- let the converter swallow the content and drop the content -- in the return value swallowOrFallback (Just converter,content) = (,content) ^>> converter >>^ fst swallowOrFallback (Nothing ,content) = (,content) ^>> fallback -------------------------------------------------------------------------------- -- Internals -------------------------------------------------------------------------------- stringToBool' :: Text -> Maybe Bool stringToBool' val | val `elem` trueValues = Just True | val `elem` falseValues = Just False | otherwise = Nothing where trueValues = ["true" ,"on" ,"1"] falseValues = ["false","off","0"] distributeValue :: a -> [b] -> [(a,b)] distributeValue = map.(,) -------------------------------------------------------------------------------- {- NOTES It might be a good idea to refactor the namespace stuff. E.g.: if a namespace constructor took a string as a parameter, things like > a ?>/< (NsText,"body") would be nicer. Together with a rename and some trickery, something like > |< NsText "body" >< NsText "p" ?> a | might even be possible. Some day, XML.Light should be replaced by something better. While doing that, it might be useful to replace String as the type of element names with something else, too. (Of course with OverloadedStrings). While doing that, maybe the types can be created in a way that something like > NsText:"body" could be used. Overloading (:) does not sounds like the best idea, but if the element name type was a list, this might be possible. Of course that would be a bit hackish, so the "right" way would probably be something like > InNS NsText "body" but isn't that a bit boring? ;) -} ================================================ FILE: src/Text/Pandoc/Readers/ODT/Namespaces.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Reader.ODT.Namespaces Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable Namespaces used in odt files. -} module Text.Pandoc.Readers.ODT.Namespaces ( Namespace (..) ) where import qualified Data.Map as M (empty, insert) import Data.Maybe (fromMaybe, listToMaybe) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Readers.ODT.Generic.Namespaces instance NameSpaceID Namespace where getInitialIRImap = nsIDmap getNamespaceID "" m = Just(m, NsXML) getNamespaceID iri m = asPair $ fromMaybe (NsOther iri) (findID iri) where asPair nsID = Just (M.insert nsID iri m, nsID) findID :: NameSpaceIRI -> Maybe Namespace findID iri = listToMaybe [nsID | (iri',nsID) <- nsIDs, iri' `T.isPrefixOf` iri] nsIDmap :: NameSpaceIRIs Namespace nsIDmap = foldr (uncurry $ flip M.insert) M.empty nsIDs data Namespace = -- Open Document core NsOffice | NsStyle | NsText | NsTable | NsForm | NsDraw | Ns3D | NsAnim | NsChart | NsConfig | NsDB | NsMeta | NsNumber | NsScript | NsManifest | NsPresentation -- Metadata | NsODF -- Compatible elements | NsXSL_FO | NsSVG | NsSmil -- External standards | NsMathML | NsXForms | NsXLink | NsXHtml | NsGRDDL | NsDublinCore -- Metadata manifest | NsPKG -- Others | NsOpenFormula -- Core XML (basically only for the 'id'-attribute) | NsXML -- Fallback | NsOther Text deriving ( Eq, Ord, Show ) -- | Not the actual iri's, but large prefixes of them - this way there are -- less versioning problems and the like. nsIDs :: [(Text, Namespace)] nsIDs = [ ("urn:oasis:names:tc:opendocument:xmlns:animation" , NsAnim ), ("urn:oasis:names:tc:opendocument:xmlns:chart" , NsChart ), ("urn:oasis:names:tc:opendocument:xmlns:config" , NsConfig ), ("urn:oasis:names:tc:opendocument:xmlns:database" , NsDB ), ("urn:oasis:names:tc:opendocument:xmlns:dr3d" , Ns3D ), ("urn:oasis:names:tc:opendocument:xmlns:drawing" , NsDraw ), ("urn:oasis:names:tc:opendocument:xmlns:form" , NsForm ), ("urn:oasis:names:tc:opendocument:xmlns:manifest" , NsManifest ), ("urn:oasis:names:tc:opendocument:xmlns:meta" , NsMeta ), ("urn:oasis:names:tc:opendocument:xmlns:datastyle" , NsNumber ), ("urn:oasis:names:tc:opendocument:xmlns:of" , NsOpenFormula ), ("urn:oasis:names:tc:opendocument:xmlns:office:1.0" , NsOffice ), ("urn:oasis:names:tc:opendocument:xmlns:presentation" , NsPresentation ), ("urn:oasis:names:tc:opendocument:xmlns:script" , NsScript ), ("urn:oasis:names:tc:opendocument:xmlns:style" , NsStyle ), ("urn:oasis:names:tc:opendocument:xmlns:table" , NsTable ), ("urn:oasis:names:tc:opendocument:xmlns:text" , NsText ), ("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible", NsXSL_FO ), ("urn:oasis:names:tc:opendocument:xmlns:smil-compatible" , NsSmil ), ("urn:oasis:names:tc:opendocument:xmlns:svg-compatible" , NsSVG ), ("http://docs.oasis-open.org/ns/office/1.2/meta/odf" , NsODF ), ("http://docs.oasis-open.org/ns/office/1.2/meta/pkg" , NsPKG ), ("http://purl.org/dc/elements" , NsDublinCore ), ("http://www.w3.org/2003/g/data-view" , NsGRDDL ), ("http://www.w3.org/1998/Math/MathML" , NsMathML ), ("http://www.w3.org/1999/xhtml" , NsXHtml ), ("http://www.w3.org/2002/xforms" , NsXForms ), ("http://www.w3.org/1999/xlink" , NsXLink ) ] ================================================ FILE: src/Text/Pandoc/Readers/ODT/StyleReader.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE Arrows #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.ODT.StyleReader Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable Reader for the style information in an odt document. -} module Text.Pandoc.Readers.ODT.StyleReader ( Style (..) , StyleName , StyleFamily (..) , Styles (..) , StyleProperties (..) , TextProperties (..) , ParaProperties (..) , VerticalTextPosition (..) , ListItemNumberFormat (..) , ListLevel , ListStyle (..) , ListLevelStyle (..) , ListLevelType (..) , LengthOrPercent (..) , lookupStyle , getListLevelStyle , getStyleFamily , lookupDefaultStyle' , lookupListStyleByName , extendedStylePropertyChain , readStylesAt ) where import Prelude hiding (Applicative(..)) import Control.Applicative hiding (liftA, liftA2, liftA3) import Control.Arrow import Data.Default import qualified Data.Foldable as F import qualified Data.List as L import qualified Data.Map as M import Data.Maybe import Data.Text (Text) import qualified Data.Text as T import qualified Data.Set as S import qualified Text.Pandoc.XML.Light as XML import Text.Pandoc.Shared (safeRead, tshow) import Text.Pandoc.Readers.ODT.Arrows.Utils import Text.Pandoc.Readers.ODT.Generic.Fallible import qualified Text.Pandoc.Readers.ODT.Generic.SetMap as SM import Text.Pandoc.Readers.ODT.Generic.Utils import Text.Pandoc.Readers.ODT.Generic.XMLConverter import Text.Pandoc.Readers.ODT.Base import Text.Pandoc.Readers.ODT.Namespaces readStylesAt :: XML.Element -> Fallible Styles readStylesAt e = runConverter' readAllStyles mempty e -------------------------------------------------------------------------------- -- Reader for font declarations and font pitches -------------------------------------------------------------------------------- -- Pandoc has no support for different font pitches. Yet knowing them can be -- very helpful in cases where Pandoc has more semantics than OpenDocument. -- In these cases, the pitch can help deciding as what to define a block of -- text. So let's start with a type for font pitches: data FontPitch = PitchVariable | PitchFixed deriving ( Eq, Show ) instance Lookupable FontPitch where lookupTable = [ ("variable" , PitchVariable) , ("fixed" , PitchFixed ) ] instance Default FontPitch where def = PitchVariable -- The font pitch can be specified in a style directly. Normally, however, -- it is defined in the font. That is also the specs' recommendation. -- -- Thus, we want type FontFaceName = Text type FontPitches = M.Map FontFaceName FontPitch -- To get there, the fonts have to be read and the pitches extracted. -- But the resulting map are only needed at one later place, so it should not be -- transported on the value level, especially as we already use a state arrow. -- So instead, the resulting map is lifted into the state of the reader. -- (An alternative might be ImplicitParams, but again, we already have a state.) -- -- So the main style readers will have the types type StyleReader a b = XMLReader FontPitches a b -- and type StyleReaderSafe a b = XMLReaderSafe FontPitches a b -- respectively. -- -- But before we can work with these, we need to define the reader that reads -- the fonts: -- | A reader for font pitches fontPitchReader :: XMLReader s a FontPitches fontPitchReader = executeInSub NsOffice "font-face-decls" ( withEveryL NsStyle "font-face" (liftAsSuccess ( findAttr' NsStyle "name" &&& lookupDefaultingAttr NsStyle "font-pitch" )) >>?^ ( M.fromList . L.foldl' accumLegalPitches [] ) ) `ifFailedDo` returnV (Right M.empty) where accumLegalPitches ls (Nothing,_) = ls accumLegalPitches ls (Just n,p) = (n,p):ls -- | A wrapper around the font pitch reader that lifts the result into the -- state. readFontPitches :: StyleReader a a readFontPitches = producingExtraState () () fontPitchReader -- | Looking up a pitch in the state of the arrow. -- -- The function does the following: -- * Look for the font pitch in an attribute. -- * If that fails, look for the font name, look up the font in the state -- and use the pitch from there. -- * Return the result in a Maybe -- findPitch :: XMLReaderSafe FontPitches a (Maybe FontPitch) findPitch = ( lookupAttr NsStyle "font-pitch" `ifFailedDo` findAttr NsStyle "font-name" >>? ( keepingTheValue getExtraState >>% M.lookup >>^ maybeToChoice ) ) >>> choiceToMaybe -------------------------------------------------------------------------------- -- Definitions of main data -------------------------------------------------------------------------------- type StyleName = Text -- | There are two types of styles: named styles with a style family and an -- optional style parent, and default styles for each style family, -- defining default style properties data Styles = Styles { stylesByName :: M.Map StyleName Style , listStylesByName :: M.Map StyleName ListStyle , defaultStyleMap :: M.Map StyleFamily StyleProperties } deriving ( Show ) -- Styles from a monoid under union instance Semigroup Styles where (Styles sBn1 dSm1 lsBn1) <> (Styles sBn2 dSm2 lsBn2) = Styles (M.union sBn1 sBn2) (M.union dSm1 dSm2) (M.union lsBn1 lsBn2) instance Monoid Styles where mempty = Styles M.empty M.empty M.empty mappend = (<>) -- Not all families from the specifications are implemented, only those we need. -- But there are none that are not mentioned here. data StyleFamily = FaText | FaParagraph -- | FaTable | FaTableCell | FaTableColumn | FaTableRow -- | FaGraphic | FaDrawing | FaChart -- | FaPresentation -- | FaRuby deriving ( Eq, Ord, Show ) instance Lookupable StyleFamily where lookupTable = [ ( "text" , FaText ) , ( "paragraph" , FaParagraph ) -- , ( "table" , FaTable ) -- , ( "table-cell" , FaTableCell ) -- , ( "table-column" , FaTableColumn ) -- , ( "table-row" , FaTableRow ) -- , ( "graphic" , FaGraphic ) -- , ( "drawing-page" , FaDrawing ) -- , ( "chart" , FaChart ) -- , ( "presentation" , FaPresentation ) -- , ( "ruby" , FaRuby ) ] -- | A named style data Style = Style { styleFamily :: Maybe StyleFamily , styleParentName :: Maybe StyleName , listStyle :: Maybe StyleName , styleProperties :: StyleProperties } deriving ( Eq, Show ) data StyleProperties = SProps { textProperties :: Maybe TextProperties , paraProperties :: Maybe ParaProperties -- , tableColProperties :: Maybe TColProperties -- , tableRowProperties :: Maybe TRowProperties -- , tableCellProperties :: Maybe TCellProperties -- , tableProperties :: Maybe TableProperties -- , graphicProperties :: Maybe GraphProperties } deriving ( Eq, Show ) instance Default StyleProperties where def = SProps { textProperties = Just def , paraProperties = Just def } data TextProperties = PropT { isEmphasised :: Bool , isStrong :: Bool , pitch :: Maybe FontPitch , verticalPosition :: VerticalTextPosition , underline :: Maybe UnderlineMode , strikethrough :: Maybe UnderlineMode } deriving ( Eq, Show ) instance Default TextProperties where def = PropT { isEmphasised = False , isStrong = False , pitch = Just def , verticalPosition = def , underline = Nothing , strikethrough = Nothing } data ParaProperties = PropP { paraNumbering :: ParaNumbering , indentation :: LengthOrPercent , margin_left :: LengthOrPercent } deriving ( Eq, Show ) instance Default ParaProperties where def = PropP { paraNumbering = NumberingNone , indentation = def , margin_left = def } ---- -- All the little data types that make up the properties ---- data VerticalTextPosition = VPosNormal | VPosSuper | VPosSub deriving ( Eq, Show ) instance Default VerticalTextPosition where def = VPosNormal instance Read VerticalTextPosition where readsPrec _ s = [ (VPosSub , s') | ("sub" , s') <- lexS ] ++ [ (VPosSuper , s') | ("super" , s') <- lexS ] ++ [ (signumToVPos n , s') | ( n , s') <- readPercent s ] where lexS = lex s signumToVPos n | n < 0 = VPosSub | n > 0 = VPosSuper | otherwise = VPosNormal data UnderlineMode = UnderlineModeNormal | UnderlineModeSkipWhitespace deriving ( Eq, Show ) instance Lookupable UnderlineMode where lookupTable = [ ( "continuous" , UnderlineModeNormal ) , ( "skip-white-space" , UnderlineModeSkipWhitespace ) ] data ParaNumbering = NumberingNone | NumberingKeep | NumberingRestart Int deriving ( Eq, Show ) data LengthOrPercent = LengthValueMM Int | PercentValue Int deriving ( Eq, Show ) instance Default LengthOrPercent where def = LengthValueMM 0 instance Read LengthOrPercent where readsPrec _ s = [ (PercentValue percent , s' ) | (percent , s' ) <- readPercent s] ++ [ (LengthValueMM lengthMM , s'') | (length' , s' ) <- reads s , (unit , s'') <- reads s' , let lengthMM = estimateInMillimeter length' unit ] data XslUnit = XslUnitMM | XslUnitCM | XslUnitInch | XslUnitPoints | XslUnitPica | XslUnitPixel | XslUnitEM instance Show XslUnit where show XslUnitMM = "mm" show XslUnitCM = "cm" show XslUnitInch = "in" show XslUnitPoints = "pt" show XslUnitPica = "pc" show XslUnitPixel = "px" show XslUnitEM = "em" instance Read XslUnit where readsPrec _ "mm" = [(XslUnitMM , "")] readsPrec _ "cm" = [(XslUnitCM , "")] readsPrec _ "in" = [(XslUnitInch , "")] readsPrec _ "pt" = [(XslUnitPoints , "")] readsPrec _ "pc" = [(XslUnitPica , "")] readsPrec _ "px" = [(XslUnitPixel , "")] readsPrec _ "em" = [(XslUnitEM , "")] readsPrec _ _ = [] -- | Rough conversion of measures into millimetres. -- Pixels and em's are actually implementation dependent/relative measures, -- so I could not really easily calculate anything exact here even if I wanted. -- But I do not care about exactness right now, as I only use measures -- to determine if a paragraph is "indented" or not. estimateInMillimeter :: Double -> XslUnit -> Int estimateInMillimeter n XslUnitMM = round n estimateInMillimeter n XslUnitCM = round $ n * 10 estimateInMillimeter n XslUnitInch = round $ n * 25.4 estimateInMillimeter n XslUnitPoints = round $ n * (1/72) * 25.4 estimateInMillimeter n XslUnitPica = round $ n * 12 * (1/72) * 25.4 estimateInMillimeter n XslUnitPixel = round $ n * (1/72) * 25.4 estimateInMillimeter n XslUnitEM = round $ n * 16 * (1/72) * 25.4 ---- -- List styles ---- type ListLevel = Int newtype ListStyle = ListStyle { levelStyles :: M.Map ListLevel ListLevelStyle } deriving ( Eq, Show ) -- getListLevelStyle :: ListLevel -> ListStyle -> Maybe ListLevelStyle getListLevelStyle level ListStyle{..} = let (lower , exactHit , _) = M.splitLookup level levelStyles in exactHit <|> fmap fst (M.maxView lower) -- findBy (`M.lookup` levelStyles) [level, (level-1) .. 1] -- \^ simpler, but in general less efficient data ListLevelStyle = ListLevelStyle { listLevelType :: ListLevelType , listItemPrefix :: Maybe Text , listItemSuffix :: Maybe Text , listItemFormat :: ListItemNumberFormat , listItemStart :: Int } deriving ( Eq, Ord ) instance Show ListLevelStyle where show ListLevelStyle{..} = " listItemPrefix) ++ show listItemFormat ++ maybeToString (T.unpack <$> listItemSuffix) ++ ">" where maybeToString = fromMaybe "" data ListLevelType = LltBullet | LltImage | LltNumbered deriving ( Eq, Ord, Show ) data ListItemNumberFormat = LinfNone | LinfNumber | LinfRomanLC | LinfRomanUC | LinfAlphaLC | LinfAlphaUC | LinfString String deriving ( Eq, Ord ) instance Show ListItemNumberFormat where show LinfNone = "" show LinfNumber = "1" show LinfRomanLC = "i" show LinfRomanUC = "I" show LinfAlphaLC = "a" show LinfAlphaUC = "A" show (LinfString s) = s instance Default ListItemNumberFormat where def = LinfNone instance Read ListItemNumberFormat where readsPrec _ "" = [(LinfNone , "")] readsPrec _ "1" = [(LinfNumber , "")] readsPrec _ "i" = [(LinfRomanLC , "")] readsPrec _ "I" = [(LinfRomanUC , "")] readsPrec _ "a" = [(LinfAlphaLC , "")] readsPrec _ "A" = [(LinfAlphaUC , "")] readsPrec _ s = [(LinfString s , "")] -------------------------------------------------------------------------------- -- Readers -- -- ...it seems like a whole lot of this should be automatically derivable -- or at least moveable into a class. Most of this is data concealed in -- code. -------------------------------------------------------------------------------- -- readAllStyles :: StyleReader a Styles readAllStyles = ( readFontPitches >>?! ( readAutomaticStyles &&& readStyles )) >>?%? chooseMax -- all top elements are always on the same hierarchy level -- readStyles :: StyleReader a Styles readStyles = executeInSub NsOffice "styles" $ liftAsSuccess $ liftA3 Styles ( tryAll NsStyle "style" readStyle >>^ M.fromList ) ( tryAll NsText "list-style" readListStyle >>^ M.fromList ) ( tryAll NsStyle "default-style" readDefaultStyle >>^ M.fromList ) -- readAutomaticStyles :: StyleReader a Styles readAutomaticStyles = executeInSub NsOffice "automatic-styles" $ liftAsSuccess $ liftA3 Styles ( tryAll NsStyle "style" readStyle >>^ M.fromList ) ( tryAll NsText "list-style" readListStyle >>^ M.fromList ) ( returnV M.empty ) -- readDefaultStyle :: StyleReader a (StyleFamily, StyleProperties) readDefaultStyle = lookupAttr NsStyle "family" >>?! keepingTheValue readStyleProperties -- readStyle :: StyleReader a (StyleName,Style) readStyle = findAttr NsStyle "name" >>?! keepingTheValue ( liftA4 Style ( lookupAttr' NsStyle "family" ) ( findAttr' NsStyle "parent-style-name" ) ( findAttr' NsStyle "list-style-name" ) readStyleProperties ) -- readStyleProperties :: StyleReaderSafe a StyleProperties readStyleProperties = liftA2 SProps ( readTextProperties >>> choiceToMaybe ) ( readParaProperties >>> choiceToMaybe ) -- readTextProperties :: StyleReader a TextProperties readTextProperties = executeInSub NsStyle "text-properties" $ liftAsSuccess ( liftA6 PropT ( searchAttr NsXSL_FO "font-style" False isFontEmphasised ) ( searchAttr NsXSL_FO "font-weight" False isFontBold ) findPitch ( getAttr NsStyle "text-position" ) readUnderlineMode readStrikeThroughMode ) where isFontEmphasised = [("normal",False),("italic",True),("oblique",True)] isFontBold = ("normal",False):("bold",True) :map ((,True) . tshow) ([100,200..900]::[Int]) readUnderlineMode :: StyleReaderSafe a (Maybe UnderlineMode) readUnderlineMode = readLineMode "text-underline-mode" "text-underline-style" readStrikeThroughMode :: StyleReaderSafe a (Maybe UnderlineMode) readStrikeThroughMode = readLineMode "text-line-through-mode" "text-line-through-style" readLineMode :: Text -> Text -> StyleReaderSafe a (Maybe UnderlineMode) readLineMode modeAttr styleAttr = proc x -> do isUL <- searchAttr NsStyle styleAttr False isLinePresent -< x mode <- lookupAttr' NsStyle modeAttr -< x if isUL then case mode of Just m -> returnA -< Just m Nothing -> returnA -< Just UnderlineModeNormal else returnA -< Nothing where isLinePresent = ("none",False) : map (,True) [ "dash" , "dot-dash" , "dot-dot-dash" , "dotted" , "long-dash" , "solid" , "wave" ] -- readParaProperties :: StyleReader a ParaProperties readParaProperties = executeInSub NsStyle "paragraph-properties" $ liftAsSuccess ( liftA3 PropP ( liftA2 readNumbering ( isSet' NsText "number-lines" ) ( readAttr' NsText "line-number" ) ) ( liftA2 readIndentation ( isSetWithDefault NsStyle "auto-text-indent" False ) ( getAttr NsXSL_FO "text-indent" ) ) ( getAttr NsXSL_FO "margin-left" ) ) where readNumbering (Just True) (Just n) = NumberingRestart n readNumbering (Just True) _ = NumberingKeep readNumbering _ _ = NumberingNone readIndentation False indent = indent readIndentation True _ = def ---- -- List styles ---- -- readListStyle :: StyleReader a (StyleName, ListStyle) readListStyle = findAttr NsStyle "name" >>?! keepingTheValue ( liftA ListStyle $ liftA3 SM.union3 ( readListLevelStyles NsText "list-level-style-number" LltNumbered ) ( readListLevelStyles NsText "list-level-style-bullet" LltBullet ) ( readListLevelStyles NsText "list-level-style-image" LltImage ) >>^ M.mapMaybe chooseMostSpecificListLevelStyle ) -- readListLevelStyles :: Namespace -> ElementName -> ListLevelType -> StyleReaderSafe a (SM.SetMap Int ListLevelStyle) readListLevelStyles namespace elementName levelType = tryAll namespace elementName (readListLevelStyle levelType) >>^ SM.fromList -- readListLevelStyle :: ListLevelType -> StyleReader a (Int, ListLevelStyle) readListLevelStyle levelType = readAttr NsText "level" >>?! keepingTheValue ( liftA5 toListLevelStyle ( returnV levelType ) ( findAttr' NsStyle "num-prefix" ) ( findAttr' NsStyle "num-suffix" ) ( getAttr NsStyle "num-format" ) ( findAttrText' NsText "start-value" ) ) where toListLevelStyle _ p s LinfNone b = ListLevelStyle LltBullet p s LinfNone (startValue b) toListLevelStyle _ p s f@(LinfString _) b = ListLevelStyle LltBullet p s f (startValue b) toListLevelStyle t p s f b = ListLevelStyle t p s f (startValue b) startValue mbx = fromMaybe 1 (mbx >>= safeRead) -- chooseMostSpecificListLevelStyle :: S.Set ListLevelStyle -> Maybe ListLevelStyle chooseMostSpecificListLevelStyle ls = F.foldr select Nothing ls where select l Nothing = Just l select ( ListLevelStyle t1 p1 s1 f1 b1 ) ( Just ( ListLevelStyle t2 p2 s2 f2 _ )) = Just $ ListLevelStyle (select' t1 t2) (p1 <|> p2) (s1 <|> s2) (selectLinf f1 f2) b1 select' LltNumbered _ = LltNumbered select' _ LltNumbered = LltNumbered select' _ _ = LltBullet selectLinf LinfNone f2 = f2 selectLinf f1 LinfNone = f1 selectLinf (LinfString _) f2 = f2 selectLinf f1 (LinfString _) = f1 selectLinf f1 _ = f1 -------------------------------------------------------------------------------- -- Tools to access style data -------------------------------------------------------------------------------- -- lookupStyle :: StyleName -> Styles -> Maybe Style lookupStyle name Styles{..} = M.lookup name stylesByName -- lookupDefaultStyle' :: Styles -> StyleFamily -> StyleProperties lookupDefaultStyle' Styles{..} family = fromMaybe def (M.lookup family defaultStyleMap) -- lookupListStyleByName :: StyleName -> Styles -> Maybe ListStyle lookupListStyleByName name Styles{..} = M.lookup name listStylesByName -- | Returns a chain of parent of the current style. The direct parent will -- 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 = L.unfoldr findNextParent style -- Ha! where findNextParent Style{..} = fmap duplicate $ (`lookupStyle` styles) =<< styleParentName -- | Looks up the style family of the current style. Normally, every style -- should have one. But if not, all parents are searched. getStyleFamily :: Style -> Styles -> Maybe StyleFamily getStyleFamily style@Style{..} styles = styleFamily <|> F.asum (map (`getStyleFamily` styles) $ parents style styles) -- | Each 'Style' has certain 'StyleProperties'. But sometimes not all property -- values are specified. Instead, a value might be inherited from a -- parent style. This function makes this chain of inheritance -- concrete and easily accessible by encapsulating the necessary lookups. -- The resulting list contains the direct properties of the style as the first -- element, the ones of the direct parent element as the next one, and so on. -- -- Note: There should also be default properties for each style family. These -- are @not@ contained in this list because properties inherited from -- parent elements take precedence over default styles. -- -- This function is primarily meant to be used through convenience wrappers. -- stylePropertyChain :: Style -> Styles -> [StyleProperties] stylePropertyChain style styles = map styleProperties (style : parents style styles) -- extendedStylePropertyChain :: [Style] -> Styles -> [StyleProperties] extendedStylePropertyChain [] _ = [] extendedStylePropertyChain [style] styles = stylePropertyChain style styles ++ maybeToList (fmap (lookupDefaultStyle' styles) (getStyleFamily style styles)) extendedStylePropertyChain (style:trace) styles = stylePropertyChain style styles ++ extendedStylePropertyChain trace styles ================================================ FILE: src/Text/Pandoc/Readers/ODT.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Reader.ODT Copyright : Copyright (C) 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : Martin Linnemann Stability : alpha Portability : portable Entry point to the odt reader. -} module Text.Pandoc.Readers.ODT ( readODT ) where import Codec.Archive.Zip import Text.Pandoc.XML.Light import Text.Pandoc.Walk import Data.Char (isDigit) import qualified Data.ByteString.Lazy as B import System.FilePath import Control.Monad.Except (throwError) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.MediaBag import Text.Pandoc.Options import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Readers.ODT.ContentReader import Text.Pandoc.Readers.ODT.StyleReader import Text.Pandoc.Readers.ODT.Generic.Fallible import Text.Pandoc.Readers.ODT.Generic.XMLConverter import Text.Pandoc.Shared (filteredFilesFromArchive) readODT :: PandocMonad m => ReaderOptions -> B.ByteString -> m Pandoc readODT opts bytes = case readODT' opts bytes of Right (doc, mb) -> do P.setMediaBag mb return $ walk makeFigure doc Left e -> throwError e -- the ODT parser uses old-style figures: an image with title beginning -- "fig:" in a paragraph by itself. Convert these to new Figure elements. makeFigure :: Block -> Block makeFigure (Para [ Image (ident, classes, kvs) capt (src, tit) ]) | "fig:" `T.isPrefixOf` tit = Figure (ident, [], []) (Caption Nothing [Plain capt']) [Plain [Image ("", classes, kvs) capt (src, "")]] where capt' = case capt of -- strip "Figure 1:" for consistency (Str _ : Space : Str t : Space : xs) | T.all (\c -> isDigit c || c == ':') t , ":" `T.isSuffixOf` t -> xs xs -> xs makeFigure x = x -- readODT' :: ReaderOptions -> B.ByteString -> Either PandocError (Pandoc, MediaBag) readODT' _ bytes = bytesToODT bytes-- of -- Right (pandoc, mediaBag) -> Right (pandoc , mediaBag) -- Left err -> Left err -- bytesToODT :: B.ByteString -> Either PandocError (Pandoc, MediaBag) bytesToODT bytes = case toArchiveOrFail bytes of Right archive -> archiveToODT archive Left err -> Left $ PandocParseError $ "Could not unzip ODT: " <> T.pack err -- archiveToODT :: Archive -> Either PandocError (Pandoc, MediaBag) archiveToODT archive = do let onFailure msg Nothing = Left $ PandocParseError msg onFailure _ (Just x) = Right x contentEntry <- onFailure "Could not find content.xml" (findEntryByPath "content.xml" archive) stylesEntry <- onFailure "Could not find styles.xml" (findEntryByPath "styles.xml" archive) contentElem <- entryToXmlElem contentEntry stylesElem <- entryToXmlElem stylesEntry styles <- either (\_ -> Left $ PandocParseError "Could not read styles") Right (chooseMax (readStylesAt stylesElem ) (readStylesAt contentElem)) let filePathIsODTMedia :: FilePath -> Bool filePathIsODTMedia fp = let (dir, name) = splitFileName fp in (dir == "Pictures/") || (dir /= "./" && name == "content.xml") let media = filteredFilesFromArchive archive filePathIsODTMedia let startState = readerState styles media either (\_ -> Left $ PandocParseError "Could not convert opendocument") Right (runConverter' read_body startState contentElem) -- entryToXmlElem :: Entry -> Either PandocError Element entryToXmlElem entry = case parseXMLElement . UTF8.toTextLazy . fromEntry $ entry of Right x -> Right x Left msg -> Left $ PandocXMLError (T.pack $ eRelativePath entry) msg ================================================ FILE: src/Text/Pandoc/Readers/OOXML/Shared.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.OOXML.Shared Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Shared utilities for Office Open XML (OOXML) readers (DOCX, PPTX). Provides common functions for ZIP archive handling, XML parsing, namespace management, and DrawingML parsing. -} module Text.Pandoc.Readers.OOXML.Shared ( -- * Constants emusPerInch , emuToInches , inchesToEmu -- * Types , NameSpaces , elemName , elemToNameSpaces , isElem , findChildByName , findChildrenByName , findElementByName , findAttrByName ) where import qualified Data.Map as M import qualified Data.Text as T import Data.Text (Text) import Text.Pandoc.XML.Light -- | Type alias for namespace mappings type NameSpaces = M.Map Text Text -- | English Metric Units per inch -- 1 inch = 914400 EMUs (used in OOXML for dimensions) emusPerInch :: Integer emusPerInch = 914400 -- | Convert EMUs to inches emuToInches :: Integer -> Double emuToInches n = fromIntegral n / fromIntegral emusPerInch -- | Convert inches to EMUs inchesToEmu :: Double -> Integer inchesToEmu n = round (n * fromIntegral emusPerInch) -- | Extract namespace declarations from element attributes elemToNameSpaces :: Element -> NameSpaces elemToNameSpaces = foldr (\(Attr qn val) -> case qn of QName s _ (Just "xmlns") -> M.insert s val _ -> id) mempty . elAttribs -- | Create a qualified name from namespace map, prefix, and local name elemName :: NameSpaces -> Text -> Text -> QName elemName ns prefix name = QName name (M.lookup prefix ns) (if T.null prefix then Nothing else Just prefix) -- | Check if element matches namespace prefix and local name isElem :: NameSpaces -> Text -> Text -> Element -> Bool isElem ns prefix name element = let ns' = ns <> elemToNameSpaces element in qName (elName element) == name && qURI (elName element) == M.lookup prefix ns' -- | Find first child element matching namespace and name findChildByName :: NameSpaces -> Text -> Text -> Element -> Maybe Element findChildByName ns pref name el = let ns' = ns <> elemToNameSpaces el in findChild (elemName ns' pref name) el -- | Find all children matching namespace and name findChildrenByName :: NameSpaces -> Text -> Text -> Element -> [Element] findChildrenByName ns pref name el = let ns' = ns <> elemToNameSpaces el in findChildren (elemName ns' pref name) el -- | Find element anywhere in descendants matching namespace and name findElementByName :: NameSpaces -> Text -> Text -> Element -> Maybe Element findElementByName ns pref name el = let ns' = ns <> elemToNameSpaces el in findElement (elemName ns' pref name) el -- | Find attribute value by namespace prefix and name findAttrByName :: NameSpaces -> Text -> Text -> Element -> Maybe Text findAttrByName ns pref name el = let ns' = ns <> elemToNameSpaces el in findAttr (elemName ns' pref name) el ================================================ FILE: src/Text/Pandoc/Readers/OPML.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.OPML Copyright : Copyright (C) 2013-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of OPML to 'Pandoc' document. -} module Text.Pandoc.Readers.OPML ( readOPML ) where import Control.Monad.State.Strict import Data.Default import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Options import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.Markdown (readMarkdown) import Text.Pandoc.Shared (blocksToInlines') import Text.Pandoc.Sources (ToSources(..), sourcesToText) import Text.Pandoc.XML.Light import Control.Monad.Except (throwError) type OPML m = StateT OPMLState m data OPMLState = OPMLState{ opmlSectionLevel :: Int , opmlDocTitle :: Inlines , opmlDocAuthors :: [Inlines] , opmlDocDate :: Inlines , opmlOptions :: ReaderOptions } deriving Show instance Default OPMLState where def = OPMLState{ opmlSectionLevel = 0 , opmlDocTitle = mempty , opmlDocAuthors = [] , opmlDocDate = mempty , opmlOptions = def } readOPML :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readOPML opts inp = do let sources = toSources inp (bs, st') <- runStateT (case parseXMLContents (TL.fromStrict . sourcesToText $ sources) of Left msg -> throwError $ PandocXMLError "" msg Right ns -> mapM parseBlock ns) def{ opmlOptions = opts } return $ setTitle (opmlDocTitle st') $ setAuthors (opmlDocAuthors st') $ setDate (opmlDocDate st') $ doc $ mconcat bs -- convenience function to get an attribute value, defaulting to "" attrValue :: Text -> Element -> Text attrValue attr elt = fromMaybe "" (lookupAttrBy (\x -> qName x == attr) (elAttribs elt)) -- exceptT :: PandocMonad m => Either PandocError a -> OPML m a -- exceptT = either throwError return asHtml :: PandocMonad m => Text -> OPML m Inlines asHtml s = do opts <- gets opmlOptions Pandoc _ bs <- readHtml def{ readerExtensions = readerExtensions opts } s return $ blocksToInlines' bs asMarkdown :: PandocMonad m => Text -> OPML m Blocks asMarkdown s = do opts <- gets opmlOptions Pandoc _ bs <- readMarkdown def{ readerExtensions = readerExtensions opts } s return $ fromList bs getBlocks :: PandocMonad m => Element -> OPML m Blocks getBlocks e = mconcat <$> mapM parseBlock (elContent e) parseBlock :: PandocMonad m => Content -> OPML m Blocks parseBlock (Elem e) = case qName (elName e) of "ownerName" -> mempty <$ modify (\st -> st{opmlDocAuthors = [text $ strContent e]}) "dateModified" -> mempty <$ modify (\st -> st{opmlDocDate = text $ strContent e}) "title" -> mempty <$ modify (\st -> st{opmlDocTitle = text $ strContent e}) "outline" -> gets opmlSectionLevel >>= sect . (+1) "?xml" -> return mempty _ -> getBlocks e where sect n = do headerText <- asHtml $ attrValue "text" e noteBlocks <- asMarkdown $ attrValue "_note" e modify $ \st -> st{ opmlSectionLevel = n } bs <- getBlocks e modify $ \st -> st{ opmlSectionLevel = n - 1 } let headerText' = case T.toUpper (attrValue "type" e) of "LINK" -> link (attrValue "url" e) "" headerText _ -> headerText return $ header n headerText' <> noteBlocks <> bs parseBlock _ = return mempty ================================================ FILE: src/Text/Pandoc/Readers/Org/BlockStarts.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Org.BlockStarts Copyright : Copyright (C) 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Parsers for Org-mode inline elements. -} module Text.Pandoc.Readers.Org.BlockStarts ( exampleLineStart , hline , noteMarker , tableStart , drawerStart , headerStart , metaLineStart , latexEnvStart , commentLineStart , bulletListStart , orderedListStart , endOfBlock ) where import Control.Monad (void, guard) import Data.Text (Text) import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Definition as Pandoc import Text.Pandoc.Shared (safeRead) import Text.Pandoc.Parsing (lowerAlpha, upperAlpha) import Text.Pandoc.Extensions import Text.Pandoc.Readers.LaTeX.Math (inlineEnvironmentNames) import Data.Functor (($>)) -- | Horizontal Line (five -- dashes or more) hline :: Monad m => OrgParser m () hline = try $ do skipSpaces string "-----" many (char '-') skipSpaces newline return () -- | Read the start of a header line, return the header level headerStart :: Monad m => OrgParser m Int headerStart = try $ (length <$> many1 (char '*')) <* many1 (char ' ') <* updateLastPreCharPos tableStart :: Monad m => OrgParser m Char tableStart = try $ skipSpaces *> char '|' gridTableStart :: Monad m => OrgParser m () gridTableStart = try $ skipSpaces <* char '+' <* char '-' latexEnvStart :: Monad m => OrgParser m Text latexEnvStart = try $ do skipSpaces string "\\begin{" name <- latexEnvName char '}' guard $ name `notElem` inlineEnvironmentNames pure name where latexEnvName :: Monad m => OrgParser m Text latexEnvName = try $ mappend <$> many1Char alphaNum <*> option "" (textStr "*") listCounterCookie :: Monad m => OrgParser m Int listCounterCookie = try $ string "[@" *> parseNum <* char ']' <* (skipSpaces <|> lookAhead eol) where parseNum = (safeRead =<< many1Char digit) <|> snd <$> (lowerAlpha <|> upperAlpha) bulletListStart :: Monad m => OrgParser m Int bulletListStart = try $ do ind <- length <$> many spaceChar -- Unindented lists cannot use '*' bullets. oneOf (if ind == 0 then "+-" else "*+-") skipSpaces1 <|> lookAhead eol optionMaybe listCounterCookie return (ind + 1) eol :: Monad m => OrgParser m () eol = void (char '\n') orderedListStart :: Monad m => OrgParser m (Int, ListAttributes) orderedListStart = try $ do ind <- length <$> many spaceChar fancy <- option False $ True <$ guardEnabled Ext_fancy_lists -- Ordered list markers allowed in org-mode let styles = (many1Char digit $> (if fancy then Decimal else DefaultStyle)) : if fancy then [ fst <$> lowerAlpha , fst <$> upperAlpha ] else [] let delims = [ char '.' $> (if fancy then Period else DefaultDelim) , char ')' $> (if fancy then OneParen else DefaultDelim) ] style <- choice styles delim <- choice delims skipSpaces1 <|> lookAhead eol start <- option 1 listCounterCookie return (ind + 1, (start, style, delim)) drawerStart :: Monad m => OrgParser m Text drawerStart = try $ skipSpaces *> drawerName <* skipSpaces <* newline where drawerName = char ':' *> manyTillChar nonspaceChar (char ':') metaLineStart :: Monad m => OrgParser m () metaLineStart = try $ skipSpaces <* string "#+" commentLineStart :: Monad m => OrgParser m () commentLineStart = try $ -- the first char after '#' must be a plain space character or a newline skipSpaces <* string "#" <* lookAhead (oneOf " \n") exampleLineStart :: Monad m => OrgParser m () exampleLineStart = () <$ try (skipSpaces *> char ':' *> (void (char ' ') <|> lookAhead eol)) noteMarker :: Monad m => OrgParser m Text noteMarker = try $ do char '[' choice [ many1TillChar digit (char ']') , (<>) <$> textStr "fn:" <*> many1TillChar (noneOf "\n\r\t ") (char ']') ] -- | Succeeds if the parser is at the end of a block. endOfBlock :: Monad m => OrgParser m () endOfBlock = lookAhead . try $ void blankline <|> anyBlockStart where -- Succeeds if there is a new block starting at this position. anyBlockStart :: Monad m => OrgParser m () anyBlockStart = try . choice $ [ exampleLineStart , hline , metaLineStart , commentLineStart , gridTableStart , void noteMarker , void tableStart , void drawerStart , void headerStart , void latexEnvStart , void bulletListStart , void orderedListStart ] ================================================ FILE: src/Text/Pandoc/Readers/Org/Blocks.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {- | Module : Text.Pandoc.Readers.Org.Blocks Copyright : Copyright (C) 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Parsers for Org-mode block elements. -} module Text.Pandoc.Readers.Org.Blocks ( blockList , meta ) where import Text.Pandoc.Readers.Org.BlockStarts import Text.Pandoc.Readers.Org.DocumentTree (documentTree, unprunedHeadlineToBlocks) import Text.Pandoc.Readers.Org.Inlines import Text.Pandoc.Readers.Org.Meta (metaExport, metaKey, metaLine) import Text.Pandoc.Readers.Org.ParserState import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Readers.Org.Shared (cleanLinkText, isImageFilename, originalLang, translateLang, exportsCode) import Text.Pandoc.Readers.LaTeX.Math (inlineEnvironmentNames) import Text.Pandoc.Builder (Blocks, Inlines, Many(..)) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Shared (compactify, compactifyDL, safeRead) import Control.Monad (foldM, guard, mzero, void) import Data.Bifunctor (bimap) import Data.Char (isSpace) import Data.Default (Default) import Data.Functor (($>)) import qualified Data.List as L import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Text (Text) import Data.List.NonEmpty (nonEmpty) import System.FilePath import qualified Data.Foldable as F import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.Walk as Walk import Text.Pandoc.Sources (ToSources(..)) -- -- parsing blocks -- -- | Get a list of blocks. blockList :: PandocMonad m => OrgParser m [Block] blockList = do fHeadlineTree <- documentTree blocks inline st <- getState let headlineTree = runF fHeadlineTree st unprunedHeadlineToBlocks headlineTree st -- | Get the meta information saved in the state. meta :: Monad m => OrgParser m Meta meta = do meta' <- metaExport runF meta' <$> getState blocks :: PandocMonad m => OrgParser m (F Blocks) blocks = mconcat <$> manyTill block (void (lookAhead headerStart) <|> eof) block :: PandocMonad m => OrgParser m (F Blocks) block = choice [ mempty <$ blanklines , table , dynamicBlock , orgBlock , figure , example , genericDrawer , include , specialLine , horizontalRule , list , latexFragment , noteBlock , rawOrgLine , paraOrPlain ] "block" -- | Parse a horizontal rule into a block element horizontalRule :: Monad m => OrgParser m (F Blocks) horizontalRule = return B.horizontalRule <$ try hline -- -- Block Attributes -- -- | Attributes that may be added to figures (like a name or caption). data BlockAttributes = BlockAttributes { blockAttrName :: Maybe Text , blockAttrCaption :: Maybe (F Inlines) , blockAttrKeyValues :: [(Text, Text)] } -- | Convert BlockAttributes into pandoc Attr attrFromBlockAttributes :: BlockAttributes -> Attr attrFromBlockAttributes BlockAttributes{..} = let ident = fromMaybe mempty $ lookup "id" blockAttrKeyValues classes = maybe [] T.words $ lookup "class" blockAttrKeyValues kv = filter ((`notElem` ["id", "class"]) . fst) blockAttrKeyValues in (ident, classes, kv) stringyMetaAttribute :: Monad m => OrgParser m (Text, Text) stringyMetaAttribute = try $ do metaLineStart *> notFollowedBy (stringAnyCase "begin" *> oneOf ":_") attrName <- T.toLower <$> many1TillChar nonspaceChar (char ':') skipSpaces attrValue <- anyLine <|> ("" <$ newline) return (attrName, attrValue) -- | Parse a set of block attributes. Block attributes are given through -- lines like @#+caption: block caption@ or @#+attr_html: :width 20@. -- Parsing will fail if any line contains an attribute different from -- those attributes known to work on blocks. blockAttributes :: PandocMonad m => OrgParser m BlockAttributes blockAttributes = try $ do kv <- many stringyMetaAttribute guard $ all (isBlockAttr . fst) 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 { blockAttrName = name , blockAttrCaption = caption' , blockAttrKeyValues = kvAttrs' } where isBlockAttr :: Text -> Bool isBlockAttr = flip elem [ "name", "label", "caption" , "attr_html", "attr_latex" , "results" ] appendValues :: Text -> Maybe Text -> (Text, Text) -> Maybe Text appendValues attrName accValue (key, value) = if key /= attrName then accValue else case accValue of Just acc -> Just $ acc <> " " <> value Nothing -> Just value -- | Parse key-value pairs for HTML attributes keyValues :: Monad m => OrgParser m [(Text, Text)] keyValues = try $ manyTill ((,) <$> key <*> value) newline where key :: Monad m => OrgParser m Text key = try $ skipSpaces *> char ':' *> many1Char nonspaceChar value :: Monad m => OrgParser m Text value = skipSpaces *> manyTillChar anyChar endOfValue endOfValue :: Monad m => OrgParser m () endOfValue = lookAhead (void $ try (many1 spaceChar <* key)) <|> try (skipSpaces <* lookAhead newline) -- -- Org Blocks (#+begin_... / #+end_...) -- -- | Read an org-mode block delimited by #+begin_type and #+end_type. orgBlock :: PandocMonad m => OrgParser m (F Blocks) orgBlock = try $ do blockAttrs <- blockAttributes blkType <- blockHeaderStart ($ blkType) $ case T.toLower blkType of "export" -> exportBlock "comment" -> rawBlockLines (const mempty) "html" -> rawBlockLines (return . B.rawBlock (T.toLower blkType)) "latex" -> rawBlockLines (return . B.rawBlock (T.toLower blkType)) "ascii" -> rawBlockLines (return . B.rawBlock (T.toLower blkType)) "example" -> exampleBlock blockAttrs "quote" -> \x -> ignHeaders *> parseBlockLines (fmap B.blockQuote) x "verse" -> verseBlock "src" -> codeBlock blockAttrs "note" -> admonitionBlock "note" blockAttrs "warning" -> admonitionBlock "warning" blockAttrs "tip" -> admonitionBlock "tip" blockAttrs "caution" -> admonitionBlock "caution" blockAttrs "important" -> admonitionBlock "important" blockAttrs _ -> -- case-sensitive checks case blkType of "abstract" -> metadataBlock _ -> \bt -> do params <- blockParameters let (ident, classes, kv) = attrFromBlockAttributes blockAttrs toDiv = (B.divWith (ident, classes ++ [blkType], kv <> params)) parseBlockLines (fmap toDiv) bt where blockHeaderStart :: Monad m => OrgParser m Text blockHeaderStart = try $ do skipSpaces metaLineStart stringAnyCase "begin_" many1Char (satisfy (not . isSpace)) admonitionBlock :: PandocMonad m => Text -> BlockAttributes -> Text -> OrgParser m (F Blocks) admonitionBlock blockType blockAttrs rawtext = do bls <- ignHeaders *> parseBlockLines id rawtext let id' = fromMaybe mempty $ blockAttrName blockAttrs pure $ fmap (B.divWith (id', [blockType], []) . (B.divWith ("", ["title"], []) (B.para (B.str (T.toTitle blockType))) <>)) bls exampleBlock :: PandocMonad m => BlockAttributes -> Text -> OrgParser m (F Blocks) exampleBlock blockAttrs _label = do skipSpaces (classes, kv) <- switchesAsAttributes newline content <- rawBlockContent "example" let id' = fromMaybe mempty $ blockAttrName blockAttrs let codeBlck = B.codeBlockWith (id', classes, kv) content return . return $ codeBlck rawBlockLines :: Monad m => (Text -> F Blocks) -> Text -> OrgParser m (F Blocks) rawBlockLines f blockType = ignHeaders *> (f <$> rawBlockContent blockType) parseBlockLines :: PandocMonad m => (F Blocks -> F Blocks) -> Text -> OrgParser m (F Blocks) parseBlockLines f blockType = (f <$> parsedBlockContent) where parsedBlockContent :: PandocMonad m => OrgParser m (F Blocks) parsedBlockContent = try $ do raw <- rawBlockContent blockType parseFromString blocks (raw <> "\n") -- | Read the raw string content of a block rawBlockContent :: Monad m => Text -> OrgParser m Text rawBlockContent blockType = rawBlockContent' $ stringAnyCase ("#+end_" <> blockType) -- | Read the raw string content of a block rawBlockContent' :: Monad m => OrgParser m Text -> OrgParser m Text rawBlockContent' blockEnder = try $ do blkLines <- manyTill rawLine (try $ skipSpaces <* blockEnder) tabStop <- getOption readerTabStop trimP <- orgStateTrimLeadBlkIndent <$> getState -- split lines into indentation/contents tuples let splitLines = map (T.span (\c -> c == ' ' || c == '\t')) blkLines let countSpaces = T.foldr (\case {'\t' -> (tabStop +); _ -> (1 +)}) 0 let shortestIndent = foldr (min . countSpaces . fst) maxBound . filter (not . T.null . snd) -- ignore empty lines $ splitLines let tabsToSpaces = T.replace "\t" (T.replicate tabStop " ") let reIndent = if trimP then (T.drop shortestIndent . tabsToSpaces) else id T.unlines (map (uncurry T.append . bimap reIndent commaEscaped) splitLines) <$ updateState (\s -> s { orgStateTrimLeadBlkIndent = True }) where rawLine :: Monad m => OrgParser m Text rawLine = try $ ("" <$ blankline) <|> anyLine commaEscaped suff = case T.uncons suff of Just (',', cs) | "*" <- T.take 1 cs -> cs | "#+" <- T.take 2 cs -> cs _ -> suff -- | Read but ignore all remaining block headers. ignHeaders :: Monad m => OrgParser m () ignHeaders = (() <$ newline) <|> (() <$ anyLine) -- | Read a block containing code intended for export in specific backends -- only. exportBlock :: Monad m => Text -> OrgParser m (F Blocks) exportBlock blockType = try $ do exportType <- skipSpaces *> orgArgWord <* ignHeaders contents <- rawBlockContent blockType returnF (B.rawBlock (T.toLower exportType) contents) verseBlock :: PandocMonad m => Text -> OrgParser m (F Blocks) verseBlock blockType = try $ do ignHeaders content <- rawBlockContent blockType fmap B.lineBlock . sequence <$> mapM parseVerseLine (T.lines content) where -- replace initial spaces with nonbreaking spaces to preserve -- indentation, parse the rest as normal inline parseVerseLine :: PandocMonad m => Text -> OrgParser m (F Inlines) parseVerseLine cs = do let (initialSpaces, indentedLine) = T.span isSpace cs let nbspIndent = if T.null initialSpaces then mempty else B.str $ T.map (const '\160') initialSpaces line <- parseFromString inlines (indentedLine <> "\n") return (trimInlinesF $ pure nbspIndent <> line) -- | Parses an environment of the given name and adds the result to the document -- metadata under a key of the same name. metadataBlock :: PandocMonad m => Text -> OrgParser m (F Blocks) metadataBlock blockType = try $ do ignHeaders content <- parseBlockLines id blockType meta' <- orgStateMeta <$> getState updateState $ \st -> st { orgStateMeta = B.setMeta blockType <$> content <*> meta' } return mempty -- | Read a code block and the associated results block if present. Which of -- the blocks is included in the output is determined using the "exports" -- argument in the block header. codeBlock :: PandocMonad m => BlockAttributes -> Text -> OrgParser m (F Blocks) codeBlock blockAttrs blockType = do skipSpaces (classes, kv) <- codeHeaderArgs <|> (mempty <$ ignHeaders) content <- rawBlockContent blockType resultsContent <- option mempty babelResultsBlock let identifier = fromMaybe mempty $ blockAttrName blockAttrs let classes' = case classes of c:cs | Just c' <- T.stripPrefix "jupyter-" c -> c' : "code" : cs _ -> classes let codeBlk = B.codeBlockWith (identifier, classes', kv) content let wrap = maybe pure addCaption (blockAttrCaption blockAttrs) return $ (if exportsCode kv then wrap codeBlk else mempty) <> (if exportsResults kv then resultsContent else mempty) where addCaption :: F Inlines -> Blocks -> F Blocks addCaption caption blk = B.divWith ("", ["captioned-content"], []) <$> (mkCaptionBlock caption <> pure blk) mkCaptionBlock :: F Inlines -> F Blocks mkCaptionBlock = fmap (B.divWith ("", ["caption"], []) . B.plain) exportsResults :: [(Text, Text)] -> Bool exportsResults = maybe False (`elem` ["results", "both"]) . lookup "exports" -- | Parse the result of an evaluated babel code block. babelResultsBlock :: PandocMonad m => OrgParser m (F Blocks) babelResultsBlock = try $ do blanklines resultsMarker <|> (lookAhead . void . try $ manyTill (metaLineStart *> anyLineNewline) resultsMarker) block where resultsMarker = try . void $ stringAnyCase "#+RESULTS:" *> blankline -- | Parse code block arguments codeHeaderArgs :: Monad m => OrgParser m ([Text], [(Text, Text)]) codeHeaderArgs = try $ do language <- skipSpaces *> orgArgWord (switchClasses, switchKv) <- switchesAsAttributes parameters <- manyTill blockOption newline return ( translateLang language : switchClasses , originalLang language <> switchKv <> parameters ) switchesAsAttributes :: Monad m => OrgParser m ([Text], [(Text, Text)]) switchesAsAttributes = try $ do switches <- skipSpaces *> try (switch `sepBy` many1 spaceChar) return $ foldr addToAttr ([], []) switches where addToAttr :: (Char, Maybe Text, SwitchPolarity) -> ([Text], [(Text, Text)]) -> ([Text], [(Text, Text)]) addToAttr ('n', lineNum, pol) (cls, kv) = let kv' = case lineNum of Just num -> ("startFrom", num):kv Nothing -> kv cls' = case pol of SwitchPlus -> "continuedSourceBlock":cls SwitchMinus -> cls in ("numberLines":cls', kv') addToAttr _ x = x -- | Whether a switch flag is specified with @+@ or @-@. data SwitchPolarity = SwitchPlus | SwitchMinus deriving (Show, Eq) -- | Parses a switch's polarity. switchPolarity :: Monad m => OrgParser m SwitchPolarity switchPolarity = (SwitchMinus <$ char '-') <|> (SwitchPlus <$ char '+') -- | Parses a source block switch option. switch :: Monad m => OrgParser m (Char, Maybe Text, SwitchPolarity) switch = try $ lineNumberSwitch <|> labelSwitch <|> whitespaceSwitch <|> simpleSwitch where simpleSwitch = (\pol c -> (c, Nothing, pol)) <$> switchPolarity <*> letter labelSwitch = genericSwitch 'l' $ char '"' *> many1TillChar nonspaceChar (char '"') whitespaceSwitch :: Monad m => OrgParser m (Char, Maybe Text, SwitchPolarity) whitespaceSwitch = do string "-i" updateState $ \s -> s { orgStateTrimLeadBlkIndent = False } return ('i', Nothing, SwitchMinus) -- | Generic source block switch-option parser. genericSwitch :: Monad m => Char -> OrgParser m Text -> OrgParser m (Char, Maybe Text, SwitchPolarity) genericSwitch c p = try $ do polarity <- switchPolarity <* char c <* skipSpaces arg <- optionMaybe p return (c, arg, polarity) -- | Reads a line number switch option. The line number switch can be used with -- example and source blocks. lineNumberSwitch :: Monad m => OrgParser m (Char, Maybe Text, SwitchPolarity) lineNumberSwitch = genericSwitch 'n' (manyChar digit) blockOption :: Monad m => OrgParser m (Text, Text) blockOption = try $ do argKey <- orgArgKey paramValue <- option "yes" orgParamValue return (argKey, paramValue) orgParamValue :: Monad m => OrgParser m Text orgParamValue = try $ skipSpaces *> notFollowedBy orgArgKey *> ((char '"' *> manyChar (noneOf "\n\r\"") <* char '"') <|> noneOf "\n\r" `many1TillChar` endOfValue) <* skipSpaces where endOfValue = lookAhead $ try (skipSpaces <* oneOf "\n\r") <|> try (skipSpaces1 <* orgArgKey) -- -- Dynamic block (#+begin: ... #+end:) -- -- | Parses a Dynamic Block, i.e., a block delimited by #+BEGIN: and -- #+END:. dynamicBlock :: PandocMonad m => OrgParser m (F Blocks) dynamicBlock = try $ do metaLineStart *> stringAnyCase "begin:" *> spaces blockname <- optionMaybe orgArgWord blockArgs <- blockParameters contents <- do raw <- rawBlockContent' $ metaLineStart *> stringAnyCase "end:" parseFromString blocks (raw <> "\n") let attr = ("", maybe [] (:[]) blockname, blockArgs) return $ B.divWith attr <$> contents -- | Parse block arguments; in order, this tries to parse a Lisp-style -- argument list, a set of key-value pairs using /equals/, and as a -- fallback the whole line as a single /parameters/ argument. blockParameters :: PandocMonad m => OrgParser m [(Text, Text)] blockParameters = choice [ try $ manyTill ((,) <$> orgArgKey <*> orgParamValue) newline , try $ manyTill ((,) <$> (spaces *> orgArgWord <* char '=') <*> orgArgWord) newline , (\x -> [ ("parameters", x) | not (T.null x)]) <$> (skipSpaces *> anyLine) ] -- -- Drawers -- -- | A generic drawer which has no special meaning for org-mode. -- Whether or not this drawer is included in the output depends on the drawers -- export setting. genericDrawer :: PandocMonad m => OrgParser m (F Blocks) genericDrawer = try $ do name <- T.toUpper <$> drawerStart content <- manyTill drawerLine (try drawerEnd) state <- getState -- Include drawer if it is explicitly included in or not explicitly excluded -- from the list of drawers that should be exported. PROPERTIES drawers are -- never exported. case exportDrawers . orgStateExportSettings $ state of _ | name == "PROPERTIES" -> return mempty Left names | name `elem` names -> return mempty Right names | name `notElem` names -> return mempty _ -> drawerDiv name <$> parseLines content where parseLines :: PandocMonad m => [Text] -> OrgParser m (F Blocks) parseLines = parseFromString blocks . (<> "\n") . T.unlines drawerDiv :: Text -> F Blocks -> F Blocks drawerDiv drawerName = fmap $ B.divWith (mempty, [drawerName, "drawer"], mempty) drawerLine :: Monad m => OrgParser m Text drawerLine = anyLine drawerEnd :: Monad m => OrgParser m Text drawerEnd = try $ skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline -- -- Figures -- -- | Figures or an image paragraph (i.e. an image on a line by itself). Only -- images with a caption attribute are interpreted as figures. figure :: PandocMonad m => OrgParser m (F Blocks) figure = try $ do figAttrs <- blockAttributes src <- skipSpaces *> selfTarget <* skipSpaces <* endOfParagraph case cleanLinkText src of Nothing -> mzero Just imgSrc -> do guard (isImageFilename imgSrc) let isFigure = isJust $ blockAttrCaption figAttrs return $ imageBlock isFigure figAttrs imgSrc where selfTarget :: PandocMonad m => OrgParser m Text selfTarget = try $ char '[' *> linkTarget <* char ']' imageBlock :: Bool -> BlockAttributes -> Text -> F Blocks imageBlock isFigure figAttrs imgSrc = let figName = fromMaybe mempty $ blockAttrName figAttrs figCaption = fromMaybe mempty $ blockAttrCaption figAttrs figKeyVals = blockAttrKeyValues figAttrs attr = (figName, mempty, figKeyVals) in if isFigure then (\c -> B.figureWith attr (B.simpleCaption (B.plain c)) (B.plain $ B.image imgSrc "" mempty)) <$> figCaption else B.para . B.imageWith attr imgSrc figName <$> figCaption -- | Succeeds if looking at the end of the current paragraph endOfParagraph :: Monad m => OrgParser m () endOfParagraph = try $ skipSpaces *> newline *> endOfBlock -- -- Examples -- -- | Example code marked up by a leading colon. example :: Monad m => OrgParser m (F Blocks) example = try $ returnF . exampleCode . T.unlines =<< many1 exampleLine where exampleLine :: Monad m => OrgParser m Text exampleLine = try $ exampleLineStart *> anyLine exampleCode :: Text -> Blocks exampleCode = B.codeBlockWith ("", [], []) -- -- Comments, Options and Metadata -- specialLine :: PandocMonad m => OrgParser m (F Blocks) specialLine = fmap return . try $ rawExportLine <|> printbibliographyLine <|> metaLine <|> commentLine printbibliographyLine :: PandocMonad m => OrgParser m Blocks printbibliographyLine = do try $ skipSpaces <* string "#+print_bibliography:" <* anyLine return $ B.divWith ("refs",[],[]) mempty -- | Include the content of a file. include :: PandocMonad m => OrgParser m (F Blocks) include = try $ do metaLineStart <* stringAnyCase "include:" <* skipSpaces filename <- includeTarget includeArgs <- many (try $ skipSpaces *> many1Char alphaNum) params <- keyValues blocksParser <- case includeArgs of ("example" : _) -> return $ pure . B.codeBlock <$> parseRaw ["export"] -> return . returnF $ B.fromList [] ["export", format] -> return $ pure . B.rawBlock format <$> parseRaw ("src" : rest) -> do let attr = case rest of [lang] -> (mempty, [lang], mempty) _ -> nullAttr return $ pure . B.codeBlockWith attr <$> parseRaw _ -> return $ return . B.fromList . blockFilter params <$> blockList currentDir <- takeDirectory . sourceName <$> getPosition let (startLine, endLine) = case lookup "lines" params of Nothing -> (Nothing, Nothing) Just bounds -> let boundStr = T.drop 1 (T.dropEnd 1 bounds) begStr = T.takeWhile (/= '-') boundStr endStr = T.takeWhileEnd (/= '-') boundStr in (safeRead begStr, pred <$> safeRead endStr) insertIncludedFile blocksParser toSources [currentDir] filename startLine endLine where includeTarget :: PandocMonad m => OrgParser m FilePath includeTarget = do char '"' manyTill (noneOf "\n\r\t") (char '"') parseRaw :: PandocMonad m => OrgParser m Text parseRaw = manyChar anyChar blockFilter :: [(Text, Text)] -> [Block] -> [Block] blockFilter params blks = let minlvl = lookup "minlevel" params in case (minlvl >>= safeRead :: Maybe Int) of Nothing -> blks Just lvl -> let levels = Walk.query headerLevel blks curMin = maybe 0 minimum $ nonEmpty levels in Walk.walk (shiftHeader (curMin - lvl)) blks headerLevel :: Block -> [Int] headerLevel (Header lvl _attr _content) = [lvl] headerLevel _ = [] shiftHeader :: Int -> Block -> Block shiftHeader shift blk = case blk of (Header lvl attr content) | lvl - shift > 0 -> Header (lvl - shift) attr content | otherwise -> Para content _ -> blk -- | Parses a meta line which defines a raw block. Currently recognized: -- @#+LATEX:@, @#+HTML:@, @#+TEXINFO:@, and @#+BEAMER@. rawExportLine :: PandocMonad m => OrgParser m Blocks rawExportLine = try $ do metaLineStart key <- metaKey if key `elem` ["latex", "html", "texinfo", "beamer"] then B.rawBlock key <$> anyLine else mzero -- | Parses any meta line, i.e., a line starting with @#+@, into a raw -- org block. This should be the last resort when trying to parse -- keywords. Leading spaces are discarded. rawOrgLine :: PandocMonad m => OrgParser m (F Blocks) rawOrgLine = do line <- metaLineStart *> anyLine returnF $ B.rawBlock "org" $ "#+" <> line commentLine :: Monad m => OrgParser m Blocks commentLine = commentLineStart *> anyLine $> mempty -- -- Tables -- data ColumnProperty = ColumnProperty { columnAlignment :: Maybe Alignment , columnRelWidth :: Maybe Int } deriving (Show, Eq) instance Default ColumnProperty where def = ColumnProperty Nothing Nothing data OrgTableRow = OrgContentRow (F [Blocks]) | OrgAlignRow [ColumnProperty] | OrgHlineRow -- OrgTable is strongly related to the pandoc table ADT. Using the same -- (i.e. pandoc-global) ADT would mean that the reader would break if the -- global structure was to be changed, which would be bad. The final table -- should be generated using a builder function. data OrgTable = OrgTable { orgTableColumnProperties :: [ColumnProperty] , orgTableHeader :: [Blocks] , orgTableRows :: [[Blocks]] } table :: PandocMonad m => OrgParser m (F Blocks) table = try $ do -- don't allow a table on the first line of a list item; org requires that -- tables start at first non-space character on the line let isFirstInListItem st = orgStateParserContext st == ListItemState && isNothing (orgStateLastPreCharPos st) guard . not . isFirstInListItem =<< getState blockAttrs <- blockAttributes let identMb = blockAttrName blockAttrs tbl <- gridTableWith blocks <|> orgTable withTables <- getExportSetting exportWithTables return $ if withTables then do xs <- unMany <$> tbl case F.toList xs of [Table _ _ cs th tb tf] -> do capt <- case blockAttrCaption blockAttrs of Nothing -> pure $ Caption Nothing [] Just ils -> do ils' <- ils pure $ B.simpleCaption . B.plain $ ils' let attr = (fromMaybe mempty identMb, [], blockAttrKeyValues blockAttrs) pure $ B.tableWith attr capt cs th tb tf _ -> tbl -- should not happen else mempty -- | A normal org table orgTable :: PandocMonad m => OrgParser m (F Blocks) orgTable = do lookAhead tableStart rows <- tableRows let orgTbl = normalizeTable <$> rowsToTable rows return $ orgToPandocTable <$> orgTbl orgToPandocTable :: OrgTable -> Blocks orgToPandocTable (OrgTable colProps heads lns) = let totalWidth = if any (isJust . columnRelWidth) colProps then Just . sum $ map (fromMaybe 1 . columnRelWidth) colProps else Nothing in B.tableWith nullAttr (Caption Nothing mempty) (map (convertColProp totalWidth) colProps) (TableHead nullAttr $ toHeaderRow heads) [TableBody nullAttr 0 [] $ map toRow lns] (TableFoot nullAttr []) where toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] convertColProp :: Maybe Int -> ColumnProperty -> (Alignment, ColWidth) convertColProp totalWidth colProp = let align' = fromMaybe AlignDefault $ columnAlignment colProp width' = (\w t -> fromIntegral w / fromIntegral t) <$> columnRelWidth colProp <*> totalWidth in (align', maybe ColWidthDefault ColWidth width') tableRows :: PandocMonad m => OrgParser m [OrgTableRow] tableRows = try $ many (tableAlignRow <|> tableHline <|> tableContentRow) tableContentRow :: PandocMonad m => OrgParser m OrgTableRow tableContentRow = try $ OrgContentRow . sequence <$> (tableStart *> manyTill tableContentCell newline) tableContentCell :: PandocMonad m => OrgParser m (F Blocks) tableContentCell = try $ fmap B.plain . trimInlinesF . mconcat <$> manyTill inline endOfCell tableAlignRow :: Monad m => OrgParser m OrgTableRow tableAlignRow = try $ do tableStart colProps <- many1Till columnPropertyCell newline -- Empty rows are regular (i.e. content) rows, not alignment rows. guard $ any (/= def) colProps return $ OrgAlignRow colProps columnPropertyCell :: Monad m => OrgParser m ColumnProperty columnPropertyCell = emptyOrgCell <|> propCell "alignment info" where emptyOrgCell = ColumnProperty Nothing Nothing <$ try (skipSpaces *> endOfCell) propCell = try $ ColumnProperty <$> (skipSpaces *> char '<' *> optionMaybe tableAlignFromChar) <*> (optionMaybe (many1Char digit >>= safeRead) <* char '>' <* emptyOrgCell) tableAlignFromChar :: Monad m => OrgParser m Alignment tableAlignFromChar = try $ choice [ char 'l' $> AlignLeft , char 'c' $> AlignCenter , char 'r' $> AlignRight ] tableHline :: Monad m => OrgParser m OrgTableRow tableHline = try $ OrgHlineRow <$ (tableStart *> char '-' *> anyLine) endOfCell :: Monad m => OrgParser m Char endOfCell = try $ char '|' <|> lookAhead newline rowsToTable :: [OrgTableRow] -> F OrgTable rowsToTable = foldM rowToContent emptyTable where emptyTable = OrgTable mempty mempty mempty normalizeTable :: OrgTable -> OrgTable normalizeTable (OrgTable colProps heads rows) = OrgTable colProps' heads rows where refRow = if heads /= mempty then heads else case rows of (r:_) -> r _ -> mempty cols = length refRow fillColumns base padding = take cols $ base ++ repeat padding colProps' = fillColumns colProps def -- One or more horizontal rules after the first content line mark the previous -- line as a header. All other horizontal lines are discarded. rowToContent :: OrgTable -> OrgTableRow -> F OrgTable rowToContent tbl row = case row of OrgHlineRow -> return singleRowPromotedToHeader OrgAlignRow props -> return . setProperties $ props OrgContentRow cs -> appendToBody cs where singleRowPromotedToHeader :: OrgTable singleRowPromotedToHeader = case tbl of OrgTable{ orgTableHeader = [], orgTableRows = [b] } -> tbl{ orgTableHeader = b , orgTableRows = [] } _ -> tbl setProperties :: [ColumnProperty] -> OrgTable setProperties ps = tbl{ orgTableColumnProperties = ps } appendToBody :: F [Blocks] -> F OrgTable appendToBody frow = do newRow <- frow let oldRows = orgTableRows tbl -- NOTE: This is an inefficient O(n) operation. This should be changed -- if performance ever becomes a problem. return tbl{ orgTableRows = oldRows ++ [newRow] } -- -- LaTeX fragments -- latexFragment :: PandocMonad m => OrgParser m (F Blocks) latexFragment = try $ do envName <- latexEnvStart guard $ envName `notElem` inlineEnvironmentNames texOpt <- getExportSetting exportWithLatex let envStart = "\\begin{" <> envName <> "}" let envEnd = "\\end{" <> envName <> "}" envContent <- do content <- manyTillChar anyChar (latexEnd envName) return $ envStart <> content <> envEnd returnF $ case texOpt of TeXExport -> B.rawBlock "latex" (envContent <> "\n") TeXIgnore -> mempty TeXVerbatim -> B.para . B.text $ envContent where latexEnd :: Monad m => Text -> OrgParser m () latexEnd envName = try . void $ textStr ("\\end{" <> envName <> "}") <* blankline -- -- Footnote definitions -- noteBlock :: PandocMonad m => OrgParser m (F Blocks) noteBlock = try $ do ref <- noteMarker <* skipSpaces <* updateLastPreCharPos content <- mconcat <$> many1Till block endOfFootnote addToNotesTable (ref, content) return mempty where endOfFootnote = eof <|> () <$ lookAhead noteMarker <|> () <$ lookAhead headerStart <|> () <$ lookAhead (try $ blankline *> blankline) -- Paragraphs or Plain text paraOrPlain :: PandocMonad m => OrgParser m (F Blocks) paraOrPlain = try $ do -- Make sure we are not looking at a headline notFollowedBy' headerStart ils <- inlines nl <- option False (newline $> True) -- Read block as paragraph, except if we are in a list context and the block -- is directly followed by a list item, in which case the block is read as -- plain text. try (guard nl *> notFollowedBy (inList *> (void orderedListStart <|> void bulletListStart)) $> (B.para <$> ils)) <|> return (B.plain <$> ils) -- -- list blocks -- list :: PandocMonad m => OrgParser m (F Blocks) list = choice [ definitionList, bulletList, orderedList ] "list" definitionList :: PandocMonad m => OrgParser m (F Blocks) definitionList = try $ do indent <- lookAhead bulletListStart fmap (B.definitionList . compactifyDL) . sequence <$> many1 (definitionListItem (bulletListStart `indented` indent)) bulletList :: PandocMonad m => OrgParser m (F Blocks) bulletList = try $ do indent <- lookAhead bulletListStart fmap (B.bulletList . compactify) . sequence <$> many1 (listItem (bulletListStart `indented` indent)) indented :: OrgParser m Int -> Int -> OrgParser m Int indented indentedMarker minIndent = try $ do n <- indentedMarker guard (minIndent <= n) return n orderedList :: PandocMonad m => OrgParser m (F Blocks) orderedList = try $ do (indent, attr) <- lookAhead orderedListStart fmap (B.orderedListWith attr . compactify) . sequence <$> many1 (listItem ((fst <$> orderedListStart) `indented` indent)) definitionListItem :: PandocMonad m => OrgParser m Int -> OrgParser m (F (Inlines, [Blocks])) definitionListItem parseIndentedMarker = try $ do markerLength <- parseIndentedMarker term <- manyTillChar (noneOf "\n\r") (try definitionMarker) line1 <- anyLineNewline blank <- option "" ("\n" <$ blankline) cont <- T.concat <$> many (listContinuation markerLength) term' <- parseFromString inlines term contents' <- parseFromString blocks $ line1 <> blank <> cont return $ (,) <$> term' <*> fmap (:[]) contents' where definitionMarker = spaceChar *> string "::" <* (spaceChar <|> lookAhead newline) -- | Checkbox for tasks. data Checkbox = UncheckedBox | CheckedBox | SemicheckedBox -- | Parses a checkbox in a plain list. checkbox :: PandocMonad m => OrgParser m Checkbox checkbox = do guardEnabled Ext_task_lists try (char '[' *> status <* char ']') "checkbox" where status = choice [ UncheckedBox <$ char ' ' , CheckedBox <$ char 'X' , SemicheckedBox <$ char '-' ] checkboxToInlines :: Checkbox -> Inline checkboxToInlines = B.Str . \case UncheckedBox -> "☐" SemicheckedBox -> "☐" CheckedBox -> "☒" -- | parse raw text for one list item listItem :: PandocMonad m => OrgParser m Int -> OrgParser m (F Blocks) listItem parseIndentedMarker = try . withContext ListItemState $ do markerLength <- try parseIndentedMarker box <- optionMaybe checkbox firstLine <- anyLineNewline blank <- option "" ("\n" <$ blankline) rest <- T.concat <$> many (listContinuation markerLength) contents <- parseFromString (do initial <- paraOrPlain <|> pure mempty subsequent <- blocks return $ initial <> subsequent) (firstLine <> blank <> rest) return (maybe id (prependInlines . checkboxToInlines) box <$> contents) -- | Prepend inlines to blocks, adding them to the first paragraph or -- creating a new Plain element if necessary. prependInlines :: Inline -> Blocks -> Blocks prependInlines inlns = B.fromList . prepend . B.toList where prepend (Plain is : bs) = Plain (inlns : Space : is) : bs prepend (Para is : bs) = Para (inlns : Space : is) : bs prepend bs = Plain [inlns, Space] : bs -- continuation of a list item - indented and separated by blankline or endline. -- Note: nested lists are parsed as continuations. listContinuation :: PandocMonad m => Int -> OrgParser m Text listContinuation markerLength = try $ do notFollowedBy' blankline mappend <$> (T.concat <$> many1 (listContinuation' markerLength)) <*> manyChar blankline where listContinuation' indentation = blockLines indentation <|> listLine indentation listLine indentation = try $ indentWith indentation *> anyLineNewline -- The block attributes and start must be appropriately indented, -- but the contents, and end do not. blockLines indentation = try $ lookAhead (indentWith indentation >> blockAttributes >>= (\blockAttrs -> case attrFromBlockAttributes blockAttrs of ("", [], []) -> countChar 1 anyChar _ -> indentWith indentation)) >> (snd <$> withRaw orgBlock) ================================================ FILE: src/Text/Pandoc/Readers/Org/DocumentTree.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.Readers.Org.DocumentTree Copyright : Copyright (C) 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Parsers for org-mode headlines and document subtrees -} module Text.Pandoc.Readers.Org.DocumentTree ( documentTree , unprunedHeadlineToBlocks ) where import Control.Arrow ((***), first) import Control.Monad (guard, mplus) import Data.List (intersperse) import Data.Maybe (mapMaybe) import Data.Text (Text) import Text.Pandoc.Builder (Blocks, Inlines) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Readers.Org.BlockStarts import Text.Pandoc.Readers.Org.ParserState import Text.Pandoc.Readers.Org.Parsing import qualified Data.Set as Set import qualified Data.Text as T import qualified Text.Pandoc.Builder as B -- -- Org headers -- -- | Parse input as org document tree. documentTree :: PandocMonad m => OrgParser m (F Blocks) -> OrgParser m (F Inlines) -> OrgParser m (F Headline) documentTree blocks inline = do many commentLine properties <- option mempty propertiesDrawer initialBlocks <- blocks headlines <- sequence <$> manyTill (headline blocks inline 1) eof title <- fmap docTitle . orgStateMeta <$> getState return $ do headlines' <- headlines initialBlocks' <- initialBlocks title' <- title return Headline { headlineLevel = 0 , headlineTodoMarker = Nothing , headlineText = B.fromList title' , headlineTags = mempty , headlinePlanning = emptyPlanning , headlineProperties = properties , headlineContents = initialBlocks' , headlineChildren = headlines' } where commentLine :: Monad m => OrgParser m () commentLine = commentLineStart <* anyLine -- | Create a tag containing the given string. toTag :: Text -> Tag toTag = Tag -- | The key (also called name or type) of a property. newtype PropertyKey = PropertyKey { fromKey :: Text } deriving (Show, Eq, Ord) -- | Create a property key containing the given string. Org mode keys are -- case insensitive and are hence converted to lower case. toPropertyKey :: Text -> PropertyKey toPropertyKey = PropertyKey . T.toLower -- | The value assigned to a property. newtype PropertyValue = PropertyValue { fromValue :: Text } -- | Create a property value containing the given string. toPropertyValue :: Text -> PropertyValue toPropertyValue = PropertyValue -- | Check whether the property value is non-nil (i.e. truish). isNonNil :: PropertyValue -> Bool isNonNil p = T.toLower (fromValue p) `notElem` ["()", "{}", "nil"] -- | Key/value pairs from a PROPERTIES drawer type Properties = [(PropertyKey, PropertyValue)] -- | Org mode headline (i.e. a document subtree). data Headline = Headline { headlineLevel :: Int , headlineTodoMarker :: Maybe TodoMarker , headlineText :: Inlines , headlineTags :: [Tag] , headlinePlanning :: PlanningInfo -- ^ subtree planning information , headlineProperties :: Properties , headlineContents :: Blocks , headlineChildren :: [Headline] } -- | Read an Org mode headline and its contents (i.e. a document subtree). -- @lvl@ gives the minimum acceptable level of the tree. headline :: PandocMonad m => OrgParser m (F Blocks) -> OrgParser m (F Inlines) -> Int -> OrgParser m (F Headline) headline blocks inline lvl = try $ do level <- headerStart guard (lvl <= level) todoKw <- optionMaybe todoKeyword (title, tags) <- manyThen inline endOfTitle planning <- option emptyPlanning planningInfo properties <- option mempty propertiesDrawer contents <- blocks children <- many (headline blocks inline (level + 1)) return $ do title' <- trimInlinesF (mconcat title) contents' <- contents children' <- sequence children return Headline { headlineLevel = level , headlineTodoMarker = todoKw , headlineText = title' , headlineTags = tags , headlinePlanning = planning , headlineProperties = properties , headlineContents = contents' , headlineChildren = children' } where endOfTitle :: Monad m => OrgParser m [Tag] endOfTitle = try $ do skipSpaces tags <- option [] (headerTags <* skipSpaces) newline return tags headerTags :: Monad m => OrgParser m [Tag] headerTags = try $ do char ':' endBy1 (toTag <$> orgTagWord) (char ':') manyThen :: Monad m => OrgParser m a -> OrgParser m b -> OrgParser m ([a], b) manyThen p end = (([],) <$> try end) <|> do x <- p first (x:) <$> manyThen p end -- titleFollowedByTags :: Monad m => OrgParser m (Inlines, [Tag]) -- titleFollowedByTags = do unprunedHeadlineToBlocks :: Monad m => Headline -> OrgParserState -> OrgParser m [Block] unprunedHeadlineToBlocks hdln st = let usingSelectedTags = docContainsSelectTags hdln st rootNode = if not usingSelectedTags then hdln else includeRootAndSelected hdln st rootNode' = removeExplicitlyExcludedNodes rootNode st in if not usingSelectedTags || any (`Set.member` orgStateSelectTags st) (headlineTags rootNode') then do headlineBlocks <- headlineToBlocks rootNode' -- add metadata from root node :PROPERTIES: updateState $ \s -> s{ orgStateMeta = foldr (\(PropertyKey k, PropertyValue v) m -> B.setMeta k v <$> m) (orgStateMeta s) (headlineProperties rootNode') } -- ignore first headline, it's the document's title return $ drop 1 $ B.toList headlineBlocks else do headlineBlocks <- mconcat <$> mapM headlineToBlocks (headlineChildren rootNode') return . B.toList $ headlineBlocks -- | Convert an Org mode headline (i.e. a document tree) into pandoc's Blocks headlineToBlocks :: Monad m => Headline -> OrgParser m Blocks headlineToBlocks hdln = do maxLevel <- getExportSetting exportHeadlineLevels let tags = headlineTags hdln let text = headlineText hdln let level = headlineLevel hdln case () of _ | any isArchiveTag tags -> archivedHeadlineToBlocks hdln _ | isCommentTitle text -> return mempty _ | maxLevel <= level -> headlineToHeaderWithList hdln _ | otherwise -> headlineToHeaderWithContents hdln removeExplicitlyExcludedNodes :: Headline -> OrgParserState -> Headline removeExplicitlyExcludedNodes hdln st = hdln { headlineChildren = [removeExplicitlyExcludedNodes childHdln st | childHdln <- headlineChildren hdln, not $ headlineContainsExcludeTags childHdln st] } includeRootAndSelected :: Headline -> OrgParserState -> Headline includeRootAndSelected hdln st = hdln { headlineChildren = mapMaybe (`includeAncestorsAndSelected` st) (headlineChildren hdln)} docContainsSelectTags :: Headline -> OrgParserState -> Bool docContainsSelectTags hdln st = headlineContainsSelectTags hdln st || any (`docContainsSelectTags` st) (headlineChildren hdln) includeAncestorsAndSelected :: Headline -> OrgParserState -> Maybe Headline includeAncestorsAndSelected hdln st = if headlineContainsSelectTags hdln st then Just hdln else let children = mapMaybe (`includeAncestorsAndSelected` st) (headlineChildren hdln) in case children of [] -> Nothing _ -> Just $ hdln { headlineChildren = children } headlineContainsSelectTags :: Headline -> OrgParserState -> Bool headlineContainsSelectTags hdln st = any (`Set.member` orgStateSelectTags st) (headlineTags hdln) headlineContainsExcludeTags :: Headline -> OrgParserState -> Bool headlineContainsExcludeTags hdln st = any (`Set.member` orgStateExcludeTags st) (headlineTags hdln) isArchiveTag :: Tag -> Bool isArchiveTag = (== toTag "ARCHIVE") -- | Check if the title starts with COMMENT. -- FIXME: This accesses builder internals not intended for use in situations -- like these. Replace once keyword parsing is supported. isCommentTitle :: Inlines -> Bool isCommentTitle inlns = case B.toList inlns of (Str "COMMENT":_) -> True _ -> False archivedHeadlineToBlocks :: Monad m => Headline -> OrgParser m Blocks archivedHeadlineToBlocks hdln = do archivedTreesOption <- getExportSetting exportArchivedTrees case archivedTreesOption of ArchivedTreesNoExport -> return mempty ArchivedTreesExport -> headlineToHeaderWithContents hdln ArchivedTreesHeadlineOnly -> headlineToHeader hdln headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks headlineToHeaderWithList hdln = do maxHeadlineLevels <- getExportSetting exportHeadlineLevels header <- headlineToHeader hdln listElements <- mapM headlineToBlocks (headlineChildren hdln) planningBlock <- planningToBlock (headlinePlanning hdln) let listBlock = if null listElements then mempty else B.orderedList listElements let headerText = if maxHeadlineLevels == headlineLevel hdln then header else flattenHeader header return . mconcat $ [ headerText , planningBlock , headlineContents hdln , listBlock ] where flattenHeader :: Blocks -> Blocks flattenHeader blks = case B.toList blks of (Header _ _ inlns:_) -> B.para (B.fromList inlns) _ -> mempty headlineToHeaderWithContents :: Monad m => Headline -> OrgParser m Blocks headlineToHeaderWithContents hdln = do header <- headlineToHeader hdln planningBlock <- planningToBlock (headlinePlanning hdln) childrenBlocks <- mconcat <$> mapM headlineToBlocks (headlineChildren hdln) return $ header <> planningBlock <> headlineContents hdln <> childrenBlocks headlineToHeader :: Monad m => Headline -> OrgParser m Blocks headlineToHeader hdln = do exportTodoKeyword <- getExportSetting exportWithTodoKeywords exportTags <- getExportSetting exportWithTags let todoText = if exportTodoKeyword then case headlineTodoMarker hdln of Just kw -> todoKeywordToInlines kw <> B.space Nothing -> mempty else mempty let text = todoText <> headlineText hdln <> if exportTags then tagsToInlines (headlineTags hdln) else mempty let propAttr = propertiesToAttr (headlineProperties hdln) attr <- registerHeader propAttr (headlineText hdln) return $ B.headerWith attr (headlineLevel hdln) text todoKeyword :: Monad m => OrgParser m TodoMarker todoKeyword = try $ do taskStates <- activeTodoMarkers <$> getState let kwParser tdm = try (tdm <$ textStr (todoMarkerName tdm) <* spaceChar <* updateLastPreCharPos) choice (map kwParser taskStates) todoKeywordToInlines :: TodoMarker -> Inlines todoKeywordToInlines tdm = let todoText = todoMarkerName tdm todoState = T.toLower . T.pack . show $ todoMarkerState tdm classes = [todoState, todoText] in B.spanWith (mempty, classes, mempty) (B.str todoText) propertiesToAttr :: Properties -> Attr propertiesToAttr properties = let toTextPair = fromKey *** fromValue customIdKey = toPropertyKey "custom_id" idKey = toPropertyKey "id" classKey = toPropertyKey "class" unnumberedKey = toPropertyKey "unnumbered" specialProperties = [customIdKey, idKey, classKey, unnumberedKey] id' = maybe mempty fromValue $ (lookup customIdKey properties `mplus` lookup idKey properties) cls = maybe mempty fromValue . lookup classKey $ properties kvs' = map toTextPair . filter ((`notElem` specialProperties) . fst) $ properties isUnnumbered = maybe False isNonNil . lookup unnumberedKey $ properties in (id', T.words cls ++ ["unnumbered" | isUnnumbered], kvs') tagsToInlines :: [Tag] -> Inlines tagsToInlines [] = mempty tagsToInlines tags = (B.space <>) . mconcat . intersperse (B.str "\160") . map tagToInline $ tags where tagToInline :: Tag -> Inlines tagToInline t = tagSpan t . B.smallcaps . B.str $ fromTag t -- | Wrap the given inline in a span, marking it as a tag. tagSpan :: Tag -> Inlines -> Inlines tagSpan t = B.spanWith ("", ["tag"], [("tag-name", fromTag t)]) -- | Render planning info as a block iff the respective export setting is -- enabled. planningToBlock :: Monad m => PlanningInfo -> OrgParser m Blocks planningToBlock planning = do includePlanning <- getExportSetting exportWithPlanning return $ if includePlanning then B.plain . mconcat . intersperse B.space . filter (/= mempty) $ [ datumInlines planningClosed "CLOSED" , datumInlines planningDeadline "DEADLINE" , datumInlines planningScheduled "SCHEDULED" ] else mempty where datumInlines field name = case field planning of Nothing -> mempty Just time -> B.strong (B.str name <> B.str ":") <> B.space <> B.emph (B.str time) -- | An Org timestamp, including repetition marks. TODO: improve type Timestamp = Text timestamp :: Monad m => OrgParser m Timestamp timestamp = try $ do openChar <- oneOf "<[" let isActive = openChar == '<' let closeChar = if isActive then '>' else ']' content <- many1TillChar anyChar (char closeChar) return $ T.cons openChar $ content `T.snoc` closeChar -- | Planning information for a subtree/headline. data PlanningInfo = PlanningInfo { planningClosed :: Maybe Timestamp , planningDeadline :: Maybe Timestamp , planningScheduled :: Maybe Timestamp } emptyPlanning :: PlanningInfo emptyPlanning = PlanningInfo Nothing Nothing Nothing -- | Read a single planning-related and timestamped line. planningInfo :: Monad m => OrgParser m PlanningInfo planningInfo = try $ do updaters <- many1 planningDatum <* skipSpaces <* newline return $ foldr ($) emptyPlanning updaters where planningDatum = skipSpaces *> choice [ updateWith (\s p -> p { planningScheduled = Just s}) "SCHEDULED" , updateWith (\d p -> p { planningDeadline = Just d}) "DEADLINE" , updateWith (\c p -> p { planningClosed = Just c}) "CLOSED" ] updateWith fn cs = fn <$> (string cs *> char ':' *> skipSpaces *> timestamp) -- | Read a :PROPERTIES: drawer and return the key/value pairs contained -- within. propertiesDrawer :: Monad m => OrgParser m Properties propertiesDrawer = try $ do drawerType <- drawerStart guard $ T.toUpper drawerType == "PROPERTIES" manyTill property (try endOfDrawer) where property :: Monad m => OrgParser m (PropertyKey, PropertyValue) property = try $ (,) <$> key <*> value key :: Monad m => OrgParser m PropertyKey key = fmap toPropertyKey . try $ skipSpaces *> char ':' *> many1TillChar nonspaceChar (try $ char ':' *> spaceChar) value :: Monad m => OrgParser m PropertyValue value = fmap toPropertyValue . try $ skipSpaces *> manyTillChar anyChar (try $ skipSpaces *> newline) endOfDrawer :: Monad m => OrgParser m Text endOfDrawer = try $ skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline ================================================ FILE: src/Text/Pandoc/Readers/Org/ExportSettings.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Org.ExportSettings Copyright : © 2016-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Parsers for Org-mode export options. -} module Text.Pandoc.Readers.Org.ExportSettings ( exportSettings ) where import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Logging (LogMessage (UnknownOrgExportOption)) import Text.Pandoc.Readers.Org.ParserState import Text.Pandoc.Readers.Org.Parsing import Control.Monad (mzero, void) import Data.Char (toLower) import Data.Maybe (listToMaybe) import Data.Text (Text, unpack) -- | Read and handle space separated org-mode export settings. exportSettings :: PandocMonad m => OrgParser m () exportSettings = void $ sepBy skipSpaces exportSetting -- | Setter function for export settings. type ExportSettingSetter a = a -> ExportSettings -> ExportSettings -- | Read and process a single org-mode export option. exportSetting :: PandocMonad m => OrgParser m () exportSetting = choice [ booleanSetting "^" (\val es -> es { exportSubSuperscripts = val }) , booleanSetting "'" (\val es -> es { exportSmartQuotes = val }) , booleanSetting "*" (\val es -> es { exportEmphasizedText = val }) , booleanSetting "-" (\val es -> es { exportSpecialStrings = val }) , ignoredSetting ":" , ignoredSetting "<" , booleanSetting "\\n" (\val es -> es { exportPreserveBreaks = val }) , archivedTreeSetting "arch" (\val es -> es { exportArchivedTrees = val }) , booleanSetting "author" (\val es -> es { exportWithAuthor = val }) , ignoredSetting "c" -- org-mode allows the special value `comment` for creator, which we'll -- interpret as true as it doesn't make sense in the context of Pandoc. , booleanSetting "creator" (\val es -> es { exportWithCreator = val }) , complementableListSetting "d" (\val es -> es { exportDrawers = val }) , ignoredSetting "date" , booleanSetting "e" (\val es -> es { exportWithEntities = val }) , booleanSetting "email" (\val es -> es { exportWithEmail = val }) , booleanSetting "f" (\val es -> es { exportWithFootnotes = val }) , integerSetting "H" (\val es -> es { exportHeadlineLevels = val }) , ignoredSetting "inline" , ignoredSetting "num" , booleanSetting "p" (\val es -> es { exportWithPlanning = val }) , ignoredSetting "pri" , ignoredSetting "prop" , ignoredSetting "stat" , booleanSetting "tags" (\val es -> es { exportWithTags = val }) , ignoredSetting "tasks" , texSetting "tex" (\val es -> es { exportWithLatex = val }) , ignoredSetting "timestamp" , ignoredSetting "title" , ignoredSetting "toc" , booleanSetting "todo" (\val es -> es { exportWithTodoKeywords = val }) , booleanSetting "|" (\val es -> es { exportWithTables = val }) , ignoreAndWarn ] "export setting" -- | Generic handler for export settings. Takes a parser which converts -- the plain option text into a data structure. genericExportSetting :: Monad m => OrgParser m a -> Text -> ExportSettingSetter a -> OrgParser m () genericExportSetting optionParser settingIdentifier setter = try $ do _ <- textStr settingIdentifier *> char ':' value <- optionParser updateState $ modifyExportSettings value where modifyExportSettings val st = st { orgStateExportSettings = setter val . orgStateExportSettings $ st } -- | A boolean option, either nil (False) or non-nil (True). booleanSetting :: Monad m => Text -> ExportSettingSetter Bool -> OrgParser m () booleanSetting = genericExportSetting elispBoolean -- | An integer-valued option. integerSetting :: Monad m => Text -> ExportSettingSetter Int -> OrgParser m () integerSetting = genericExportSetting parseInt where parseInt = try $ many1 digit >>= maybe mzero (return . fst) . listToMaybe . reads -- | Either the string "headline" or an elisp boolean and treated as an -- @ArchivedTreesOption@. archivedTreeSetting :: Monad m => Text -> ExportSettingSetter ArchivedTreesOption -> OrgParser m () archivedTreeSetting = genericExportSetting $ archivedTreesHeadlineSetting <|> archivedTreesBoolean where archivedTreesHeadlineSetting = ArchivedTreesHeadlineOnly <$ optionString "headline" archivedTreesBoolean = try $ do exportBool <- elispBoolean return $ if exportBool then ArchivedTreesExport else ArchivedTreesNoExport -- | A list or a complement list (i.e. a list starting with `not`). complementableListSetting :: Monad m => Text -> ExportSettingSetter (Either [Text] [Text]) -> OrgParser m () complementableListSetting = genericExportSetting $ choice [ Left <$> complementTextList , Right <$> stringList , (\b -> if b then Left [] else Right []) <$> elispBoolean ] where -- Read a plain list of strings. stringList :: Monad m => OrgParser m [Text] stringList = try $ char '(' *> sepBy elispText spaces <* char ')' -- Read an emacs lisp list specifying a complement set. complementTextList :: Monad m => OrgParser m [Text] complementTextList = try $ string "(not " *> sepBy elispText spaces <* char ')' elispText :: Monad m => OrgParser m Text elispText = try $ char '"' *> manyTillChar alphaNum (char '"') -- | Parses either @t@, @nil@, or @verbatim@ into a 'TeXExport' value. texSetting :: Monad m => Text -> ExportSettingSetter TeXExport -> OrgParser m () texSetting = genericExportSetting $ texVerbatim <|> texBoolean where texVerbatim = TeXVerbatim <$ optionString "verbatim" texBoolean = try $ do exportBool <- elispBoolean return $ if exportBool then TeXExport else TeXIgnore -- | Read but ignore the export setting. ignoredSetting :: Monad m => Text -> OrgParser m () ignoredSetting s = try (() <$ textStr s <* char ':' <* many1 nonspaceChar) -- | Read any setting string, but ignore it and emit a warning. ignoreAndWarn :: PandocMonad m => OrgParser m () ignoreAndWarn = try $ do opt <- many1Char nonspaceChar report (UnknownOrgExportOption opt) return () -- | Read an elisp boolean. Only NIL is treated as false, non-NIL values are -- interpreted as true. elispBoolean :: Monad m => OrgParser m Bool elispBoolean = try $ do value <- many1 nonspaceChar return $ case map toLower value of "nil" -> False "{}" -> False "()" -> False _ -> True -- | Try to parse a literal string as the option value. Returns the -- string on success. optionString :: Monad m => Text -> OrgParser m Text optionString s = try $ do _ <- string (unpack s) lookAhead (newline <|> spaceChar) return s ================================================ FILE: src/Text/Pandoc/Readers/Org/Inlines.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Org.Inlines Copyright : Copyright (C) 2014-2025 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Parsers for Org-mode inline elements. -} module Text.Pandoc.Readers.Org.Inlines ( inline , inlines , addToNotesTable , linkTarget ) where import Text.Pandoc.Readers.Org.BlockStarts (endOfBlock, noteMarker) import Text.Pandoc.Readers.Org.ParserState import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Readers.Org.Shared (cleanLinkText, isImageFilename, originalLang, translateLang, exportsCode) import Text.Pandoc.Builder (Inlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline) import Text.TeXMath (DisplayType (..), readTeX, writePandoc) import Text.Pandoc.Sources (ToSources(..)) import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap import Safe (lastMay) import Control.Monad (guard, mplus, mzero, unless, when, void) import Control.Monad.Trans (lift) import Data.Char (isAlphaNum, isSpace) import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T -- -- Functions acting on the parser state -- pushToInlineCharStack :: PandocMonad m => Char -> OrgParser m () pushToInlineCharStack c = updateState $ \s -> s{ orgStateEmphasisCharStack = c:orgStateEmphasisCharStack s } popInlineCharStack :: PandocMonad m => OrgParser m () popInlineCharStack = updateState $ \s -> s{ orgStateEmphasisCharStack = drop 1 . orgStateEmphasisCharStack $ s } surroundingEmphasisChar :: PandocMonad m => OrgParser m [Char] surroundingEmphasisChar = take 1 . drop 1 . orgStateEmphasisCharStack <$> getState startEmphasisNewlinesCounting :: PandocMonad m => Int -> OrgParser m () startEmphasisNewlinesCounting maxNewlines = updateState $ \s -> s{ orgStateEmphasisNewlines = Just maxNewlines } decEmphasisNewlinesCount :: PandocMonad m => OrgParser m () decEmphasisNewlinesCount = updateState $ \s -> s{ orgStateEmphasisNewlines = (\n -> n - 1) <$> orgStateEmphasisNewlines s } newlinesCountWithinLimits :: PandocMonad m => OrgParser m Bool newlinesCountWithinLimits = do st <- getState return $ ((< 0) <$> orgStateEmphasisNewlines st) /= Just True resetEmphasisNewlines :: PandocMonad m => OrgParser m () resetEmphasisNewlines = updateState $ \s -> s{ orgStateEmphasisNewlines = Nothing } addToNotesTable :: PandocMonad m => OrgNoteRecord -> OrgParser m () addToNotesTable note = do oldnotes <- orgStateNotes' <$> getState updateState $ \s -> s{ orgStateNotes' = note:oldnotes } -- | Parse a single Org-mode inline element inline :: PandocMonad m => OrgParser m (F Inlines) inline = choice [ whitespace , linebreak , cite , footnote , linkOrImage , anchor , inlineCodeBlock , str , endline , subscript -- takes precedence over underlined text , superscript , emphasizedText , code , math , displayMath , verbatim , inlineLaTeX , exportSnippet , macro , smartQuotes , specialStrings , symbol ] <* (guard =<< newlinesCountWithinLimits) "inline" -- | Read the rest of the input as inlines. inlines :: PandocMonad m => OrgParser m (F Inlines) inlines = trimInlinesF . mconcat <$> many1 inline -- treat these as potentially non-text when parsing inline: specialChars :: [Char] specialChars = "\"$'()*+-,./:;<=>@[\\]^_{|}~" whitespace :: PandocMonad m => OrgParser m (F Inlines) whitespace = pure B.space <$ skipMany1 spaceChar <* updateLastPreCharPos <* updateLastForbiddenCharPos "whitespace" linebreak :: PandocMonad m => OrgParser m (F Inlines) linebreak = try $ pure B.linebreak <$ string "\\\\" <* skipSpaces <* newline str :: PandocMonad m => OrgParser m (F Inlines) str = return . B.str <$> ( many1Char (noneOf $ specialChars ++ "\n\r ") >>= updatePositions' ) <* updateLastStrPos where updatePositions' str' = str' <$ maybe mzero (updatePositions . snd) (T.unsnoc str') -- | An endline character that can be treated as a space, not a structural -- break. This should reflect the values of the Emacs variable -- @org-element-pagaraph-separate@. endline :: PandocMonad m => OrgParser m (F Inlines) endline = try $ do newline notFollowedBy' endOfBlock decEmphasisNewlinesCount guard =<< newlinesCountWithinLimits updateLastPreCharPos useHardBreaks <- exportPreserveBreaks . orgStateExportSettings <$> getState returnF (if useHardBreaks then B.linebreak else B.softbreak) -- -- Citations -- -- We first try to parse official org-cite citations, then fall -- back to org-ref citations (which are still in wide use). -- | A citation in org-cite style orgCite :: PandocMonad m => OrgParser m (F [Citation]) orgCite = try $ do string "[cite" (sty, _variants) <- citeStyle char ':' spnl globalPref <- option mempty (try (citePrefix <* char ';')) items <- citeItems globalSuff <- option mempty (try (char ';' *> citeSuffix)) spnl char ']' return $ adjustCiteStyle sty . addPrefixToFirstItem globalPref . addSuffixToLastItem globalSuff $ items adjustCiteStyle :: CiteStyle -> (F [Citation]) -> (F [Citation]) adjustCiteStyle sty cs = do cs' <- cs case cs' of [] -> return [] (d:ds) -- TODO needs refinement -> case sty of TextStyle -> return $ d{ citationMode = AuthorInText , citationSuffix = dropWhile (== Space) (citationSuffix d)} : ds NoAuthorStyle -> return $ d{ citationMode = SuppressAuthor } : ds _ -> return (d:ds) addPrefixToFirstItem :: (F Inlines) -> (F [Citation]) -> (F [Citation]) addPrefixToFirstItem aff cs = do cs' <- cs aff' <- aff case cs' of [] -> return [] (d:ds) -> return (d{ citationPrefix = B.toList aff' <> citationPrefix d }:ds) addSuffixToLastItem :: (F Inlines) -> (F [Citation]) -> (F [Citation]) addSuffixToLastItem aff cs = do cs' <- cs aff' <- aff case lastMay cs' of Nothing -> return cs' Just d -> return (init cs' ++ [d{ citationSuffix = citationSuffix d <> B.toList aff' }]) citeItems :: PandocMonad m => OrgParser m (F [Citation]) citeItems = sequence <$> sepBy1' citeItem (char ';' <* void (many spaceChar)) citeItem :: PandocMonad m => OrgParser m (F Citation) citeItem = try $ do pref <- citePrefix itemKey <- orgCiteKey suff <- citeSuffix return $ do pre' <- pref suf' <- suff return Citation { citationId = itemKey , citationPrefix = B.toList pre' , citationSuffix = B.toList suf' , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } orgCiteKey :: PandocMonad m => OrgParser m Text orgCiteKey = do char '@' T.pack <$> many1 (satisfy orgCiteKeyChar) orgCiteKeyChar :: Char -> Bool orgCiteKeyChar c = isAlphaNum c || c `elem` ['.',':','?','!','`','\'','/','*','@','+','|', '(',')','{','}','<','>','&','_','^','$','#', '%','~','-'] rawAffix :: PandocMonad m => Bool -> OrgParser m Text rawAffix isPrefix = snd <$> withRaw (many (affixChar <|> try (void (char '[' >> rawAffix isPrefix >> char ']')))) where affixChar = void $ satisfy $ \c -> not (c == '^' || c == ';' || c == '[' || c == ']') && (not isPrefix || c /= '@') citePrefix :: PandocMonad m => OrgParser m (F Inlines) citePrefix = rawAffix True >>= parseFromString (trimInlinesF . mconcat <$> many inline) citeSuffix :: PandocMonad m => OrgParser m (F Inlines) citeSuffix = rawAffix False >>= parseFromString (mconcat <$> many inline) citeStyle :: PandocMonad m => OrgParser m (CiteStyle, [CiteVariant]) citeStyle = do sty <- option NilStyle $ try $ char '/' *> orgCiteStyle variants <- option [] $ try $ char '/' *> orgCiteVariants return (sty, variants) orgCiteStyle :: PandocMonad m => OrgParser m CiteStyle orgCiteStyle = try $ do s <- many1 letter case s of "author" -> pure AuthorStyle "a" -> pure AuthorStyle "noauthor" -> pure NoAuthorStyle "na" -> pure NoAuthorStyle "nocite" -> pure NociteStyle "n" -> pure NociteStyle "text" -> pure TextStyle "t" -> pure TextStyle "note" -> pure NoteStyle "ft" -> pure NoteStyle "numeric" -> pure NumericStyle "nb" -> pure NumericStyle "nil" -> pure NilStyle _ -> fail $ "Unknown org cite style " <> show s orgCiteVariants :: PandocMonad m => OrgParser m [CiteVariant] orgCiteVariants = (sepBy1' fullnameVariant (char '-')) <|> (many1 onecharVariant) where fullnameVariant = choice $ map try [ Bare <$ string "bare" , Caps <$ string "caps" , Full <$ string "full" ] onecharVariant = choice [ Bare <$ char 'b' , Caps <$ char 'c' , Full <$ char 'f' ] data CiteStyle = AuthorStyle | NoAuthorStyle | LocatorsStyle | NociteStyle | TextStyle | NoteStyle | NumericStyle | NilStyle deriving Show data CiteVariant = Caps | Bare | Full deriving Show spnl :: PandocMonad m => OrgParser m () spnl = skipSpaces *> optional (newline *> notFollowedBy blankline *> skipSpaces) cite :: PandocMonad m => OrgParser m (F Inlines) cite = do guardEnabled Ext_citations (cs, raw) <- withRaw $ try $ choice [ orgCite , orgRefCite ] return $ flip B.cite (B.text raw) <$> cs -- org-ref orgRefCite :: PandocMonad m => OrgParser m (F [Citation]) orgRefCite = try $ choice [ normalOrgRefCite , fmap (:[]) <$> linkLikeOrgRefCite ] normalOrgRefCite :: PandocMonad m => OrgParser m (F [Citation]) normalOrgRefCite = try $ do mode <- orgRefCiteMode firstCitation <- orgRefCiteList mode moreCitations <- many (try $ char ',' *> orgRefCiteList mode) return . sequence $ firstCitation : moreCitations where -- A list of org-ref style citation keys, parsed as citation of the given -- citation mode. orgRefCiteList :: PandocMonad m => CitationMode -> OrgParser m (F Citation) orgRefCiteList citeMode = try $ do key <- orgRefCiteKey returnF Citation { citationId = key , citationPrefix = mempty , citationSuffix = mempty , citationMode = citeMode , citationNoteNum = 0 , citationHash = 0 } -- | Read a link-like org-ref style citation. The citation includes pre and -- post text. However, multiple citations are not possible due to limitations -- in the syntax. linkLikeOrgRefCite :: PandocMonad m => OrgParser m (F Citation) linkLikeOrgRefCite = try $ do _ <- string "[[" mode <- orgRefCiteMode key <- orgRefCiteKey _ <- string "][" pre <- trimInlinesF . mconcat <$> manyTill inline (try $ string "::") spc <- option False (True <$ spaceChar) suf <- trimInlinesF . mconcat <$> manyTill inline (try $ string "]]") return $ do pre' <- pre suf' <- suf return Citation { citationId = key , citationPrefix = B.toList pre' , citationSuffix = B.toList (if spc then B.space <> suf' else suf') , citationMode = mode , citationNoteNum = 0 , citationHash = 0 } -- | Read a citation key. The characters allowed in citation keys are taken -- from the `org-ref-cite-re` variable in `org-ref.el`. orgRefCiteKey :: PandocMonad m => OrgParser m Text orgRefCiteKey = let citeKeySpecialChars = "-_:\\./" :: String isCiteKeySpecialChar c = c `elem` citeKeySpecialChars isCiteKeyChar c = isAlphaNum c || isCiteKeySpecialChar c endOfCitation = try $ do many $ satisfy isCiteKeySpecialChar satisfy $ not . isCiteKeyChar in try $ do optional (char '&') -- this is used in org-ref v3 satisfy isCiteKeyChar `many1TillChar` lookAhead endOfCitation -- | Supported citation types. Only a small subset of org-ref types is -- supported for now. TODO: rewrite this, use LaTeX reader as template. orgRefCiteMode :: PandocMonad m => OrgParser m CitationMode orgRefCiteMode = choice $ map (\(s, mode) -> mode <$ try (string s <* char ':')) [ ("cite", AuthorInText) , ("citep", NormalCitation) , ("citep*", NormalCitation) , ("citet", AuthorInText) , ("citet*", AuthorInText) , ("citeyear", SuppressAuthor) ] footnote :: PandocMonad m => OrgParser m (F Inlines) footnote = try $ do note <- inlineNote <|> referencedNote withNote <- getExportSetting exportWithFootnotes return $ if withNote then note else mempty inlineNote :: PandocMonad m => OrgParser m (F Inlines) inlineNote = try $ do string "[fn:" ref <- manyChar alphaNum char ':' note <- fmap B.para . trimInlinesF . mconcat <$> many1Till inline (char ']') unless (T.null ref) $ addToNotesTable ("fn:" <> ref, note) return $ B.note <$> note referencedNote :: PandocMonad m => OrgParser m (F Inlines) referencedNote = try $ do ref <- noteMarker return $ do notes <- asksF orgStateNotes' case lookup ref notes of Nothing -> return . B.str $ "[" <> ref <> "]" Just contents -> do st <- askF let contents' = runF contents st{ orgStateNotes' = [] } return $ B.note contents' linkOrImage :: PandocMonad m => OrgParser m (F Inlines) linkOrImage = explicitOrImageLink <|> selflinkOrImage <|> angleLink <|> plainLink "link or image" explicitOrImageLink :: PandocMonad m => OrgParser m (F Inlines) explicitOrImageLink = try $ do char '[' srcF <- applyCustomLinkFormat =<< possiblyEmptyLinkTarget descr <- enclosedRaw (char '[') (char ']') titleF <- parseFromString (mconcat <$> many inline) descr char ']' return $ do src <- srcF title <- titleF case cleanLinkText descr of Just imgSrc | isImageFilename imgSrc -> return . B.link src "" $ B.image imgSrc mempty mempty _ -> linkToInlinesF src title selflinkOrImage :: PandocMonad m => OrgParser m (F Inlines) selflinkOrImage = try $ do target <- char '[' *> linkTarget <* char ']' case cleanLinkText target of Nothing -> case T.uncons target of Just ('#', _) -> returnF $ B.link target "" (B.str target) _ -> return $ internalLink target (B.str target) Just nonDocTgt -> if isImageFilename nonDocTgt then returnF $ B.image nonDocTgt "" "" else returnF $ B.link nonDocTgt "" (B.str target) plainLink :: PandocMonad m => OrgParser m (F Inlines) plainLink = try $ do (orig, src) <- uri returnF $ B.link src "" (B.str orig) angleLink :: PandocMonad m => OrgParser m (F Inlines) angleLink = try $ do char '<' link <- plainLink char '>' return link linkTarget :: PandocMonad m => OrgParser m Text linkTarget = T.pack <$> enclosedByPair1 '[' ']' (noneOf "\n\r[]") possiblyEmptyLinkTarget :: PandocMonad m => OrgParser m Text possiblyEmptyLinkTarget = try linkTarget <|> ("" <$ string "[]") applyCustomLinkFormat :: Text -> OrgParser m (F Text) applyCustomLinkFormat link = do let (linkType, rest) = T.break (== ':') link return $ do formatter <- M.lookup linkType <$> asksF orgStateLinkFormatters return $ maybe link ($ T.drop 1 rest) formatter -- | Take a link and return a function which produces new inlines when given -- description inlines. linkToInlinesF :: Text -> Inlines -> F Inlines linkToInlinesF linkStr = case T.uncons linkStr of Nothing -> pure . B.link mempty "" -- wiki link (empty by convention) Just ('#', _) -> pure . B.link linkStr "" -- document-local fraction _ -> case cleanLinkText linkStr of Just extTgt -> return . B.link extTgt "" Nothing -> internalLink linkStr -- other internal link internalLink :: Text -> Inlines -> F Inlines internalLink link title = do ids <- asksF orgStateAnchorIds if link `elem` ids then return $ B.link ("#" <> link) "" title else let attr' = ("", ["spurious-link"] , [("target", link)]) in return $ B.spanWith attr' (B.emph title) -- | Parse an anchor like @<>@ and return an empty span with -- @anchor-id@ set as id. Legal anchors in org-mode are defined through -- @org-target-regexp@, which is fairly liberal. Since no link is created if -- @anchor-id@ contains spaces, we are more restrictive in what is accepted as -- an anchor. anchor :: PandocMonad m => OrgParser m (F Inlines) anchor = do anchorId <- orgAnchor returnF $ B.spanWith (solidify anchorId, [], []) mempty -- | Replace every char but [a-zA-Z0-9_.-:] with a hyphen '-'. This mirrors -- the org function @org-export-solidify-link-text@. solidify :: Text -> Text solidify = T.map replaceSpecialChar where replaceSpecialChar c | isAlphaNum c = c | c `elem` ("_.-:" :: String) = c | otherwise = '-' -- | Parses an inline code block and marks it as an babel block. inlineCodeBlock :: PandocMonad m => OrgParser m (F Inlines) inlineCodeBlock = try $ do string "src_" lang <- many1Char orgArgWordChar opts <- option [] $ enclosedByPair '[' ']' inlineBlockOption inlineCode <- T.pack <$> enclosedByPair1 '{' '}' (noneOf "\n\r") let attrClasses = [translateLang lang] let attrKeyVal = originalLang lang <> opts let codeInlineBlck = B.codeWith ("", attrClasses, attrKeyVal) inlineCode returnF $ if exportsCode opts then codeInlineBlck else mempty where inlineBlockOption :: PandocMonad m => OrgParser m (Text, Text) inlineBlockOption = try $ do argKey <- orgArgKey paramValue <- option "yes" orgInlineParamValue return (argKey, paramValue) orgInlineParamValue :: PandocMonad m => OrgParser m Text orgInlineParamValue = try $ skipSpaces *> notFollowedBy (char ':') *> many1Char (noneOf "\t\n\r ]") <* skipSpaces emphasizedText :: PandocMonad m => OrgParser m (F Inlines) emphasizedText = do state <- getState guard . exportEmphasizedText . orgStateExportSettings $ state try $ choice [ emph , strong , strikeout , underline ] enclosedByPair :: PandocMonad m => Char -- ^ opening char -> Char -- ^ closing char -> OrgParser m a -- ^ parser -> OrgParser m [a] enclosedByPair s e p = char s *> manyTill p (char e) enclosedByPair1 :: PandocMonad m => Char -- ^ opening char -> Char -- ^ closing char -> OrgParser m a -- ^ parser -> OrgParser m [a] enclosedByPair1 s e p = char s *> many1Till p (char e) emph :: PandocMonad m => OrgParser m (F Inlines) emph = fmap B.emph <$> emphasisBetween '/' strong :: PandocMonad m => OrgParser m (F Inlines) strong = fmap B.strong <$> emphasisBetween '*' strikeout :: PandocMonad m => OrgParser m (F Inlines) strikeout = fmap B.strikeout <$> emphasisBetween '+' underline :: PandocMonad m => OrgParser m (F Inlines) underline = fmap B.underline <$> emphasisBetween '_' verbatim :: PandocMonad m => OrgParser m (F Inlines) verbatim = return . B.codeWith ("", ["verbatim"], []) <$> verbatimBetween '=' code :: PandocMonad m => OrgParser m (F Inlines) code = return . B.code <$> verbatimBetween '~' -- | Returns 'True' if the parser position right after a string, and 'False' -- otherwise. isAfterString :: PandocMonad m => OrgParser m Bool isAfterString = do pos <- getPosition st <- getState pure $ getLastStrPos st == Just pos -- | Parses subscript markup. Subscripts must be preceded by a string. subscript :: PandocMonad m => OrgParser m (F Inlines) subscript = do guard =<< isAfterString fmap B.subscript <$> try (char '_' *> subOrSuperExpr) -- | Parses superscript markup. Superscript must be preceded by a string. superscript :: PandocMonad m => OrgParser m (F Inlines) superscript = do guard =<< isAfterString fmap B.superscript <$> try (char '^' *> subOrSuperExpr) math :: PandocMonad m => OrgParser m (F Inlines) math = return . B.math <$> choice [ math1CharBetween '$' , mathTextBetween '$' , rawMathBetween "\\(" "\\)" ] displayMath :: PandocMonad m => OrgParser m (F Inlines) displayMath = return . B.displayMath <$> choice [ rawMathBetween "\\[" "\\]" , rawMathBetween "$$" "$$" ] updatePositions :: PandocMonad m => Char -> OrgParser m Char updatePositions c = do st <- getState let emphasisPreChars = orgStateEmphasisPreChars st when (c `elem` emphasisPreChars) updateLastPreCharPos when (c `elem` emphasisForbiddenBorderChars) updateLastForbiddenCharPos return c symbol :: PandocMonad m => OrgParser m (F Inlines) symbol = return . B.str . T.singleton <$> (oneOf specialChars >>= updatePositions) emphasisBetween :: PandocMonad m => Char -> OrgParser m (F Inlines) emphasisBetween c = try $ do startEmphasisNewlinesCounting emphasisAllowedNewlines res <- enclosedInlines (emphasisStart c) (emphasisEnd c) isTopLevelEmphasis <- null . orgStateEmphasisCharStack <$> getState when isTopLevelEmphasis resetEmphasisNewlines return res verbatimBetween :: PandocMonad m => Char -> OrgParser m Text verbatimBetween c = newlinesToSpaces <$> try (emphasisStart c *> many1TillNOrLessNewlines 1 verbatimChar (emphasisEnd c)) where verbatimChar = noneOf "\n\r" >>= updatePositions newlinesToSpaces = T.map (\d -> if d == '\n' then ' ' else d) -- | Parses a raw string delimited by @c@ using Org's math rules mathTextBetween :: PandocMonad m => Char -> OrgParser m Text mathTextBetween c = try $ do mathStart c body <- many1TillNOrLessNewlines mathAllowedNewlines (noneOf (c:"\n\r")) (lookAhead $ mathEnd c) final <- mathEnd c return $ T.snoc body final -- | Parse a single character between @c@ using math rules math1CharBetween :: PandocMonad m => Char -> OrgParser m Text math1CharBetween c = try $ do char c res <- noneOf $ c:mathForbiddenBorderChars char c eof <|> () <$ lookAhead (oneOf mathPostChars) return $ T.singleton res rawMathBetween :: PandocMonad m => Text -> Text -> OrgParser m Text rawMathBetween s e = try $ textStr s *> manyTillChar anyChar (try $ textStr e) -- | Parses the start (opening character) of emphasis emphasisStart :: PandocMonad m => Char -> OrgParser m Char emphasisStart c = try $ do guard =<< afterEmphasisPreChar char c lookAhead (noneOf emphasisForbiddenBorderChars) pushToInlineCharStack c -- nested inlines are allowed, so mark this position as one which might be -- followed by another inline. updateLastPreCharPos return c -- | Parses the closing character of emphasis emphasisEnd :: PandocMonad m => Char -> OrgParser m Char emphasisEnd c = try $ do guard =<< notAfterForbiddenBorderChar char c eof <|> () <$ lookAhead acceptablePostChars updateLastStrPos popInlineCharStack return c where acceptablePostChars = do emphasisPostChars <- orgStateEmphasisPostChars <$> getState surroundingEmphasisChar >>= \x -> oneOf (x ++ emphasisPostChars) mathStart :: PandocMonad m => Char -> OrgParser m Char mathStart c = try $ char c <* notFollowedBy' (oneOf (c:mathForbiddenBorderChars)) mathEnd :: PandocMonad m => Char -> OrgParser m Char mathEnd c = try $ do res <- noneOf (c:mathForbiddenBorderChars) char c eof <|> () <$ lookAhead (oneOf mathPostChars) return res enclosedInlines :: (PandocMonad m, Show b) => OrgParser m a -> OrgParser m b -> OrgParser m (F Inlines) enclosedInlines start end = try $ trimInlinesF . mconcat <$> enclosed start end inline enclosedRaw :: (PandocMonad m, Show b) => OrgParser m a -> OrgParser m b -> OrgParser m Text enclosedRaw start end = try $ start *> (onSingleLine <|> spanningTwoLines) where onSingleLine = try $ many1TillChar (noneOf "\n\r") end spanningTwoLines = try $ anyLine >>= \f -> mappend (f <> " ") <$> onSingleLine -- | Like many1Till, but parses at most @n+1@ lines. @p@ must not consume -- newlines. many1TillNOrLessNewlines :: PandocMonad m => Int -> OrgParser m Char -> OrgParser m a -> OrgParser m Text many1TillNOrLessNewlines n p end = try $ nMoreLines (Just n) mempty >>= oneOrMore where nMoreLines Nothing cs = return cs nMoreLines (Just 0) cs = try $ (cs ++) <$> finalLine nMoreLines k cs = try $ (final k cs <|> rest k cs) >>= uncurry nMoreLines final _ cs = (\x -> (Nothing, cs ++ x)) <$> try finalLine rest m cs = (\x -> (minus1 <$> m, cs ++ x ++ "\n")) <$> try (manyTill p newline) finalLine = try $ manyTill p end minus1 k = k - 1 oneOrMore cs = T.pack cs <$ guard (not $ null cs) -- Org allows customization of the way it reads emphasis. We use the defaults -- here (see, e.g., the Emacs Lisp variable `org-emphasis-regexp-components` -- for details). -- | Chars not allowed at the (inner) border of emphasis emphasisForbiddenBorderChars :: [Char] emphasisForbiddenBorderChars = "\t\n\r \x200B" -- | The maximum number of newlines within emphasisAllowedNewlines :: Int emphasisAllowedNewlines = 1 -- LaTeX-style math: see `org-latex-regexps` for details -- | Chars allowed after an inline ($...$) math statement mathPostChars :: [Char] mathPostChars = "\t\n \"'),-.:;?" -- | Chars not allowed at the (inner) border of math mathForbiddenBorderChars :: [Char] mathForbiddenBorderChars = "\t\n\r ,;.$" -- | Maximum number of newlines in an inline math statement mathAllowedNewlines :: Int mathAllowedNewlines = 2 -- | Whether we are right behind a char allowed before emphasis afterEmphasisPreChar :: PandocMonad m => OrgParser m Bool afterEmphasisPreChar = do pos <- getPosition lastPrePos <- orgStateLastPreCharPos <$> getState return $ maybe True (== pos) lastPrePos -- | Whether the parser is right after a forbidden border char notAfterForbiddenBorderChar :: PandocMonad m => OrgParser m Bool notAfterForbiddenBorderChar = do pos <- getPosition lastFBCPos <- orgStateLastForbiddenCharPos <$> getState return $ lastFBCPos /= Just pos -- | Read a sub- or superscript expression subOrSuperExpr :: PandocMonad m => OrgParser m (F Inlines) subOrSuperExpr = try $ simpleSubOrSuperText <|> (choice [ charsInBalanced '{' '}' (T.singleton <$> noneOf "\n\r") , enclosing ('(', ')') <$> charsInBalanced '(' ')' (T.singleton <$> noneOf "\n\r") ] >>= parseFromString (mconcat <$> many inline)) where enclosing (left, right) s = T.cons left $ T.snoc s right simpleSubOrSuperText :: PandocMonad m => OrgParser m (F Inlines) simpleSubOrSuperText = try $ do state <- getState guard . exportSubSuperscripts . orgStateExportSettings $ state return . B.str <$> choice [ textStr "*" , mappend <$> option "" (T.singleton <$> oneOf "+-") <*> many1Char alphaNum ] inlineLaTeX :: PandocMonad m => OrgParser m (F Inlines) inlineLaTeX = try $ do cmd <- inlineLaTeXCommand texOpt <- getExportSetting exportWithLatex allowEntities <- getExportSetting exportWithEntities ils <- parseAsInlineLaTeX cmd texOpt maybe mzero returnF $ if "\\begin{" `T.isPrefixOf` cmd then ils else parseAsMathMLSym allowEntities cmd `mplus` parseAsMath cmd texOpt `mplus` ils where parseAsInlineLaTeX :: PandocMonad m => Text -> TeXExport -> OrgParser m (Maybe Inlines) parseAsInlineLaTeX cs = \case TeXExport -> maybeRight <$> runParserT (B.rawInline "latex" . snd <$> withRaw inlineCommand) state "" (toSources cs) TeXIgnore -> return (Just mempty) TeXVerbatim -> return (Just $ B.text cs) parseAsMathMLSym :: Bool -> Text -> Maybe Inlines parseAsMathMLSym allowEntities cs = do -- drop initial backslash and any trailing "{}" let clean = T.dropWhileEnd (`elem` ("{}" :: String)) . T.drop 1 -- If entities are disabled, then return the string as text, but -- only if this *is* a MathML entity. case B.str <$> MathMLEntityMap.getUnicode (clean cs) of Just _ | not allowEntities -> Just $ B.str cs x -> x state :: ParserState state = def{ stateOptions = def{ readerExtensions = enableExtension Ext_raw_tex (readerExtensions def) } } parseAsMath :: Text -> TeXExport -> Maybe Inlines parseAsMath cs = \case TeXExport -> maybeRight (readTeX cs) >>= fmap B.fromList . writePandoc DisplayInline TeXIgnore -> Just mempty TeXVerbatim -> Just $ B.str cs maybeRight :: Either a b -> Maybe b maybeRight = either (const Nothing) Just inlineLaTeXCommand :: PandocMonad m => OrgParser m Text inlineLaTeXCommand = try $ do rest <- getInput st <- getState parsed <- (lift . lift) $ runParserT rawLaTeXInline st "source" rest case parsed of Right cs -> do -- drop any trailing whitespace, those are not part of the command as -- far as org mode is concerned. let cmdNoSpc = T.dropWhileEnd isSpace cs let len = T.length cmdNoSpc count len anyChar return cmdNoSpc _ -> mzero exportSnippet :: PandocMonad m => OrgParser m (F Inlines) exportSnippet = try $ do string "@@" format <- many1TillChar (alphaNum <|> char '-') (char ':') snippet <- manyTillChar anyChar (try $ string "@@") returnF $ B.rawInline format snippet macro :: PandocMonad m => OrgParser m (F Inlines) macro = try $ do recursionDepth <- orgStateMacroDepth <$> getState guard $ recursionDepth < 15 string "{{{" name <- manyChar alphaNum args <- ([] <$ string "}}}") <|> char '(' *> argument `sepBy` char ',' <* eoa expander <- lookupMacro name <$> getState case expander of Nothing -> mzero Just fn -> do updateState $ \s -> s { orgStateMacroDepth = recursionDepth + 1 } res <- parseFromString (mconcat <$> many inline) $ fn args updateState $ \s -> s { orgStateMacroDepth = recursionDepth } return res where argument = manyChar $ notFollowedBy eoa *> (escapedComma <|> noneOf ",") escapedComma = try $ char '\\' *> oneOf ",\\" eoa = string ")}}}" smartQuotes :: PandocMonad m => OrgParser m (F Inlines) smartQuotes = do guard =<< getExportSetting exportSmartQuotes choice [doubleQuoted, singleQuoted, orgApostrophe] where orgApostrophe = do (char '\'' <|> char '\8217') <* updateLastPreCharPos <* updateLastForbiddenCharPos returnF (B.str "\x2019") specialStrings :: PandocMonad m => OrgParser m (F Inlines) specialStrings = do guard =<< getExportSetting exportSpecialStrings choice [orgDash, orgEllipses, shyHyphen] where shyHyphen = pure <$> (B.str "\173" <$ string "\\-") <* updatePositions '-' orgDash = pure <$> dash <* updatePositions '-' orgEllipses = pure <$> ellipses <* updatePositions '.' singleQuoted :: PandocMonad m => OrgParser m (F Inlines) singleQuoted = try $ do singleQuoteStart updatePositions '\'' withQuoteContext InSingleQuote $ fmap B.singleQuoted . trimInlinesF . mconcat <$> many1Till inline (singleQuoteEnd <* updatePositions '\'') -- doubleQuoted will handle regular double-quoted sections, as well -- as dialogues with an open double-quote without a close double-quote -- in the same paragraph. doubleQuoted :: PandocMonad m => OrgParser m (F Inlines) doubleQuoted = try $ do doubleQuoteStart updatePositions '"' contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline) let doubleQuotedContent = withQuoteContext InDoubleQuote $ do doubleQuoteEnd updateLastForbiddenCharPos return . fmap B.doubleQuoted . trimInlinesF $ contents let leftQuoteAndContent = return $ pure (B.str "\8220") <> contents doubleQuotedContent <|> leftQuoteAndContent ================================================ FILE: src/Text/Pandoc/Readers/Org/Meta.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Org.Meta Copyright : Copyright (C) 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Parsers for Org-mode meta declarations. -} module Text.Pandoc.Readers.Org.Meta ( metaExport , metaKey , metaLine ) where import Text.Pandoc.Readers.Org.BlockStarts import Text.Pandoc.Readers.Org.ExportSettings (exportSettings) import Text.Pandoc.Readers.Org.Inlines import Text.Pandoc.Readers.Org.ParserState import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Shared (blocksToInlines, safeRead) import Text.Pandoc.URI (urlEncode) import Control.Monad (mzero, void) import Data.List (intercalate, intersperse) import Data.Map (Map) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Map as Map import qualified Data.Set as Set import qualified Data.Text as T -- | Returns the current meta, respecting export options. metaExport :: Monad m => OrgParser m (F Meta) metaExport = do st <- getState let settings = orgStateExportSettings st return $ (if exportWithAuthor settings then id else removeMeta "author") . (if exportWithCreator settings then id else removeMeta "creator") . (if exportWithEmail settings then id else removeMeta "email") <$> orgStateMeta st removeMeta :: Text -> Meta -> Meta removeMeta key meta' = let metaMap = unMeta meta' in Meta $ Map.delete key metaMap -- | Parse and handle a single line containing meta information -- The order, in which blocks are tried, makes sure that we're not looking at -- the beginning of a block, so we don't need to check for it metaLine :: PandocMonad m => OrgParser m Blocks metaLine = try $ mempty <$ metaLineStart <* keywordLine keywordLine :: PandocMonad m => OrgParser m () keywordLine = try $ do key <- T.toLower <$> metaKey case Map.lookup key keywordHandlers of Nothing -> fail $ "Unknown keyword: " ++ T.unpack key Just hd -> hd metaKey :: Monad m => OrgParser m Text metaKey = T.toLower <$> many1Char (noneOf ": \n\r") <* char ':' <* skipSpaces infix 0 ~~> (~~>) :: a -> b -> (a, b) a ~~> b = (a, b) keywordHandlers :: PandocMonad m => Map Text (OrgParser m ()) keywordHandlers = Map.fromList [ "author" ~~> lineOfInlines `parseThen` collectLines "author" , "bibliography" ~~> fmap pure anyLine `parseThen` collectAsList "bibliography" , "creator" ~~> fmap pure anyLine `parseThen` B.setMeta "creator" , "date" ~~> lineOfInlines `parseThen` B.setMeta "date" , "description" ~~> lineOfInlines `parseThen` collectLines "description" , "email" ~~> fmap pure anyLine `parseThen` B.setMeta "email" , "exclude_tags" ~~> tagList >>= updateState . setExcludedTags , "header-includes" ~~> lineOfInlines `parseThen` collectLines "header-includes" -- HTML-specifix export settings , "html_head" ~~> metaExportSnippet "html" `parseThen` collectAsList "header-includes" , "html_head_extra" ~~> metaExportSnippet "html" `parseThen` collectAsList "header-includes" , "institute" ~~> lineOfInlines `parseThen` collectLines "institute" -- topic keywords , "keywords" ~~> lineOfInlines `parseThen` collectLines "keywords" -- document language , "language" ~~> fmap pure anyLine `parseThen` B.setMeta "lang" -- LaTeX-specific export settings , "latex_class" ~~> fmap pure anyLine `parseThen` B.setMeta "documentclass" , "latex_class_options" ~~> (pure . T.filter (`notElem` ("[]" :: String)) <$> anyLine) `parseThen` B.setMeta "classoption" , "latex_header" ~~> metaExportSnippet "latex" `parseThen` collectAsList "header-includes" , "latex_header_extra" ~~> metaExportSnippet "latex" `parseThen` collectAsList "header-includes" -- link and macro , "link" ~~> addLinkFormatter , "macro" ~~> macroDefinition >>= updateState . registerMacro -- pandoc-specific way to include references in the bibliography , "nocite" ~~> lineOfInlines `parseThen` collectLines "nocite" -- compact way to set export settings , "options" ~~> exportSettings -- pandoc-specific way to configure emphasis recognition , "pandoc-emphasis-post" ~~> emphChars >>= updateState . setEmphasisPostChar , "pandoc-emphasis-pre" ~~> emphChars >>= updateState . setEmphasisPreChar -- result markers (ignored) , "result" ~~> void anyLine , "select_tags" ~~> tagList >>= updateState . setSelectedTags , "seq_todo" ~~> todoSequence >>= updateState . registerTodoSequence , "subtitle" ~~> lineOfInlines `parseThen` collectLines "subtitle" , "title" ~~> lineOfInlines `parseThen` collectLines "title" , "todo" ~~> todoSequence >>= updateState . registerTodoSequence , "typ_todo" ~~> todoSequence >>= updateState . registerTodoSequence ] parseThen :: PandocMonad m => OrgParser m (F a) -> (a -> Meta -> Meta) -> OrgParser m () parseThen p modMeta = do value <- p meta <- orgStateMeta <$> getState updateState (\st -> st { orgStateMeta = modMeta <$> value <*> meta }) collectLines :: Text -> Inlines -> Meta -> Meta collectLines key value meta = let value' = appendValue meta (B.toList value) in B.setMeta key value' meta where appendValue :: Meta -> [Inline] -> MetaValue appendValue m v = MetaInlines $ curInlines m <> v curInlines m = case collectInlines <$> lookupMeta key m of Nothing -> [] Just [] -> [] Just xs -> xs <> [B.SoftBreak] collectInlines :: MetaValue -> [Inline] collectInlines = \case MetaInlines inlns -> inlns MetaList ml -> intercalate [B.SoftBreak] $ map collectInlines ml MetaString s -> [B.Str s] MetaBlocks blks -> blocksToInlines blks MetaMap _map -> [] MetaBool _bool -> [] -- | Accumulate the result as a MetaList under the given key. collectAsList :: B.ToMetaValue a => Text -> a -> Meta -> Meta collectAsList key value meta = let value' = metaListAppend meta (B.toMetaValue value) in B.setMeta key value' meta where metaListAppend m v = MetaList (curList m ++ [v]) curList m = case lookupMeta key m of Just (MetaList ms) -> ms Just x -> [x] _ -> [] -- | Read an format specific meta definition metaExportSnippet :: Monad m => Text -> OrgParser m (F Inlines) metaExportSnippet format = pure . B.rawInline format <$> anyLine -- | Parse a link type definition (like @wp https://en.wikipedia.org/wiki/@). addLinkFormatter :: Monad m => OrgParser m () addLinkFormatter = try $ do linkType <- T.cons <$> letter <*> manyChar (alphaNum <|> oneOf "-_") <* skipSpaces formatter <- parseFormat updateState $ \s -> let fs = orgStateLinkFormatters s in s{ orgStateLinkFormatters = Map.insert linkType formatter fs } -- | An ad-hoc, single-argument-only implementation of a printf-style format -- parser. parseFormat :: Monad m => OrgParser m (Text -> Text) parseFormat = try $ replacePlain <|> replaceUrl <|> justAppend where -- inefficient replacePlain = try $ (\x -> T.concat . flip intersperse x) <$> sequence [tillSpecifier 's', rest] replaceUrl = try $ (\x -> T.concat . flip intersperse x . urlEncode) <$> sequence [tillSpecifier 'h', rest] justAppend = try $ (<>) <$> rest rest = manyTillChar anyChar (eof <|> () <$ oneOf "\n\r") tillSpecifier c = manyTillChar (noneOf "\n\r") (try $ string ('%':c:"")) tagList :: Monad m => OrgParser m [Tag] tagList = do skipSpaces map Tag <$> many (orgTagWord <* skipSpaces) <* newline setExcludedTags :: [Tag] -> OrgParserState -> OrgParserState setExcludedTags tags st = let finalSet = if orgStateExcludeTagsChanged st then foldr Set.insert (orgStateExcludeTags st) tags else Set.fromList tags in st { orgStateExcludeTags = finalSet, orgStateExcludeTagsChanged = True } setSelectedTags :: [Tag] -> OrgParserState -> OrgParserState setSelectedTags tags st = let finalSet = if orgStateSelectTagsChanged st then foldr Set.insert (orgStateSelectTags st) tags else Set.fromList tags in st { orgStateSelectTags = finalSet, orgStateSelectTagsChanged = True } setEmphasisPreChar :: Maybe [Char] -> OrgParserState -> OrgParserState setEmphasisPreChar csMb st = let preChars = fromMaybe (orgStateEmphasisPreChars defaultOrgParserState) csMb in st { orgStateEmphasisPreChars = preChars } setEmphasisPostChar :: Maybe [Char] -> OrgParserState -> OrgParserState setEmphasisPostChar csMb st = let postChars = fromMaybe (orgStateEmphasisPostChars defaultOrgParserState) csMb in st { orgStateEmphasisPostChars = postChars } -- | Parses emphasis border character like @".,?!"@ emphChars :: Monad m => OrgParser m (Maybe [Char]) emphChars = do skipSpaces safeRead <$> anyLine lineOfInlines :: PandocMonad m => OrgParser m (F Inlines) lineOfInlines = do updateLastPreCharPos trimInlinesF . mconcat <$> manyTill inline newline -- | Parses ToDo sequences / keywords like @TODO DOING | DONE@. todoSequence :: Monad m => OrgParser m TodoSequence todoSequence = try $ do todoKws <- todoKeywords doneKws <- optionMaybe $ todoDoneSep *> doneKeywords newline -- There must be at least one DONE keyword. The last TODO keyword is -- taken if necessary. case doneKws of Just done -> return $ keywordsToSequence todoKws done Nothing -> case reverse todoKws of [] -> mzero -- no keywords present (x:xs) -> return $ keywordsToSequence (reverse xs) [x] where todoKeyword :: Monad m => OrgParser m Text todoKeyword = do keyword <- many1Char nonspaceChar let cleanKeyword = T.takeWhile (/= '(') keyword skipSpaces return cleanKeyword todoKeywords :: Monad m => OrgParser m [Text] todoKeywords = try $ let endOfKeywords = todoDoneSep <|> void newline in manyTill todoKeyword (lookAhead endOfKeywords) doneKeywords :: Monad m => OrgParser m [Text] doneKeywords = try $ manyTill (todoKeyword <* optional todoDoneSep) (lookAhead newline) todoDoneSep :: Monad m => OrgParser m () todoDoneSep = void . try $ skipSpaces *> char '|' <* skipSpaces1 keywordsToSequence :: [Text] -> [Text] -> TodoSequence keywordsToSequence todo done = let todoMarkers = map (TodoMarker Todo) todo doneMarkers = map (TodoMarker Done) done in todoMarkers ++ doneMarkers macroDefinition :: Monad m => OrgParser m (Text, [Text] -> Text) macroDefinition = try $ do macroName <- many1Char nonspaceChar <* skipSpaces firstPart <- expansionPart (elemOrder, parts) <- unzip <$> many ((,) <$> placeholder <*> expansionPart) let expander = mconcat . alternate (firstPart:parts) . reorder elemOrder return (macroName, expander) where placeholder :: Monad m => OrgParser m Int placeholder = try . fmap (fromMaybe 1 . safeRead) $ char '$' *> many1Char digit expansionPart :: Monad m => OrgParser m Text expansionPart = try $ manyChar (notFollowedBy placeholder *> noneOf "\n\r") alternate :: [a] -> [a] -> [a] alternate [] ys = ys alternate xs [] = xs alternate (x:xs) (y:ys) = x : y : alternate xs ys reorder :: [Int] -> [Text] -> [Text] reorder perm xs = let element n = take 1 $ drop (n - 1) xs in concatMap element perm ================================================ FILE: src/Text/Pandoc/Readers/Org/ParserState.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Org.ParserState Copyright : Copyright (C) 2014-2025 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Define the Org-mode parser state. -} module Text.Pandoc.Readers.Org.ParserState ( OrgParserState (..) , defaultOrgParserState , OrgParserLocal (..) , OrgNoteRecord , Tag(..) , HasReaderOptions (..) , HasQuoteContext (..) , HasMacros (..) , TodoMarker (..) , TodoSequence , TodoState (..) , activeTodoMarkers , registerTodoSequence , MacroExpander , lookupMacro , registerMacro , F , askF , asksF , trimInlinesF , runF , returnF , ExportSettings (..) , ArchivedTreesOption (..) , TeXExport (..) , optionsToParserState ) where import Control.Monad.Reader (ReaderT, asks, local) import Data.Default (Default (..)) import qualified Data.Map as M import qualified Data.Set as Set import Data.Text (Text) import Text.Pandoc.Builder (Blocks) import Text.Pandoc.Definition (Meta (..), nullMeta) import Text.Pandoc.Logging import Text.Pandoc.Options (ReaderOptions (..), Extension(..), isEnabled) import Text.Pandoc.Parsing (Future, HasIdentifierList (..), HasIncludeFiles (..), HasLastStrPosition (..), HasLogMessages (..), HasMacros (..), HasQuoteContext (..), HasReaderOptions (..), ParserContext (..), QuoteContext (..), SourcePos, askF, asksF, returnF, runF, trimInlinesF) import Text.Pandoc.TeX (Macro) -- | This is used to delay evaluation until all relevant information has been -- parsed and made available in the parser state. type F = Future OrgParserState -- | An inline note / footnote containing the note key and its (inline) value. type OrgNoteRecord = (Text, F Blocks) -- | Table of footnotes type OrgNoteTable = [OrgNoteRecord] -- | Map of functions for link transformations. The map key is refers to the -- link-type, the corresponding function transforms the given link string. type OrgLinkFormatters = M.Map Text (Text -> Text) -- | Macro expander function type MacroExpander = [Text] -> Text -- | Tag newtype Tag = Tag { fromTag :: Text } deriving (Show, Eq, Ord) -- | The states in which a todo item can be data TodoState = Todo | Done deriving (Eq, Ord, Show) -- | A ToDo keyword like @TODO@ or @DONE@. data TodoMarker = TodoMarker { todoMarkerState :: TodoState , todoMarkerName :: Text } deriving (Show, Eq) -- | Collection of todo markers in the order in which items should progress type TodoSequence = [TodoMarker] -- | Org-mode parser state data OrgParserState = OrgParserState { orgStateAnchorIds :: [Text] , orgStateEmphasisCharStack :: [Char] , orgStateEmphasisPreChars :: [Char] -- ^ Chars allowed to occur before -- emphasis; spaces and newlines are -- always ok in addition to what is -- specified here. , orgStateEmphasisPostChars :: [Char] -- ^ Chars allowed at after emphasis , orgStateEmphasisNewlines :: Maybe Int , orgStateExcludeTags :: Set.Set Tag , orgStateExcludeTagsChanged :: Bool , orgStateExportSettings :: ExportSettings , orgStateIdentifiers :: Set.Set Text , orgStateIncludeFiles :: [Text] , orgStateLastForbiddenCharPos :: Maybe SourcePos , orgStateLastPreCharPos :: Maybe SourcePos , orgStateLastStrPos :: Maybe SourcePos , orgStateLinkFormatters :: OrgLinkFormatters , orgStateMacros :: M.Map Text MacroExpander , orgStateMacroDepth :: Int , orgStateMeta :: F Meta , orgStateNotes' :: OrgNoteTable , orgStateOptions :: ReaderOptions , orgStateParserContext :: ParserContext , orgStateSelectTags :: Set.Set Tag , orgStateSelectTagsChanged :: Bool , orgStateTodoSequences :: [TodoSequence] , orgStateTrimLeadBlkIndent :: Bool , orgLogMessages :: [LogMessage] , orgMacros :: M.Map Text Macro } newtype OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext } instance Default OrgParserLocal where def = OrgParserLocal NoQuote instance HasReaderOptions OrgParserState where extractReaderOptions = orgStateOptions instance HasLastStrPosition OrgParserState where getLastStrPos = orgStateLastStrPos setLastStrPos pos st = st{ orgStateLastStrPos = pos } instance Monad m => HasQuoteContext st (ReaderT OrgParserLocal m) where getQuoteContext = asks orgLocalQuoteContext withQuoteContext q = local (\s -> s{orgLocalQuoteContext = q}) instance HasIdentifierList OrgParserState where extractIdentifierList = orgStateIdentifiers updateIdentifierList f s = s{ orgStateIdentifiers = f (orgStateIdentifiers s) } instance HasLogMessages OrgParserState where addLogMessage msg st = st{ orgLogMessages = msg : orgLogMessages st } getLogMessages st = reverse $ orgLogMessages st instance HasMacros OrgParserState where extractMacros st = orgMacros st updateMacros f st = st{ orgMacros = f (orgMacros st) } instance HasIncludeFiles OrgParserState where getIncludeFiles = orgStateIncludeFiles addIncludeFile f st = st { orgStateIncludeFiles = f : orgStateIncludeFiles st } dropLatestIncludeFile st = st { orgStateIncludeFiles = drop 1 $ orgStateIncludeFiles st } instance Default OrgParserState where def = defaultOrgParserState defaultOrgParserState :: OrgParserState defaultOrgParserState = OrgParserState { orgStateAnchorIds = [] , orgStateEmphasisPreChars = "-\t ('\"{\x200B" , orgStateEmphasisPostChars = "-\t\n .,:!?;'\")}[\x200B" , orgStateEmphasisCharStack = [] , orgStateEmphasisNewlines = Nothing , orgStateExportSettings = def , orgStateExcludeTags = Set.singleton $ Tag "noexport" , orgStateExcludeTagsChanged = False , orgStateIdentifiers = Set.empty , orgStateIncludeFiles = [] , orgStateLastForbiddenCharPos = Nothing , orgStateLastPreCharPos = Nothing , orgStateLastStrPos = Nothing , orgStateLinkFormatters = M.empty , orgStateMacros = M.empty , orgStateMacroDepth = 0 , orgStateMeta = return nullMeta , orgStateNotes' = [] , orgStateOptions = def , orgStateParserContext = NullState , orgStateSelectTags = Set.singleton $ Tag "export" , orgStateSelectTagsChanged = False , orgStateTrimLeadBlkIndent = True , orgStateTodoSequences = [] , orgLogMessages = [] , orgMacros = M.empty } optionsToParserState :: ReaderOptions -> OrgParserState optionsToParserState opts = let exportSettings = defaultExportSettings { exportSmartQuotes = isEnabled Ext_smart opts || isEnabled Ext_smart_quotes opts , exportSpecialStrings = isEnabled Ext_smart opts || isEnabled Ext_special_strings opts } in def { orgStateOptions = opts , orgStateExportSettings = exportSettings } registerTodoSequence :: TodoSequence -> OrgParserState -> OrgParserState registerTodoSequence todoSeq st = let curSeqs = orgStateTodoSequences st in st{ orgStateTodoSequences = todoSeq : curSeqs } -- | Get the current todo/done sequences. If no custom todo sequences have been -- defined, return a list containing just the default todo/done sequence. activeTodoSequences :: OrgParserState -> [TodoSequence] activeTodoSequences st = let curSeqs = orgStateTodoSequences st in if null curSeqs then [[ TodoMarker Todo "TODO" , TodoMarker Done "DONE" ]] else curSeqs activeTodoMarkers :: OrgParserState -> TodoSequence activeTodoMarkers = concat . activeTodoSequences lookupMacro :: Text -> OrgParserState -> Maybe MacroExpander lookupMacro macroName = M.lookup macroName . orgStateMacros registerMacro :: (Text, MacroExpander) -> OrgParserState -> OrgParserState registerMacro (name, expander) st = let curMacros = orgStateMacros st in st{ orgStateMacros = M.insert name expander curMacros } -- -- Export Settings -- -- | Options for the way archived trees are handled. data ArchivedTreesOption = ArchivedTreesExport -- ^ Export the complete tree | ArchivedTreesNoExport -- ^ Exclude archived trees from exporting | ArchivedTreesHeadlineOnly -- ^ Export only the headline, discard the contents -- | Options for the handling of LaTeX environments and fragments. -- Represents allowed values of Emacs variable @org-export-with-latex@. data TeXExport = TeXExport -- ^ Include raw TeX in the output | TeXIgnore -- ^ Ignore raw TeX | TeXVerbatim -- ^ Keep everything in verbatim -- | Export settings -- These settings can be changed via OPTIONS statements. data ExportSettings = ExportSettings { exportArchivedTrees :: ArchivedTreesOption -- ^ How to treat archived trees , exportDrawers :: Either [Text] [Text] -- ^ Specify drawer names which should be exported. @Left@ names are -- explicitly excluded from the resulting output while @Right@ means that -- only the listed drawer names should be included. , exportEmphasizedText :: Bool -- ^ Parse emphasized text , exportHeadlineLevels :: Int -- ^ Maximum depth of headlines, deeper headlines are convert to list , exportPreserveBreaks :: Bool -- ^ Whether to preserve linebreaks , exportSmartQuotes :: Bool -- ^ Parse quotes smartly , exportSpecialStrings :: Bool -- ^ Parse ellipses and dashes smartly , exportSubSuperscripts :: Bool -- ^ TeX-like syntax for sub- and superscripts , exportWithAuthor :: Bool -- ^ Include author in final meta-data , exportWithCreator :: Bool -- ^ Include creator in final meta-data , exportWithEmail :: Bool -- ^ Include email in final meta-data , exportWithEntities :: Bool -- ^ Include MathML-like entities , exportWithFootnotes :: Bool -- ^ Include footnotes , exportWithLatex :: TeXExport -- ^ Handling of raw TeX commands , exportWithPlanning :: Bool -- ^ Keep planning info after headlines , exportWithTags :: Bool -- ^ Keep tags as part of headlines , exportWithTables :: Bool -- ^ Include tables , exportWithTodoKeywords :: Bool -- ^ Keep TODO keywords in headers } instance Default ExportSettings where def = defaultExportSettings defaultExportSettings :: ExportSettings defaultExportSettings = ExportSettings { exportArchivedTrees = ArchivedTreesHeadlineOnly , exportDrawers = Left ["LOGBOOK"] , exportEmphasizedText = True , exportHeadlineLevels = 3 , exportPreserveBreaks = False , exportSmartQuotes = False , exportSpecialStrings = True , exportSubSuperscripts = True , exportWithAuthor = True , exportWithCreator = True , exportWithEmail = True , exportWithEntities = True , exportWithFootnotes = True , exportWithLatex = TeXExport , exportWithPlanning = False , exportWithTags = True , exportWithTables = True , exportWithTodoKeywords = True } ================================================ FILE: src/Text/Pandoc/Readers/Org/Parsing.hs ================================================ {- | Module : Text.Pandoc.Readers.Org.Parsing Copyright : Copyright (C) 2014-2025 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Org-mode parsing utilities. Most functions are simply re-exports from @Text.Pandoc.Parsing@, some functions are adapted to Org-mode specific functionality. -} module Text.Pandoc.Readers.Org.Parsing ( OrgParser , anyLine , anyLineNewline , indentWith , blanklines , newline , parseFromString , skipSpaces1 , inList , withContext , getExportSetting , updateLastForbiddenCharPos , updateLastPreCharPos , orgArgKey , orgArgWord , orgArgWordChar , orgTagWord , orgTagWordChar , orgAnchor -- * Re-exports from Text.Pandoc.Parser , ParserContext (..) , textStr , countChar , manyChar , many1Char , manyTillChar , many1Till , many1TillChar , notFollowedBy' , sepBy1' , spaceChar , nonspaceChar , skipSpaces , blankline , enclosed , stringAnyCase , charsInBalanced , uri , withRaw , readWithM , guardEnabled , updateLastStrPos , notAfterString , getLastStrPos , ParserState (..) , registerHeader , QuoteContext (..) , singleQuoteStart , singleQuoteEnd , doubleQuoteStart , doubleQuoteEnd , dash , ellipses , citeKey , gridTableWith , insertIncludedFile , runParser , runParserT , getInput , char , letter , digit , alphaNum , skipMany1 , spaces , anyChar , satisfy , string , count , eof , noneOf , oneOf , lookAhead , notFollowedBy , many , many1 , manyTill , (<|>) , () , choice , try , sepBy , sepBy1 , sepEndBy1 , endBy1 , option , optional , optionMaybe , getState , updateState , SourcePos , sourceName , getPosition ) where import Data.Text (Text) import Text.Pandoc.Readers.Org.ParserState import Text.Pandoc.Parsing hiding (anyLine, blanklines, newline, parseFromString) import qualified Text.Pandoc.Parsing as P import Control.Monad (guard) import Control.Monad.Reader (ReaderT) -- | The parser used to read org files. type OrgParser m = ParsecT Sources OrgParserState (ReaderT OrgParserLocal m) -- -- Adaptions and specializations of parsing utilities -- -- | Parse any line of text anyLine :: Monad m => OrgParser m Text anyLine = P.anyLine <* updateLastPreCharPos <* updateLastForbiddenCharPos -- | Like @'Text.Pandoc.Parsing'@, but resets the position of the last character -- allowed before emphasised text. parseFromString :: Monad m => OrgParser m a -> Text -> OrgParser m a parseFromString parser str' = do updateState $ \s -> s{ orgStateLastPreCharPos = Nothing } result <- P.parseFromString parser str' updateState $ \s -> s { orgStateLastPreCharPos = Nothing } return result -- | Skip one or more tab or space characters. skipSpaces1 :: Monad m => OrgParser m () skipSpaces1 = skipMany1 spaceChar -- | Like @Text.Parsec.Char.newline@, but causes additional state changes. newline :: Monad m => OrgParser m Char newline = P.newline <* updateLastPreCharPos <* updateLastForbiddenCharPos -- | Like @Text.Parsec.Char.blanklines@, but causes additional state changes. blanklines :: Monad m => OrgParser m Text blanklines = P.blanklines <* updateLastPreCharPos <* updateLastForbiddenCharPos -- | Succeeds when we're in list context. inList :: Monad m => OrgParser m () inList = do ctx <- orgStateParserContext <$> getState guard (ctx == ListItemState) -- | Parse in different context withContext :: Monad m => ParserContext -- ^ New parser context -> OrgParser m a -- ^ Parsec to run in that context -> OrgParser m a withContext context parser = do oldContext <- orgStateParserContext <$> getState updateState $ \s -> s{ orgStateParserContext = context } result <- parser updateState $ \s -> s{ orgStateParserContext = oldContext } return result -- -- Parsec state functions -- -- | Get an export setting. getExportSetting :: Monad m => (ExportSettings -> a) -> OrgParser m a getExportSetting s = s . orgStateExportSettings <$> getState -- | Set the current position as the last position at which a forbidden char -- was found (i.e. a character which is not allowed at the inner border of -- markup). updateLastForbiddenCharPos :: Monad m => OrgParser m () updateLastForbiddenCharPos = getPosition >>= \p -> updateState $ \s -> s{ orgStateLastForbiddenCharPos = Just p} -- | Set the current parser position as the position at which a character was -- seen which allows inline markup to follow. updateLastPreCharPos :: Monad m => OrgParser m () updateLastPreCharPos = getPosition >>= \p -> updateState $ \s -> s{ orgStateLastPreCharPos = Just p} -- -- Org key-value parsing -- -- | Read the key of a plist style key-value list. orgArgKey :: Monad m => OrgParser m Text orgArgKey = try $ skipSpaces *> char ':' *> many1Char orgArgWordChar -- | Read the value of a plist style key-value list. orgArgWord :: Monad m => OrgParser m Text orgArgWord = many1Char orgArgWordChar -- | Chars treated as part of a word in plists. orgArgWordChar :: Monad m => OrgParser m Char orgArgWordChar = alphaNum <|> oneOf "-_" orgTagWord :: Monad m => OrgParser m Text orgTagWord = many1Char orgTagWordChar orgTagWordChar :: Monad m => OrgParser m Char orgTagWordChar = alphaNum <|> oneOf "@%#_" orgAnchor :: Monad m => OrgParser m Text orgAnchor = try $ do string "<<" anchorId <- many1Char (noneOf "\t\n\r<>\"' ") string ">>" skipSpaces recordAnchorId anchorId return anchorId recordAnchorId :: Monad m => Text -> OrgParser m () recordAnchorId i = updateState $ \s -> s{ orgStateAnchorIds = i : orgStateAnchorIds s } ================================================ FILE: src/Text/Pandoc/Readers/Org/Shared.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Org.Shared Copyright : Copyright (C) 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Utility functions used in other Pandoc Org modules. -} module Text.Pandoc.Readers.Org.Shared ( cleanLinkText , isImageFilename , originalLang , translateLang , exportsCode ) where import Control.Applicative ((<|>)) import Data.Char (isAlphaNum) import Data.Text (Text) import qualified Data.Text as T import System.FilePath (isValid, takeExtension) import qualified System.FilePath.Posix as Posix import qualified System.FilePath.Windows as Windows -- | Check whether the given string looks like the path to of URL of an image. isImageFilename :: Text -> Bool isImageFilename fp = hasImageExtension && (isValid (T.unpack fp) || isKnownProtocolUri) where hasImageExtension = takeExtension (T.unpack $ T.toLower fp) `elem` imageExtensions isKnownProtocolUri = any (\x -> (x <> "://") `T.isPrefixOf` fp) protocols imageExtensions = [ ".jpeg", ".jpg", ".png", ".gif", ".svg", ".webp", ".jxl", ".avif" ] protocols = [ "file", "http", "https" ] -- | Cleanup and canonicalize a string describing a link. Return @Nothing@ if -- the string does not appear to be a link. cleanLinkText :: Text -> Maybe Text cleanLinkText s | Just f <- toFileSchema s = Just f -- absolute path | Just _ <- T.stripPrefix "./" s = Just s -- relative path | Just _ <- T.stripPrefix "../" s = Just s -- relative path -- Relative path or URL (file schema) | Just s' <- T.stripPrefix "file:" s = if "//" `T.isPrefixOf` s' then Just s else toFileSchema s' <|> Just s' | isUrl s = Just s | otherwise = Nothing where toFileSchema :: Text -> Maybe Text toFileSchema t | Windows.isAbsolute (T.unpack t) = Just ("file:///" <> t) | Posix.isAbsolute (T.unpack t) = Just ("file://" <> t) | otherwise = Nothing isUrl :: Text -> Bool isUrl cs = let (scheme, path) = T.break (== ':') cs in T.all (\c -> isAlphaNum c || T.any (== c) ".-") scheme && not (T.null path) -- | Creates an key-value pair marking the original language name specified for -- a piece of source code. -- | Creates an key-value attributes marking the original language name -- specified for a piece of source code. originalLang :: Text -> [(Text, Text)] originalLang lang = let transLang = translateLang lang in [("org-language", lang) | transLang /= lang] -- | Translate from Org-mode's programming language identifiers to those used -- by Pandoc. This is useful to allow for proper syntax highlighting in -- Pandoc output. translateLang :: Text -> Text translateLang cs = case cs of "C" -> "c" "C++" -> "cpp" "emacs-lisp" -> "commonlisp" -- emacs lisp is not supported "js" -> "javascript" "lisp" -> "commonlisp" "R" -> "r" "sh" -> "bash" "sqlite" -> "sql" _ -> cs exportsCode :: [(Text, Text)] -> Bool exportsCode = maybe True (`elem` ["code", "both"]) . lookup "exports" ================================================ FILE: src/Text/Pandoc/Readers/Org.hs ================================================ {- | Module : Text.Pandoc.Readers.Org Copyright : Copyright (C) 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Conversion of org-mode formatted plain text to 'Pandoc' document. -} module Text.Pandoc.Readers.Org ( readOrg ) where import Text.Pandoc.Readers.Org.Blocks (blockList, meta) import Text.Pandoc.Readers.Org.ParserState (optionsToParserState) import Text.Pandoc.Readers.Org.Parsing (OrgParser, readWithM) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing (reportLogMessages) import Text.Pandoc.Sources (ToSources(..), ensureFinalNewlines) import Control.Monad.Except (throwError) import Control.Monad.Reader (runReaderT) -- | Parse org-mode string and return a Pandoc document. readOrg :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options -> a -> m Pandoc readOrg opts s = do parsed <- flip runReaderT def $ readWithM parseOrg (optionsToParserState opts) (ensureFinalNewlines 2 (toSources s)) case parsed of Right result -> return result Left e -> throwError e -- -- Parser -- parseOrg :: PandocMonad m => OrgParser m Pandoc parseOrg = do blocks' <- blockList meta' <- meta reportLogMessages return $ Pandoc meta' blocks' ================================================ FILE: src/Text/Pandoc/Readers/Pod.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Readers.Pod Copyright : © 2024 Evan Silberman License : GNU GPL, version 2 or above Maintainer : Evan Silberman Stability : WIP Portability : portable Conversion of Pod to 'Pandoc' documents -} module Text.Pandoc.Readers.Pod (readPod) where import Control.Monad (void) import Control.Monad.Except (throwError) import Data.Char (isAsciiUpper, digitToInt) import Data.Default (Default) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Parsing.General (isSpaceChar) import Text.Pandoc.XML (lookupEntity) import Text.Pandoc.Class.PandocMonad (PandocMonad(..)) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Error import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B import qualified Data.Text as T import qualified Data.Text.Read as TR import Text.Pandoc.Shared (stringify, textToIdentifier, tshow) import Data.Set (Set) import Data.Functor (($>)) import Data.Maybe (listToMaybe, fromMaybe) import Numeric (readOct) data PodState = PodState { logMessages :: [LogMessage] , headerIds :: Set T.Text , options :: ReaderOptions } deriving (Show) instance HasLogMessages PodState where addLogMessage msg st = st{ logMessages = msg : logMessages st } getLogMessages st = reverse $ logMessages st instance HasIdentifierList PodState where extractIdentifierList = headerIds updateIdentifierList f st = st{headerIds = f (headerIds st)} instance HasReaderOptions PodState where extractReaderOptions = options instance Default PodState where def = PodState { logMessages = [] , headerIds = mempty , options = def } data PodLinkDestination = LinkUrl Inlines T.Text | LinkMan Inlines (Maybe Inlines) | LinkInternal Inlines deriving (Show) defaultLinkName :: PodLinkDestination -> Inlines defaultLinkName (LinkUrl inl _) = inl defaultLinkName (LinkMan nm (Just sec)) = B.doubleQuoted sec <> " in " <> nm defaultLinkName (LinkMan nm Nothing) = nm defaultLinkName (LinkInternal sec) = B.doubleQuoted sec type PodParser m = ParsecT Sources PodState m readPod :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readPod _ s = do let sources = toSources s p <- readWithM parsePod def sources case p of Right result -> return result Left e -> throwError e parsePod :: PandocMonad m => PodParser m Pandoc parsePod = do -- We don't actually start processing Pod until we encounter a Pod command. -- If we never encounter a Pod command, the document is still valid Pod, it -- just contains no content. notPod bs <- manyTill block eof reportLogMessages return $ B.doc $ mconcat bs block :: PandocMonad m => PodParser m Blocks block = verbatim <|> paragraph <|> command "Pod paragraph" command :: PandocMonad m => PodParser m Blocks command = do try (char '=' >> notFollowedBy (string "item" <|> string "back" <|> string "end")) header <|> pod <|> cut <|> over <|> for <|> begin <|> encoding "Pod command" cmd :: PandocMonad m => T.Text -> PodParser m () cmd nm = do textStr nm notFollowedBy nonspaceChar void $ many spaceChar encoding :: PandocMonad m => PodParser m Blocks encoding = do cmd "encoding" anyLine optional blanklines logMessage $ IgnoredElement "=encoding; Pandoc requires UTF-8 input" return mempty header :: PandocMonad m => PodParser m Blocks header = do string "head" dig <- oneOf "123456" void blankline <|> skipMany1 spaceChar ins <- inlines attrs <- registerHeader B.nullAttr ins optional blanklines return $ B.headerWith attrs (digitToInt dig) ins pod :: PandocMonad m => PodParser m Blocks pod = do cmd "pod" optional (try inlines) optional blanklines return mempty cut :: PandocMonad m => PodParser m Blocks cut = cmd "cut" *> notPod notPod :: PandocMonad m => PodParser m Blocks notPod = do manyTill anyLine (eof <|> void (try (lookAhead (char '=' *> letter)))) return mempty over :: PandocMonad m => PodParser m Blocks over = do cmd "over" anyLine blanklines optional $ try (char '=' *> cut) bs <- list <|> blockquote string "=back" <* blanklines return bs list :: PandocMonad m => PodParser m Blocks list = try bulletList <|> try orderedList <|> definitionList bulletList :: PandocMonad m => PodParser m Blocks bulletList = B.bulletList <$> many1 (item (many spaceChar *> optional (char '*'))) orderedList :: PandocMonad m => PodParser m Blocks orderedList = do start <- item1 more <- many orderedItem return $ B.orderedList (start : more) where item1 = item $ spaces *> char '1' *> optional (char '.') orderedItem = item $ spaces *> many digit *> optional (char '.') item :: PandocMonad m => PodParser m () -> PodParser m Blocks item p = do try (cmd "=item") p blanklines mconcat <$> many block "runaway item" definitionList :: PandocMonad m => PodParser m Blocks definitionList = B.definitionList <$> many1 dlItem where dlItem = do try (cmd "=item") spaces term <- inlines blanklines -- perlpodspec sez the /section part of a link can refer to either -- a header or a dl item, hence treating it as a "header" here attrs <- registerHeader B.nullAttr term defn <- mconcat <$> many block "runaway dlitem" return (B.spanWith attrs term, [defn]) blockquote :: PandocMonad m => PodParser m Blocks blockquote = B.blockQuote . mconcat <$> many block "runaway blockquote" paragraph :: PandocMonad m => PodParser m Blocks paragraph = do try (notFollowedBy (char '=' *> letter)) inl <- inlines optional blanklines return $ B.para $ B.trimInlines inl inlines :: PandocMonad m => PodParser m Inlines inlines = mconcat <$> many1 (format <|> whitespace <|> str) -- perlpodspec sez: -- If a Pod processor sees any formatting code other than the ones listed, -- that processor must by default treat this as an error. format :: PandocMonad m => PodParser m Inlines format = try $ do ctrl <- satisfy isAsciiUpper p <- getPosition lookAhead (char '<') case ctrl of 'B' -> B.strong <$> argument 'C' -> B.code . stringify <$> argument 'F' -> B.spanWith (mempty, ["filename"], mempty) <$> argument 'I' -> B.emph <$> argument 'S' -> argument -- TODO map nbsps 'X' -> argument $> mempty 'Z' -> argument $> mempty 'E' -> do a <- stringify <$> argument case entity a of -- per spec: -- Pod parsers, when faced with some unknown "E" code, -- shouldn't simply replace it with nullstring (by default, at -- least), but may pass it through as a string consisting of the -- literal characters E, less-than, identifier, greater-than. Nothing -> do logMessage $ SkippedContent ("unknown entity " <> a) p return $ B.str $ "E<" <> a <> ">" Just e -> return $ B.str e 'L' -> link x -> throwError $ PandocParseError $ T.snoc "unknown Pod formatting code " x where argument = try expandedArg <|> compactArg "argument" innerStr = B.str <$> many1Char (podCharLess ">") compactArg = do char '<' mconcat <$> manyTill (format <|> whitespace <|> innerStr) (char '>') expandedArg = do openLen <- length <$> many1 (char '<') let close = T.pack $ replicate openLen '>' skipMany1 spaceChar <|> void blankline arg <- mconcat <$> many (format <|> try (whitespace <* notFollowedBy (textStr close)) <|> str) many1 spaceChar textStr close return arg -- Some legacy entity names are required to be parsed by Pod formatters oct = listToMaybe . readOct @Integer entity "apos" = Just "'" entity "sol" = Just "/" entity "verbar" = Just "|" entity "lchevron" = Just "«" entity "rchevron" = Just "»" entity (T.stripPrefix "0x" -> Just suf) = lookupEntity $ "#x" <> suf entity (T.stripPrefix "0" -> Just suf) | Just (n, "") <- oct (T.unpack suf) = lookupEntity $ "#" <> tshow n entity (TR.decimal @Integer -> Right (x, "")) = lookupEntity $ "#" <> tshow x -- named entities in Commonmark.Entity de facto have to be looked up with -- the semicolon at the end. perlpodspec says arguments to E<> must be -- alphanumeric, so an argument that already has a trailing semicolon -- is bogus anyway, so just paste the semicolon on unconditionally. entity x = lookupEntity (x <> ";") -- god knows there must be a higher order way of writing this thing, where we -- have multiple different possible parser states within the link argument -- varying depending on whether the link is expanded or not, but at least I -- understand what I've done. This would be less wacky with a lexing step. link :: PandocMonad m => PodParser m Inlines link = do identifier <- textToIdentifier <$> getOption readerExtensions (name, dest) <- try expandedLinkArg <|> compactLinkArg return $ mkLink identifier name dest where compactLinkArg = do char '<' name <- linkName whitespace ">" dest <- linkDest whitespace (char '>') ">" char '>' return (mconcat <$> name, dest) expandedLinkArg = do openLen <- length <$> many1 (char '<') let closeStr = textStr (T.pack $ replicate openLen '>') let close = skipMany1 spaceChar *> closeStr let sp = try $ many1 spaceChar *> notFollowedBy closeStr $> B.space many1 spaceChar name <- linkName sp "" dest <- linkDest sp close "" close return (mconcat <$> name, dest) mkLink identifier name dest = let name' = fromMaybe (defaultLinkName dest) name in case dest of LinkUrl _ href -> B.link href "" name' LinkMan nm Nothing -> B.linkWith (mempty, mempty, [("manual", stringify nm)]) "" "" name' LinkMan nm (Just sc) -> B.linkWith (mempty, mempty, [("manual", stringify nm), ("section", stringify sc)]) "" "" name' LinkInternal sc -> B.link ("#" <> identifier (stringify sc)) "" name' linkName sp ex = optionMaybe $ try $ many (try format <|> sp <|> B.str <$> many1Char (podCharLess ('|':ex))) <* char '|' linkDest sp close ex = try (url ex) <|> internal sp close ex <|> man sp close ex -- perlpodspec sez: -- Note that you can distinguish URL-links from anything else by the -- fact that they match m/\A\w+:[^:\s]\S*\z/. -- This is obviously not an RFC-compliant matcher for a URI scheme, but -- this is what the specification and the canonical implementation (Pod::Simple) -- do for deciding that a link target "looks like" a URL, as opposed to a -- manual page reference, so what we are doing here is roughly equivalent -- even though it is nonsense url ex = do scheme <- many1Char (letter <|> digit <|> char '_') colon <- T.singleton <$> char ':' <* notFollowedBy (char ':') rst <- many (format <|> B.str <$> many1Char (podCharLess ex)) return $ LinkUrl (B.str scheme <> B.str colon <> mconcat rst) (scheme <> colon <> stringify rst) quotedSection sp close ex = do let mystr = B.str <$> many1Char (podCharLess ('\"':ex) <|> try (char '"' <* notFollowedBy close)) char '"' ins <- mconcat <$> many1 (format <|> sp <|> mystr) char '"' return ins section sp close ex = try (quotedSection sp close ex) <|> mconcat <$> many1 (format <|> sp <|> B.str <$> many1Char (podCharLess ex)) internal sp close ex = do char '/' LinkInternal <$> section sp close ex notSlash sp ex = format <|> sp <|> B.str <$> many1Char (podCharLess ('/':ex)) man sp close ex = do page <- mconcat <$> many (notSlash sp ex) sec <- optionMaybe $ char '/' *> section sp close ex return $ LinkMan page sec whitespace :: PandocMonad m => PodParser m Inlines whitespace = try $ do many1 spaceChar *> optional newline <|> many spaceChar *> void newline notFollowedBy blankline return B.space podCharLess :: PandocMonad m => String -> PodParser m Char podCharLess exclude = try (satisfy isAsciiUpper <* notFollowedBy (char '<')) <|> satisfy (\c -> not (isSpaceChar c || isAsciiUpper c || elem c exclude)) podChar :: PandocMonad m => PodParser m Char podChar = try (satisfy isAsciiUpper <* notFollowedBy (char '<')) <|> satisfy (\c -> not (isSpaceChar c || isAsciiUpper c)) str :: PandocMonad m => PodParser m Inlines str = B.str <$> many1Char podChar nonEmptyLine :: PandocMonad m => PodParser m T.Text nonEmptyLine = try $ do pre <- manyChar spaceChar something <- T.singleton <$> nonspaceChar post <- anyLineNewline return $ pre <> something <> post verbatim :: PandocMonad m => PodParser m Blocks verbatim = do start <- startVerbatimLine lns <- many (nonEmptyLine <|> try (do b <- blanklines l <- startVerbatimLine return $ b <> l)) optional blanklines return $ B.codeBlock $ mconcat $ start:lns where startVerbatimLine = many1Char spaceChar <> nonEmptyLine -- =begin/=end/=for and data paragraphs -- The =begin/=end (and single-paragraph =for variant) markers in Pod are -- designed as an extension point for specific formatters -- -- this doesn't strictly match the intent of "=begin :ident" pod blocks, which -- are still meant to be processed specially by the formatter, and only land in -- the output upon request, i.e. pod2html will process "=begin :html" blocks as -- Pod and include them in the regular output. Since the regions contain Pod -- markup it seems to me that the best thing to do is parse the markup and put -- a classname on it, allowing users to respond as desired with filters. -- Pandoc doesn't have a built-in concept of parsed Divs that are only rendered -- to certain formats, just raw blocks. -- -- perlpodspec allows nesting of =begin/=end regions but we currently don't -- because it would be annoying and we have something somewhat useful we -- can do with these blocks which is treat them as RawBlocks, which matches -- the intent reasonably well, and that gets weirder if we parse a nested -- structure. It seems unlikely this would be encountered in the wild. regionIdentifier :: PandocMonad m => PodParser m T.Text regionIdentifier = many1Char (alphaNum <|> oneOf "-_") for :: PandocMonad m => PodParser m Blocks for = do string "for" many1 spaceChar forDiv <|> forData forDiv :: PandocMonad m => PodParser m Blocks forDiv = do char ':' cls <- regionIdentifier many1 spaceChar B.divWith (mempty, [cls], mempty) <$> paragraph forData :: PandocMonad m => PodParser m Blocks forData = do fmt <- regionIdentifier ln1 <- anyLineNewline lns <- many nonEmptyLine optional blanklines return $ B.rawBlock fmt (T.concat (ln1 : lns)) begin :: PandocMonad m => PodParser m Blocks begin = do cmd "begin" beginDiv <|> beginData beginDiv :: PandocMonad m => PodParser m Blocks beginDiv = do char ':' cls <- regionIdentifier anyLine -- "parameters" may appear in this position blanklines bs <- mconcat <$> many block textStr ("=end :" <> cls) <* blanklines return $ B.divWith (mempty, [cls], mempty) bs beginData :: PandocMonad m => PodParser m Blocks beginData = do fmt <- regionIdentifier anyLine blanklines lns <- mconcat <$> many (try rawCut <|> rawLine) textStr ("=end " <> fmt) <* blanklines return $ B.rawBlock fmt lns where rawCut = do char '=' *> cut pod "=pod to close =cut within =begin/=end" return mempty rawLine = do try (notFollowedBy (char '=' *> letter)) anyLineNewline ================================================ FILE: src/Text/Pandoc/Readers/Pptx/Parse.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Pptx.Parse Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Parsing of PPTX archive to intermediate representation. -} module Text.Pandoc.Readers.Pptx.Parse ( Pptx(..) , PresentationDoc(..) , PptxSlide(..) , SlideId(..) , archiveToPptx ) where import Codec.Archive.Zip (Archive, Entry, findEntryByPath, fromEntry) import qualified Data.ByteString.Lazy as B import Data.List (find) import Data.Maybe (mapMaybe) import qualified Data.Text as T import qualified Data.Text.Lazy.Encoding as TL import Data.Text (Text) import System.FilePath (splitFileName) import Text.Pandoc.Readers.OOXML.Shared import Text.Pandoc.XML.Light import Text.Read (readMaybe) -- | Slide identifier newtype SlideId = SlideId Int deriving (Show, Eq, Ord) -- | Complete PPTX document (intermediate representation) data Pptx = Pptx { pptxPresentation :: PresentationDoc , pptxSlides :: [PptxSlide] , pptxArchive :: Archive } deriving (Show) -- | Individual slide data data PptxSlide = PptxSlide { slideId :: SlideId , slidePath :: FilePath , slideElement :: Element -- The parsed p:sld element , slideRels :: [(Text, Text)] -- Slide relationships } deriving (Show) -- | Presentation-level information from presentation.xml data PresentationDoc = PresentationDoc { presNameSpaces :: NameSpaces , presSlideSize :: (Integer, Integer) -- (width, height) in pixels , presSlideIds :: [(SlideId, Text)] -- (slideId, relationshipId) } deriving (Show) -- | Parse PPTX archive to intermediate representation archiveToPptx :: Archive -> Either Text Pptx archiveToPptx archive = do -- Find and parse presentation.xml presPath <- getPresentationXmlPath archive presElem <- loadXMLFromArchive archive presPath presDoc <- elemToPresentation presElem -- Load presentation relationships to resolve slide paths presRelsPath <- getPresentationRelsPath archive presPath presRels <- loadRelationships archive presRelsPath -- Parse each slide slides <- mapM (parseSlide archive presRels) (presSlideIds presDoc) return $ Pptx presDoc slides archive -- | Find presentation.xml via root relationships getPresentationXmlPath :: Archive -> Either Text FilePath getPresentationXmlPath archive = do -- Load _rels/.rels relsEntry <- maybeToEither "Missing _rels/.rels" $ findEntryByPath "_rels/.rels" archive relsElem <- parseXMLFromEntry relsEntry -- The Relationships element has a default namespace, but Relationship children don't use prefix -- We need to look at all children regardless of namespace let relElems = onlyElems $ elContent relsElem -- Look for relationship containing "officeDocument" in Type attribute case find isOfficeDocRel relElems of Nothing -> Left $ "No presentation.xml relationship found. Found " <> T.pack (show (length relElems)) <> " relationships." Just rel -> do target <- maybeToEither "Missing Target attribute" $ findAttr (unqual "Target") rel return $ T.unpack target -- Convert Text to FilePath where isOfficeDocRel el = case findAttr (unqual "Type") el of -- Must end with "/officeDocument" to avoid matching "/extended-properties" Just relType -> "/officeDocument" `T.isSuffixOf` relType Nothing -> False -- | Load and parse XML from archive entry loadXMLFromArchive :: Archive -> FilePath -> Either Text Element loadXMLFromArchive archive path = do entry <- maybeToEither ("Entry not found: " <> T.pack path) $ findEntryByPath path archive let xmlBytes = fromEntry entry parseXMLFromBS xmlBytes -- | Parse XML from ByteString parseXMLFromBS :: B.ByteString -> Either Text Element parseXMLFromBS = parseXMLElement . TL.decodeUtf8 -- | Parse XML from Entry parseXMLFromEntry :: Entry -> Either Text Element parseXMLFromEntry = parseXMLFromBS . fromEntry -- | Parse presentation.xml element to PresentationDoc elemToPresentation :: Element -> Either Text PresentationDoc elemToPresentation presElem = do let ns = elemToNameSpaces presElem -- Extract slide size (with defaults) let sizeElem = findChildByName ns "p" "sldSz" presElem (widthEMU, heightEMU) = case sizeElem of Just el -> let cx = readAttrInt "cx" el cy = readAttrInt "cy" el in (cx, cy) Nothing -> (9144000, 6858000) -- Default 10" x 7.5" -- Convert EMUs to pixels (approximate for metadata) let width = widthEMU `div` emusPerInch height = heightEMU `div` emusPerInch -- Extract slide ID list (optional - some presentations may have no slides) let sldIdLstElem = findChildByName ns "p" "sldIdLst" presElem slideRefs <- case sldIdLstElem of Nothing -> return [] -- No slides is valid for templates/masters-only presentations Just el -> do let sldIdElems = findChildren (elemName ns "p" "sldId") el mapM (extractSlideRef ns) (zip [1..] sldIdElems) return $ PresentationDoc { presNameSpaces = ns , presSlideSize = (width, height) , presSlideIds = slideRefs } -- | Extract slide ID and relationship ID from p:sldId element extractSlideRef :: NameSpaces -> (Int, Element) -> Either Text (SlideId, Text) extractSlideRef ns (idx, sldIdElem) = do relId <- maybeToEither ("Missing r:id in slide " <> T.pack (show idx)) $ findAttrByName ns "r" "id" sldIdElem return (SlideId idx, relId) -- | Safe read attribute as Integer (with default of 0) readAttrInt :: Text -> Element -> Integer readAttrInt attrName el = case findAttr (unqual attrName) el of Just str -> case readMaybe (T.unpack str) of Just n -> n Nothing -> 0 Nothing -> 0 -- | Get presentation relationships path getPresentationRelsPath :: Archive -> FilePath -> Either Text FilePath getPresentationRelsPath _archive presPath = -- ppt/presentation.xml → ppt/_rels/presentation.xml.rels let (dir, file) = splitFileName presPath relsPath = dir ++ "/_rels/" ++ file ++ ".rels" in Right relsPath -- | Load relationships from .rels file loadRelationships :: Archive -> FilePath -> Either Text [(Text, Text)] loadRelationships archive relsPath = case findEntryByPath relsPath archive of Nothing -> Right [] -- No relationships is OK Just entry -> do relsElem <- parseXMLFromEntry entry let relElems = onlyElems $ elContent relsElem return $ mapMaybe extractRelationship relElems where extractRelationship el = do relId <- findAttr (unqual "Id") el target <- findAttr (unqual "Target") el return (relId, target) -- | Parse a single slide parseSlide :: Archive -> [(Text, Text)] -> (SlideId, Text) -> Either Text PptxSlide parseSlide archive rels (sid, relId) = do -- Resolve relationship to get slide path target <- maybeToEither ("Relationship not found: " <> relId) $ lookup relId rels -- Resolve relative path: ppt/slides/slide1.xml let slidePath' = "ppt/" <> T.unpack target -- Load and parse slide XML slideElem <- loadXMLFromArchive archive slidePath' -- Load slide-specific relationships slideRelsPath <- getPresentationRelsPath archive slidePath' slideRels' <- loadRelationships archive slideRelsPath return $ PptxSlide sid slidePath' slideElem slideRels' -- | Helper: Maybe a -> Either Text a maybeToEither :: Text -> Maybe a -> Either Text a maybeToEither err Nothing = Left err maybeToEither _ (Just x) = Right x ================================================ FILE: src/Text/Pandoc/Readers/Pptx/Shapes.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -Wno-partial-fields #-} {- | Module : Text.Pandoc.Readers.Pptx.Shapes Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Parsing of PPTX shapes (text boxes, images, tables, diagrams). -} module Text.Pandoc.Readers.Pptx.Shapes ( PptxShape(..) , PptxParagraph(..) , BulletType(..) , parseShapes , parseShape , shapeToBlocks , isTitlePlaceholder , extractDrawingMLText ) where import Codec.Archive.Zip (Archive, findEntryByPath, fromEntry) import qualified Data.ByteString.Lazy as B import Data.List (find, groupBy) import Data.Maybe (mapMaybe) import qualified Data.Text as T import Data.Text (Text) import Text.Read (readMaybe) import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition import Text.Pandoc.Readers.OOXML.Shared import Text.Pandoc.Readers.Pptx.SmartArt import Text.Pandoc.XML.Light -- | Paragraph with bullet/numbering information data PptxParagraph = PptxParagraph { paraLevel :: Int -- Bullet level (0, 1, 2...) , paraBullet :: BulletType , paraText :: Text } deriving (Show) -- | Bullet type data BulletType = NoBullet | Bullet -- Has bullet (character detected or implicit) | WingdingsBullet -- Detected via Wingdings symbol deriving (Show, Eq) -- | Shape types in PPTX slides data PptxShape = PptxTextBox [PptxParagraph] -- Parsed paragraphs with bullet info | PptxPicture { picRelId :: Text -- Relationship ID (lazy loading) , picTitle :: Text , picAlt :: Text } | PptxTable [[Text]] -- Simple text cells for now | PptxDiagramRef { dgmDataRelId :: Text -- Relationship to data.xml , dgmLayoutRelId :: Text -- Relationship to layout.xml } | PptxGraphic Text -- Placeholder for other graphics deriving (Show) -- | Parse all shapes from shape tree parseShapes :: NameSpaces -> Element -> [PptxShape] parseShapes ns spTreeElem = let shapeElems = onlyElems $ elContent spTreeElem -- Merge parent namespaces with element namespaces ns' = ns <> elemToNameSpaces spTreeElem in mapMaybe (parseShape ns') shapeElems -- | Parse individual shape element parseShape :: NameSpaces -> Element -> Maybe PptxShape parseShape ns el -- Text box: with | isElem ns "p" "sp" el = case findChildByName ns "p" "txBody" el of Just txBody -> let paras = parseParagraphs ns txBody in if null paras then Nothing else Just $ PptxTextBox paras Nothing -> Nothing -- Picture: | isElem ns "p" "pic" el = do nvPicPr <- findChildByName ns "p" "nvPicPr" el cNvPr <- findChildByName ns "p" "cNvPr" nvPicPr let title = maybe "" id $ findAttr (unqual "name") cNvPr alt = maybe "" id $ findAttr (unqual "descr") cNvPr -- Get blip relationship ID blipFill <- findChildByName ns "p" "blipFill" el blip <- findChildByName ns "a" "blip" blipFill relId <- findAttrByName ns "r" "embed" blip return $ PptxPicture relId title alt -- GraphicFrame: table or diagram | isElem ns "p" "graphicFrame" el = case findChildByName ns "a" "graphic" el >>= findChildByName ns "a" "graphicData" of Nothing -> Nothing Just graphicData -> case findAttr (unqual "uri") graphicData of Nothing -> Just $ PptxGraphic "no-uri" Just uri -> if "table" `T.isInfixOf` uri then -- Table case findChildByName ns "a" "tbl" graphicData of Just tbl -> let rows = parseTableRows ns tbl in Just $ PptxTable rows Nothing -> Nothing else if "diagram" `T.isInfixOf` uri then -- SmartArt diagram - dgm namespace is declared inline on relIds element let dgmRelIds = find (\e -> qName (elName e) == "relIds") (elChildren graphicData) in case dgmRelIds of Nothing -> Just $ PptxGraphic "diagram-no-relIds" Just relIdsElem -> -- Get r:dm and r:lo attributes (r namespace is in parent) let ns' = ns <> elemToNameSpaces relIdsElem in case (findAttrByName ns' "r" "dm" relIdsElem, findAttrByName ns' "r" "lo" relIdsElem) of (Just dataRelId, Just layoutRelId) -> Just $ PptxDiagramRef dataRelId layoutRelId _ -> Just $ PptxGraphic "diagram-missing-rels" else -- Other graphic (chart, etc.) Just $ PptxGraphic ("other: " <> uri) -- Skip other shapes for now | otherwise = Nothing -- | Parse table rows (simple text extraction) parseTableRows :: NameSpaces -> Element -> [[Text]] parseTableRows ns tblElem = let trElems = findChildrenByName ns "a" "tr" tblElem in map (parseTableRow ns) trElems parseTableRow :: NameSpaces -> Element -> [Text] parseTableRow ns trElem = let tcElems = findChildrenByName ns "a" "tc" trElem in map extractCellText tcElems where extractCellText tcElem = -- Get text from txBody/a:p/a:r/a:t case findChildByName ns "a" "txBody" tcElem of Just txBody -> extractDrawingMLText txBody Nothing -> "" -- | Convert shape to Pandoc blocks shapeToBlocks :: PandocMonad m => Archive -> [(Text, Text)] -> PptxShape -> m [Block] shapeToBlocks _archive _rels (PptxTextBox paras) = return $ paragraphsToBlocks paras shapeToBlocks archive rels (PptxPicture relId title alt) = do -- Resolve relationship to get media path case lookup relId rels of Nothing -> return [] -- Image not found Just target -> do let mediaPath = resolveMediaPath target -- Load image bytes and add to MediaBag case loadMediaFromArchive archive mediaPath of Nothing -> return [] Just mediaBytes -> do P.insertMedia (T.unpack mediaPath) Nothing mediaBytes let altText = if T.null alt then [] else [Str alt] return [Para [Image nullAttr altText (mediaPath, title)]] shapeToBlocks _archive _rels (PptxTable rows) = -- Simple table representation for now case rows of [] -> return [] (headerRow:bodyRows) -> do let makeCell text = Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str text]] headerCells = map makeCell headerRow bodyCells = map (map makeCell) bodyRows caption = Caption Nothing [] colSpec = replicate (length headerRow) (AlignDefault, ColWidthDefault) headerRow' = Row nullAttr headerCells bodyRows' = map (Row nullAttr) bodyCells thead = TableHead nullAttr [headerRow'] tbody = [TableBody nullAttr 0 [] bodyRows'] tfoot = TableFoot nullAttr [] return [Table nullAttr caption colSpec thead tbody tfoot] shapeToBlocks archive rels (PptxDiagramRef dataRelId layoutRelId) = do -- Parse SmartArt diagram case parseDiagram archive rels dataRelId layoutRelId of Left err -> do -- Failed to parse diagram, return placeholder return [Para [Str $ "[Diagram parse error: " <> err <> "]"]] Right diagram -> return $ diagramToBlocks diagram shapeToBlocks _archive _rels (PptxGraphic text) = -- Placeholder for other graphics (charts, etc.) return [Para [Str $ "[Graphic: " <> text <> "]"]] -- | Resolve media path (handle relative paths) resolveMediaPath :: Text -> Text resolveMediaPath target = if "../media/" `T.isPrefixOf` target then "ppt/media/" <> T.drop 9 target -- "../media/" = 9 chars else if "media/" `T.isPrefixOf` target then "ppt/" <> target else target -- | Load media file from archive loadMediaFromArchive :: Archive -> Text -> Maybe B.ByteString loadMediaFromArchive archive path = case findEntryByPath (T.unpack path) archive of Just entry -> Just $ fromEntry entry Nothing -> Nothing -- | Parse paragraphs from text box parseParagraphs :: NameSpaces -> Element -> [PptxParagraph] parseParagraphs ns txBody = let pElems = findChildrenByName ns "a" "p" txBody in map (parseParagraph ns) pElems -- | Parse individual paragraph parseParagraph :: NameSpaces -> Element -> PptxParagraph parseParagraph ns pElem = let level = parseBulletLevel ns pElem bullet = detectBulletType ns pElem text = extractParagraphText ns pElem in PptxParagraph level bullet text -- | Parse bullet level from paragraph properties parseBulletLevel :: NameSpaces -> Element -> Int parseBulletLevel ns pElem = case findChildByName ns "a" "pPr" pElem >>= findAttr (unqual "lvl") >>= (\s -> readMaybe (T.unpack s) :: Maybe Int) of Just lvl -> lvl Nothing -> 0 -- Default to level 0 -- | Detect bullet type detectBulletType :: NameSpaces -> Element -> BulletType detectBulletType ns pElem = -- Check for explicit case findChildByName ns "a" "pPr" pElem >>= findChildByName ns "a" "buChar" of Just _buCharElem -> Bullet Nothing -> -- Check for Wingdings symbol (common in PowerPoint) if hasWingdingsSymbol ns pElem then WingdingsBullet else NoBullet -- | Check if paragraph starts with Wingdings symbol hasWingdingsSymbol :: NameSpaces -> Element -> Bool hasWingdingsSymbol ns pElem = let runs = findChildrenByName ns "a" "r" pElem checkRun r = case findChildByName ns "a" "rPr" r >>= findChildByName ns "a" "sym" of Just symElem -> case findAttr (unqual "typeface") symElem of Just typeface -> "Wingdings" `T.isInfixOf` typeface Nothing -> False Nothing -> False in any checkRun runs -- | Extract text from paragraph extractParagraphText :: NameSpaces -> Element -> Text extractParagraphText _ns pElem = -- Find all elements and concatenate let textElems = filterElementsName (\qn -> qName qn == "t") pElem texts = map strContent textElems in T.unwords $ filter (not . T.null) texts -- | Extract text from DrawingML element (finds all descendants) extractDrawingMLText :: Element -> Text extractDrawingMLText el = let textElems = filterElementsName (\qn -> qName qn == "t") el texts = map strContent textElems in T.unwords $ filter (not . T.null) texts -- | Convert paragraphs to blocks, grouping bullets into lists paragraphsToBlocks :: [PptxParagraph] -> [Block] paragraphsToBlocks paras = -- If we have multiple paragraphs with bullets, group them let hasBullets = any (\p -> paraBullet p /= NoBullet) paras in if hasBullets then groupBulletParagraphs paras else map (\p -> Para [Str $ paraText p]) paras -- | Group bullet paragraphs into lists groupBulletParagraphs :: [PptxParagraph] -> [Block] groupBulletParagraphs paras = let grouped = groupBy sameBulletLevel paras in concatMap groupToBlock grouped where sameBulletLevel p1 p2 = (paraBullet p1 /= NoBullet) && (paraBullet p2 /= NoBullet) && (paraLevel p1 == paraLevel p2) groupToBlock :: [PptxParagraph] -> [Block] groupToBlock [] = [] groupToBlock ps@(p:_) | paraBullet p /= NoBullet = -- Bullet list let items = map (\para -> [Plain [Str $ paraText para]]) ps in [BulletList items] | otherwise = -- Plain paragraph map (\para -> Para [Str $ paraText para]) ps -- | Check if shape is title placeholder (also used in Slides module) isTitlePlaceholder :: NameSpaces -> Element -> Bool isTitlePlaceholder ns el = case findChildByName ns "p" "nvSpPr" el >>= findChildByName ns "p" "nvPr" >>= findChildByName ns "p" "ph" of Just phElem -> case findAttr (unqual "type") phElem of Just phType -> phType == "title" || phType == "ctrTitle" Nothing -> False Nothing -> False ================================================ FILE: src/Text/Pandoc/Readers/Pptx/Slides.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Pptx.Slides Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Conversion of PPTX slides to Pandoc AST blocks. -} module Text.Pandoc.Readers.Pptx.Slides ( pptxToOutput ) where import Codec.Archive.Zip (Archive) import Data.List (find) import Data.Maybe (mapMaybe) import qualified Data.Text as T import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Readers.OOXML.Shared import Text.Pandoc.Readers.Pptx.Parse import Text.Pandoc.Readers.Pptx.Shapes import Text.Pandoc.XML.Light -- | Convert Pptx intermediate representation to Pandoc AST pptxToOutput :: PandocMonad m => ReaderOptions -> Pptx -> m (Meta, [Block]) pptxToOutput _opts pptx = do let slides = pptxSlides pptx archive = pptxArchive pptx -- Convert each slide to blocks slideBlocks <- concat <$> mapM (slideToBlocks archive) slides return (mempty, slideBlocks) -- | Convert slide to blocks slideToBlocks :: PandocMonad m => Archive -> PptxSlide -> m [Block] slideToBlocks archive slide = do let SlideId n = slideId slide slideElem = slideElement slide rels = slideRels slide ns = elemToNameSpaces slideElem -- Extract title from title placeholder title = extractSlideTitle ns slideElem -- Create header slideIdent = "slide-" <> T.pack (show n) headerText = if T.null title then "Slide " <> T.pack (show n) else title header = Header 2 (slideIdent, [], []) [Str headerText] -- Parse shapes and convert to blocks case findChildByName ns "p" "cSld" slideElem >>= findChildByName ns "p" "spTree" of Nothing -> return [header] Just spTree -> do -- Filter out title placeholder shapes before parsing let allShapeElems = onlyElems $ elContent spTree nonTitleShapeElems = filter (not . isTitlePlaceholder ns) allShapeElems shapes = mapMaybe (parseShape ns) nonTitleShapeElems shapeBlocks <- concat <$> mapM (shapeToBlocks archive rels) shapes return $ header : shapeBlocks -- | Extract title from title placeholder extractSlideTitle :: NameSpaces -> Element -> Text extractSlideTitle ns slideElem = case findChildByName ns "p" "cSld" slideElem >>= findChildByName ns "p" "spTree" of Nothing -> "" Just spTree -> -- Find shape with ph type="title" let shapes = onlyElems $ elContent spTree titleShape = find (isTitlePlaceholder ns) shapes in maybe "" extractDrawingMLText titleShape -- isTitlePlaceholder is imported from Shapes module ================================================ FILE: src/Text/Pandoc/Readers/Pptx/SmartArt.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Pptx.SmartArt Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable SmartArt diagram parsing and text extraction for PPTX. -} module Text.Pandoc.Readers.Pptx.SmartArt ( PptxDiagram(..) , parseDiagram , diagramToBlocks ) where import Codec.Archive.Zip (Archive, findEntryByPath, fromEntry) import qualified Data.Map.Strict as M import Data.Maybe (mapMaybe) import qualified Data.Text as T import qualified Data.Text.Lazy.Encoding as TL import Data.Text (Text) import Text.Pandoc.Definition import Text.Pandoc.Readers.OOXML.Shared import Text.Pandoc.XML.Light -- | SmartArt diagram data data PptxDiagram = PptxDiagram { diagramType :: Text -- Layout type (chevron, cycle, etc.) , diagramNodes :: [(Text, [Text])] -- (nodeText, childTexts) } deriving (Show) -- | Parse SmartArt diagram from relationship IDs parseDiagram :: Archive -> [(Text, Text)] -- Slide relationships -> Text -- data relationship ID -> Text -- layout relationship ID -> Either Text PptxDiagram parseDiagram archive rels dataRelId layoutRelId = do -- Resolve relationships to file paths dataTarget <- maybeToEither ("Relationship not found: " <> dataRelId) $ lookup dataRelId rels layoutTarget <- maybeToEither ("Relationship not found: " <> layoutRelId) $ lookup layoutRelId rels -- Resolve relative paths (diagrams are in ../diagrams/ from slides/) let dataPath = resolveDiagramPath dataTarget layoutPath = resolveDiagramPath layoutTarget -- Load XML files dataElem <- loadXMLFromArchive archive dataPath layoutElem <- loadXMLFromArchive archive layoutPath -- Extract layout type layoutType <- extractLayoutType layoutElem -- Extract text nodes with hierarchy nodes <- extractDiagramNodes dataElem return $ PptxDiagram layoutType nodes -- | Resolve diagram path (handle ../diagrams/ relative paths) resolveDiagramPath :: Text -> FilePath resolveDiagramPath target = if "../diagrams/" `T.isPrefixOf` target then "ppt/diagrams/" ++ T.unpack (T.drop 12 target) -- "../diagrams/" = 12 chars else T.unpack target -- | Load XML from archive loadXMLFromArchive :: Archive -> FilePath -> Either Text Element loadXMLFromArchive archive path = case findEntryByPath path archive of Nothing -> Left $ "File not found in archive: " <> T.pack path Just entry -> let xmlBytes = fromEntry entry lazyText = TL.decodeUtf8 xmlBytes in parseXMLElement lazyText -- | Extract layout type from layout XML extractLayoutType :: Element -> Either Text Text extractLayoutType layoutElem = do -- Look for uniqueId attribute: "urn:.../layout/chevron2" case findAttr (unqual "uniqueId") layoutElem of Just uid -> -- Extract last part after last / let layoutName = T.takeWhileEnd (/= '/') uid in Right layoutName Nothing -> -- Fallback: look for title case findChildByName ns "dgm" "title" layoutElem >>= findAttr (unqual "val") of Just title -> Right title Nothing -> Right "unknown" where ns = elemToNameSpaces layoutElem -- | Extract text nodes from diagram data extractDiagramNodes :: Element -> Either Text [(Text, [Text])] extractDiagramNodes dataElem = do let ns = elemToNameSpaces dataElem -- Find point list ptLst <- maybeToEither "Missing dgm:ptLst" $ findChildByName ns "dgm" "ptLst" dataElem let ptElems = findChildrenByName ns "dgm" "pt" ptLst -- Build node map: modelId → text let nodeMap = M.fromList $ mapMaybe (extractNodeText ns) ptElems -- Parse connections let cxnLst = findChildByName ns "dgm" "cxnLst" dataElem connections = maybe [] (parseConnections ns) cxnLst -- Build parent-child map let parentMap = buildParentMap connections -- Find parent nodes (nodes that have children) let parentIds = M.keys parentMap -- Build hierarchy - only show nodes that are parents -- (children are shown under their parents) let hierarchy = map (buildNodeWithChildren nodeMap parentMap) parentIds -- Filter out nodes with empty text (presentation nodes) validHierarchy = filter (\(nodeText, _) -> not $ T.null nodeText) hierarchy return validHierarchy -- | Extract text from a point element (returns Nothing if no text) extractNodeText :: NameSpaces -> Element -> Maybe (Text, Text) extractNodeText ns ptElem = do modelId <- findAttr (unqual "modelId") ptElem -- Extract text from dgm:t element (which contains a:p/a:r/a:t) let text = case findChildByName ns "dgm" "t" ptElem of Just tElem -> -- Recursively get ALL text content from all descendants getAllText tElem Nothing -> "" -- Only return nodes with actual text if T.null (T.strip text) then Nothing else return (modelId, text) -- | Connection between nodes data Connection = Connection { connType :: Text , connSrc :: Text , connDest :: Text } deriving (Show) -- | Parse connections parseConnections :: NameSpaces -> Element -> [Connection] parseConnections ns cxnLst = let cxnElems = findChildrenByName ns "dgm" "cxn" cxnLst in mapMaybe (parseConnection ns) cxnElems parseConnection :: NameSpaces -> Element -> Maybe Connection parseConnection _ns cxnElem = do let cxnType = maybe "" id $ findAttr (unqual "type") cxnElem -- Empty if no type srcId <- findAttr (unqual "srcId") cxnElem destId <- findAttr (unqual "destId") cxnElem return $ Connection cxnType srcId destId -- | Build parent-child map from connections -- Use connections WITHOUT a type attribute (these are the data hierarchy) buildParentMap :: [Connection] -> M.Map Text [Text] buildParentMap connections = let dataConnections = filter (\c -> T.null (connType c)) connections in foldr addConn M.empty dataConnections where addConn conn m = M.insertWith (++) (connSrc conn) [connDest conn] m -- | Build node with its children buildNodeWithChildren :: M.Map Text Text -> M.Map Text [Text] -> Text -> (Text, [Text]) buildNodeWithChildren nodeMap parentMap nodeId = let nodeText = M.findWithDefault "" nodeId nodeMap childIds = M.findWithDefault [] nodeId parentMap -- Only include children that have text childTexts = filter (not . T.null) $ map (\cid -> M.findWithDefault "" cid nodeMap) childIds in (nodeText, childTexts) -- | Convert diagram to Pandoc blocks diagramToBlocks :: PptxDiagram -> [Block] diagramToBlocks diagram = let nodes = diagramNodes diagram layoutType = diagramType diagram -- Build content blocks contentBlocks = concatMap nodeToBlocks nodes in [Div ("", ["smartart", layoutType], [("layout", layoutType)]) contentBlocks] -- | Convert node to blocks nodeToBlocks :: (Text, [Text]) -> [Block] nodeToBlocks (nodeText, childTexts) = if null childTexts then [Para [Strong [Str nodeText]]] else [ Para [Strong [Str nodeText]] , BulletList [[Plain [Str child]] | child <- childTexts] ] -- | Recursively extract all text from an element and its descendants getAllText :: Element -> Text getAllText el = let textFromContent (Text cdata) = cdData cdata textFromContent (Elem e) = getAllText e textFromContent _ = "" texts = map textFromContent (elContent el) in T.unwords $ filter (not . T.null) texts -- Helper functions maybeToEither :: Text -> Maybe a -> Either Text a maybeToEither err Nothing = Left err maybeToEither _ (Just x) = Right x ================================================ FILE: src/Text/Pandoc/Readers/Pptx.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Pptx Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Conversion of PPTX (PowerPoint) documents to 'Pandoc' document. -} module Text.Pandoc.Readers.Pptx (readPptx) where import qualified Data.ByteString.Lazy as B import qualified Data.Text as T import Codec.Archive.Zip (toArchiveOrFail) import Control.Monad.Except (throwError) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition (Pandoc(..)) import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Readers.Pptx.Parse (archiveToPptx) import Text.Pandoc.Readers.Pptx.Slides (pptxToOutput) -- | Read PPTX file into Pandoc AST readPptx :: PandocMonad m => ReaderOptions -> B.ByteString -> m Pandoc readPptx opts bytes = case toArchiveOrFail bytes of Right archive -> case archiveToPptx archive of Right pptx -> do -- Convert Pptx intermediate to Pandoc AST (meta, blocks) <- pptxToOutput opts pptx return $ Pandoc meta blocks Left err -> throwError $ PandocParseError $ "Failed to parse PPTX: " <> err Left err -> throwError $ PandocParseError $ "Failed to unpack PPTX archive: " <> T.pack err ================================================ FILE: src/Text/Pandoc/Readers/RIS.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.RIS Copyright : Copyright (C) 2022-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Parses RIS bibliographies into a Pandoc document with empty body and `references` and `nocite` fields in the metadata. A wildcard `nocite` is used so that if the document is rendered in another format, the entire bibliography will be printed. -} module Text.Pandoc.Readers.RIS ( readRIS ) where import Text.Pandoc.Options import Text.Pandoc.Definition import Text.Pandoc.Parsing import Data.Char (isAsciiUpper, isDigit, isSpace, ord, chr) import qualified Data.List as L import Citeproc (Reference(..), ItemId(..), Val(..), Date(..), DateParts(..), toVariable) import Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Citeproc.MetaValue (referenceToMetaValue) import Text.Pandoc.Citeproc.Name (toName, NameOpts(..)) import Control.Monad.Except (throwError) import qualified Data.Text as T import Data.Text (Text) import Data.Maybe (fromMaybe) import qualified Data.Map as M import Safe (readMay) -- | Read RIS from an input string and return a Pandoc document. -- The document will have only metadata, with an empty body. -- The metadata will contain a `references` field with the -- bibliography entries, and a `nocite` field with the wildcard `[@*]`. readRIS :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readRIS _opts inp = do parsed <- readWithM risReferences () inp case parsed of Right refs -> do refs' <- mapM (traverse (return . text)) refs return $ setMeta "nocite" (cite [Citation {citationId = "*" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0}] (str "[@*]")) $ setMeta "references" (map referenceToMetaValue refs') $ B.doc mempty Left e -> throwError e type RISParser m = ParsecT Sources () m risLine :: PandocMonad m => RISParser m (Text, Text) risLine = do key <- T.pack <$> count 2 (satisfy (\c -> isAsciiUpper c || isDigit c)) _ <- many1 spaceChar char '-' val <- (many1 spaceChar *> anyLine) <|> mempty <$ newline return (key, T.strip val) risSeparator :: PandocMonad m => RISParser m () risSeparator = do try $ string "ER" _ <- many1 spaceChar char '-' _ <- anyLine optional blanklines risRecord :: PandocMonad m => RISParser m [(Text, Text)] risRecord = manyTill risLine risSeparator risRecordToReference :: [(Text, Text)] -> Reference Text risRecordToReference keys = addId $ foldr go defref keys where go (key, val) = case key of "TY" -> \ref -> ref{ referenceType = fromMaybe "misc" (M.lookup val risTypes) } "ID" -> \ref -> ref{ referenceId = ItemId val } "VL" -> addVar "volume" val "KW" -> \ref -> ref{ referenceVariables = M.alter (\x -> case x of Nothing -> Just $ TextVal val Just (TextVal kws) -> Just (TextVal (kws <> ", " <> val)) _ -> x) "keyword" (referenceVariables ref) } "PB" -> addVar "publisher" val "PP" -> addVar "publisher-place" val "DO" -> addVar "DOI" val "SP" -> \ref -> case M.lookup "page" (referenceVariables ref) of Nothing -> addVar "page" val ref Just (FancyVal eg) -> addVar "page" (val <> eg) ref _ -> ref "EP" -> \ref -> case M.lookup "page" (referenceVariables ref) of Nothing -> addVar "page" ("-" <> val) ref Just (FancyVal eg) -> addVar "page" (val <> "-" <> eg) ref _ -> ref "AU" -> addName "author" val "A1" -> addName "author" val "ED" -> addName "editor" val "A2" -> addName "editor" val "TI" -> addVar "title" val "T1" -> addVar "title" val "CT" -> addVar "title" val "BT" -> \ref -> if referenceType ref == "book" then addVar "title" val ref else addVar "container-title" val ref "JO" -> addVar "container-title" val "JF" -> addVar "container-title" val "T2" -> addVar "container-title" val "ET" -> addVar "edition" val "NV" -> addVar "number-of-volumes" val "AB" -> addVar "abstract" val "PY" -> addYear "issued" val "Y1" -> addYear "issued" val "IS" -> addVar "issue" val "SN" -> addVar "ISSN" val "LA" -> addVar "language" val "UR" -> addVar "url" val "LK" -> addVar "url" val _ -> id -- TODO addVar k v r = r{ referenceVariables = M.insert (toVariable k) (FancyVal v) (referenceVariables r) } addName k v r = let new = toName NameOpts{ nameOptsPrefixIsNonDroppingParticle = False , nameOptsUseJuniorComma = False } . B.toList . B.text $ v f Nothing = Just (NamesVal new) f (Just (NamesVal ns)) = Just (NamesVal (new ++ ns)) f (Just x) = Just x in r{ referenceVariables = M.alter f k (referenceVariables r) } addYear k v r = let d = DateVal $ case readMay (T.unpack v) of Nothing -> Date { dateParts = [] , dateCirca = False , dateSeason = Nothing , dateLiteral = Just v } Just y -> Date { dateParts = [DateParts [y]] , dateCirca = False , dateSeason = Nothing , dateLiteral = Nothing } in r{ referenceVariables = M.insert k d (referenceVariables r) } defref = Reference{ referenceId = mempty , referenceType = mempty , referenceDisambiguation = Nothing , referenceVariables = mempty } addId rec = if referenceId rec == mempty then rec{ referenceId = ItemId (authors <> pubdate) } else rec authors = T.intercalate "_" $ [T.takeWhile (\c -> c /= ',' && not (isSpace c)) n | (k, n) <- keys, k == "AU" || k == "A1"] pubdate = mconcat ["_" <> d | (k, d) <- keys, k == "PY" || k == "Y1"] risReferences :: PandocMonad m => RISParser m [Reference Text] risReferences = do recs <- many risRecord spaces eof return $ fixDuplicateIds $ map risRecordToReference recs fixDuplicateIds :: [Reference Text] -> [Reference Text] fixDuplicateIds = reverse . snd . L.foldl' go (mempty, []) where go (ids_seen, refs) ref = case M.lookup (referenceId ref) ids_seen of Nothing -> (M.insert (referenceId ref) (ord 'a') ids_seen, ref:refs) Just n -> (M.insert (referenceId ref) (n+1) ids_seen, ref{ referenceId = ItemId . (<> T.singleton (chr n)) . unItemId $ referenceId ref } : refs) risTypes :: M.Map Text Text risTypes = M.fromList [ ("ABST", "article") , ("ADVS", "motion-picture") , ("AGGR", "dataset") , ("ANCIENT", "book") , ("ART", "graphic") , ("BILL", "bill") , ("BLOG", "post-weblog") , ("BOOK", "book") , ("CASE", "legal_case") , ("CHAP", "chapter") , ("CHART", "graphic") , ("CLSWK", "book") , ("COMP", "program") , ("CONF", "paper-conference") , ("CPAPER", "paper-conference") , ("CTLG", "catalog") , ("DATA", "dataset") , ("DBASE", "dataset") , ("DICT", "book") , ("EBOOK", "book") , ("ECHAP", "chapter") , ("EDBOOK", "book") , ("EJOUR", "article") , ("WEB", "webpage") , ("ENCYC", "entry-encyclopedia") , ("EQUA", "figure") , ("FIGURE", "figure") , ("GEN", "entry") , ("GOVDOC", "report") , ("GRANT", "report") , ("HEAR", "report") , ("ICOMM", "personal_communication") , ("INPR", "article-journal") , ("JFULL", "article-journal") , ("JOUR", "article-journal") , ("LEGAL", "legal_case") , ("MANSCPT", "manuscript") , ("MAP", "map") , ("MGZN", "article-magazine") , ("MPCT", "motion-picture") , ("MULTI", "webpage") , ("MUSIC", "musical_score") , ("NEWS", "article-newspaper") , ("PAMP", "pamphlet") , ("PAT", "patent") , ("PCOMM", "personal_communication") , ("RPRT", "report") , ("SER", "article") , ("SLIDE", "graphic") , ("SOUND", "musical_score") , ("STAND", "report") , ("STAT", "legislation") , ("THES", "thesis") , ("UNBILL", "bill") , ("UNPB", "unpublished") , ("VIDEO", "graphic") ] ================================================ FILE: src/Text/Pandoc/Readers/RST.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.RST Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion from reStructuredText to 'Pandoc' document. -} module Text.Pandoc.Readers.RST ( readRST ) where import Control.Arrow (second) import Control.Monad (forM_, guard, liftM, mplus, mzero, when, unless, void) import Control.Monad.Except (throwError) import Control.Monad.Identity (Identity (..)) import Data.Char (isHexDigit, isSpace, toUpper, isAlphaNum, generalCategory, GeneralCategory(OpenPunctuation, InitialQuote, FinalQuote, DashPunctuation, OtherSymbol)) import Data.List (deleteFirstsBy, elemIndex, nub, partition, sort, transpose) import qualified Data.Map as M import Data.Maybe (fromMaybe, maybeToList, isJust, isNothing, catMaybes) import Data.Sequence (ViewR (..), viewr) import Data.Text (Text) import qualified Data.Text as T import Text.Printf (printf) import Text.Pandoc.Builder (Blocks, Inlines, fromList, setMeta, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, readFileFromDirs, fetchItem, getTimestamp) import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.ImageSize (lengthToDim, scaleDimension) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Walk (walkM) import qualified Text.Pandoc.UTF8 as UTF8 import Data.Time.Format import System.FilePath (takeDirectory) -- TODO: -- [ ] .. parsed-literal -- | Parse reStructuredText string and return Pandoc document. readRST :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options -> a -> m Pandoc readRST opts s = do parsed <- readWithM parseRST def{ stateOptions = opts } (ensureFinalNewlines 2 (toSources s)) case parsed of Right result -> return result Left e -> throwError e type RSTParser m = ParsecT Sources ParserState m -- -- Constants and data structure definitions --- bulletListMarkers :: [Char] bulletListMarkers = "*+-•‣⁃" underlineChars :: [Char] underlineChars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" -- treat these as potentially non-text when parsing inline: specialChars :: [Char] specialChars = "\\`|*_<>$:/[]{}()-.\"'\8216\8217\8220\8221" -- -- parsing documents -- isHeader :: Int -> Block -> Bool isHeader n (Header x _ _) = x == n isHeader _ _ = False -- | Promote all headers in a list of blocks. (Part of -- title transformation for RST.) promoteHeaders :: Int -> [Block] -> [Block] promoteHeaders num (Header level attr text:rest) = Header (level - num) attr text:promoteHeaders num rest promoteHeaders num (other:rest) = other:promoteHeaders num rest promoteHeaders _ [] = [] -- | If list of blocks starts with a header (or a header and subheader) -- of level that are not found elsewhere, return it as a title and -- promote all the other headers. Also process a definition list right -- after the title block as metadata. titleTransform :: ([Block], Meta) -- ^ list of blocks, metadata -> ([Block], Meta) -- ^ modified list of blocks, metadata titleTransform (bs, meta) = let (bs', meta') = case bs of (Header 1 _ head1:Header 2 _ head2:rest) | not (any (isHeader 1) rest || any (isHeader 2) rest) -> -- tit/sub (promoteHeaders 2 rest, setMeta "title" (fromList head1) $ setMeta "subtitle" (fromList head2) meta) (Header 1 _ head1:rest) | not (any (isHeader 1) rest) -> -- title only (promoteHeaders 1 rest, setMeta "title" (fromList head1) meta) _ -> (bs, meta) in case bs' of (DefinitionList ds : rest) -> (rest, metaFromDefList ds meta') _ -> (bs', meta') metaFromDefList :: [([Inline], [[Block]])] -> Meta -> Meta metaFromDefList ds meta = adjustAuthors $ foldr f meta ds where f (k,v) = case v of [[Plain ils]] -> setMeta (T.toLower (stringify k)) $ MetaInlines ils _ -> setMeta (T.toLower (stringify k)) $ mconcat $ map fromList v adjustAuthors (Meta metamap) = Meta $ M.adjust splitAuthors "author" $ M.mapKeys (\k -> if k == "authors" then "author" else k) metamap splitAuthors (MetaInlines xs) = MetaList $ map MetaInlines $ splitAuthors' xs splitAuthors x = x splitAuthors' = map normalizeSpaces . splitOnSemi . concatMap factorSemi normalizeSpaces = reverse . dropWhile isSp . reverse . dropWhile isSp isSp Space = True isSp SoftBreak = True isSp LineBreak = True isSp _ = False splitOnSemi = splitBy (==Str ";") factorSemi (Str "") = [] factorSemi (Str s) = case T.break (==';') s of (xs,"") -> [Str xs] (xs,T.uncons -> Just (';',ys)) -> Str xs : Str ";" : factorSemi (Str ys) (xs,ys) -> Str xs : factorSemi (Str ys) factorSemi x = [x] parseRST :: PandocMonad m => RSTParser m Pandoc parseRST = do standalone <- getOption readerStandalone optional blanklines -- skip blank lines at beginning of file blocks <- B.toList <$> parseBlocks citations <- sort . M.toList . stateCitations <$> getState citationItems <- mapM parseCitation citations let refBlock = [Div ("citations",[],[]) $ B.toList $ B.definitionList citationItems | not (null citationItems)] state <- getState let meta = stateMeta state let (blocks', meta') = if standalone then titleTransform (blocks, meta) else (blocks, meta) let reversedNotes = stateNotes state updateState $ \s -> s { stateNotes = reverse reversedNotes } doc <- walkM resolveReferences =<< walkM resolveBlockSubstitutions (Pandoc meta' (blocks' ++ refBlock)) reportLogMessages return doc resolveBlockSubstitutions :: PandocMonad m => Block -> RSTParser m Block resolveBlockSubstitutions x@(Para [Link _attr _ (s,_)]) | Just ref <- T.stripPrefix "##SUBST##" s = do substTable <- stateSubstitutions <$> getState let key@(Key key') = toKey $ stripFirstAndLast ref case M.lookup key substTable of Nothing -> do pos <- getPosition logMessage $ ReferenceNotFound (tshow key') pos return x Just target -> case B.toList target of [bl] -> return bl bls -> return $ Div nullAttr bls resolveBlockSubstitutions x = return x resolveReferences :: PandocMonad m => Inline -> RSTParser m Inline resolveReferences x@(Link _ ils (s,_)) | Just ref <- T.stripPrefix "##REF##" s = do let isAnonKey (Key (T.uncons -> Just ('_',_))) = True isAnonKey _ = False state <- getState let keyTable = stateKeys state let anonKeys = sort $ filter isAnonKey $ M.keys keyTable key <- if ref == "_" -- anonymous key then case anonKeys of [] -> mzero -- TODO log? (k:_) -> return k else return $ toKey ref ((src,tit), attr) <- lookupKey [] key -- if anonymous link, remove key so it won't be used again when (isAnonKey key) $ updateState $ \st -> st{ stateKeys = M.delete key keyTable } return $ Link attr ils (src, tit) | Just ref <- T.stripPrefix "##NOTE##" s = do state <- getState let notes = stateNotes state case lookup ref notes of Nothing -> do pos <- getPosition logMessage $ ReferenceNotFound ref pos return x Just raw -> do -- We temporarily empty the note list while parsing the note, -- so that we don't get infinite loops with notes inside notes... -- Note references inside other notes are allowed in reST, but -- not yet in this implementation. updateState $ \st -> st{ stateNotes = [] } contents <- parseFromString' parseBlocks raw let newnotes = if ref == "*" || ref == "#" -- auto-numbered -- delete the note so the next auto-numbered note -- doesn't get the same contents: then deleteFirstsBy (==) notes [(ref,raw)] else notes updateState $ \st -> st{ stateNotes = newnotes } return $ Note (B.toList contents) | Just ref <- T.stripPrefix "##SUBST##" s = do substTable <- stateSubstitutions <$> getState let key@(Key key') = toKey $ stripFirstAndLast ref case M.lookup key substTable of Nothing -> do pos <- getPosition logMessage $ ReferenceNotFound (tshow key') pos return x Just target -> case B.toList target of [Para [t]] -> return t [Para xs] -> return $ Span nullAttr xs bls -> return $ Span nullAttr $ blocksToInlines bls | otherwise = return x resolveReferences x = return x parseCitation :: PandocMonad m => (Text, Text) -> RSTParser m (Inlines, [Blocks]) parseCitation (ref, raw) = do contents <- parseFromString' parseBlocks raw return (B.spanWith (ref, ["citation-label"], []) (B.str ref), [contents]) -- -- parsing blocks -- parseBlocks :: PandocMonad m => RSTParser m Blocks parseBlocks = mconcat <$> manyTill block eof block :: PandocMonad m => RSTParser m Blocks block = choice [ codeBlock , blockQuote , fieldList , optionList , referenceKey , noteBlock , citationBlock , directive , anchor , comment , header , hrule , lineBlock -- must go before definitionList , table , list , lhsCodeBlock , para , mempty <$ blanklines ] "block" -- -- field list -- rawFieldListItem :: Monad m => Int -> RSTParser m (Text, Text) rawFieldListItem minIndent = try $ do indent <- length <$> many (char ' ') guard $ indent >= minIndent char ':' name <- many1TillChar (noneOf "\n") (char ':') (void (lookAhead newline)) <|> skipMany1 spaceChar first <- anyLine rest <- option "" $ try $ do lookAhead (count indent (char ' ') >> spaceChar) indentedBlock let raw = (if T.null first then "" else first <> "\n") <> rest <> (if T.null first && T.null rest then "" else "\n") return (name, raw) fieldListItem :: PandocMonad m => Int -> RSTParser m (Inlines, [Blocks]) fieldListItem minIndent = try $ do (name, raw) <- rawFieldListItem minIndent term <- parseInlineFromText name contents <- parseFromString' parseBlocks raw optional blanklines let defn = case B.toList contents of [Para ils] -> [B.plain $ B.fromList ils] -- see #7766 _ -> [contents] return (term, defn) fieldList :: PandocMonad m => RSTParser m Blocks fieldList = try $ do indent <- length <$> lookAhead (many spaceChar) items <- many1 $ fieldListItem indent case items of [] -> return mempty items' -> return $ B.definitionList items' optionList :: PandocMonad m => RSTParser m Blocks optionList = B.definitionList <$> many1 optionListItem optionListItem :: PandocMonad m => RSTParser m (Inlines, [Blocks]) optionListItem = try $ do opts <- snd <$> withRaw (do let anyOpt = shortOpt <|> longOpt <|> dosOpt anyOpt many $ try (char ',' <* many spaceChar *> anyOpt)) -- at least two spaces rawfirst <- try (char ' ' *> many1 (char ' ') *> anyLineNewline) <|> try (mempty <$ skipMany spaceChar <* newline) bodyElements <- do raw <- option "" indentedBlock parseFromString' parseBlocks $ (rawfirst <> raw) <> "\n\n" optional blanklines pure (B.code opts, [bodyElements]) shortOpt :: PandocMonad m => RSTParser m () shortOpt = try $ do char '-' alphaNum optional $ try (optional (char ' ') *> optArg) optArg :: PandocMonad m => RSTParser m () optArg = do c <- letter <|> char '<' if c == '<' then void $ manyTill (noneOf "<>") (char '>') else skipMany (alphaNum <|> char '_' <|> char '-') longOpt :: PandocMonad m => RSTParser m () longOpt = try $ do char '-' char '-' alphaNum skipMany1 (alphaNum <|> char '-' <|> char '_') optional $ try (oneOf " =" *> optArg) dosOpt :: PandocMonad m => RSTParser m () dosOpt = try $ do char '/' alphaNum <|> char '?' optional $ try (char ' ' *> optArg) -- -- line block -- lineBlock :: PandocMonad m => RSTParser m Blocks lineBlock = try $ do lines' <- lineBlockLines lines'' <- mapM parseInlineFromText lines' return $ B.lineBlock lines'' lineBlockDirective :: PandocMonad m => Text -> RSTParser m Blocks lineBlockDirective body = do lines' <- mapM parseInlineFromText $ T.lines $ stripTrailingNewlines body return $ B.lineBlock lines' -- -- paragraph block -- -- note: paragraph can end in a :: starting a code block para :: PandocMonad m => RSTParser m Blocks para = try $ do result <- trimInlines . mconcat <$> many1 inline option (B.plain result) $ try $ do newline blanklines case viewr (B.unMany result) of ys :> Str xs | "::" `T.isSuffixOf` xs -> do raw <- option mempty codeBlockBody return $ B.para (B.Many ys <> B.str (T.take (T.length xs - 1) xs)) <> raw _ -> return (B.para result) plain :: PandocMonad m => RSTParser m Blocks plain = B.plain . trimInlines . mconcat <$> many1 inline -- -- header blocks -- header :: PandocMonad m => RSTParser m Blocks header = doubleHeader <|> singleHeader "header" -- a header with lines on top and bottom doubleHeader :: PandocMonad m => RSTParser m Blocks doubleHeader = do (txt, c) <- doubleHeader' -- check to see if we've had this kind of header before. -- if so, get appropriate level. if not, add to list. state <- getState let headerTable = stateHeaderTable state let (headerTable',level) = case elemIndex (DoubleHeader c) headerTable of Just ind -> (headerTable, ind + 1) Nothing -> (headerTable ++ [DoubleHeader c], length headerTable + 1) setState (state { stateHeaderTable = headerTable' }) attr@(ident,_,_) <- registerHeader nullAttr txt let key = toKey (stringify txt) updateState $ \s -> s { stateKeys = M.insert key (("#" <> ident,""), nullAttr) $ stateKeys s } return $ B.headerWith attr level txt doubleHeader' :: PandocMonad m => RSTParser m (Inlines, Char) doubleHeader' = try $ do c <- oneOf underlineChars rest <- many (char c) -- the top line let lenTop = length (c:rest) skipSpaces newline txt <- trimInlines . mconcat <$> many1 (notFollowedBy blankline >> inline) pos <- getPosition let len = sourceColumn pos - 1 when (len > lenTop) $ Prelude.fail "title longer than border" blankline -- spaces and newline count lenTop (char c) -- the bottom line blanklines return (txt, c) -- a header with line on the bottom only singleHeader :: PandocMonad m => RSTParser m Blocks singleHeader = do (txt, c) <- singleHeader' state <- getState let headerTable = stateHeaderTable state let (headerTable',level) = case elemIndex (SingleHeader c) headerTable of Just ind -> (headerTable, ind + 1) Nothing -> (headerTable ++ [SingleHeader c], length headerTable + 1) setState (state { stateHeaderTable = headerTable' }) attr@(ident,_,_) <- registerHeader nullAttr txt let key = toKey (stringify txt) updateState $ \s -> s { stateKeys = M.insert key (("#" <> ident,""), nullAttr) $ stateKeys s } return $ B.headerWith attr level txt singleHeader' :: PandocMonad m => RSTParser m (Inlines, Char) singleHeader' = try $ do notFollowedBy' whitespace lookAhead $ anyLine >> oneOf underlineChars txt <- trimInlines . mconcat <$> many1 (notFollowedBy blankline >> inline) pos <- getPosition let len = sourceColumn pos - 1 blankline c <- oneOf underlineChars count (len - 1) (char c) many (char c) blanklines return (txt, c) -- -- hrule block -- hrule :: Monad m => ParsecT Sources st m Blocks hrule = try $ do chr <- oneOf underlineChars count 3 (char chr) skipMany (char chr) blankline blanklines return B.horizontalRule -- -- code blocks -- -- read a line indented by a given string indentedLine :: (HasReaderOptions st, Monad m) => Int -> ParsecT Sources st m Text indentedLine indents = try $ do lookAhead spaceChar gobbleAtMostSpaces indents anyLine -- one or more indented lines, possibly separated by blank lines. -- any amount of indentation will work. indentedBlock :: (HasReaderOptions st, Monad m) => ParsecT Sources st m Text indentedBlock = try $ do indents <- length <$> lookAhead (many1 spaceChar) lns <- many1 $ try $ do b <- option "" blanklines l <- indentedLine indents return (b <> l) optional blanklines return $ T.unlines lns quotedBlock :: Monad m => ParsecT Sources st m Text quotedBlock = try $ do quote <- lookAhead $ oneOf "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" lns <- many1 $ lookAhead (char quote) >> anyLine optional blanklines return $ T.unlines lns codeBlockStart :: Monad m => ParsecT Sources st m Char codeBlockStart = string "::" >> blankline >> blankline codeBlock :: Monad m => ParsecT Sources ParserState m Blocks codeBlock = try $ codeBlockStart >> codeBlockBody codeBlockBody :: Monad m => ParsecT Sources ParserState m Blocks codeBlockBody = do lang <- stateRstHighlight <$> getState try $ B.codeBlockWith ("", maybeToList lang, []) . stripTrailingNewlines <$> (indentedBlock <|> quotedBlock) lhsCodeBlock :: Monad m => RSTParser m Blocks lhsCodeBlock = try $ do getPosition >>= guard . (==1) . sourceColumn guardEnabled Ext_literate_haskell optional codeBlockStart lns <- latexCodeBlock <|> birdCodeBlock blanklines return $ B.codeBlockWith ("", ["haskell","literate"], []) $ T.intercalate "\n" lns latexCodeBlock :: Monad m => ParsecT Sources st m [Text] latexCodeBlock = try $ do try (latexBlockLine "\\begin{code}") many1Till anyLine (try $ latexBlockLine "\\end{code}") where latexBlockLine s = skipMany spaceChar >> string s >> blankline birdCodeBlock :: Monad m => ParsecT Sources st m [Text] birdCodeBlock = filterSpace <$> many1 birdTrackLine where filterSpace lns = -- if (as is normal) there is always a space after >, drop it if all (\ln -> T.null ln || T.take 1 ln == " ") lns then map (T.drop 1) lns else lns birdTrackLine :: Monad m => ParsecT Sources st m Text birdTrackLine = char '>' >> anyLine -- -- block quotes -- blockQuote :: PandocMonad m => RSTParser m Blocks blockQuote = do raw <- indentedBlock -- parse the extracted block, which may contain various block elements: contents <- parseFromString' parseBlocks $ raw <> "\n\n" return $ B.blockQuote contents {- Unsupported options for include: tab-width encoding -} includeDirective :: PandocMonad m => Text -> [(Text, Text)] -> Text -> RSTParser m Blocks includeDirective top fields body = do let f = T.unpack $ trim top guard $ not $ null f guard $ T.null (trim body) let startLine = lookup "start-line" fields >>= safeRead let endLine = lookup "end-line" fields >>= safeRead let classes = maybe [] T.words (lookup "class" fields) let ident = maybe "" trimr $ lookup "name" fields let parser = case lookup "code" fields `mplus` lookup "literal" fields of Just lang -> (codeblock ident classes fields (trimr lang) False . sourcesToText) <$> getInput Nothing -> parseBlocks let isLiteral = isJust (lookup "code" fields `mplus` lookup "literal" fields) let selectLines = (case trim <$> lookup "end-before" fields of Just patt -> takeWhile (not . (patt `T.isInfixOf`)) Nothing -> id) . (case trim <$> lookup "start-after" fields of Just patt -> drop 1 . dropWhile (not . (patt `T.isInfixOf`)) Nothing -> id) let toStream t = Sources [(initialPos f, (T.unlines . selectLines . T.lines $ t) <> if isLiteral then mempty else "\n")] -- see #7436 currentDir <- takeDirectory . sourceName <$> getPosition insertIncludedFile parser toStream [currentDir] f startLine endLine -- -- list blocks -- list :: PandocMonad m => RSTParser m Blocks list = choice [ bulletList, orderedList, definitionList ] "list" definitionListItem :: PandocMonad m => RSTParser m (Inlines, [Blocks]) definitionListItem = try $ do -- avoid capturing a directive or comment notFollowedBy (try $ char '.' >> char '.') term <- trimInlines . mconcat <$> many1Till inline endline raw <- indentedBlock -- parse the extracted block, which may contain various block elements: contents <- parseFromString' parseBlocks $ raw <> "\n" return (term, [contents]) definitionList :: PandocMonad m => RSTParser m Blocks definitionList = B.definitionList <$> many1 definitionListItem -- parses bullet list start and returns its length (inc. following whitespace) bulletListStart :: Monad m => ParsecT Sources st m Int bulletListStart = try $ do notFollowedBy' hrule -- because hrules start out just like lists marker <- oneOf bulletListMarkers white <- many1 spaceChar <|> "" <$ lookAhead (char '\n') return $ length (marker:white) -- parses ordered list start and returns its length (inc following whitespace) orderedListStart :: Monad m => ListNumberStyle -> ListNumberDelim -> RSTParser m Int orderedListStart style delim = try $ do (_, markerLen) <- withHorizDisplacement (orderedListMarker style delim) white <- many1 spaceChar <|> "" <$ lookAhead (char '\n') return $ markerLen + length white -- parse a line of a list item listLine :: Monad m => Int -> RSTParser m Text listLine markerLength = try $ do notFollowedBy blankline indentWith markerLength anyLineNewline -- parse raw text for one list item, excluding start marker and continuations rawListItem :: Monad m => RSTParser m Int -> RSTParser m (Int, Text) rawListItem start = try $ do markerLength <- start firstLine <- anyLineNewline restLines <- many (listLine markerLength) return (markerLength, firstLine <> T.concat restLines) -- continuation of a list item - indented and separated by blankline or -- (in compact lists) endline. -- Note: nested lists are parsed as continuations. listContinuation :: Monad m => Int -> RSTParser m Text listContinuation markerLength = try $ do blanks <- many1Char blankline result <- many1 (listLine markerLength) return $ blanks <> T.concat result listItem :: PandocMonad m => RSTParser m Int -> RSTParser m Blocks listItem start = try $ do (markerLength, first) <- rawListItem start rest <- many (listContinuation markerLength) skipMany1 blankline <|> void (lookAhead start) -- parsing with ListItemState forces markers at beginning of lines to -- count as list item markers, even if not separated by blank space. -- see definition of "endline" state <- getState let oldContext = stateParserContext state setState $ state {stateParserContext = ListItemState} -- parse the extracted block, which may itself contain block elements parsed <- parseFromString' parseBlocks $ T.concat (first:rest) <> "\n" updateState (\st -> st {stateParserContext = oldContext}) return $ case B.toList parsed of [Para xs] -> B.singleton $ Plain xs [Para xs, BulletList ys] -> B.fromList [Plain xs, BulletList ys] [Para xs, OrderedList s ys] -> B.fromList [Plain xs, OrderedList s ys] [Para xs, DefinitionList ys] -> B.fromList [Plain xs, DefinitionList ys] _ -> parsed orderedList :: PandocMonad m => RSTParser m Blocks orderedList = try $ do (start, style, delim) <- lookAhead (anyOrderedListMarker <* spaceChar) items <- many1 (listItem (orderedListStart style delim)) let items' = compactify items return $ B.orderedListWith (start, style, delim) items' bulletList :: PandocMonad m => RSTParser m Blocks bulletList = B.bulletList . compactify <$> many1 (listItem bulletListStart) -- -- directive (e.g. comment, container, compound-paragraph) -- comment :: Monad m => RSTParser m Blocks comment = try $ do string ".." skipMany1 spaceChar <|> void (lookAhead newline) -- notFollowedBy' directiveLabel -- comment comes after directive so unnec. _ <- anyLine optional indentedBlock optional blanklines return mempty directiveLabel :: Monad m => RSTParser m Text directiveLabel = T.toLower <$> many1TillChar (letter <|> char '-') (try $ string "::") directive :: PandocMonad m => RSTParser m Blocks directive = try $ do string ".." directive' directive' :: PandocMonad m => RSTParser m Blocks directive' = do skipMany1 spaceChar label <- directiveLabel skipMany spaceChar top <- manyChar $ satisfy (/='\n') <|> try (char '\n' <* notFollowedBy' (rawFieldListItem 1) <* many1 (char ' ') <* notFollowedBy blankline) newline fields <- do fieldIndent <- length <$> lookAhead (many (char ' ')) if fieldIndent == 0 then return [] else many $ rawFieldListItem fieldIndent let mbfile = trim <$> lookup "file" fields body <- case mbfile of Just f | label == "raw" -> do currentDir <- takeDirectory . sourceName <$> getPosition fromMaybe mempty <$> readFileFromDirs [currentDir] (T.unpack f) _ -> option "" $ try $ blanklines >> indentedBlock optional blanklines let body' = body <> "\n\n" name = trim $ fromMaybe "" (lookup "name" fields) classes = T.words $ maybe "" trim (lookup "class" fields) keyvals = [(k, trim v) | (k, v) <- fields, k /= "name", k /= "class"] imgAttr cl = (name, classes, alignClasses, widthAttr ++ heightAttr) where alignClasses = T.words $ maybe "" trim (lookup cl fields) <> maybe "" (\x -> "align-" <> trim x) (lookup "align" fields) scale = case trim <$> lookup "scale" fields of Just v -> case T.unsnoc v of Just (vv, '%') -> case safeRead vv of Just (percent :: Double) -> percent / 100.0 Nothing -> 1.0 _ -> case safeRead v of Just (s :: Double) -> s Nothing -> 1.0 Nothing -> 1.0 widthAttr = maybe [] (\x -> [("width", tshow $ scaleDimension scale x)]) $ lookup "width" fields >>= (lengthToDim . T.filter (not . isSpace)) heightAttr = maybe [] (\x -> [("height", tshow $ scaleDimension scale x)]) $ lookup "height" fields >>= (lengthToDim . T.filter (not . isSpace)) case label of "include" -> includeDirective top fields body' "table" -> tableDirective top fields body' "list-table" -> listTableDirective top fields body' "csv-table" -> csvTableDirective top fields body' "line-block" -> lineBlockDirective body' "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body) "role" -> addNewRole top $ map (second trim) fields "container" -> B.divWith (name, "container" : T.words top ++ classes, []) <$> parseFromString' parseBlocks body' "replace" -> B.para <$> -- consumed by substKey parseInlineFromText (trim top) "date" -> B.para <$> do -- consumed by substKey t <- getTimestamp let format = case T.unpack (T.strip top) of [] -> "%Y-%m-%d" x -> x return $ B.text $ T.pack $ formatTime defaultTimeLocale format t "unicode" -> B.para <$> -- consumed by substKey parseInlineFromText (trim $ unicodeTransform top) "compound" -> parseFromString' parseBlocks body' "pull-quote" -> B.blockQuote <$> parseFromString' parseBlocks body' "epigraph" -> B.blockQuote <$> parseFromString' parseBlocks body' "highlights" -> B.blockQuote <$> parseFromString' parseBlocks body' "rubric" -> B.para . B.strong <$> parseInlineFromText top _ | label `elem` ["attention","caution","danger","error","hint", "important","note","tip","warning","admonition"] -> do bod <- parseFromString' parseBlocks $ top <> "\n\n" <> body' let lab = case label of "admonition" -> mempty (T.uncons -> Just (l, ls)) -> B.divWith ("",["title"],[]) (B.para (B.str $ T.cons (toUpper l) ls)) _ -> mempty return $ B.divWith (name,label:classes,keyvals) (lab <> bod) "sidebar" -> do let subtit = maybe "" trim $ lookup "subtitle" fields tit <- B.para . B.strong <$> parseInlineFromText (trim top <> if T.null subtit then "" else ": " <> subtit) bod <- parseFromString' parseBlocks body' return $ B.divWith (name,"sidebar":classes,keyvals) $ tit <> bod "topic" -> do tit <- B.para . B.strong <$> parseInlineFromText top bod <- parseFromString' parseBlocks body' return $ B.divWith (name,"topic":classes,keyvals) $ tit <> bod "default-role" -> mempty <$ updateState (\s -> s { stateRstDefaultRole = case trim top of "" -> stateRstDefaultRole def role -> role }) "highlight" -> mempty <$ updateState (\s -> s { stateRstHighlight = case trim top of "" -> stateRstHighlight def lang -> Just lang }) x | x == "code" || x == "code-block" || x == "sourcecode" -> return $ codeblock name classes (map (second trimr) fields) (trim top) True body "aafig" -> do let attribs = (name, ["aafig"], map (second trimr) fields) return $ B.codeBlockWith attribs $ stripTrailingNewlines body "math" -> return $ B.para $ (case mkAttr name classes fields of attr | attr == nullAttr -> id | otherwise -> B.spanWith attr) $ mconcat $ map B.displayMath $ toChunks $ top <> "\n\n" <> body "figure" -> do (caption, legend) <- parseFromString' extractCaption body' let src = escapeURI $ trim top let (imgident, imgcls, aligncls, imgkvs) = imgAttr "class" let (figclasskv, _) = partition ((== "figclass") . fst) keyvals let figcls = concatMap (T.words . snd) figclasskv let figattr = ("", figcls ++ aligncls, []) let capt = B.caption Nothing (B.plain caption <> legend) let alt = maybe caption (B.text . trim) (lookup "alt" fields) return $ B.figureWith figattr capt $ B.plain (B.imageWith (imgident, imgcls, imgkvs) src "" alt) "image" -> do let src = escapeURI $ trim top let alt = B.str $ maybe "image" trim $ lookup "alt" fields let attr = (ident, cls ++ align, dims) where (ident, cls, align, dims) = imgAttr "class" return $ B.para $ case lookup "target" fields of Just t -> B.link (escapeURI $ trim t) "" $ B.imageWith attr src "" alt Nothing -> B.imageWith attr src "" alt "bibliography" -> pure $ B.divWith ("refs",[],[]) mempty "class" -> do let attrs = (name, T.words (trim top), map (second trimr) fields) -- directive content or the first immediately following element children <- case body of "" -> block _ -> parseFromString' parseBlocks body' return $ case B.toList children of [Header lev attrs' ils] | T.null body -> -- # see #6699 B.headerWith (attrs' <> attrs) lev (B.fromList ils) _ -> B.divWith attrs children other -> do pos <- getPosition logMessage $ SkippedContent (".. " <> other) pos bod <- parseFromString' parseBlocks $ top <> "\n\n" <> body' return $ B.divWith (name, other:classes, keyvals) bod tableDirective :: PandocMonad m => Text -> [(Text, Text)] -> Text -> RSTParser m Blocks tableDirective top fields body = do bs <- parseFromString' parseBlocks body case B.toList bs of [Table attr _ tspecs' thead@(TableHead _ thrs) tbody tfoot] -> do let (aligns', widths') = unzip tspecs' title <- parseFromString' (trimInlines . mconcat <$> many inline) top columns <- getOption readerColumns let numOfCols = case thrs of [] -> 0 (r:_) -> rowLength r let normWidths ws = strictPos . (/ max 1.0 (fromIntegral (columns - numOfCols))) <$> ws let widths = case trim <$> lookup "widths" fields of Just "auto" -> replicate numOfCols ColWidthDefault Just "grid" -> widths' Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ splitTextBy (`elem` (" ," :: String)) specs Nothing -> widths' -- align is not applicable since we can't represent whole table align let tspecs = zip aligns' widths return $ B.singleton $ Table attr (B.caption Nothing (B.plain title)) tspecs thead tbody tfoot _ -> do pos <- getPosition logMessage $ SkippedContent body pos return mempty where -- only valid on the very first row of a table section rowLength (Row _ rb) = sum $ cellLength <$> rb cellLength (Cell _ _ _ (ColSpan w) _) = max 1 w strictPos w | w > 0 = ColWidth w | otherwise = ColWidthDefault -- TODO: :stub-columns:. -- Only the first row becomes the header even if header-rows: > 1, -- since Pandoc doesn't support a table with multiple header rows. -- We don't need to parse :align: as it represents the whole table align. listTableDirective :: PandocMonad m => Text -> [(Text, Text)] -> Text -> RSTParser m Blocks listTableDirective top fields body = do bs <- parseFromString' parseBlocks body title <- parseFromString' (trimInlines . mconcat <$> many inline) top let rows = takeRows $ B.toList bs headerRowsNum = fromMaybe (0 :: Int) $ lookup "header-rows" fields >>= safeRead (headerRow,bodyRows,numOfCols) = case rows of x:xs -> if headerRowsNum > 0 then (x, xs, length x) else ([], rows, length x) _ -> ([],[],0) widths = case trim <$> lookup "widths" fields of Just "auto" -> replicate numOfCols ColWidthDefault Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ splitTextBy (`elem` (" ," :: String)) specs _ -> replicate numOfCols ColWidthDefault toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] return $ B.table (B.simpleCaption $ B.plain title) (zip (replicate numOfCols AlignDefault) widths) (TableHead nullAttr $ toHeaderRow headerRow) [TableBody nullAttr 0 [] $ map toRow bodyRows] (TableFoot nullAttr []) where takeRows [BulletList rows] = map takeCells rows takeRows _ = [] takeCells [BulletList cells] = map B.fromList cells takeCells _ = [] normWidths ws = strictPos . (/ max 1 (sum ws)) <$> ws strictPos w | w > 0 = ColWidth w | otherwise = ColWidthDefault csvTableDirective :: PandocMonad m => Text -> [(Text, Text)] -> Text -> RSTParser m Blocks csvTableDirective top fields rawcsv = do let explicitHeader = trim <$> lookup "header" fields let opts = defaultCSVOptions{ csvDelim = case trim <$> lookup "delim" fields of Just "tab" -> '\t' Just "space" -> ' ' Just (T.unpack -> [c]) -> c _ -> ',' , csvQuote = case trim <$> lookup "quote" fields of Just (T.unpack -> [c]) -> Just c _ -> Just '"' , csvEscape = case trim <$> lookup "escape" fields of Just (T.unpack -> [c]) -> Just c _ -> Nothing , csvKeepSpace = case trim <$> lookup "keepspace" fields of Just "true" -> True _ -> False } let headerRowsNum = fromMaybe (case explicitHeader of Just _ -> 1 :: Int Nothing -> 0 :: Int) $ lookup "header-rows" fields >>= safeRead rawcsv' <- case trim <$> lookup "file" fields `mplus` lookup "url" fields of Just u -> do (bs, _) <- fetchItem u return $ UTF8.toText bs Nothing -> return rawcsv let header' = case explicitHeader of Just h -> parseCSV defaultCSVOptions h Nothing -> Right [] let res = parseCSV opts rawcsv' case (<>) <$> header' <*> res of Left e -> throwError $ fromParsecError (toSources rawcsv') e Right rawrows -> do let parseRow = mapM parseCell rows <- mapM parseRow rawrows let (headerRow,bodyRows,numOfCols) = case rows of x:xs -> if headerRowsNum > 0 then (x, xs, length x) else ([], rows, length x) _ -> ([],[],0) title <- parseFromString' (trimInlines . mconcat <$> many inline) top let strictPos w | w > 0 = ColWidth w | otherwise = ColWidthDefault let normWidths ws = strictPos . (/ max 1 (sum ws)) <$> ws let widths = case trim <$> lookup "widths" fields of Just "auto" -> replicate numOfCols ColWidthDefault Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ splitTextBy (`elem` (" ," :: String)) specs _ -> replicate numOfCols ColWidthDefault let toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] return $ B.table (B.simpleCaption $ B.plain title) (zip (replicate numOfCols AlignDefault) widths) (TableHead nullAttr $ toHeaderRow headerRow) [TableBody nullAttr 0 [] $ map toRow bodyRows] (TableFoot nullAttr []) singleParaToPlain :: Blocks -> Blocks singleParaToPlain bs = case B.toList bs of [Para ils] -> B.fromList [Plain ils] _ -> bs parseCell :: PandocMonad m => Text -> RSTParser m Blocks parseCell t = singleParaToPlain <$> parseFromString' parseBlocks (trim t <> "\n\n") -- TODO: -- - Only supports :format: fields with a single format for :raw: roles, -- change Text.Pandoc.Definition.Format to fix addNewRole :: PandocMonad m => Text -> [(Text, Text)] -> RSTParser m Blocks addNewRole roleText fields = do pos <- getPosition (role, parentRole) <- parseFromString' inheritedRole roleText customRoles <- stateRstCustomRoles <$> getState let getBaseRole (r, f, a) roles = case M.lookup r roles of Just (r', f', a') -> getBaseRole (r', f', a') roles Nothing -> (r, f, a) (baseRole, baseFmt, baseAttr) = getBaseRole (parentRole, Nothing, nullAttr) customRoles fmt = if parentRole == "raw" then lookup "format" fields else baseFmt updateClasses :: [Text] -> [Text] updateClasses oldClasses = let codeLanguageClass = if baseRole == "code" then maybeToList (lookup "language" fields) else [] -- if no ":class:" field is given, the default is the role name classFieldClasses = maybe [role] T.words (lookup "class" fields) -- nub in case role name & language class are the same in nub (classFieldClasses ++ codeLanguageClass ++ oldClasses) attr = let (ident, baseClasses, keyValues) = baseAttr in (ident, updateClasses baseClasses, keyValues) -- warn about syntax we ignore forM_ fields $ \(key, _) -> case key of "language" -> when (baseRole /= "code") $ logMessage $ SkippedContent ":language: [because parent of role is not :code:]" pos "format" -> when (baseRole /= "raw") $ logMessage $ SkippedContent ":format: [because parent of role is not :raw:]" pos _ -> logMessage $ SkippedContent (":" <> key <> ":") pos when (parentRole == "raw" && countKeys "format" > 1) $ logMessage $ SkippedContent ":format: [after first in definition of role]" pos when (parentRole == "code" && countKeys "language" > 1) $ logMessage $ SkippedContent ":language: [after first in definition of role]" pos updateState $ \s -> s { stateRstCustomRoles = M.insert role (baseRole, fmt, attr) customRoles } return mempty where countKeys k = length . filter (== k) . map fst $ fields inheritedRole = (,) <$> roleName <*> ((char '(' *> roleName <* char ')') <|> pure "span") -- Can contain character codes as decimal numbers or -- hexadecimal numbers, prefixed by 0x, x, \x, U+, u, or \u -- or as XML-style hexadecimal character entities, e.g. ᨫ -- or text, which is used as-is. Comments start with .. unicodeTransform :: Text -> Text unicodeTransform t | Just xs <- T.stripPrefix ".." t = unicodeTransform $ T.dropWhile (/= '\n') xs -- comment | Just xs <- T.stripPrefix "0x" t = go "0x" xs | Just xs <- T.stripPrefix "x" t = go "x" xs | Just xs <- T.stripPrefix "\\x" t = go "\\x" xs | Just xs <- T.stripPrefix "U+" t = go "U+" xs | Just xs <- T.stripPrefix "u" t = go "u" xs | Just xs <- T.stripPrefix "\\u" t = go "\\u" xs | Just xs <- T.stripPrefix "&#x" t = maybe ("&#x" <> unicodeTransform xs) -- drop semicolon (\(c,s) -> T.cons c $ unicodeTransform $ T.drop 1 s) $ extractUnicodeChar xs | Just (x, xs) <- T.uncons t = T.cons x $ unicodeTransform xs | otherwise = "" where go pref zs = maybe (pref <> unicodeTransform zs) (\(c,s) -> T.cons c $ unicodeTransform s) $ extractUnicodeChar zs extractUnicodeChar :: Text -> Maybe (Char, Text) extractUnicodeChar s = fmap (\c -> (c,rest)) mbc where (ds,rest) = T.span isHexDigit s mbc = safeRead ("'\\x" <> ds <> "'") extractCaption :: PandocMonad m => RSTParser m (Inlines, Blocks) extractCaption = do capt <- trimInlines . mconcat <$> many inline legend <- optional blanklines >> (mconcat <$> many block) return (capt,legend) -- divide string by blanklines, and surround with -- \begin{aligned}...\end{aligned} if needed. toChunks :: Text -> [Text] toChunks = dropWhile T.null . map (addAligned . trim . T.unlines) . splitBy (T.all (`elem` (" \t" :: String))) . T.lines -- we put this in an aligned environment if it contains \\, see #4254 where addAligned s = if "\\\\" `T.isInfixOf` s then "\\begin{aligned}\n" <> s <> "\n\\end{aligned}" else s codeblock :: Text -> [Text] -> [(Text, Text)] -> Text -> Bool -> Text -> Blocks codeblock ident classes fields lang rmTrailingNewlines body = B.codeBlockWith attribs $ stripTrailingNewlines' body where stripTrailingNewlines' = if rmTrailingNewlines then stripTrailingNewlines else id attribs = (ident, classes', kvs) classes' = lang : ["numberLines" | isJust (lookup "number-lines" fields)] ++ classes kvs = [(k,v) | (k,v) <- fields, k /= "number-lines", k /= "class", k /= "id", k /= "name"] ++ case lookup "number-lines" fields of Just v | not (T.null v) -> [("startFrom", v)] _ -> [] -- | Creates element attributes from a name, list of classes, and fields. -- Removes fields named @name@, @id@, or @class@. mkAttr :: Text -> [Text] -> [(Text, Text)] -> Attr mkAttr ident classes fields = (ident, classes, fields') where fields' = [(k, v') | (k, v) <- fields , let v' = trimr v , k /= "name", k /= "id", k /= "class"] --- --- note block --- noteBlock :: Monad m => RSTParser m Blocks noteBlock = try $ do (ref, raw) <- noteBlock' noteMarker updateState $ \s -> s { stateNotes = (ref, raw) : stateNotes s } return mempty citationBlock :: Monad m => RSTParser m Blocks citationBlock = try $ do (ref, raw) <- noteBlock' citationMarker updateState $ \s -> s { stateCitations = M.insert ref raw (stateCitations s), stateKeys = M.insert (toKey ref) (("#" <> ref,""), ("",["citation"],[])) (stateKeys s) } return mempty noteBlock' :: Monad m => RSTParser m Text -> RSTParser m (Text, Text) noteBlock' marker = try $ do string ".." spaceChar >> skipMany spaceChar ref <- marker first <- (spaceChar >> skipMany spaceChar >> anyLine) <|> (newline >> return "") blanks <- option "" blanklines rest <- option "" indentedBlock let raw = first <> "\n" <> blanks <> rest <> "\n" return (ref, raw) citationMarker :: Monad m => RSTParser m Text citationMarker = do char '[' res <- simpleReferenceName char ']' return res noteMarker :: Monad m => RSTParser m Text noteMarker = do char '[' res <- many1Char digit <|> try (char '#' >> liftM ("#" <>) simpleReferenceName) <|> countChar 1 (oneOf "#*") char ']' return res -- -- reference key -- quotedReferenceName :: PandocMonad m => RSTParser m Text quotedReferenceName = try $ do char '`' >> notFollowedBy (char '`') -- `` means inline code! manyTillChar anyChar (char '`') -- Simple reference names are single words consisting of alphanumerics -- plus isolated (no two adjacent) internal hyphens, underscores, -- periods, colons and plus signs; no whitespace or other characters -- are allowed. simpleReferenceName :: Monad m => ParsecT Sources st m Text simpleReferenceName = do x <- alphaNum xs <- many $ alphaNum <|> try (oneOf "-_:+." <* lookAhead alphaNum) return $ T.pack (x:xs) referenceName :: PandocMonad m => RSTParser m Text referenceName = quotedReferenceName <|> simpleReferenceName referenceKey :: PandocMonad m => RSTParser m Blocks referenceKey = do choice [substKey, anonymousKey, regularKey] optional blanklines return mempty targetURI :: Monad m => ParsecT Sources st m Text targetURI = do skipSpaces optional $ try $ newline >> notFollowedBy blankline contents <- trim <$> many1Char (satisfy (/='\n') <|> try (newline >> many1 spaceChar >> noneOf " \t\n")) blanklines return $ stripBackticks contents where stripBackticks t | Just xs <- T.stripSuffix "`_" t = T.dropWhile (=='`') xs <> "_" | Just _ <- T.stripSuffix "_" t = t | otherwise = escapeURI t substKey :: PandocMonad m => RSTParser m () substKey = try $ do string ".." skipMany1 spaceChar (alt,ref) <- withRaw $ trimInlines . mconcat <$> enclosed (char '|') (char '|') inline res <- B.toList <$> directive' bls <- case res of -- use alt unless :alt: attribute on image: [Para [Image attr [Str "image"] (src,tit)]] -> return $ B.para $ B.imageWith attr src tit alt [Para [Link _ [Image attr [Str "image"] (src,tit)] (src',tit')]] -> return $ B.para $ B.link src' tit' (B.imageWith attr src tit alt) _ -> return $ B.fromList res let key = toKey $ stripFirstAndLast ref updateState $ \s -> s{ stateSubstitutions = M.insert key bls $ stateSubstitutions s } anonymousKey :: PandocMonad m => RSTParser m () anonymousKey = try $ do oneOfStrings [".. __:", "__"] skipMany1 spaceChar src <- targetURI -- we need to ensure that the keys are ordered by occurrence in -- the document. numKeys <- M.size . stateKeys <$> getState let key = toKey $ "_" <> T.pack (printf "%04d" numKeys) updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } referenceNames :: PandocMonad m => RSTParser m [Text] referenceNames = do let rn = try $ do string ".. _" ref <- quotedReferenceName <|> manyChar ( noneOf "\\:\n" <|> try (char '\n' <* string " " <* notFollowedBy blankline) <|> try (char '\\' *> char ':') <|> try (char ':' <* lookAhead alphaNum) ) char ':' return ref first <- rn rest <- many (try (blanklines *> rn)) return (first:rest) regularKey :: PandocMonad m => RSTParser m () regularKey = try $ do -- we allow several references to the same URL, e.g. -- .. _hello: -- .. _goodbye: url.com refs <- referenceNames src <- targetURI guard $ not (T.null src) let keys = map toKey refs forM_ keys $ \key -> updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } anchor :: PandocMonad m => RSTParser m Blocks anchor = try $ do refs <- referenceNames blanklines forM_ refs $ \rawkey -> updateState $ \s -> s { stateKeys = M.insert (toKey rawkey) (("#" <> rawkey,""), nullAttr) (stateKeys s) } b <- block let addDiv ref = B.divWith (ref, [], []) let emptySpanWithId id' = Span (id',[],[]) [] -- put identifier on next block: case B.toList b of [Header lev (_,classes,kvs) txt] -> case reverse refs of [] -> return b (r:rs) -> return $ B.singleton $ Header lev (r,classes,kvs) (txt ++ map emptySpanWithId rs) -- we avoid generating divs for headers, -- because it hides them from promoteHeader, see #4240 _ -> return $ foldr addDiv b refs -- -- tables -- -- General tables TODO: -- - figure out if leading spaces are acceptable and if so, add -- support for them -- -- Simple tables TODO: -- - multiline support -- - ensure that rightmost column span does not need to reach end -- - require at least 2 columns dashedLine :: Monad m => Char -> ParsecT Sources st m (Int, Int) dashedLine ch = do dashes <- many1 (char ch) sp <- many (char ' ') return (length dashes, length sp) simpleDashedLines :: Monad m => Char -> ParsecT Sources st m [(Int,Int)] simpleDashedLines ch = try $ do lines' <- many1 (dashedLine ch) skipMany spaceChar newline return $ addSpaces lines' where addSpaces [] = [] addSpaces [(dashes, _)] = [(dashes, dashes)] -- Don't count trailing whitespaces addSpaces ((dashes, sp) : moreLines) = (dashes, dashes + sp) : addSpaces moreLines -- Parse a table row separator simpleTableSep :: Monad m => Char -> RSTParser m () simpleTableSep ch = void (simpleDashedLines ch) -- Parse a table footer simpleTableFooter :: Monad m => RSTParser m () simpleTableFooter = try $ simpleTableSep '=' >> void blanklines -- Parse a raw line and split it into chunks by indices. simpleTableRawLine :: Monad m => [Int] -> RSTParser m [(Text, ColSpan)] simpleTableRawLine indices = do row <- rowWithOptionalColSpan case simpleTableSplitLine indices row of Just rowLine -> return rowLine Nothing -> Prelude.fail "col spans don't match" simpleTableRawLineWithInitialEmptyCell :: Monad m => [Int] -> RSTParser m [(Text, ColSpan)] simpleTableRawLineWithInitialEmptyCell indices = try $ do cs <- simpleTableRawLine indices let isEmptyCell = T.all (\c -> c == ' ' || c == '\t') case cs of c:_ | isEmptyCell (fst c) -> return cs _ -> mzero -- Parse a table row and return a list of blocks (columns). simpleTableRow :: PandocMonad m => [Int] -> RSTParser m [(Blocks, RowSpan, ColSpan)] simpleTableRow indices = do notFollowedBy' (void blanklines <|> simpleTableFooter) firstLine <- simpleTableRawLine indices conLines <- many $ simpleTableRawLineWithInitialEmptyCell indices let cols = map T.unlines . transpose $ (map fst firstLine) : (map (map fst) conLines) ++ [replicate (length indices) "" | not (null conLines)] let rowParser = mapM parseCell cols fmap (\blocks -> zip3 blocks (repeat 1) (map snd firstLine)) rowParser simpleTableSplitLine :: [Int] -> (Text, Maybe [Int]) -> Maybe [(Text, ColSpan)] simpleTableSplitLine indices (line, maybeColspanIndices) = fmap (zip tableLines) columnSpans where splitTableLines lineIndices = map trimr $ drop 1 $ splitTextByIndices (init lineIndices) line (tableLines, columnSpans) = case maybeColspanIndices of Nothing -> (splitTableLines indices, Just $ repeat 1) Just colSpanIndices -> (splitTableLines colSpanIndices, colSpans indices colSpanIndices) simpleTableHeader :: PandocMonad m => Bool -- ^ Headerless table -> RSTParser m ([[(Blocks, RowSpan, ColSpan)]], [Alignment], [Int]) simpleTableHeader headless = try $ do optional blanklines dashes <- simpleDashedLines '=' rawContent <- if headless then return [("", Nothing)] else many1 $ notFollowedBy (simpleDashedLines '=') >> rowWithOptionalColSpan unless headless $ simpleTableSep '=' let (lines', indices) = dashedLinesToLinesWithIndices dashes let aligns = replicate (length lines') AlignDefault let rawHeads = if headless then [] else map (simpleTableSplitLine indices) rawContent when (any isNothing rawHeads) $ Prelude.fail "col spans don't match" let justRawHeads = catMaybes rawHeads let rawHeads' = map fst <$> justRawHeads let columnSpans = map snd <$> justRawHeads heads <- mapM (mapM $ parseFromString' (mconcat <$> many plain) . trim) rawHeads' let headsWithSpans = zipWith3 zip3 heads singleRowSpans columnSpans return (headsWithSpans, aligns, indices) rowWithOptionalColSpan :: Monad m => RSTParser m (Text, Maybe [Int]) rowWithOptionalColSpan = do line <- anyLine colSpanHyphens <- optionMaybe $ simpleDashedLines '-' let colSpan = fmap colSpanFromHyphens colSpanHyphens return (line, colSpan) where colSpanFromHyphens colSpanHyphens = snd $ dashedLinesToLinesWithIndices colSpanHyphens -- Parse a simple table. simpleTable :: PandocMonad m => Bool -- ^ Headerless table -> RSTParser m Blocks simpleTable headless = do let wrapIdFst (a, b, c) = (Identity a, b, c) wrapId = fmap Identity tbl <- runIdentity <$> tableWithSpans (wrapIdFst <$> simpleTableHeader headless) (wrapId <$> simpleTableRow) sep simpleTableFooter -- Simple tables get 0s for relative column widths (i.e., use default) case B.toList tbl of [Table attr cap spec th tb tf] -> return $ B.singleton $ Table attr cap (rewidth spec) th tb tf _ -> throwError $ PandocShouldNeverHappenError "tableWith returned something unexpected" where sep = return () -- optional (simpleTableSep '-') rewidth = fmap $ fmap $ const ColWidthDefault gridTable :: PandocMonad m => RSTParser m Blocks gridTable = runIdentity <$> gridTableWith (Identity <$> parseBlocks) table :: PandocMonad m => RSTParser m Blocks table = gridTable <|> simpleTable False <|> simpleTable True "table" -- -- inline -- inline :: PandocMonad m => RSTParser m Inlines inline = (note -- can start with whitespace, so try before ws <|> do notAfterString >>= guard (link <|> inlineAnchor <|> strong <|> emph) <|> code <|> subst <|> interpretedRole <|> inlineContent) "inline" -- strings, spaces and other characters that can appear either by -- themselves or within inline markup inlineContent :: PandocMonad m => RSTParser m Inlines inlineContent = choice [ whitespace , str , endline , smart , escapedChar , symbol ] "inline content" parseInlineFromText :: PandocMonad m => Text -> RSTParser m Inlines parseInlineFromText = parseFromString' (trimInlines . mconcat <$> many inline) escapedChar :: Monad m => RSTParser m Inlines escapedChar = do c <- escaped anyChar if c == ' ' || c == '\n' || c == '\r' -- '\ ' is null in RST then return mempty else do unless (canPrecedeOpener c) updateLastStrPos return $ B.str $ T.singleton c canPrecedeOpener :: Char -> Bool canPrecedeOpener c = generalCategory c `elem` [OpenPunctuation, InitialQuote, FinalQuote, DashPunctuation, OtherSymbol] symbol :: Monad m => RSTParser m Inlines symbol = do c <- oneOf specialChars unless (canPrecedeOpener c) updateLastStrPos return $ B.str $ T.singleton c -- parses inline code, between codeStart and codeEnd code :: Monad m => RSTParser m Inlines code = try $ do string "``" result <- manyTillChar anyChar (try (string "``")) return $ B.code $ trim $ T.unwords $ T.lines result -- succeeds only if we're not right after a str (ie. in middle of word) atStart :: Monad m => RSTParser m a -> RSTParser m a atStart p = do pos <- getPosition st <- getState -- single quote start can't be right after str guard $ stateLastStrPos st /= Just pos p emph :: PandocMonad m => RSTParser m Inlines emph = B.emph . trimInlines . mconcat <$> enclosed (atStart $ char '*') (char '*') inlineContent strong :: PandocMonad m => RSTParser m Inlines strong = B.strong . trimInlines . mconcat <$> enclosed (atStart $ string "**") (try $ string "**") inlineContent -- Note, this doesn't precisely implement the complex rule in -- http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules -- but it should be good enough for most purposes -- -- TODO: -- - Classes are silently discarded in addNewRole -- - Allows direct use of the :raw: role, rST only allows inherited use. interpretedRole :: PandocMonad m => RSTParser m Inlines interpretedRole = try $ do (role, contents) <- roleBefore <|> roleAfter renderRole contents Nothing role nullAttr renderRole :: PandocMonad m => Text -> Maybe Text -> Text -> Attr -> RSTParser m Inlines renderRole contents fmt role attr = case role of "sup" -> return $ B.superscript $ treatAsText contents "superscript" -> return $ B.superscript $ treatAsText contents "sub" -> return $ B.subscript $ treatAsText contents "subscript" -> return $ B.subscript $ treatAsText contents "mark" -> return $ B.spanWith ("",["mark"],[]) $ treatAsText contents "emphasis" -> return $ B.emph $ treatAsText contents "strong" -> return $ B.strong $ treatAsText contents "rfc-reference" -> return $ rfcLink contents "RFC" -> return $ rfcLink contents "pep-reference" -> return $ pepLink contents "PEP" -> return $ pepLink contents "literal" -> return $ B.codeWith attr contents "math" -> return $ B.math contents "title-reference" -> titleRef contents "title" -> titleRef contents "t" -> titleRef contents "code" -> return $ B.codeWith attr contents "span" -> return $ B.spanWith attr $ treatAsText contents "raw" -> return $ B.rawInline (fromMaybe "" fmt) contents custom | Just citeType <- T.stripPrefix "cite" custom -> cite citeType contents | otherwise -> do customRoles <- stateRstCustomRoles <$> getState case M.lookup custom customRoles of Just (newRole, newFmt, newAttr) -> renderRole contents newFmt newRole newAttr Nothing -> -- undefined role return $ B.codeWith ("",["interpreted-text"],[("role",role)]) contents where titleRef ref = return $ B.spanWith ("",["title-ref"],[]) $ treatAsText ref rfcLink rfcNo = B.link rfcUrl ("RFC " <> rfcNo) $ B.str ("RFC " <> rfcNo) where rfcUrl = "http://www.faqs.org/rfcs/rfc" <> rfcNo <> ".html" pepLink pepNo = B.link pepUrl ("PEP " <> pepNo) $ B.str ("PEP " <> pepNo) where padNo = T.replicate (4 - T.length pepNo) "0" <> pepNo pepUrl = "http://www.python.org/dev/peps/pep-" <> padNo <> "/" treatAsText = B.text . handleEscapes handleEscapes = T.concat . removeSpace . T.splitOn "\\" where headSpace t = fromMaybe t $ T.stripPrefix " " t removeSpace (x:xs) = x : map headSpace xs removeSpace [] = [] cite :: PandocMonad m => Text -> Text -> RSTParser m Inlines cite citeType rawcite = do let citations = case map parseCite (T.splitOn "," rawcite) of (c:cs) | citeType == ":t" || citeType == ":ct" -> c{ citationMode = AuthorInText } : cs | citeType == ":year" || citeType == ":yearpar" -> c{ citationMode = SuppressAuthor } : cs cs -> cs pure $ B.cite citations (B.str rawcite) parseCite :: Text -> Citation parseCite t = let (_, pref, suff, ident) = T.foldl go (ParseStart, "", "", "") t in Citation{citationId = ident ,citationPrefix = B.toList $ B.text pref ,citationSuffix = B.toList $ B.text suff ,citationMode = NormalCitation ,citationNoteNum = 0 ,citationHash = 0} where go (ParseStart, p, s, i) '{' = (ParsePrefix, p, s, i) go (ParseStart, p, s, i) c = (ParseId, p, s, T.snoc i c) go (ParsePrefix, p, s, i) '}' = (ParseId, p, s, i) go (ParsePrefix, p, s, i) c = (ParsePrefix, T.snoc p c, s, i) go (ParseId, p, s, i) '{' = (ParseSuffix, p, s, i) go (ParseId, p, s, i) c = (ParseId, p, s, T.snoc i c) go (ParseSuffix, p, s, i) '}' = (ParseSuffix, p, s, i) go (ParseSuffix, p, s, i) c = (ParseSuffix, p, T.snoc s c, i) data ParseCiteState = ParseStart | ParsePrefix | ParseSuffix | ParseId deriving (Show) -- single words consisting of alphanumerics plus isolated (no two adjacent) -- internal hyphens, underscores, periods, colons and plus signs; -- no whitespace or other characters are allowed roleName :: PandocMonad m => RSTParser m Text roleName = many1Char (alphaNum <|> try (oneOf "-_.:+" <* lookAhead alphaNum)) roleMarker :: PandocMonad m => RSTParser m Text roleMarker = char ':' *> roleName <* char ':' roleBefore :: PandocMonad m => RSTParser m (Text,Text) roleBefore = try $ do role <- roleMarker contents <- unmarkedInterpretedText return (role,contents) roleAfter :: PandocMonad m => RSTParser m (Text,Text) roleAfter = try $ do contents <- unmarkedInterpretedText role <- roleMarker <|> (stateRstDefaultRole <$> getState) return (role,contents) unmarkedInterpretedText :: PandocMonad m => RSTParser m Text unmarkedInterpretedText = try $ do atStart (char '`') contents <- mconcat <$> many1 ( many1 (noneOf "`\\\n") <|> (char '\\' >> ((\c -> ['\\',c]) <$> noneOf "\n")) <|> (string "\n" <* notFollowedBy blankline) <|> try (string "`" <* notFollowedBy (void roleMarker) <* lookAhead (satisfy isAlphaNum)) ) char '`' return $ T.pack contents whitespace :: PandocMonad m => RSTParser m Inlines whitespace = B.space <$ skipMany1 spaceChar "whitespace" str :: Monad m => RSTParser m Inlines str = do let strChar = noneOf ("\t\n " ++ specialChars) result <- many1Char strChar updateLastStrPos return $ B.str result -- an endline character that can be treated as a space, not a structural break endline :: Monad m => RSTParser m Inlines endline = try $ do newline notFollowedBy blankline -- parse potential list-starts at beginning of line differently in a list: st <- getState when (stateParserContext st == ListItemState) $ notFollowedBy (anyOrderedListMarker >> spaceChar) >> notFollowedBy' bulletListStart return B.softbreak -- -- links -- link :: PandocMonad m => RSTParser m Inlines link = choice [explicitLink, referenceLink, autoLink] "link" explicitLink :: PandocMonad m => RSTParser m Inlines explicitLink = try $ do char '`' notFollowedBy (char '`') -- `` marks start of inline code label' <- trimInlines . mconcat <$> manyTill (notFollowedBy (char '`') >> inlineContent) (char '<') src <- trim . T.pack . filter (/= '\n') <$> -- see #10279 manyTill (noneOf ">\n" <|> (char '\n' <* notFollowedBy blankline)) (char '>') skipSpaces string "`_" optional $ char '_' -- anonymous form let src' | isURI src = escapeURI src | otherwise = case T.unsnoc src of Just (xs, '_') -> "##REF##" <> xs _ -> src let label'' = if label' == mempty then B.str src else label' let key = toKey $ stringify label' unless (key == Key mempty) $ do updateState $ \s -> s{ stateKeys = M.insert key ((src',""), nullAttr) $ stateKeys s } return $ B.linkWith nullAttr src' "" label'' citationName :: PandocMonad m => RSTParser m Text citationName = do raw <- citationMarker return $ "[" <> raw <> "]" -- We store the reference link label as the link target, -- preceded by '##REF##'. This is replaced after the AST -- has been built by the resolved reference. referenceLink :: PandocMonad m => RSTParser m Inlines referenceLink = try $ do ref <- (referenceName <|> citationName) <* char '_' isAnonymous <- (True <$ char '_') <|> pure False eof <|> notFollowedBy alphaNum let ref' = if isAnonymous then "_" else ref pure $ B.linkWith nullAttr ("##REF##" <> ref') "" (B.text ref) -- We keep a list of oldkeys so we can detect lookup loops. lookupKey :: PandocMonad m => [Key] -> Key -> RSTParser m ((Text, Text), Attr) lookupKey oldkeys key = do pos <- getPosition state <- getState let keyTable = stateKeys state case M.lookup key keyTable of Nothing -> do let Key key' = key logMessage $ ReferenceNotFound key' pos return (("",""),nullAttr) -- check for keys of the form link_, which need to be resolved: Just ((u, ""),_) | T.length u > 1, T.last u == '_', T.head u /= '#' -> do let rawkey = T.init u let newkey = toKey rawkey if newkey `elem` oldkeys then do -- TODO the pos is not going to be accurate -- because we're calling this after the AST is -- constructed. Probably good to remove that -- parameter form CircularReference at some point. logMessage $ CircularReference rawkey pos return (("",""),nullAttr) else lookupKey (key:oldkeys) newkey Just val -> return val autoURI :: Monad m => RSTParser m Inlines autoURI = do (orig, src) <- uri return $ B.link src "" $ B.str orig autoEmail :: Monad m => RSTParser m Inlines autoEmail = do (orig, src) <- emailAddress return $ B.link src "" $ B.str orig autoLink :: PandocMonad m => RSTParser m Inlines autoLink = autoURI <|> autoEmail subst :: PandocMonad m => RSTParser m Inlines subst = try $ do (_,ref) <- withRaw $ enclosed (char '|') (char '|') inline let substlink = B.linkWith nullAttr ("##SUBST##" <> ref) "" (B.text ref) reflink <- option False (True <$ char '_') if reflink then do let linkref = T.drop 1 $ T.dropEnd 1 ref return $ B.linkWith nullAttr ("##REF##" <> linkref) "" substlink else return substlink note :: PandocMonad m => RSTParser m Inlines note = try $ do optional whitespace ref <- noteMarker char '_' pure $ B.linkWith nullAttr ("##NOTE##" <> ref) "" (B.text ref) smart :: PandocMonad m => RSTParser m Inlines smart = smartPunctuation inline inlineAnchor :: PandocMonad m => RSTParser m Inlines inlineAnchor = try $ do char '_' name <- quotedReferenceName <|> simpleReferenceName let ident = textToIdentifier mempty name updateState $ \s -> s{ stateKeys = M.insert (toKey name) (("#" <> ident, ""), nullAttr) (stateKeys s) } pure $ B.spanWith (ident,[],[]) (B.text name) dashedLinesToLinesWithIndices :: [(Int, Int)] -> ([Int], [Int]) dashedLinesToLinesWithIndices dashes = let lines' = map snd dashes indices = scanl (+) 0 lines' in (lines', indices) -- | Determines column spans by appying indices of a table border with column span indices. -- -- The indices need to align. colSpans :: [Int] -> [Int] -> Maybe [ColSpan] colSpans [] [] = Just [] colSpans [] _ = Nothing colSpans _ [] = Nothing colSpans (index : indices) colSpanIndices@(colIndex : colSpanIndicesTail) | index /= colIndex = colSpans indices colSpanIndices | otherwise = -- For a matching index start counting the column spans. let (spanCount, remainingIndices, remainingColSpanindices) = colSpanCount indices colSpanIndicesTail 1 in (:) spanCount <$> colSpans remainingIndices remainingColSpanindices -- | Counts column spans by consuming all non-matching indices until a matching one is encountered. -- -- If the indices match, the end of a column span has been encountered and the -- column count can be returned. Otherwise, if the indices don't match, add to -- the span count until a matching index is found. colSpanCount :: [Int] -> [Int] -> ColSpan -> (ColSpan, [Int], [Int]) colSpanCount [] colSpanIndices spanCount = (spanCount, [], colSpanIndices) colSpanCount _ [] spanCount = (spanCount, [], []) colSpanCount indices@(index : indicesTail) colSpanIndices@(colIndex : colSpanIndicesTail) spanCount | index == colIndex = case colSpanIndicesTail of [] -> (spanCount, indicesTail, colSpanIndicesTail) _ -> (spanCount, indices, colSpanIndices) | otherwise = colSpanCount indicesTail colSpanIndices $ spanCount + 1 ================================================ FILE: src/Text/Pandoc/Readers/RTF.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.RTF Copyright : Copyright (C) 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane () Stability : alpha Portability : portable Conversion of RTF documents 'Pandoc' document. We target version 1.5 of the RTF spec. -} module Text.Pandoc.Readers.RTF (readRTF) where import qualified Data.IntMap as IntMap import qualified Data.Sequence as Seq import Control.Monad import Control.Monad.Except (throwError) import Crypto.Hash (hashWith, SHA1(SHA1)) import qualified Data.List as L import Data.Word (Word8, Word16) import Data.Default import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Read as TR import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad (..), insertMedia, report) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Logging (LogMessage(UnsupportedCodePage)) import Text.Pandoc.Shared (tshow) import Data.Char (isAlphaNum, chr, isAscii, isLetter, isSpace, ord) import qualified Data.ByteString.Lazy as BL import Data.Maybe (mapMaybe, fromMaybe) import Safe (lastMay, initSafe, headDef) -- import Debug.Trace -- TODO: -- [ ] more complex table features -- -- | Read RTF from an input string and return a Pandoc document. readRTF :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readRTF opts s = do let sources = toSources s parsed <- readWithM parseRTF def{ sOptions = opts } sources case parsed of Left e -> throwError e Right d -> return d data CharSet = ANSI | Mac | Pc | Pca deriving (Show, Eq) -- first index is the list (or override) id, second is the list level type ListTable = IntMap.IntMap ListLevelTable type ListLevelTable = IntMap.IntMap ListType data RTFState = RTFState { sOptions :: ReaderOptions , sCharSet :: CharSet , sGroupStack :: [Properties] , sListStack :: [List] , sCurrentCell :: Blocks , sTableRows :: [TableRow] -- reverse order , sTextContent :: [(Properties, Text)] , sMetadata :: [(Text, Inlines)] , sFontTable :: FontTable , sStylesheet :: Stylesheet , sListTable :: ListTable , sListOverrideTable :: ListTable , sEatChars :: Int } deriving (Show) instance Default RTFState where def = RTFState { sOptions = def , sCharSet = ANSI , sGroupStack = [] , sListStack = [] , sCurrentCell = mempty , sTableRows = [] , sTextContent = [] , sMetadata = [] , sFontTable = mempty , sStylesheet = mempty , sListTable = mempty , sListOverrideTable = mempty , sEatChars = 0 } type FontTable = IntMap.IntMap FontFamily data FontFamily = Roman | Swiss | Modern | Script | Decor | Tech | Bidi deriving (Show, Eq) data StyleType = ParagraphStyle | SectionStyle | CharStyle | TableStyle deriving (Show, Eq) data Style = Style { styleNum :: Int , styleType :: StyleType , styleBasedOn :: Maybe Int , styleName :: Text , styleFormatting :: [Tok] } deriving (Show, Eq) type Stylesheet = IntMap.IntMap Style data PictType = Emfblip | Pngblip | Jpegblip deriving (Show, Eq) data Pict = Pict { picType :: Maybe PictType , picWidth :: Maybe Int , picHeight :: Maybe Int , picWidthGoal :: Maybe Int , picHeightGoal :: Maybe Int , picBinary :: Bool , picData :: Text , picName :: Text , picBytes :: BL.ByteString } deriving (Show, Eq) instance Default Pict where def = Pict { picType = Nothing , picWidth = Nothing , picHeight = Nothing , picWidthGoal = Nothing , picHeightGoal = Nothing , picBinary = False , picData = mempty , picName = mempty , picBytes = mempty } data Properties = Properties { gBold :: Bool , gItalic :: Bool , gCaps :: Bool , gDeleted :: Bool , gSub :: Bool , gSuper :: Bool , gSmallCaps :: Bool , gUnderline :: Bool , gHyperlink :: Maybe Text , gAnchor :: Maybe Text , gImage :: Maybe Pict , gFontFamily :: Maybe FontFamily , gHidden :: Bool , gUC :: Int -- number of ansi chars to skip after unicode char , gFootnote :: Maybe Blocks , gOutlineLevel :: Maybe ListLevel , gListOverride :: Maybe Override , gListLevel :: Maybe Int , gInTable :: Bool } deriving (Show, Eq) instance Default Properties where def = Properties { gBold = False , gItalic = False , gCaps = False , gDeleted = False , gSub = False , gSuper = False , gSmallCaps = False , gUnderline = False , gHyperlink = Nothing , gAnchor = Nothing , gImage = Nothing , gFontFamily = Nothing , gHidden = False , gUC = 1 , gFootnote = Nothing , gOutlineLevel = Nothing , gListOverride = Nothing , gListLevel = Nothing , gInTable = False } type RTFParser m = ParsecT Sources RTFState m data ListType = Bullet | Ordered ListAttributes deriving (Show, Eq) type Override = Int type ListLevel = Int data List = List Override ListLevel ListType [Blocks] -- items in reverse order deriving (Show, Eq) newtype TableRow = TableRow [Blocks] -- cells in reverse order deriving (Show, Eq) parseRTF :: PandocMonad m => RTFParser m Pandoc parseRTF = do skipMany nl bs <- many tok >>= foldM processTok mempty >>= emitBlocks unclosed <- closeContainers let doc = B.doc $ bs <> unclosed kvs <- sMetadata <$> getState pure $ foldr (uncurry B.setMeta) doc kvs data Tok = Tok !SourcePos !TokContents deriving (Show, Eq) data TokContents = ControlWord !Text !(Maybe Int) | ControlSymbol !Char | UnformattedText !Text | BinData !BL.ByteString | HexVals [Word8] | Grouped [Tok] deriving (Show, Eq) tok :: PandocMonad m => RTFParser m Tok tok = do pos <- getPosition Tok pos <$!> ((controlThing <|> unformattedText <|> grouped) <* skipMany nl) where controlThing = do char '\\' *> ( controlWord <|> (HexVals <$> many1 hexVal) <|> (ControlSymbol <$> anyChar) ) controlWord = do name <- letterSequence param <- parameter <* optional delimChar case name of "bin" -> do let n = fromMaybe 0 param spaces -- NOTE: We assume here that if the document contains binary -- data, it will not be valid UTF-8 and hence it will have been -- read as latin1, so we can recover the data in the following -- way. This is probably not completely reliable, but I don't -- know if we can do better without making this reader take -- a ByteString input. dat <- BL.pack . map (fromIntegral . ord) <$> count n anyChar return $! BinData dat _ -> return $! ControlWord name param parameter = do hyph <- option False $ True <$ char '-' rest <- many digit if null rest then return Nothing else do let pstr = T.pack rest case TR.decimal pstr of Right (!i,_) -> return $! Just $! if hyph then (-1) * i else i _ -> return Nothing hexVal = do char '\'' x <- hexDigit y <- hexDigit return $ hexToWord (T.pack [x,y]) letterSequence = T.pack <$> many1 (satisfy (\c -> isAscii c && isLetter c)) unformattedText = do ts <- filter (\c -> c /= '\r' && c /= '\n') <$> ( many1 (satisfy (\c -> not (isSpecial c) || c == '\r' || c == '\n'))) return $! UnformattedText $! T.pack ts grouped = do char '{' skipMany nl ts <- manyTill tok (char '}') case ts of Tok _ (ControlWord "rtf" (Just 1)) : _ -> do setInput mempty -- discard remaining input: content after the \rtf1 -- group can be non-RTF _ -> return () return $! Grouped ts nl :: PandocMonad m => RTFParser m () nl = void (char '\n' <|> char '\r') isSpecial :: Char -> Bool isSpecial '{' = True isSpecial '}' = True isSpecial '\\' = True isSpecial '\n' = True isSpecial _ = False delimChar :: PandocMonad m => RTFParser m Char delimChar = satisfy (\c -> not (isAlphaNum c || isSpecial c)) modifyGroup :: PandocMonad m => (Properties -> Properties) -> RTFParser m () modifyGroup f = updateState $ \st -> st{ sGroupStack = case sGroupStack st of [] -> [] (x:xs) -> f x : xs } addFormatting :: (Properties, Text) -> Inlines addFormatting (_, "\n") = B.linebreak addFormatting (props, _) | gHidden props = mempty addFormatting (props, _) | Just bs <- gFootnote props = B.note bs addFormatting (props, txt) = (if gBold props then B.strong else id) . (if gItalic props then B.emph else id) . (if gDeleted props then B.strikeout else id) . (if gSub props then B.subscript else id) . (if gSuper props then B.superscript else id) . (if gSmallCaps props then B.smallcaps else id) . (if gUnderline props then B.underline else id) . (case gHyperlink props of Nothing -> id Just linkdest -> B.link linkdest mempty) . (case gAnchor props of Nothing -> id Just ident -> B.spanWith (ident,[],[])) . (case gFontFamily props of Just Modern -> B.code _ -> case gImage props of Just pict -> let attr = ("",[], (case picWidthGoal pict of Nothing -> [] Just w -> [("width", tshow (fromIntegral w / 1440 :: Double) <> "in")]) ++ (case picHeightGoal pict of Nothing -> [] Just h -> [("height", tshow (fromIntegral h / 1440 :: Double) <> "in")])) in B.imageWith attr (picName pict) "" . B.text Nothing -> B.text) . (if gCaps props then T.toUpper else id) $ txt addText :: PandocMonad m => Text -> RTFParser m () addText t = do gs <- sGroupStack <$> getState let !props = case gs of (x:_) -> x _ -> def updateState (\s -> s{ sTextContent = (props, t) : sTextContent s }) inGroup :: PandocMonad m => RTFParser m a -> RTFParser m a inGroup p = do updateState $ \st -> st{ sGroupStack = case sGroupStack st of [] -> [def] (x:xs) -> (x:x:xs) } -- inherit current group's properties result <- p updateState $ \st -> st{ sGroupStack = case sGroupStack st of [] -> [] -- should not happen (_:xs) -> xs } return result getStyleFormatting :: PandocMonad m => Int -> RTFParser m [Tok] getStyleFormatting stynum = do stylesheet <- sStylesheet <$> getState case IntMap.lookup stynum stylesheet of Nothing -> return [] Just sty -> case styleBasedOn sty of Just i -> (<> styleFormatting sty) <$> getStyleFormatting i Nothing -> return $ styleFormatting sty isMetadataField :: Text -> Bool isMetadataField "title" = True isMetadataField "subject" = True isMetadataField "author" = True isMetadataField "manager" = True isMetadataField "company" = True isMetadataField "operator" = True isMetadataField "category" = True isMetadataField "keywords" = True isMetadataField "comment" = True isMetadataField "doccomm" = True isMetadataField "hlinkbase" = True isMetadataField "generator" = True isMetadataField _ = False isHeaderFooter :: Text -> Bool isHeaderFooter "header" = True isHeaderFooter "headerl" = True isHeaderFooter "headerr" = True isHeaderFooter "headerf" = True isHeaderFooter "footer" = True isHeaderFooter "footerl" = True isHeaderFooter "footerr" = True isHeaderFooter "footerf" = True isHeaderFooter _ = False boolParam :: Maybe Int -> Bool boolParam (Just 0) = False boolParam _ = True isUnderline :: Text -> Bool isUnderline "ul" = True isUnderline "uld" = True isUnderline "uldash" = True isUnderline "uldashd" = True isUnderline "uldashdd" = True isUnderline "uldb" = True isUnderline "ulth" = True isUnderline "ulthd" = True isUnderline "ulthdash" = True isUnderline "ulw" = True isUnderline "ulwave" = True isUnderline _ = False processTok :: PandocMonad m => Blocks -> Tok -> RTFParser m Blocks processTok bs (Tok pos tok') = do setPosition pos case tok' of HexVals{} -> return () UnformattedText{} -> return () _ -> updateState $ \s -> s{ sEatChars = 0 } case tok' of Grouped (Tok _ (ControlSymbol '*') : toks@(firsttok:_)) -> case firsttok of Tok _ (ControlWord "shppict" _) -> inGroup (foldM processTok bs toks) Tok _ (ControlWord "shpinst" _) -> inGroup (foldM processTok bs toks) _ -> bs <$ (do oldTextContent <- sTextContent <$> getState processTok mempty (Tok pos (Grouped toks)) updateState $ \st -> st{ sTextContent = oldTextContent }) Grouped (Tok _ (ControlWord "shp" _) : toks) -> inGroup (foldM processTok bs toks) Grouped [ Tok _ (ControlWord "sp" _) , Tok _ (Grouped [Tok _ (ControlWord "sn" _), Tok _ (UnformattedText sn)]) , Tok _ (Grouped (Tok _ (ControlWord "sv" _) : svtoks)) ] -> case sn of "pib" -> inGroup (foldM processTok bs svtoks) _ -> pure bs Grouped (Tok _ (ControlWord "fonttbl" _) : toks) -> inGroup $ do updateState $ \s -> s{ sFontTable = processFontTable toks } pure bs Grouped (Tok _ (ControlWord "field" _) : toks) -> inGroup $ handleField bs toks Grouped (Tok _ (ControlWord "pict" _) : toks) -> bs <$ inGroup (handlePict toks) Grouped (Tok _ (ControlWord "stylesheet" _) : toks) -> bs <$ inGroup (handleStylesheet toks) Grouped (Tok _ (ControlWord "listtext" _) : _) -> do -- eject any previous list items...sometimes TextEdit -- doesn't put in a \par emitBlocks bs Grouped (Tok _ (ControlWord "pgdsc" _) : _) -> pure bs Grouped (Tok _ (ControlWord "colortbl" _) : _) -> pure bs Grouped (Tok _ (ControlWord "listtable" _) : toks) -> bs <$ inGroup (handleListTable toks) Grouped (Tok _ (ControlWord "listoverridetable" _) : toks) -> bs <$ inGroup (handleListOverrideTable toks) Grouped (Tok _ (ControlWord "wgrffmtfilter" _) : _) -> pure bs Grouped (Tok _ (ControlWord "themedata" _) : _) -> pure bs Grouped (Tok _ (ControlWord "colorschememapping" _) : _) -> pure bs Grouped (Tok _ (ControlWord "datastore" _) : _) -> pure bs Grouped (Tok _ (ControlWord "latentstyles" _) : _) -> pure bs Grouped (Tok _ (ControlWord "pntxta" _) : _) -> pure bs -- TODO Grouped (Tok _ (ControlWord "pntxtb" _) : _) -> pure bs -- TODO Grouped (Tok _ (ControlWord "xmlnstbl" _) : _) -> pure bs Grouped (Tok _ (ControlWord "filetbl" _) : _) -> pure bs Grouped (Tok _ (ControlWord "expandedcolortbl" _) : _) -> pure bs Grouped (Tok _ (ControlWord "listtables" _) : _) -> pure bs Grouped (Tok _ (ControlWord "revtbl" _) : _) -> pure bs Grouped (Tok _ (ControlWord "bkmkstart" _) : Tok _ (UnformattedText t) : _) -> do -- TODO ideally we'd put the span around bkmkstart/end, but this -- is good for now: modifyGroup (\g -> g{ gAnchor = Just $ T.strip t }) pure bs Grouped (Tok _ (ControlWord "bkmkend" _) : _) -> do modifyGroup (\g -> g{ gAnchor = Nothing }) pure bs Grouped (Tok _ (ControlWord f _) : _) | isHeaderFooter f -> pure bs Grouped (Tok _ (ControlWord "footnote" _) : toks) -> do noteBs <- inGroup $ processDestinationToks toks modifyGroup (\g -> g{ gFootnote = Just noteBs }) addText "*" modifyGroup (\g -> g{ gFootnote = Nothing }) return bs Grouped (Tok _ (ControlWord "info" _) : toks) -> bs <$ inGroup (processDestinationToks toks) Grouped (Tok _ (ControlWord f _) : toks) | isMetadataField f -> inGroup $ do foldM_ processTok mempty toks annotatedToks <- reverse . sTextContent <$> getState updateState $ \s -> s{ sTextContent = [] } let ils = B.trimInlines . mconcat $ map addFormatting annotatedToks updateState $ \s -> s{ sMetadata = (f, ils) : sMetadata s } pure bs Grouped toks -> inGroup (foldM processTok bs toks) UnformattedText t -> bs <$ do -- return $! traceShowId $! (pos, t) eatChars <- sEatChars <$> getState case eatChars of 0 -> addText t n | n < T.length t -> do updateState $ \s -> s{ sEatChars = 0 } addText (T.drop n t) | otherwise -> do updateState $ \s -> s{ sEatChars = n - T.length t } HexVals ws -> bs <$ do eatChars <- sEatChars <$> getState let ws' = drop eatChars ws updateState $ \s -> s{ sEatChars = if null ws' then eatChars - length ws else 0 } charset <- sCharSet <$> getState case charset of ANSI -> addText $ T.pack $ map defaultAnsiWordToChar ws' Mac -> addText $ T.pack $ map macToChar ws' Pc -> addText $ T.pack $ map pcToChar ws' Pca -> addText $ T.pack $ map pcaToChar ws' ControlWord "ansi" _ -> bs <$ updateState (\s -> s{ sCharSet = ANSI }) ControlWord "ansicpg" (Just cpg) | cpg /= 1252 -> bs <$ report (UnsupportedCodePage cpg) ControlWord "mac" _ -> bs <$ updateState (\s -> s{ sCharSet = Mac }) ControlWord "pc" _ -> bs <$ updateState (\s -> s{ sCharSet = Pc }) ControlWord "pca" _ -> bs <$ updateState (\s -> s{ sCharSet = Pca }) ControlWord "outlinelevel" mbp -> bs <$ modifyGroup (\g -> g{ gOutlineLevel = mbp }) ControlWord "ls" mbp -> bs <$ modifyGroup (\g -> g{ gListOverride = mbp }) ControlWord "ilvl" mbp -> bs <$ modifyGroup (\g -> g{ gListLevel = mbp }) ControlSymbol '\\' -> bs <$ addText "\\" ControlSymbol '{' -> bs <$ addText "{" ControlSymbol '}' -> bs <$ addText "}" ControlSymbol '~' -> bs <$ addText "\x00a0" ControlSymbol '-' -> bs <$ addText "\x00ad" ControlSymbol '_' -> bs <$ addText "\x2011" ControlWord "trowd" _ -> bs <$ do -- add new row updateState $ \s -> s{ sTableRows = TableRow [] : sTableRows s , sCurrentCell = mempty } ControlWord "cell" _ -> bs <$ do new <- emitBlocks mempty curCell <- (<> new) . sCurrentCell <$> getState updateState $ \s -> s{ sTableRows = case sTableRows s of TableRow cs : rs -> TableRow (curCell : cs) : rs [] -> [TableRow [curCell]] -- shouldn't happen , sCurrentCell = mempty } 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" ControlWord "ldblquote" _ -> bs <$ addText "\x201C" ControlWord "rdblquote" _ -> bs <$ addText "\x201D" ControlWord "emdash" _ -> bs <$ addText "\x2014" ControlWord "emspace" _ -> bs <$ addText "\x2003" ControlWord "enspace" _ -> bs <$ addText "\x2002" ControlWord "endash" _ -> bs <$ addText "\x2013" ControlWord "bullet" _ -> bs <$ addText "\x2022" ControlWord "tab" _ -> bs <$ addText "\t" ControlWord "line" _ -> bs <$ addText "\n" ControlSymbol '\n' -> bs <$ addText "\n" ControlSymbol '\r' -> bs <$ addText "\n" ControlWord "uc" (Just i) -> bs <$ modifyGroup (\g -> g{ gUC = i }) ControlWord "cs" (Just n) -> do getStyleFormatting n >>= foldM processTok bs ControlWord "s" (Just n) -> do getStyleFormatting n >>= foldM processTok bs ControlWord "ds" (Just n) -> do getStyleFormatting n >>= foldM processTok bs ControlWord "f" (Just i) -> bs <$ do fontTable <- sFontTable <$> getState modifyGroup (\g -> g{ gFontFamily = IntMap.lookup i fontTable }) ControlWord "u" (Just i) -> bs <$ do st <- getState let curgroup = case sGroupStack st of [] -> def (x:_) -> x updateState $ \s -> s{ sEatChars = gUC curgroup } -- "RTF control words generally accept signed 16-bit numbers as -- arguments. For this reason, Unicode values greater than 32767 -- must be expressed as negative numbers." let codepoint :: Word16 codepoint = fromIntegral i addText (T.singleton (chr $ fromIntegral codepoint)) ControlWord "caps" mbp -> bs <$ modifyGroup (\g -> g{ gCaps = boolParam mbp }) ControlWord "deleted" mbp -> bs <$ modifyGroup (\g -> g{ gDeleted = boolParam mbp }) ControlWord "b" mbp -> bs <$ modifyGroup (\g -> g{ gBold = boolParam mbp }) ControlWord "i" mbp -> bs <$ modifyGroup (\g -> g{ gItalic = boolParam mbp }) ControlWord "sub" mbp -> bs <$ modifyGroup (\g -> g{ gSub = boolParam mbp }) ControlWord "super" mbp -> bs <$ modifyGroup (\g -> g{ gSuper = boolParam mbp }) ControlWord "nosupersub" mbp -> bs <$ modifyGroup (\g -> g{ gSuper = not $ boolParam mbp , gSub = not $ boolParam mbp }) ControlWord "up" mbp -> bs <$ modifyGroup (\g -> g{ gSuper = boolParam mbp }) ControlWord "strike" mbp -> bs <$ modifyGroup (\g -> g{ gDeleted = boolParam mbp }) ControlWord "strikedl" mbp -> bs <$ modifyGroup (\g -> g{ gDeleted = boolParam mbp }) ControlWord "striked" mbp -> bs <$ modifyGroup (\g -> g{ gDeleted = boolParam mbp }) ControlWord "scaps" mbp -> bs <$ modifyGroup (\g -> g{ gSmallCaps = boolParam mbp }) ControlWord "v" mbp -> bs <$ modifyGroup (\g -> g{ gHidden = boolParam mbp }) ControlWord x mbp | isUnderline x -> bs <$ modifyGroup (\g -> g{ gUnderline = boolParam mbp }) ControlWord "ulnone" _ -> bs <$ modifyGroup (\g -> g{ gUnderline = False }) ControlWord "pard" _ -> do newbs <- emitBlocks bs modifyGroup (const def) getStyleFormatting 0 >>= foldM processTok newbs ControlWord "par" _ -> emitBlocks bs _ -> pure bs processDestinationToks :: PandocMonad m => [Tok] -> RTFParser m Blocks processDestinationToks toks = do textContent <- sTextContent <$> getState liststack <- sListStack <$> getState updateState $ \s -> s{ sTextContent = mempty , sListStack = [] } result <- inGroup $ foldM processTok mempty toks >>= emitBlocks unclosed <- closeContainers updateState $ \s -> s{ sTextContent = textContent , sListStack = liststack } return $ result <> unclosed -- close lists >= level closeLists :: PandocMonad m => Int -> RTFParser m Blocks closeLists lvl = do lists <- sListStack <$> getState case lists of (List _ lvl' lt items : rest) | lvl' >= lvl -> do let newlist = (case lt of Bullet -> B.bulletList Ordered listAttr -> B.orderedListWith listAttr) (reverse items) updateState $ \s -> s{ sListStack = rest } case rest of [] -> do updateState $ \s -> s{ sListStack = rest } pure newlist (List lo lvl'' lt' [] : rest') -> do -- should not happen updateState $ \s -> s{ sListStack = List lo lvl'' lt' [newlist] : rest' } closeLists lvl (List lo lvl'' lt' (i:is) : rest') -> do updateState $ \s -> s{ sListStack = List lo lvl'' lt' (i <> newlist : is) : rest' } closeLists lvl _ -> pure mempty closeTable :: PandocMonad m => RTFParser m Blocks closeTable = do rawrows <- sTableRows <$> getState if null rawrows then return mempty else do let getCells (TableRow cs) = reverse cs let rows = map getCells . reverse $ rawrows updateState $ \s -> s{ sCurrentCell = mempty , sTableRows = [] } return $ B.simpleTable [] rows closeContainers :: PandocMonad m => RTFParser m Blocks closeContainers = do tbl <- closeTable lists <- closeLists 0 return $ tbl <> lists trimFinalLineBreak :: Inlines -> Inlines trimFinalLineBreak ils = case Seq.viewr (B.unMany ils) of rest Seq.:> LineBreak -> B.Many rest _ -> ils emitBlocks :: PandocMonad m => Blocks -> RTFParser m Blocks emitBlocks bs = do annotatedToks <- reverse . sTextContent <$> getState updateState $ \s -> s{ sTextContent = [] } let justCode = def{ gFontFamily = Just Modern } let prop = case annotatedToks of [] -> def ((p,_):_) -> p tbl <- if gInTable prop || null annotatedToks then pure mempty else closeTable new <- case annotatedToks of [] -> pure mempty _ | Just lst <- gListOverride prop -> do let level = fromMaybe 0 $ gListLevel prop listOverrideTable <- sListOverrideTable <$> getState let listType = fromMaybe Bullet $ IntMap.lookup lst listOverrideTable >>= IntMap.lookup level lists <- sListStack <$> getState -- get para contents of list item let newbs = B.para . B.trimInlines . trimFinalLineBreak . mconcat $ map addFormatting annotatedToks case lists of (List lo parentlevel _lt items : cs) | lo == lst , parentlevel == level -- add another item to existing list -> do updateState $ \s -> s{ sListStack = List lo level listType (newbs:items) : cs } pure mempty | lo /= lst || level < parentlevel -- close parent list and add new list -> do new <- closeLists level -- close open lists > level updateState $ \s -> s{ sListStack = List lst level listType [newbs] : sListStack s } pure new _ -> do -- add new list (level > parentlevel) updateState $ \s -> s{ sListStack = List lst level listType [newbs] : sListStack s } pure mempty | Just lvl <- gOutlineLevel prop -> do lists <- closeLists 0 pure $ lists <> B.header (lvl + 1) (B.trimInlines . mconcat $ map addFormatting $ removeCommonFormatting annotatedToks) | all ((== justCode) . fst) annotatedToks -> do lists <- closeLists 0 pure $ lists <> B.codeBlock (mconcat $ map snd annotatedToks) | all (T.all isSpace . snd) annotatedToks -> closeLists 0 | otherwise -> do lists <- closeLists 0 pure $ lists <> B.para (B.trimInlines . trimFinalLineBreak . mconcat $ map addFormatting annotatedToks) if gInTable prop then do updateState $ \s -> s{ sCurrentCell = sCurrentCell s <> new } pure bs else do pure $ bs <> tbl <> new -- Headers often have a style applied. We usually want to remove -- this, because headers will have their own styling in the target -- format. removeCommonFormatting :: [(Properties, Text)] -> [(Properties, Text)] removeCommonFormatting = (\ts -> if all (gBold . fst) ts then map (\(p,t) -> (p{ gBold = False }, t)) ts else ts) . (\ts -> if all (gItalic . fst) ts then map (\(p,t) -> (p{ gItalic = False }, t)) ts else ts) -- {\field{\*\fldinst{HYPERLINK "http://pandoc.org"}}{\fldrslt foo}} handleField :: PandocMonad m => Blocks -> [Tok] -> RTFParser m Blocks handleField bs ts = do 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) :instrtoks :_)), Tok _ (Grouped (Tok _ (ControlWord "fldrslt" Nothing) :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 getHyperlink [] = Nothing getHyperlink (Tok _ (UnformattedText w) : rest) | Just w' <- unquote <$> T.stripPrefix "HYPERLINK" (T.strip w) = if T.null w' then case rest of (Tok _ (ControlSymbol '\\') : Tok _ (UnformattedText b) : _) | Just bkmrk <- unquote <$> T.stripPrefix "l " (T.strip b) -> Just $ "#" <> bkmrk _ -> Just mempty else Just w' getHyperlink (_:ts) = getHyperlink ts unquote :: Text -> Text unquote = T.dropWhile (=='"') . T.dropWhileEnd (=='"') . T.strip handleListTable :: PandocMonad m => [Tok] -> RTFParser m () handleListTable toks = do mapM_ handleList toks handleList :: PandocMonad m => Tok -> RTFParser m () handleList (Tok _ (Grouped (Tok _ (ControlWord "list" _) : toks))) = do let listid = headDef 0 [n | Tok _ (ControlWord "listid" (Just n)) <- toks] let levels = [ts | Tok _ (Grouped (Tok _ (ControlWord "listlevel" _) : ts)) <- toks] tbl <- foldM handleListLevel mempty (zip [0..] levels) updateState $ \s -> s{ sListTable = IntMap.insert listid tbl $ sListTable s } handleList _ = return () handleListLevel :: PandocMonad m => ListLevelTable -> (Int, [Tok]) -> RTFParser m ListLevelTable handleListLevel levelTable (lvl, toks) = do let start = headDef 1 [n | Tok _ (ControlWord "levelstartat" (Just n)) <- toks] let mbNumberStyle = case [n | Tok _ (ControlWord "levelnfc" (Just n)) <- toks] of [] -> Nothing (0:_) -> Just Decimal (1:_) -> Just UpperRoman (2:_) -> Just LowerRoman (3:_) -> Just UpperAlpha (4:_) -> Just LowerAlpha (23:_) -> Nothing (255:_) -> Nothing _ -> Just DefaultStyle let listType = case mbNumberStyle of Nothing -> Bullet Just numStyle -> Ordered (start,numStyle,Period) return $ IntMap.insert lvl listType levelTable handleListOverrideTable :: PandocMonad m => [Tok] -> RTFParser m () handleListOverrideTable toks = mapM_ handleListOverride toks handleListOverride :: PandocMonad m => Tok -> RTFParser m () handleListOverride (Tok _ (Grouped (Tok _ (ControlWord "listoverride" _) : toks))) = do let listid = headDef 0 [n | Tok _ (ControlWord "listid" (Just n)) <- toks] let lsn = headDef 0 [n | Tok _ (ControlWord "ls" (Just n)) <- toks] -- TODO override stuff, esp. start num -- for now we just handle indirection listTable <- sListTable <$> getState case IntMap.lookup listid listTable of Nothing -> return () Just tbl -> updateState $ \s -> s{ sListOverrideTable = IntMap.insert lsn tbl $ sListOverrideTable s } handleListOverride _ = return () handleStylesheet :: PandocMonad m => [Tok] -> RTFParser m () handleStylesheet toks = do let styles = mapMaybe parseStyle toks updateState $ \s -> s{ sStylesheet = IntMap.fromList $ zip (map styleNum styles) styles } parseStyle :: Tok -> Maybe Style parseStyle (Tok _ (Grouped toks)) = do let (styType, styNum, rest) = case toks of Tok _ (ControlWord "s" (Just n)) : ts -> (ParagraphStyle, n, ts) Tok _ (ControlWord "ds" (Just n)) : ts -> (SectionStyle, n, ts) Tok _ (ControlWord "cs" (Just n)) : ts -> (CharStyle, n, ts) Tok _ (ControlWord "ts" (Just n)) : ts -> (TableStyle, n, ts) _ -> (ParagraphStyle, 0, toks) let styName = case lastMay rest of Just (Tok _ (UnformattedText t)) -> T.dropWhileEnd (==';') t _ -> mempty let isBasedOn (Tok _ (ControlWord "sbasedon" (Just _))) = True isBasedOn _ = False let styBasedOn = case L.find isBasedOn toks of Just (Tok _ (ControlWord "sbasedon" (Just i))) -> Just i _ -> Nothing let isStyleControl (Tok _ (ControlWord x _)) = x `elem` ["cs", "s", "ds", "additive", "sbasedon", "snext", "sautoupd", "shidden", "keycode", "alt", "shift", "ctrl", "fn"] isStyleControl _ = False let styFormatting = filter (not . isStyleControl) (initSafe rest) return $ Style{ styleNum = styNum , styleType = styType , styleBasedOn = styBasedOn , styleName = styName , styleFormatting = styFormatting } parseStyle _ = Nothing hexToWord :: Text -> Word8 hexToWord t = case TR.hexadecimal t of Left _ -> 0 Right (x,_) -> x handlePict :: PandocMonad m => [Tok] -> RTFParser m () handlePict toks = do let pict = L.foldl' getPictData def toks let altText = "image" let bytes = if picBinary pict then picBytes pict else BL.pack $ map hexToWord $ T.chunksOf 2 $ picData pict let (mimetype, ext) = case picType pict of Just Emfblip -> (Just "image/x-emf", ".emf") Just Pngblip -> (Just "image/png", ".png") Just Jpegblip -> (Just "image/jpeg", ".jpg") Nothing -> (Nothing, "") case mimetype of Just mt -> do let pictname = show (hashWith SHA1 $ BL.toStrict bytes) <> ext insertMedia pictname (Just mt) bytes modifyGroup $ \g -> g{ gImage = Just pict{ picName = T.pack pictname, picBytes = bytes } } addText altText modifyGroup $ \g -> g{ gImage = Nothing } _ -> return () where getPictData :: Pict -> Tok -> Pict getPictData pict (Tok _ tok') = case tok' of ControlWord "emfblip" _-> pict{ picType = Just Emfblip } ControlWord "pngblip" _-> pict{ picType = Just Pngblip } ControlWord "jpegblip" _-> pict{ picType = Just Jpegblip } ControlWord "picw" (Just w) -> pict{ picWidth = Just w } ControlWord "pich" (Just h) -> pict{ picHeight = Just h } ControlWord "picwgoal" (Just w) -> pict{ picWidthGoal = Just w } ControlWord "pichgoal" (Just h) -> pict{ picHeightGoal = Just h } BinData d | not (BL.null d) -> pict{ picBinary = True, picBytes = picBytes pict <> d } UnformattedText t -> pict{ picData = t } _ -> pict processFontTable :: [Tok] -> FontTable processFontTable = snd . L.foldl' go (0, mempty) where go (fontnum, tbl) (Tok _ tok') = case tok' of (ControlWord "f" (Just i)) -> (i, tbl) (ControlWord "fnil" _) -> (fontnum, tbl) (ControlWord "froman" _) -> (fontnum, IntMap.insert fontnum Roman tbl) (ControlWord "fswiss" _) -> (fontnum, IntMap.insert fontnum Swiss tbl) (ControlWord "fmodern" _) -> (fontnum, IntMap.insert fontnum Modern tbl) (ControlWord "fscript" _) -> (fontnum, IntMap.insert fontnum Script tbl) (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) -> L.foldl' go (fontnum, tbl) ts _ -> (fontnum, tbl) defaultAnsiWordToChar :: Word8 -> Char defaultAnsiWordToChar i = case i of 128 -> '\8364' 130 -> '\8218' 131 -> '\402' 132 -> '\8222' 133 -> '\8230' 134 -> '\8224' 135 -> '\8225' 136 -> '\710' 137 -> '\8240' 138 -> '\352' 139 -> '\8249' 140 -> '\338' 142 -> '\381' 145 -> '\8216' 146 -> '\8217' 147 -> '\8220' 148 -> '\8221' 149 -> '\8226' 150 -> '\8211' 151 -> '\8212' 152 -> '\732' 153 -> '\8482' 154 -> '\353' 155 -> '\8250' 156 -> '\339' 158 -> '\382' 159 -> '\376' 173 -> '\xAD' _ -> chr (fromIntegral i) macToChar :: Word8 -> Char macToChar i = chr $ case i of 0x80 -> 0xC4 0x81 -> 0xC5 0x82 -> 0xC7 0x83 -> 0xC9 0x84 -> 0xD1 0x85 -> 0xD6 0x86 -> 0xDC 0x87 -> 0xE1 0x88 -> 0xE0 0x89 -> 0xE2 0x8A -> 0xE4 0x8B -> 0xE3 0x8C -> 0xE5 0x8D -> 0xE7 0x8E -> 0xE9 0x8F -> 0xE8 0x90 -> 0xEA 0x91 -> 0xEB 0x92 -> 0xED 0x93 -> 0xEC 0x94 -> 0xEE 0x95 -> 0xEF 0x96 -> 0xF1 0x97 -> 0xF3 0x98 -> 0xF2 0x99 -> 0xF4 0x9A -> 0xF6 0x9B -> 0xF5 0x9C -> 0xFA 0x9D -> 0xF9 0x9E -> 0xFB 0x9F -> 0xFC 0xA0 -> 0xDD 0xA1 -> 0xB0 0xA2 -> 0xA2 0xA3 -> 0xA3 0xA4 -> 0xA7 0xA5 -> 0xD7 0xA6 -> 0xB6 0xA7 -> 0xDF 0xA8 -> 0xAE 0xA9 -> 0xA9 0xAA -> 0xB2 0xAB -> 0xB4 0xAC -> 0xA8 0xAD -> 0xB3 0xAE -> 0xC6 0xAF -> 0xD8 0xB0 -> 0xB9 0xB1 -> 0xB1 0xB2 -> 0xBC 0xB3 -> 0xBD 0xB4 -> 0xA5 0xB5 -> 0xB5 0xBA -> 0xBE 0xBB -> 0xAA 0xBC -> 0xBA 0xBE -> 0xE6 0xBF -> 0xF8 0xC0 -> 0xBF 0xC1 -> 0xA1 0xC2 -> 0xAC 0xC3 -> 0x0141 0xC4 -> 0x0192 0xC5 -> 0x02CB 0xC7 -> 0xAB 0xC8 -> 0xBB 0xC9 -> 0xA6 0xCA -> 0xA0 0xCB -> 0xC0 0xCC -> 0xC3 0xCD -> 0xD5 0xCE -> 0x0152 0xCF -> 0x0153 0xD0 -> 0xAD 0xD4 -> 0x0142 0xD6 -> 0xF7 0xD8 -> 0xFF 0xD9 -> 0x0178 0xDB -> 0xA4 0xDC -> 0xD0 0xDD -> 0xF0 0xDE -> 0xDE 0xDF -> 0xFE 0xE0 -> 0xFD 0xE1 -> 0xB7 0xE5 -> 0xC2 0xE6 -> 0xCA 0xE7 -> 0xC1 0xE8 -> 0xCB 0xE9 -> 0xC8 0xEA -> 0xCD 0xEB -> 0xCE 0xEC -> 0xCF 0xED -> 0xCC 0xEE -> 0xD3 0xEF -> 0xD4 0xF1 -> 0xD2 0xF2 -> 0xDA 0xF3 -> 0xDB 0xF4 -> 0xD9 0xF5 -> 0x0131 0xF6 -> 0x02C6 0xF7 -> 0x02DC 0xF8 -> 0xAF 0xF9 -> 0x02D8 0xFA -> 0x02D9 0xFB -> 0x02DA 0xFC -> 0xB8 0xFD -> 0x02DD 0xFE -> 0x02DB 0xFF -> 0x02C7 _ -> fromIntegral i pcToChar :: Word8 -> Char pcToChar i = chr $ case i of 0x80 -> 0xc7 0x81 -> 0xfc 0x82 -> 0xe9 0x83 -> 0xe2 0x84 -> 0xe4 0x85 -> 0xe0 0x86 -> 0xe5 0x87 -> 0xe7 0x88 -> 0xea 0x89 -> 0xeb 0x8a -> 0xe8 0x8b -> 0xef 0x8c -> 0xee 0x8d -> 0xec 0x8e -> 0xc4 0x8f -> 0xc5 0x90 -> 0xc9 0x91 -> 0xe6 0x92 -> 0xc6 0x93 -> 0xf4 0x94 -> 0xf6 0x95 -> 0xf2 0x96 -> 0xfb 0x97 -> 0xf9 0x98 -> 0xff 0x99 -> 0xd6 0x9a -> 0xdc 0x9b -> 0xa2 0x9c -> 0xa3 0x9d -> 0xa5 0x9e -> 0x20a7 0x9f -> 0x0192 0xa0 -> 0xe1 0xa1 -> 0xed 0xa2 -> 0xf3 0xa3 -> 0xfa 0xa4 -> 0xf1 0xa5 -> 0xd1 0xa6 -> 0xaa 0xa7 -> 0xba 0xa8 -> 0xbf 0xa9 -> 0x2310 0xaa -> 0xac 0xab -> 0xbd 0xac -> 0xbc 0xad -> 0xa1 0xae -> 0xab 0xaf -> 0xbb 0xb0 -> 0x2591 0xb1 -> 0x2592 0xb2 -> 0x2593 0xb3 -> 0x2502 0xb4 -> 0x2524 0xb5 -> 0x2561 0xb6 -> 0x2562 0xb7 -> 0x2556 0xb8 -> 0x2555 0xb9 -> 0x2563 0xba -> 0x2551 0xbb -> 0x2557 0xbc -> 0x255d 0xbd -> 0x255c 0xbe -> 0x255b 0xbf -> 0x2510 0xc0 -> 0x2514 0xc1 -> 0x2534 0xc2 -> 0x252c 0xc3 -> 0x251c 0xc4 -> 0x2500 0xc5 -> 0x253c 0xc6 -> 0x255e 0xc7 -> 0x255f 0xc8 -> 0x255a 0xc9 -> 0x2554 0xca -> 0x2569 0xcb -> 0x2566 0xcc -> 0x2560 0xcd -> 0x2550 0xce -> 0x256c 0xcf -> 0x2567 0xd0 -> 0x2568 0xd1 -> 0x2564 0xd2 -> 0x2565 0xd3 -> 0x2559 0xd4 -> 0x2558 0xd5 -> 0x2552 0xd6 -> 0x2553 0xd7 -> 0x256b 0xd8 -> 0x256a 0xd9 -> 0x2518 0xda -> 0x250c 0xdb -> 0x2588 0xdc -> 0x2584 0xdd -> 0x258c 0xde -> 0x2590 0xdf -> 0x2580 0xe0 -> 0x03b1 0xe1 -> 0xdf 0xe2 -> 0x0393 0xe3 -> 0x03c0 0xe4 -> 0x03a3 0xe5 -> 0x03c3 0xe6 -> 0xb5 0xe7 -> 0x03c4 0xe8 -> 0x03a6 0xe9 -> 0x0398 0xea -> 0x03a9 0xeb -> 0x03b4 0xec -> 0x221e 0xed -> 0x03c6 0xee -> 0x03b5 0xef -> 0x2229 0xf0 -> 0x2261 0xf1 -> 0xb1 0xf2 -> 0x2265 0xf3 -> 0x2264 0xf4 -> 0x2320 0xf5 -> 0x2321 0xf6 -> 0xf7 0xf7 -> 0x2248 0xf8 -> 0xb0 0xf9 -> 0x2219 0xfa -> 0xb7 0xfb -> 0x221a 0xfc -> 0x207f 0xfd -> 0xb2 0xfe -> 0x25a0 0xff -> 0xa0 _ -> fromIntegral i pcaToChar :: Word8 -> Char pcaToChar i = chr $ case i of 0x80 -> 0x00c7 0x81 -> 0x00fc 0x82 -> 0x00e9 0x83 -> 0x00e2 0x84 -> 0x00e4 0x85 -> 0x00e0 0x86 -> 0x00e5 0x87 -> 0x00e7 0x88 -> 0x00ea 0x89 -> 0x00eb 0x8a -> 0x00e8 0x8b -> 0x00ef 0x8c -> 0x00ee 0x8d -> 0x00ec 0x8e -> 0x00c4 0x8f -> 0x00c5 0x90 -> 0x00c9 0x91 -> 0x00e6 0x92 -> 0x00c6 0x93 -> 0x00f4 0x94 -> 0x00f6 0x95 -> 0x00f2 0x96 -> 0x00fb 0x97 -> 0x00f9 0x98 -> 0x00ff 0x99 -> 0x00d6 0x9a -> 0x00dc 0x9b -> 0x00f8 0x9c -> 0x00a3 0x9d -> 0x00d8 0x9e -> 0x00d7 0x9f -> 0x0192 0xa0 -> 0x00e1 0xa1 -> 0x00ed 0xa2 -> 0x00f3 0xa3 -> 0x00fa 0xa4 -> 0x00f1 0xa5 -> 0x00d1 0xa6 -> 0x00aa 0xa7 -> 0x00ba 0xa8 -> 0x00bf 0xa9 -> 0x00ae 0xaa -> 0x00ac 0xab -> 0x00bd 0xac -> 0x00bc 0xad -> 0x00a1 0xae -> 0x00ab 0xaf -> 0x00bb 0xb0 -> 0x2591 0xb1 -> 0x2592 0xb2 -> 0x2593 0xb3 -> 0x2502 0xb4 -> 0x2524 0xb5 -> 0x00c1 0xb6 -> 0x00c2 0xb7 -> 0x00c0 0xb8 -> 0x00a9 0xb9 -> 0x2563 0xba -> 0x2551 0xbb -> 0x2557 0xbc -> 0x255d 0xbd -> 0x00a2 0xbe -> 0x00a5 0xbf -> 0x2510 0xc0 -> 0x2514 0xc1 -> 0x2534 0xc2 -> 0x252c 0xc3 -> 0x251c 0xc4 -> 0x2500 0xc5 -> 0x253c 0xc6 -> 0x00e3 0xc7 -> 0x00c3 0xc8 -> 0x255a 0xc9 -> 0x2554 0xca -> 0x2569 0xcb -> 0x2566 0xcc -> 0x2560 0xcd -> 0x2550 0xce -> 0x256c 0xcf -> 0x00a4 0xd0 -> 0x00f0 0xd1 -> 0x00d0 0xd2 -> 0x00ca 0xd3 -> 0x00cb 0xd4 -> 0x00c8 0xd5 -> 0x0131 0xd6 -> 0x00cd 0xd7 -> 0x00ce 0xd8 -> 0x00cf 0xd9 -> 0x2518 0xda -> 0x250c 0xdb -> 0x2588 0xdc -> 0x2584 0xdd -> 0x00a6 0xde -> 0x00cc 0xdf -> 0x2580 0xe0 -> 0x00d3 0xe1 -> 0x00df 0xe2 -> 0x00d4 0xe3 -> 0x00d2 0xe4 -> 0x00f5 0xe5 -> 0x00d5 0xe6 -> 0x00b5 0xe7 -> 0x00fe 0xe8 -> 0x00de 0xe9 -> 0x00da 0xea -> 0x00db 0xeb -> 0x00d9 0xec -> 0x00fd 0xed -> 0x00dd 0xee -> 0x00af 0xef -> 0x00b4 0xf0 -> 0x00ad 0xf1 -> 0x00b1 0xf2 -> 0x2017 0xf3 -> 0x00be 0xf4 -> 0x00b6 0xf5 -> 0x00a7 0xf6 -> 0x00f7 0xf7 -> 0x00b8 0xf8 -> 0x00b0 0xf9 -> 0x00a8 0xfa -> 0x00b7 0xfb -> 0x00b9 0xfc -> 0x00b3 0xfd -> 0x00b2 0xfe -> 0x25a0 0xff -> 0x00a0 _ -> fromIntegral i ================================================ FILE: src/Text/Pandoc/Readers/Roff/Escape.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilyDependencies #-} module Text.Pandoc.Readers.Roff.Escape ( escape, escapeArg, escIgnore, RoffLikeLexer(..), ) where import Text.Pandoc.Class.PandocMonad ( PandocMonad(..), report, PandocMonad(..), report ) import Control.Monad ( mzero, mplus, mzero, mplus ) import Data.Char (chr, isAscii, isAlphaNum) import qualified Data.Map as M import qualified Data.Text as T import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Parsing import Text.Pandoc.Shared (safeRead) import qualified Data.Text.Normalize as Normalize import Text.Pandoc.RoffChar (characterCodes, combiningAccents) -- | Functions and typeclass for escaping special characters in languages -- that inherit the Roff syntax. -- -- For various reasons, the mdoc reader doesn't directly reuse the previously -- existing roff lexer. The main one is to make it possible to simultaneously -- process roff escapes and to tokenize mdoc macros correctly based on the -- control line contents. The extracted interface here allows the same `escape` -- function to work with lexers that target different token types and support -- different subsets of the original roff language. type Lexer m x = ParsecT Sources (State x) m -- | Lexers for Roff macro class (Monoid (Token x)) => RoffLikeLexer x where -- | Type family for the lexer state type State x = a | a -> x -- | Type family for the token type being lexed type Token x = a | a -> x -- | Turn a `T.Text` into a token of the output emit :: T.Text -> Token x -- | Attempt to parse a roff predefined string sequence and push its expansion -- onto the input stream. expandString :: PandocMonad m => Lexer m x () -- | Parse the name of a defined string and return its expansion as a `Token` escString :: PandocMonad m => Lexer m x (Token x) -- | Parse the escape character backslash :: PandocMonad m => Lexer m x () -- | If the given custom macro is defined in this document, emit a -- tokenized "1", otherwise emit a tokenized "0", implementing the roff -- escape @\A@. checkDefined :: PandocMonad m => T.Text -> Lexer m x (Token x) -- | Output an appropriate token for the @\E@ escape sequence. In roff, @\E@ -- is an "escape character intended to not be interpreted in copy mode". -- If the lexer being defined doesn't implement copy mode, @\E@ can just be -- lexed by 'backslash' escE :: PandocMonad m => Lexer m x (Token x) -- | Lex the low-level roff font selection escape @\f@. escFont :: PandocMonad m => Lexer m x (Token x) characterCodeMap :: M.Map T.Text Char characterCodeMap = M.fromList $ map (\(x,y) -> (y,x)) characterCodes combiningAccentsMap :: M.Map T.Text Char combiningAccentsMap = M.fromList $ map (\(x,y) -> (y,x)) combiningAccents escape :: (PandocMonad m, RoffLikeLexer x) => Lexer m x (Token x) escape = try $ do backslash escapeGlyph <|> escapeNormal escapeGlyph :: (PandocMonad m, RoffLikeLexer x) => Lexer m x (Token x) escapeGlyph = do c <- lookAhead (oneOf ['[','(']) escapeArg >>= resolveGlyph c resolveGlyph :: (PandocMonad m, RoffLikeLexer x) => Char -> T.Text -> Lexer m x (Token x) resolveGlyph delimChar glyph = do let cs = T.replace "_u" " u" glyph -- unicode glyphs separated by _ (case T.words cs of [] -> mzero [s] -> case M.lookup s characterCodeMap `mplus` readUnicodeChar s of Nothing -> mzero Just c -> return $ emit $ T.singleton c (s:ss) -> do basechar <- case M.lookup s characterCodeMap `mplus` readUnicodeChar s of Nothing -> case T.unpack s of [ch] | isAscii ch && isAlphaNum ch -> return ch _ -> mzero Just c -> return c let addAccents [] xs = return $ Normalize.normalize Normalize.NFC $ T.reverse xs addAccents (a:as) xs = case M.lookup a combiningAccentsMap `mplus` readUnicodeChar a of Just x -> addAccents as $ T.cons x xs Nothing -> mzero addAccents ss (T.singleton basechar) >>= \xs -> return $ emit xs) <|> case delimChar of '[' -> escUnknown ("\\[" <> glyph <> "]") '(' -> escUnknown ("\\(" <> glyph) '\'' -> escUnknown ("\\C'" <> glyph <> "'") _ -> Prelude.fail "resolveGlyph: unknown glyph delimiter" readUnicodeChar :: T.Text -> Maybe Char readUnicodeChar t = case T.uncons t of Just ('u', cs) | T.length cs > 3 -> chr <$> safeRead ("0x" <> cs) _ -> Nothing escapeNormal :: (PandocMonad m, RoffLikeLexer x) => Lexer m x (Token x) escapeNormal = do c <- noneOf "{}" optional expandString let groffSkip = [escapeArg, countChar 1 (satisfy (/='\n'))] case c of ' ' -> return $ emit " " -- mandoc_char(7) says this should be a nonbreaking space '"' -> mempty <$ skipMany (satisfy (/='\n')) -- line comment '#' -> mempty <$ manyTill anyChar newline '%' -> return mempty -- optional hyphenation '&' -> return mempty -- nonprintable zero-width ')' -> return mempty -- nonprintable zero-width '*' -> escString ',' -> return mempty -- to fix spacing after roman '-' -> return $ emit "-" '.' -> return $ emit "." '/' -> return mempty -- to fix spacing before roman '0' -> return $ emit "\x2007" -- digit-width space ':' -> return mempty -- zero-width break 'A' -> quoteArg >>= checkDefined 'B' -> escIgnore 'B' [quoteArg] 'C' -> quoteArg >>= resolveGlyph '\'' 'D' -> escIgnore 'D' [quoteArg] 'E' -> escE 'F' -> escIgnore 'F' groffSkip 'H' -> escIgnore 'H' [quoteArg] 'L' -> escIgnore 'L' [quoteArg] 'M' -> escIgnore 'M' groffSkip 'N' -> escIgnore 'N' [quoteArg] 'O' -> escIgnore 'O' [countChar 1 (oneOf ['0','1'])] 'R' -> escIgnore 'R' [quoteArg] 'S' -> escIgnore 'S' [quoteArg] 'V' -> escIgnore 'V' groffSkip 'X' -> escIgnore 'X' [quoteArg] 'Y' -> escIgnore 'Y' groffSkip 'Z' -> escIgnore 'Z' [quoteArg] '\'' -> return $ emit "'" '\n' -> return mempty -- line continuation '^' -> return $ emit "\x200A" -- 1/12 em space '_' -> return $ emit "_" '`' -> return $ emit "`" 'a' -> return mempty -- "non-interpreted leader character" 'b' -> escIgnore 'b' [quoteArg] 'c' -> return mempty -- interrupt text processing 'd' -> escIgnore 'd' [] -- forward down 1/2em 'e' -> return $ emit "\\" 'f' -> escFont 'g' -> escIgnore 'g' groffSkip 'h' -> escIgnore 'h' [quoteArg] 'k' -> escIgnore 'k' groffSkip 'l' -> escIgnore 'l' [quoteArg] 'm' -> escIgnore 'm' groffSkip 'n' -> escIgnore 'm' groffSkip 'o' -> escIgnore 'o' [quoteArg] 'p' -> escIgnore 'p' [] 'r' -> escIgnore 'r' [] 's' -> escIgnore 's' [escapeArg, signedNumber] 't' -> return $ emit "\t" 'u' -> escIgnore 'u' [] 'v' -> escIgnore 'v' [quoteArg] 'w' -> escIgnore 'w' [quoteArg] 'x' -> escIgnore 'x' [quoteArg] 'z' -> escIgnore 'z' [countChar 1 anyChar] '|' -> return $ emit "\x2006" --1/6 em space '~' -> return $ emit "\160" -- nonbreaking space '\\' -> return $ emit "\\" _ -> return $ emit $ T.singleton c -- man 7 groff: "If a backslash is followed by a character that -- does not constitute a defined escape sequence, the backslash -- is silently ignored and the character maps to itself." escIgnore :: (PandocMonad m, RoffLikeLexer x) => Char -> [Lexer m x T.Text] -> Lexer m x (Token x) escIgnore c argparsers = do pos <- getPosition arg <- snd <$> withRaw (choice argparsers) <|> return "" report $ SkippedContent ("\\" <> T.cons c arg) pos return mempty escUnknown :: (PandocMonad m, RoffLikeLexer x) => T.Text -> Lexer m x (Token x) escUnknown s = do pos <- getPosition report $ SkippedContent s pos return $ emit "\xFFFD" signedNumber :: (PandocMonad m, RoffLikeLexer x) => Lexer m x T.Text signedNumber = try $ do sign <- option "" ("-" <$ char '-' <|> "" <$ char '+') ds <- many1Char digit return (sign <> ds) -- Parses: [..] or (.. escapeArg :: (PandocMonad m, RoffLikeLexer x) => Lexer m x T.Text escapeArg = choice [ char '[' *> optional expandString *> manyTillChar (noneOf ['\n',']']) (char ']') , char '(' *> optional expandString *> countChar 2 (satisfy (/='\n')) ] -- Parses: '..' quoteArg :: (PandocMonad m, RoffLikeLexer x) => Lexer m x T.Text quoteArg = char '\'' *> manyTillChar (noneOf ['\n','\'']) (char '\'') ================================================ FILE: src/Text/Pandoc/Readers/Roff.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE TypeFamilies #-} {- | Module : Text.Pandoc.Readers.Roff Copyright : Copyright (C) 2018-2020 Yan Pashkovsky and John MacFarlane License : GNU GPL, version 2 or above Maintainer : Yan Pashkovsky Stability : WIP Portability : portable Tokenizer for roff formats (man, ms). -} module Text.Pandoc.Readers.Roff ( FontSpec(..) , defaultFontSpec , LinePart(..) , Arg , TableOption , CellFormat(..) , TableRow , RoffToken(..) , RoffTokens(..) , linePartsToText , lexRoff ) where import Safe (lastDef) import Control.Monad (void, guard) import Control.Monad.Except (throwError) import Text.Pandoc.Class.PandocMonad (getResourcePath, readFileFromDirs, PandocMonad(..), report) import Data.Char (isLower, toLower, toUpper, isAlphaNum) import Data.Default (Default) import qualified Data.Map as M import Data.List (intercalate) import qualified Data.Text as T import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Shared (safeRead) import Text.Pandoc.Readers.Roff.Escape import qualified Data.Sequence as Seq import qualified Data.Foldable as Foldable -- import Debug.Trace (traceShowId) -- -- Data Types -- data FontSpec = FontSpec{ fontBold :: Bool , fontItalic :: Bool , fontMonospace :: Bool } deriving (Show, Eq, Ord) defaultFontSpec :: FontSpec defaultFontSpec = FontSpec False False False data LinePart = RoffStr T.Text | Font FontSpec | MacroArg Int deriving Show instance RoffLikeLexer RoffTokens where -- The token stream is a list of 'LinePart's type Token RoffTokens = [LinePart] type State RoffTokens = RoffState emit t = [RoffStr t] expandString = try $ do pos <- getPosition char '\\' char '*' cs <- escapeArg <|> countChar 1 anyChar s <- linePartsToText <$> resolveText cs pos addToInput s escString = try $ do pos <- getPosition (do cs <- escapeArg <|> countChar 1 anyChar resolveText cs pos) <|> mempty <$ char 'S' backslash = do char '\\' mode <- roffMode <$> getState case mode of -- experimentally, it seems you don't always need to double -- the backslash in macro defs. It's essential with \\$1, -- but not with \\f[I]. So we make the second one optional. CopyMode -> optional $ char '\\' NormalMode -> return () checkDefined name = do macros <- customMacros <$> getState case M.lookup name macros of Just _ -> return [RoffStr "1"] Nothing -> return [RoffStr "0"] -- \E is ignored in copy mode escE = do mode <- roffMode <$> getState case mode of CopyMode -> return mempty NormalMode -> return [RoffStr "\\"] escFont = do font <- escapeArg <|> countChar 1 alphaNum font' <- if T.null font || font == "P" then prevFont <$> getState else return $ foldr processFontLetter defaultFontSpec $ T.unpack font updateState $ \st -> st{ prevFont = currentFont st , currentFont = font' } return [Font font'] where processFontLetter c fs | isLower c = processFontLetter (toUpper c) fs processFontLetter 'B' fs = fs{ fontBold = True } processFontLetter 'I' fs = fs{ fontItalic = True } processFontLetter 'C' fs = fs{ fontMonospace = True } processFontLetter _ fs = fs -- do nothing type Arg = [LinePart] type TableOption = (T.Text, T.Text) data CellFormat = CellFormat { columnType :: Char , pipePrefix :: Bool , pipeSuffix :: Bool , columnSuffixes :: [T.Text] } deriving (Show, Eq, Ord) type TableRow = ([CellFormat], [RoffTokens]) data RoffToken = TextLine [LinePart] | EmptyLine | ControlLine T.Text [Arg] SourcePos | Tbl [TableOption] [TableRow] SourcePos deriving Show newtype RoffTokens = RoffTokens { unRoffTokens :: Seq.Seq RoffToken } deriving (Show, Semigroup, Monoid) singleTok :: RoffToken -> RoffTokens singleTok t = RoffTokens (Seq.singleton t) data RoffMode = NormalMode | CopyMode deriving Show data RoffState = RoffState { customMacros :: M.Map T.Text RoffTokens , prevFont :: FontSpec , currentFont :: FontSpec , tableTabChar :: Char , roffMode :: RoffMode , lastExpression :: Maybe Bool , afterConditional :: Bool } deriving Show instance Default RoffState where def = RoffState { customMacros = M.fromList $ map (\(n, s) -> (n, singleTok (TextLine [RoffStr s]))) [ ("Tm", "\x2122") , ("lq", "\x201C") , ("rq", "\x201D") , ("R", "\x00AE") ] , prevFont = defaultFontSpec , currentFont = defaultFontSpec , tableTabChar = '\t' , roffMode = NormalMode , lastExpression = Nothing , afterConditional = False } type RoffLexer m = ParsecT Sources RoffState m -- -- Lexer: T.Text -> RoffToken -- eofline :: (Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m () eofline = void newline <|> eof <|> () <$ lookAhead (string "\\}") spacetab :: (Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m Char spacetab = char ' ' <|> char '\t' -- separate function from lexMacro since real man files sometimes do not -- follow the rules lexComment :: PandocMonad m => RoffLexer m RoffTokens lexComment = do try $ string ".\\\"" skipMany $ noneOf "\n" eofline return mempty lexMacro :: PandocMonad m => RoffLexer m RoffTokens lexMacro = do pos <- getPosition st <- getState guard $ sourceColumn pos == 1 || afterConditional st char '.' <|> char '\'' skipMany spacetab macroName <- manyChar (satisfy isAlphaNum) case macroName of "nop" -> return mempty "ie" -> lexConditional "ie" "if" -> lexConditional "if" "el" -> lexConditional "el" "while" -> lexConditional "while" -- this doesn't get the semantics right but -- avoids parse errors _ -> do args <- lexArgs case macroName of "" -> return mempty "TS" -> lexTable pos "de" -> lexMacroDef args "de1" -> lexMacroDef args "ds" -> lexStringDef args "ds1" -> lexStringDef args "sp" -> return $ singleTok EmptyLine "so" -> lexIncludeFile args _ -> resolveMacro macroName args pos lexTable :: PandocMonad m => SourcePos -> RoffLexer m RoffTokens lexTable pos = do skipMany lexComment spaces opts <- try tableOptions <|> [] <$ optional (char ';') case lookup "tab" opts of Just (T.uncons -> Just (c, _)) -> updateState $ \st -> st{ tableTabChar = c } _ -> updateState $ \st -> st{ tableTabChar = '\t' } spaces skipMany lexComment spaces rows <- lexTableRows morerows <- many $ try $ do string ".T&" skipMany spacetab newline lexTableRows string ".TE" skipMany spacetab eofline return $ singleTok $ Tbl opts (rows <> concat morerows) pos lexTableRows :: PandocMonad m => RoffLexer m [TableRow] lexTableRows = do aligns <- tableFormatSpec spaces skipMany $ lexComment <|> try (mempty <$ (string ".sp" >> skipMany spaceChar >> newline)) spaces rows <- many (notFollowedBy (try (string ".TE") <|> try (string ".T&")) >> tableRow) return $ zip aligns rows tableCell :: PandocMonad m => RoffLexer m RoffTokens tableCell = do pos <- getPosition (enclosedCell <|> simpleCell) >>= lexRoff pos . T.pack where enclosedCell = do try (string "T{") manyTill anyChar (try (string "T}")) simpleCell = do tabChar <- tableTabChar <$> getState many (notFollowedBy (char tabChar <|> newline) >> anyChar) tableRow :: PandocMonad m => RoffLexer m [RoffTokens] tableRow = do tabChar <- tableTabChar <$> getState c <- tableCell cs <- many $ try (char tabChar >> tableCell) skipMany spacetab eofline skipMany lexComment return (c:cs) tableOptions :: PandocMonad m => RoffLexer m [TableOption] tableOptions = many1 tableOption <* spaces <* char ';' tableOption :: PandocMonad m => RoffLexer m TableOption tableOption = do k <- many1Char letter v <- option "" $ try $ do skipMany spacetab char '(' manyTillChar anyChar (char ')') skipMany spacetab optional (char ',' >> skipMany spacetab) return (k,v) tableFormatSpec :: PandocMonad m => RoffLexer m [[CellFormat]] tableFormatSpec = do first <- tableFormatSpecLine rest <- many $ try $ (newline <|> char ',') *> tableFormatSpecLine let speclines = first:rest spaces char '.' return $ speclines <> repeat (lastDef [] speclines) -- last line is default tableFormatSpecLine :: PandocMonad m => RoffLexer m [CellFormat] tableFormatSpecLine = many1 $ skipMany spacetab *> tableColFormat <* skipMany spacetab tableColFormat :: PandocMonad m => RoffLexer m CellFormat tableColFormat = do pipePrefix' <- option False $ True <$ try (string "|" <* notFollowedBy spacetab) c <- oneOf ['a','A','c','C','l','L','n','N','r','R','s','S','^','_','-', '=','|'] suffixes <- many $ try (skipMany spacetab *> countChar 1 digit) <|> (do x <- oneOf ['b','B','d','D','e','E','f','F','i','I','m','M', 'p','P','t','T','u','U','v','V','w','W','x','X', 'z','Z'] num <- case toLower x of 'w' -> many1 digit <|> (do char '(' xs <- manyTill anyChar (char ')') return ("(" <> xs <> ")")) <|> return "" 'f' -> count 1 alphaNum <* skipMany spacetab 'm' -> count 1 alphaNum <* skipMany spacetab _ -> return "" return $ T.pack $ x : num) pipeSuffix' <- option False $ True <$ string "|" return $ CellFormat { columnType = c , pipePrefix = pipePrefix' , pipeSuffix = pipeSuffix' , columnSuffixes = suffixes } -- We don't fully handle the conditional. But we do -- include everything under '.ie n', which occurs commonly -- in man pages. lexConditional :: PandocMonad m => T.Text -> RoffLexer m RoffTokens lexConditional mname = do pos <- getPosition skipMany spacetab mbtest <- if mname == "el" then fmap not . lastExpression <$> getState else expression skipMany spacetab st <- getState -- save state, so we can reset it ifPart <- do optional $ try $ char '\\' >> newline lexGroup <|> do updateState $ \s -> s{ afterConditional = True } t <- manToken updateState $ \s -> s{ afterConditional = False } return t case mbtest of Nothing -> do setState st -- reset state, so we don't record macros in skipped section report $ SkippedContent (T.cons '.' mname) pos return mempty Just True -> return ifPart Just False -> do setState st return mempty expression :: PandocMonad m => RoffLexer m (Maybe Bool) expression = do raw <- charsInBalanced '(' ')' (T.singleton <$> (satisfy (/= '\n'))) <|> many1Char nonspaceChar returnValue $ case raw of "1" -> Just True "n" -> Just True -- nroff mode "t" -> Just False -- troff mode _ -> Nothing where returnValue v = do updateState $ \st -> st{ lastExpression = v } return v lexGroup :: PandocMonad m => RoffLexer m RoffTokens lexGroup = do groupstart mconcat <$> manyTill manToken groupend where groupstart = try $ string "\\{" <* optional (try (string "\\\n")) groupend = try $ string "\\}" lexIncludeFile :: PandocMonad m => [Arg] -> RoffLexer m RoffTokens lexIncludeFile args = do pos <- getPosition case args of (f:_) -> do let fp = linePartsToText f dirs <- getResourcePath result <- readFileFromDirs dirs $ T.unpack fp case result of Nothing -> report $ CouldNotLoadIncludeFile fp pos Just s -> addToInput s return mempty [] -> return mempty resolveMacro :: PandocMonad m => T.Text -> [Arg] -> SourcePos -> RoffLexer m RoffTokens resolveMacro macroName args pos = do macros <- customMacros <$> getState case M.lookup macroName macros of Nothing -> return $ singleTok $ ControlLine macroName args pos Just ts -> do let fillLP (MacroArg i) zs = case drop (i - 1) args of [] -> zs (ys:_) -> ys <> zs fillLP z zs = z : zs let fillMacroArg (TextLine lineparts) = TextLine (foldr fillLP [] lineparts) fillMacroArg x = x return $ RoffTokens . fmap fillMacroArg . unRoffTokens $ ts lexStringDef :: PandocMonad m => [Arg] -> RoffLexer m RoffTokens lexStringDef args = do -- string definition case args of [] -> Prelude.fail "No argument to .ds" (x:ys) -> do let ts = singleTok $ TextLine (intercalate [RoffStr " " ] ys) let stringName = linePartsToText x updateState $ \st -> st{ customMacros = M.insert stringName ts (customMacros st) } return mempty lexMacroDef :: PandocMonad m => [Arg] -> RoffLexer m RoffTokens lexMacroDef args = do -- macro definition updateState $ \st -> st{ roffMode = CopyMode } (macroName, stopMacro) <- case args of (x : y : _) -> return (linePartsToText x, linePartsToText y) -- optional second arg (x:_) -> return (linePartsToText x, ".") [] -> Prelude.fail "No argument to .de" let stop = try $ do char '.' <|> char '\'' skipMany spacetab textStr stopMacro _ <- lexArgs return () ts <- mconcat <$> manyTill manToken stop updateState $ \st -> st{ customMacros = M.insert macroName ts (customMacros st) , roffMode = NormalMode } return mempty lexArgs :: PandocMonad m => RoffLexer m [Arg] lexArgs = do args <- many $ try oneArg skipMany spacetab eofline return args where oneArg :: PandocMonad m => RoffLexer m [LinePart] oneArg = do skipMany $ try $ string "\\\n" -- continuation line try quotedArg <|> plainArg -- try, because there are some erroneous files, e.g. linux/bpf.2 plainArg :: PandocMonad m => RoffLexer m [LinePart] plainArg = do skipMany spacetab mconcat <$> many1 (macroArg <|> escape <|> regularText <|> unescapedQuote) where unescapedQuote = char '"' >> return [RoffStr "\""] quotedArg :: PandocMonad m => RoffLexer m [LinePart] quotedArg = do skipMany spacetab char '"' xs <- mconcat <$> many (macroArg <|> escape <|> regularText <|> spaceTabChar <|> escapedQuote) char '"' return xs where escapedQuote = try $ do char '"' char '"' return [RoffStr "\""] -- strings and macros share namespace resolveText :: PandocMonad m => T.Text -> SourcePos -> RoffLexer m [LinePart] resolveText stringname pos = do RoffTokens ts <- resolveMacro stringname [] pos case Foldable.toList ts of [TextLine xs] -> return xs _ -> do report $ SkippedContent ("unknown string " <> stringname) pos return mempty lexLine :: PandocMonad m => RoffLexer m RoffTokens lexLine = do mode <- roffMode <$> getState case mode of CopyMode -> optional $ try $ string "\\&" NormalMode -> return () lnparts <- mconcat <$> many1 linePart eofline go lnparts where -- return empty line if we only have empty strings; -- this can happen if the line just contains \f[C], for example. go [] = return mempty go (RoffStr "" : xs) = go xs go xs = return $ singleTok $ TextLine xs linePart :: PandocMonad m => RoffLexer m [LinePart] linePart = macroArg <|> escape <|> regularText <|> quoteChar <|> spaceTabChar macroArg :: PandocMonad m => RoffLexer m [LinePart] macroArg = try $ do pos <- getPosition backslash char '$' x <- escapeArg <|> countChar 1 digit case safeRead x of Just i -> return [MacroArg i] Nothing -> do report $ SkippedContent ("illegal macro argument " <> x) pos return [] regularText :: PandocMonad m => RoffLexer m [LinePart] regularText = do s <- many1Char $ noneOf "\n\r\t \\\"" return [RoffStr s] quoteChar :: PandocMonad m => RoffLexer m [LinePart] quoteChar = do char '"' return [RoffStr "\""] spaceTabChar :: PandocMonad m => RoffLexer m [LinePart] spaceTabChar = do c <- spacetab return [RoffStr $ T.singleton c] lexEmptyLine :: PandocMonad m => RoffLexer m RoffTokens lexEmptyLine = newline >> return (singleTok EmptyLine) manToken :: PandocMonad m => RoffLexer m RoffTokens manToken = lexComment <|> lexMacro <|> lexLine <|> lexEmptyLine linePartsToText :: [LinePart] -> T.Text linePartsToText = mconcat . map go where go (RoffStr s) = s go _ = mempty -- | Tokenize a string as a sequence of roff tokens. lexRoff :: PandocMonad m => SourcePos -> T.Text -> m RoffTokens lexRoff pos txt = do eithertokens <- readWithM (do setPosition pos mconcat <$> many manToken) def txt case eithertokens of Left e -> throwError e Right tokenz -> return tokenz ================================================ FILE: src/Text/Pandoc/Readers/TWiki.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.TWiki Copyright : Copyright (C) 2014 Alexander Sulfrian License : GNU GPL, version 2 or above Maintainer : Alexander Sulfrian Stability : alpha Portability : portable Conversion of twiki text to 'Pandoc' document. -} module Text.Pandoc.Readers.TWiki ( readTWiki ) where import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isAlphaNum, isDigit, isUpper, isLower, isLetter) import qualified Data.Foldable as F import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import Text.HTML.TagSoup import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) import Text.Pandoc.Shared (tshow) import Text.Pandoc.XML (fromEntities) -- | Read twiki from an input string and return a Pandoc document. readTWiki :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readTWiki opts s = do let sources = ensureFinalNewlines 2 (toSources s) res <- readWithM parseTWiki def{ stateOptions = opts } sources case res of Left e -> throwError e Right d -> return d type TWParser = ParsecT Sources ParserState -- -- utility functions -- tryMsg :: Text -> TWParser m a -> TWParser m a tryMsg msg p = try p T.unpack msg htmlElement :: PandocMonad m => Text -> TWParser m (Attr, Text) htmlElement tag = tryMsg tag $ do (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) content <- T.pack <$> manyTill anyChar (endtag <|> endofinput) return (htmlAttrToPandoc attr, trim content) where endtag = void $ htmlTag (~== TagClose tag) endofinput = lookAhead $ try $ skipMany blankline >> skipSpaces >> eof trim = T.dropAround (=='\n') htmlAttrToPandoc :: [Attribute Text] -> Attr htmlAttrToPandoc attrs = (ident, classes, keyvals) where ident = fromMaybe "" $ lookup "id" attrs classes = maybe [] T.words $ lookup "class" attrs keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"] parseHtmlContentWithAttrs :: PandocMonad m => Text -> TWParser m a -> TWParser m (Attr, [a]) parseHtmlContentWithAttrs tag parser = do (attr, content) <- htmlElement tag parsedContent <- try $ parseContent content return (attr, parsedContent) where parseContent = parseFromString' $ manyTill parser endOfContent endOfContent = try $ skipMany blankline >> skipSpaces >> eof parseCharHtmlContentWithAttrs :: PandocMonad m => Text -> TWParser m Char -> TWParser m (Attr, Text) parseCharHtmlContentWithAttrs tag = fmap go . parseHtmlContentWithAttrs tag where go (x, y) = (x, T.pack y) parseHtmlContent :: PandocMonad m => Text -> TWParser m a -> TWParser m [a] parseHtmlContent tag p = snd <$> parseHtmlContentWithAttrs tag p -- -- main parser -- parseTWiki :: PandocMonad m => TWParser m Pandoc parseTWiki = B.doc . mconcat <$> many block <* spaces <* eof -- -- block parsers -- block :: PandocMonad m => TWParser m B.Blocks block = do res <- mempty <$ skipMany1 blankline <|> blockElements <|> para skipMany blankline trace (T.take 60 $ tshow $ B.toList res) return res blockElements :: PandocMonad m => TWParser m B.Blocks blockElements = choice [ separator , header , verbatim , literal , list "" , table , blockQuote , noautolink ] separator :: PandocMonad m => TWParser m B.Blocks separator = tryMsg "separator" $ string "---" >> newline >> return B.horizontalRule header :: PandocMonad m => TWParser m B.Blocks header = tryMsg "header" $ do string "---" level <- length <$> many1 (char '+') guard $ level <= 6 classes <- option [] $ string "!!" >> return ["unnumbered"] skipSpaces content <- B.trimInlines . mconcat <$> manyTill inline newline attr <- registerHeader ("", classes, []) content return $ B.headerWith attr level content verbatim :: PandocMonad m => TWParser m B.Blocks verbatim = uncurry B.codeBlockWith <$> (htmlElement "verbatim" <|> htmlElement "pre") literal :: PandocMonad m => TWParser m B.Blocks literal = rawBlock <$> htmlElement "literal" where format (_, _, kvs) = fromMaybe "html" $ lookup "format" kvs rawBlock (attrs, content) = B.rawBlock (format attrs) content list :: PandocMonad m => Text -> TWParser m B.Blocks list prefix = choice [ bulletList prefix , orderedList prefix , definitionList prefix] definitionList :: PandocMonad m => Text -> TWParser m B.Blocks definitionList prefix = tryMsg "definitionList" $ do indent <- lookAhead $ textStr prefix *> many1 (textStr " ") <* textStr "$ " elements <- many $ parseDefinitionListItem (prefix <> T.concat indent) return $ B.definitionList elements where parseDefinitionListItem :: PandocMonad m => Text -> TWParser m (B.Inlines, [B.Blocks]) parseDefinitionListItem indent = do textStr (indent <> "$ ") >> skipSpaces term <- many1Till inline $ string ": " line <- listItemLine indent $ string "$ " return (mconcat term, [line]) bulletList :: PandocMonad m => Text -> TWParser m B.Blocks bulletList prefix = tryMsg "bulletList" $ parseList prefix (char '*') (char ' ') orderedList :: PandocMonad m => Text -> TWParser m B.Blocks orderedList prefix = tryMsg "orderedList" $ parseList prefix (oneOf "1iIaA") (string ". ") parseList :: PandocMonad m => Text -> TWParser m Char -> TWParser m a -> TWParser m B.Blocks parseList prefix marker delim = do (indent, style) <- lookAhead $ textStr prefix *> listStyle <* delim blocks <- many $ parseListItem (prefix <> indent) (char style <* delim) return $ case style of '1' -> B.orderedListWith (1, DefaultStyle, DefaultDelim) blocks 'i' -> B.orderedListWith (1, LowerRoman, DefaultDelim) blocks 'I' -> B.orderedListWith (1, UpperRoman, DefaultDelim) blocks 'a' -> B.orderedListWith (1, LowerAlpha, DefaultDelim) blocks 'A' -> B.orderedListWith (1, UpperAlpha, DefaultDelim) blocks _ -> B.bulletList blocks where listStyle = do indent <- many1 $ textStr " " style <- marker return (T.concat indent, style) parseListItem :: (PandocMonad m, Show a) => Text -> TWParser m a -> TWParser m B.Blocks parseListItem prefix marker = textStr prefix >> marker >> listItemLine prefix marker listItemLine :: (PandocMonad m, Show a) => Text -> TWParser m a -> TWParser m B.Blocks listItemLine prefix marker = mconcat <$> (lineContent >>= parseContent) where lineContent = do content <- anyLine continuation <- optionMaybe listContinuation return $ filterSpaces content <> "\n" <> maybe "" (" " <>) continuation filterSpaces = T.dropWhileEnd (== ' ') listContinuation = notFollowedBy (textStr prefix >> marker) >> string " " >> lineContent parseContent = parseFromString' $ many1 $ nestedList <|> parseInline parseInline = B.plain . mconcat <$> many1Till inline (lastNewline <|> newlineBeforeNestedList) nestedList = list prefix lastNewline = try $ char '\n' <* eof newlineBeforeNestedList = try $ char '\n' <* lookAhead nestedList table :: PandocMonad m => TWParser m B.Blocks table = try $ do thead <- optionMaybe (unzip <$> many1Till tableParseHeader newline) rows <- many1 tableParseRow return $ buildTable mempty rows $ fromMaybe (align rows, columns rows) thead where buildTable caption rows (aligns, heads) = B.table (B.simpleCaption $ B.plain caption) aligns (TableHead nullAttr $ toHeaderRow heads) [TableBody nullAttr 0 [] $ map toRow rows] (TableFoot nullAttr []) align rows = replicate (columnCount rows) (AlignDefault, ColWidthDefault) columns rows = replicate (columnCount rows) mempty columnCount (r:_) = length r columnCount [] = 0 toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] tableParseHeader :: PandocMonad m => TWParser m ((Alignment, ColWidth), B.Blocks) tableParseHeader = try $ do char '|' leftSpaces <- length <$> many spaceChar char '*' content <- tableColumnContent (char '*' >> skipSpaces >> char '|') char '*' rightSpaces <- length <$> many spaceChar optional tableEndOfRow return (tableAlign leftSpaces rightSpaces, content) where tableAlign left right | left >= 2 && left == right = (AlignCenter, ColWidthDefault) | left > right = (AlignRight, ColWidthDefault) | otherwise = (AlignLeft, ColWidthDefault) tableParseRow :: PandocMonad m => TWParser m [B.Blocks] tableParseRow = many1Till tableParseColumn newline tableParseColumn :: PandocMonad m => TWParser m B.Blocks tableParseColumn = char '|' *> skipSpaces *> tableColumnContent (skipSpaces >> char '|') <* skipSpaces <* optional tableEndOfRow tableEndOfRow :: PandocMonad m => TWParser m Char tableEndOfRow = lookAhead (try $ char '|' >> char '\n') >> char '|' tableColumnContent :: PandocMonad m => TWParser m a -> TWParser m B.Blocks tableColumnContent end = B.plain . mconcat <$> manyTill content (lookAhead $ try end) where content = continuation <|> inline continuation = try $ char '\\' >> newline >> return mempty blockQuote :: PandocMonad m => TWParser m B.Blocks blockQuote = B.blockQuote . mconcat <$> parseHtmlContent "blockquote" block noautolink :: PandocMonad m => TWParser m B.Blocks noautolink = do (_, content) <- htmlElement "noautolink" st <- getState setState $ st{ stateAllowLinks = False } blocks <- try $ parseContent content setState $ st{ stateAllowLinks = True } return $ mconcat blocks where parseContent = parseFromString' $ many block para :: PandocMonad m => TWParser m B.Blocks para = result . mconcat <$> many1Till inline endOfParaElement where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof endOfPara = try $ blankline >> skipMany1 blankline newBlockElement = try $ blankline >> void blockElements result content = if F.all (==Space) content then mempty else B.para $ B.trimInlines content -- -- inline parsers -- inline :: PandocMonad m => TWParser m B.Inlines inline = choice [ whitespace , br , macro , strong , strongHtml , strongAndEmph , emph , emphHtml , boldCode , smart , link , htmlComment , code , codeHtml , nop , autoLink , str , symbol ] "inline" whitespace :: PandocMonad m => TWParser m B.Inlines whitespace = lb <|> regsp where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space br :: PandocMonad m => TWParser m B.Inlines br = try $ string "%BR%" >> return B.linebreak linebreak :: PandocMonad m => TWParser m B.Inlines linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) where lastNewline = eof >> return mempty innerNewline = return B.space between :: (Monoid c, PandocMonad m, Show b) => TWParser m a -> TWParser m b -> (TWParser m b -> TWParser m c) -> TWParser m c between start end p = mconcat <$> try (start >> notFollowedBy whitespace >> many1Till (p end) end) enclosed :: (Monoid b, PandocMonad m, Show a) => TWParser m a -> (TWParser m a -> TWParser m b) -> TWParser m b enclosed sep p = between sep (try $ sep <* endMarker) p where endMarker = lookAhead $ void endSpace <|> void (oneOf ".,!?:)|") <|> eof endSpace = (spaceChar <|> newline) >> return B.space macro :: PandocMonad m => TWParser m B.Inlines macro = macroWithParameters <|> withoutParameters where withoutParameters = emptySpan <$> enclosed (char '%') (const macroName) emptySpan name = buildSpan name [] mempty macroWithParameters :: PandocMonad m => TWParser m B.Inlines macroWithParameters = try $ do char '%' name <- macroName (content, kvs) <- attributes char '%' return $ buildSpan name kvs $ B.str content buildSpan :: Text -> [(Text, Text)] -> B.Inlines -> B.Inlines buildSpan className kvs = B.spanWith attrs where attrs = ("", ["twiki-macro", className] ++ additionalClasses, kvsWithoutClasses) additionalClasses = maybe [] T.words $ lookup "class" kvs kvsWithoutClasses = [(k,v) | (k,v) <- kvs, k /= "class"] macroName :: PandocMonad m => TWParser m Text macroName = do first <- letter rest <- many $ alphaNum <|> char '_' return $ T.pack $ first:rest attributes :: PandocMonad m => TWParser m (Text, [(Text, Text)]) attributes = foldr (either mkContent mkKvs) ("", []) <$> (char '{' *> spnl *> many (attribute <* spnl) <* char '}') where spnl = skipMany (spaceChar <|> newline) mkContent c ("", kvs) = (c, kvs) mkContent c (rest, kvs) = (c <> " " <> rest, kvs) mkKvs kv (cont, rest) = (cont, kv : rest) attribute :: PandocMonad m => TWParser m (Either Text (Text, Text)) attribute = withKey <|> withoutKey where withKey = try $ do key <- macroName char '=' curry Right key <$> parseValue False withoutKey = try $ Left <$> parseValue True parseValue allowSpaces = fromEntities <$> (withQuotes <|> withoutQuotes allowSpaces) withQuotes = between (char '"') (char '"') (\_ -> countChar 1 $ noneOf ['"']) withoutQuotes allowSpaces | allowSpaces = many1Char $ noneOf "}" | otherwise = many1Char $ noneOf " }" nestedInlines :: (Show a, PandocMonad m) => TWParser m a -> TWParser m B.Inlines nestedInlines end = innerSpace <|> nestedInline where innerSpace = try $ whitespace <* notFollowedBy end nestedInline = notFollowedBy whitespace >> inline strong :: PandocMonad m => TWParser m B.Inlines strong = try $ B.strong <$> enclosed (char '*') nestedInlines strongHtml :: PandocMonad m => TWParser m B.Inlines strongHtml = B.strong . mconcat <$> (parseHtmlContent "strong" inline <|> parseHtmlContent "b" inline) strongAndEmph :: PandocMonad m => TWParser m B.Inlines strongAndEmph = try $ B.emph . B.strong <$> enclosed (string "__") nestedInlines emph :: PandocMonad m => TWParser m B.Inlines emph = try $ B.emph <$> enclosed (char '_') (\p -> notFollowedBy (char '|') >> nestedInlines p) -- emphasis closers can't cross table cell boundaries, see #3921 emphHtml :: PandocMonad m => TWParser m B.Inlines emphHtml = B.emph . mconcat <$> (parseHtmlContent "em" inline <|> parseHtmlContent "i" inline) nestedString :: (Show a, PandocMonad m) => TWParser m a -> TWParser m Text nestedString end = innerSpace <|> countChar 1 nonspaceChar where innerSpace = try $ many1Char spaceChar <* notFollowedBy end boldCode :: PandocMonad m => TWParser m B.Inlines boldCode = try $ B.strong . B.code . fromEntities <$> enclosed (string "==") nestedString htmlComment :: PandocMonad m => TWParser m B.Inlines htmlComment = htmlTag isCommentTag >> return mempty code :: PandocMonad m => TWParser m B.Inlines code = try $ B.code . fromEntities <$> enclosed (char '=') nestedString codeHtml :: PandocMonad m => TWParser m B.Inlines codeHtml = do (attrs, content) <- parseCharHtmlContentWithAttrs "code" anyChar return $ B.codeWith attrs $ fromEntities content autoLink :: PandocMonad m => TWParser m B.Inlines autoLink = try $ do state <- getState guard $ stateAllowLinks state (text, url) <- parseLink guard $ checkLink (T.last url) return $ makeLink (text, url) where parseLink = notFollowedBy nop >> (uri <|> emailAddress) makeLink (text, url) = B.link url "" $ B.str text checkLink c | c == '/' = True | otherwise = isAlphaNum c str :: PandocMonad m => TWParser m B.Inlines str = B.str <$> (many1Char alphaNum <|> characterReference) nop :: PandocMonad m => TWParser m B.Inlines nop = try $ (void exclamation <|> void nopTag) >> followContent where exclamation = char '!' nopTag = stringAnyCase "" followContent = B.str . fromEntities <$> many1Char nonspaceChar symbol :: PandocMonad m => TWParser m B.Inlines symbol = B.str <$> countChar 1 nonspaceChar smart :: PandocMonad m => TWParser m B.Inlines smart = smartPunctuation inline link :: PandocMonad m => TWParser m B.Inlines link = try $ do st <- getState guard $ stateAllowLinks st setState $ st{ stateAllowLinks = False } (url, title, classes, content) <- linkText <|> simpleWikiLink setState $ st{ stateAllowLinks = True } return $ B.linkWith ("",classes,[]) url title content linkText :: PandocMonad m => TWParser m (Text, Text, [Text], B.Inlines) linkText = do string "[[" url <- T.pack <$> many1Till anyChar (char ']') content <- option (B.str url) (mconcat <$> linkContent) char ']' return (url, "", [], content) where linkContent = char '[' >> many1Till anyChar (char ']') >>= parseLinkContent . T.pack parseLinkContent = parseFromString' $ many1 inline simpleWikiLink :: PandocMonad m => TWParser m (Text, Text, [Text], B.Inlines) simpleWikiLink = do w <- wikiWord return (w, "", ["wikilink"], B.str w) where wikiWord = do cs <- many1 $ satisfy (\x -> isLetter x && isUpper x) ds <- many1 $ satisfy (\x -> isDigit x || (isLetter x && isLower x)) es <- many1 $ satisfy (\x -> isLetter x && isUpper x) fs <- many $ satisfy isAlphaNum return $ T.pack $ cs ++ ds ++ es ++ fs ================================================ FILE: src/Text/Pandoc/Readers/Textile.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.Textile Copyright : Copyright (C) 2010-2012 Paul Rivier 2010-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Paul Rivier Stability : alpha Portability : portable Conversion from Textile to 'Pandoc' document, based on the spec available at https://www.promptworks.com/textile/. Implemented and parsed: - Paragraphs - Code blocks - Lists - blockquote - Inlines : strong, emph, cite, code, deleted, superscript, subscript, links - footnotes - HTML-specific and CSS-specific attributes on headers Left to be implemented: - dimension sign - all caps - continued blocks (ex bq..) TODO : refactor common patterns across readers : - more ... -} module Text.Pandoc.Readers.Textile ( readTextile) where import Control.Monad (guard, liftM) import Control.Monad.Except (throwError) import Data.Char (digitToInt, isUpper) 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) import qualified Data.Text as T import Text.HTML.TagSoup (Tag (..), fromAttrib) import Text.HTML.TagSoup.Match import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.CSS import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared (trim, tshow) import Text.Read (readMaybe) -- | Parse a Textile text and return a Pandoc document. readTextile :: (PandocMonad m, ToSources a) => ReaderOptions -- ^ Reader options -> a -> m Pandoc readTextile opts s = do let sources = ensureFinalNewlines 2 (toSources s) parsed <- readWithM parseTextile def{ stateOptions = opts } sources case parsed of Right result -> return result Left e -> throwError e type TextileParser = ParsecT Sources ParserState -- | Generate a Pandoc ADT from a textile document parseTextile :: PandocMonad m => TextileParser m Pandoc parseTextile = do many blankline startPos <- getPosition -- go through once just to get list of reference keys and notes -- docMinusKeys is the raw document with blanks where the keys/notes were... let firstPassParser = do pos <- getPosition t <- noteBlock <|> referenceKey <|> lineClump return (pos, t) manyTill firstPassParser eof >>= setInput . Sources setPosition startPos st' <- getState let reversedNotes = stateNotes st' updateState $ \s -> s { stateNotes = reverse reversedNotes } -- now parse it for real... Pandoc nullMeta . B.toList <$> parseBlocks -- FIXME noteMarker :: PandocMonad m => TextileParser m Text noteMarker = do skipMany spaceChar string "fn" T.pack <$> manyTill digit (string "." <|> try (string "^.")) noteBlock :: PandocMonad m => TextileParser m Text noteBlock = try $ do startPos <- getPosition ref <- noteMarker optional blankline contents <- T.unlines <$> many1Till anyLine (blanklines <|> noteBlock) endPos <- getPosition let newnote = (ref, contents <> "\n") st <- getState let oldnotes = stateNotes st updateState $ \s -> s { stateNotes = newnote : oldnotes } -- return blanks so line count isn't affected return $ T.replicate (sourceLine endPos - sourceLine startPos) "\n" referenceKey :: PandocMonad m => TextileParser m Text referenceKey = try $ do pos <- getPosition char '[' refName <- T.pack <$> many1Till nonspaceChar (char ']') refDestination <- T.pack <$> many1Till anyChar newline st <- getState let oldKeys = stateKeys st let key = toKey refName -- Textile doesn't support link titles on the reference -- definition, so use the empty string let target = (refDestination, "") case M.lookup key oldKeys of Just (t, _) | not (t == target) -> -- similar to Markdown, only warn when the targets -- for matching named references differ logMessage $ DuplicateLinkReference refName pos _ -> return () updateState $ \s -> s {stateKeys = M.insert key (target, nullAttr) oldKeys } return "\n" -- | Parse document blocks parseBlocks :: PandocMonad m => TextileParser m Blocks parseBlocks = mconcat <$> manyTill block eof -- | Block parsers list tried in definition order blockParsers :: PandocMonad m => [TextileParser m Blocks] blockParsers = [ codeBlock , header , blockQuote , hrule , commentBlock , anyList , rawHtmlBlock , rawLaTeXBlock' , table , explicitBlock "p" (para <|> pure (B.para mempty)) , para , mempty <$ blanklines ] -- | Any block in the order of definition of blockParsers block :: PandocMonad m => TextileParser m Blocks block = do res <- choice blockParsers "block" trace (T.take 60 $ tshow $ B.toList res) return res commentBlock :: PandocMonad m => TextileParser m Blocks commentBlock = try $ do string "###." manyTill anyLine blanklines return mempty codeBlock :: PandocMonad m => TextileParser m Blocks codeBlock = codeBlockTextile <|> codeBlockHtml codeBlockTextile :: PandocMonad m => TextileParser m Blocks codeBlockTextile = try $ do string "bc." <|> string "pre." extended <- option False (True <$ char '.') char ' ' let starts = ["p", "table", "bq", "bc", "pre", "h1", "h2", "h3", "h4", "h5", "h6", "pre", "###", "notextile"] let ender = () <$ choice (map explicitBlockStart starts) contents <- if extended then do f <- anyLine rest <- many (notFollowedBy ender *> anyLine) return (f:rest) else manyTill anyLine blanklines return $ B.codeBlock (trimTrailingNewlines (T.unlines contents)) trimTrailingNewlines :: Text -> Text trimTrailingNewlines = T.dropWhileEnd (=='\n') -- | Code Blocks in Textile are between
     and 
    codeBlockHtml :: PandocMonad m => TextileParser m Blocks codeBlockHtml = try $ do (t@(TagOpen _ attrs),_) <- htmlTag (tagOpen (=="pre") (const True)) result' <- T.pack <$> manyTill anyChar (htmlTag (tagClose (=="pre"))) -- drop leading newline if any let result'' = case T.uncons result' of Just ('\n', xs) -> xs _ -> result' -- drop trailing newline if any let result''' = case T.unsnoc result'' of Just (xs, '\n') -> xs _ -> result'' let classes = T.words $ fromAttrib "class" t let ident = fromAttrib "id" t let kvs = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"] return $ B.codeBlockWith (ident,classes,kvs) result''' -- | Header of the form "hN. content" with N in 1..6 header :: PandocMonad m => TextileParser m Blocks header = try $ do char 'h' level <- digitToInt <$> oneOf "123456" attr <- attributes char '.' lookAhead whitespace name <- trimInlines . mconcat <$> many inline attr' <- registerHeader attr name return $ B.headerWith attr' level name -- | Blockquote of the form "bq. content" blockQuote :: PandocMonad m => TextileParser m Blocks blockQuote = try $ do string "bq" >> attributes >> char '.' >> whitespace B.blockQuote <$> para -- Horizontal rule hrule :: PandocMonad m => TextileParser m Blocks hrule = try $ do skipSpaces start <- oneOf "-*" count 2 (skipSpaces >> char start) skipMany (spaceChar <|> char start) newline optional blanklines return B.horizontalRule -- Lists handling -- | Can be a bullet list or an ordered list. This implementation is -- strict in the nesting, sublist must start at exactly "parent depth -- plus one" anyList :: PandocMonad m => TextileParser m Blocks anyList = try $ anyListAtDepth 1 <* blanklines -- | This allow one type of list to be nested into an other type, -- provided correct nesting anyListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks anyListAtDepth depth = choice [ bulletListAtDepth depth, orderedListAtDepth depth, definitionList ] -- | Bullet List of given depth, depth being the number of leading '*' bulletListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks bulletListAtDepth depth = try $ B.bulletList <$> many1 (bulletListItemAtDepth depth) -- | Bullet List Item of given depth, depth being the number of -- leading '*' bulletListItemAtDepth :: PandocMonad m => Int -> TextileParser m Blocks bulletListItemAtDepth depth = try $ do bulletListStartAtDepth depth genericListItemContentsAtDepth depth -- | Ordered List of given depth, depth being the number of -- leading '#' -- The first Ordered List Item may have a start attribute orderedListAtDepth :: PandocMonad m => Int -> TextileParser m Blocks orderedListAtDepth depth = try $ do (startNum, firstItem) <- firstOrderedListItemAtDepth depth moreItems <- many (orderedListItemAtDepth depth) let listItems = firstItem : moreItems return $ B.orderedListWith (startNum, DefaultStyle, DefaultDelim) listItems -- | The first Ordered List Item, which could have a start attribute firstOrderedListItemAtDepth :: PandocMonad m => Int -> TextileParser m (Int, Blocks) firstOrderedListItemAtDepth depth = try $ do startNum <- orderedListStartAtDepth depth contents <- genericListItemContentsAtDepth depth return (startNum, contents) -- | Ordered List Item of given depth, depth being the number of -- leading '#' orderedListItemAtDepth :: PandocMonad m => Int -> TextileParser m Blocks orderedListItemAtDepth depth = try $ do orderedListStartAtDepth depth genericListItemContentsAtDepth depth -- | Lists always start with a number of leading characters '#' or '*' -- Ordered list start characters '#' can be followed by the start attribute -- number, but bullet list characters '*' can not orderedListStartAtDepth :: PandocMonad m => Int -> TextileParser m Int orderedListStartAtDepth depth = count depth (char '#') >> try orderedListStartAttr <* (attributes >> whitespace) bulletListStartAtDepth :: PandocMonad m => Int -> TextileParser m () bulletListStartAtDepth depth = () <$ (count depth (char '*') >> attributes >> whitespace) -- | The leading characters and start attributes differ between ordered and -- unordered lists, but their contents have the same structure and can -- share a Parser genericListItemContentsAtDepth :: PandocMonad m => Int -> TextileParser m Blocks genericListItemContentsAtDepth depth = do contents <- mconcat <$> many ((B.plain . mconcat <$> many1 inline) <|> try (newline >> codeBlockHtml)) newline sublist <- option mempty (anyListAtDepth (depth + 1)) return $ contents <> sublist -- | A definition list is a set of consecutive definition items definitionList :: PandocMonad m => TextileParser m Blocks definitionList = try $ B.definitionList <$> many1 definitionListItem -- | List start character. listStart :: PandocMonad m => TextileParser m () listStart = genericListStart '*' <|> () <$ orderedListStart <|> () <$ definitionListStart genericListStart :: PandocMonad m => Char -> TextileParser m () genericListStart c = () <$ try (many1 (char c) >> whitespace) orderedListStart :: PandocMonad m => TextileParser m () orderedListStart = () <$ try (many1 (char '#') >> try orderedListStartAttr >> whitespace) basicDLStart :: PandocMonad m => TextileParser m () basicDLStart = do char '-' whitespace notFollowedBy newline definitionListStart :: PandocMonad m => TextileParser m Inlines definitionListStart = try $ do basicDLStart trimInlines . mconcat <$> many1Till inline ( try (newline *> lookAhead basicDLStart) <|> try (lookAhead (() <$ string ":=")) ) -- | A definition list item in textile begins with '- ', followed by -- the term defined, then spaces and ":=". The definition follows, on -- the same single line, or spaned on multiple line, after a line -- break. definitionListItem :: PandocMonad m => TextileParser m (Inlines, [Blocks]) definitionListItem = try $ do term <- mconcat . intersperse B.linebreak <$> many1 definitionListStart def' <- string ":=" *> optional whitespace *> (multilineDef <|> inlineDef) return (term, def') where inlineDef :: PandocMonad m => TextileParser m [Blocks] inlineDef = liftM (\d -> [B.plain d]) $ optional whitespace >> (trimInlines . mconcat <$> many inline) <* newline multilineDef :: PandocMonad m => TextileParser m [Blocks] multilineDef = try $ do optional whitespace >> newline s <- T.pack <$> many1Till anyChar (try (string "=:" >> newline)) -- this <> "\n\n" does not look very good ds <- parseFromString' parseBlocks (s <> "\n\n") return [ds] -- raw content -- | A raw Html Block, optionally followed by blanklines rawHtmlBlock :: PandocMonad m => TextileParser m Blocks rawHtmlBlock = try $ do skipMany spaceChar (_,b) <- htmlTag isBlockTag optional blanklines return $ B.rawBlock "html" b -- | Raw block of LaTeX content rawLaTeXBlock' :: PandocMonad m => TextileParser m Blocks rawLaTeXBlock' = do guardEnabled Ext_raw_tex B.rawBlock "latex" <$> (rawLaTeXBlock <* spaces) -- | In textile, paragraphs are separated by blank lines. para :: PandocMonad m => TextileParser m Blocks para = B.para . trimInlines . mconcat <$> many1 inline -- Tables toAlignment :: Char -> Alignment toAlignment '<' = AlignLeft toAlignment '>' = AlignRight toAlignment '=' = AlignCenter toAlignment _ = AlignDefault cellAttributes :: PandocMonad m => TextileParser m (Bool, Alignment) cellAttributes = try $ do isHeader <- option False (True <$ char '_') -- we just ignore colspan and rowspan markers: optional $ try $ oneOf "/\\" >> many1 digit -- we pay attention to alignments: alignment <- option AlignDefault $ toAlignment <$> oneOf "<>=" -- ignore other attributes for now: _ <- attributes char '.' return (isHeader, alignment) -- | A table cell spans until a pipe | tableCell :: PandocMonad m => TextileParser m ((Bool, Alignment), Blocks) tableCell = try $ do char '|' (isHeader, alignment) <- option (False, AlignDefault) cellAttributes notFollowedBy blankline raw <- trim . T.pack <$> many (noneOf "|\n" <|> try (char '\n' <* notFollowedBy blankline)) content <- parseFromString' parseBlocks (raw <> "\n\n") -- Convert lone Para to Plain for backward compatibility let content' = case B.toList content of [Para ils] -> B.plain (B.fromList ils) _ -> content return ((isHeader, alignment), content') -- | A table row is made of many table cells tableRow :: PandocMonad m => TextileParser m [((Bool, Alignment), Blocks)] tableRow = try $ do -- skip optional row attributes optional $ try $ do _ <- attributes char '.' many1 spaceChar many1 tableCell <* char '|' <* blankline -- | A table with an optional header. table :: PandocMonad m => TextileParser m Blocks table = try $ do -- ignore table attributes caption <- option mempty $ try $ do string "table" _ <- attributes char '.' rawcapt <- trim <$> anyLine parseFromString' (mconcat <$> many inline) rawcapt rawrows <- many1 $ skipMany ignorableRow >> tableRow skipMany ignorableRow blanklines let (headers, rows) = case rawrows of (toprow:rest) | any (fst . fst) toprow -> (toprow, rest) _ -> (mempty, rawrows) let nbOfCols = maximum $ fmap length (headers :| rows) let aligns = map (maybe AlignDefault minimum . nonEmpty) $ transpose $ map (map (snd . fst)) (headers:rows) let toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] return $ B.table (B.simpleCaption $ B.plain caption) (zip aligns (replicate nbOfCols ColWidthDefault)) (TableHead nullAttr $ toHeaderRow $ map snd headers) [TableBody nullAttr 0 [] $ map (toRow . map snd) rows] (TableFoot nullAttr []) -- | Ignore markers for cols, thead, tfoot. ignorableRow :: PandocMonad m => TextileParser m () ignorableRow = try $ do char '|' oneOf ":^-~" _ <- attributes char '.' _ <- anyLine return () explicitBlockStart :: PandocMonad m => Text -> TextileParser m Attr explicitBlockStart name = try $ do string (T.unpack name) attr <- attributes char '.' optional whitespace optional endline return attr -- | Blocks like 'p' and 'table' do not need explicit block tag. -- However, they can be used to set HTML/CSS attributes when needed. explicitBlock :: PandocMonad m => Text -- ^ block tag name -> TextileParser m Blocks -- ^ implicit block -> TextileParser m Blocks explicitBlock name blk = try $ do attr <- explicitBlockStart name contents <- blk return $ if attr == nullAttr then contents else B.divWith attr contents ---------- -- Inlines ---------- -- | Any inline element inline :: PandocMonad m => TextileParser m Inlines inline = choice inlineParsers "inline" -- | Inline parsers tried in order inlineParsers :: PandocMonad m => [TextileParser m Inlines] inlineParsers = [ str , whitespace , endline , code , escapedInline , spanGroup , inlineMarkup , groupedInlineMarkup , rawHtmlInline , rawLaTeXInline' , note , link , image , mark , B.str <$> characterReference , smartPunctuation inline , symbol ] -- | Inline markups inlineMarkup :: PandocMonad m => TextileParser m Inlines inlineMarkup = choice [ simpleInline (string "??") (B.cite []) , simpleInline (string "**") B.strong , simpleInline (string "__") B.emph , simpleInline (char '*') B.strong , simpleInline (char '_') B.emph , simpleInline (char '+') B.underline , simpleInline (char '-' <* notFollowedBy (char '-')) B.strikeout , simpleInline (char '^') B.superscript , simpleInline (char '~') B.subscript ] -- "The tag is created by percent % signs between whitespaces." -- e.g. My mother has %{color:green;}green% eyes. spanGroup :: PandocMonad m => TextileParser m Inlines spanGroup = try $ do notAfterString >>= guard char '%' *> notFollowedBy whitespace attr <- option nullAttr attributes contents <- mconcat <$> manyTill (try (((B.space <>) <$> try (whitespace *> notFollowedBy newline *> inline)) <|> try (notFollowedBy newline *> inline))) (try (char '%' <* lookAhead (newline <|> ' ' <$ whitespace))) pure $ B.spanWith attr contents -- | Trademark, registered, copyright mark :: PandocMonad m => TextileParser m Inlines mark = try $ char '(' >> (try tm <|> try reg <|> copy) reg :: PandocMonad m => TextileParser m Inlines reg = do oneOf "Rr" char ')' return $ B.str "\174" tm :: PandocMonad m => TextileParser m Inlines tm = do oneOf "Tt" oneOf "Mm" char ')' return $ B.str "\8482" copy :: PandocMonad m => TextileParser m Inlines copy = do oneOf "Cc" char ')' return $ B.str "\169" note :: PandocMonad m => TextileParser m Inlines note = try $ do ref <- char '[' *> many1 digit <* char ']' notes <- stateNotes <$> getState case lookup (T.pack ref) notes of Nothing -> Prelude.fail "note not found" Just raw -> B.note <$> parseFromString' parseBlocks raw -- | Special chars markupChars :: [Char] markupChars = "\\*#_@~-+^|%=[]&" -- | Break strings on following chars. Space tab and newline break for -- inlines breaking. Open paren breaks for mark. Quote, dash and dot -- break for smart punctuation. Punctuation breaks for regular -- punctuation. Double quote breaks for named links. > and < break -- for inline html. stringBreakers :: [Char] stringBreakers = " \t\n\r.,\"'?!;:<>«»„“”‚‘’()[]" wordBoundaries :: [Char] wordBoundaries = markupChars <> stringBreakers -- | Parse a hyphened sequence of words hyphenedWords :: PandocMonad m => TextileParser m Text hyphenedWords = do x <- wordChunk xs <- many (try $ char '-' >> wordChunk) return $ T.intercalate "-" (x:xs) wordChunk :: PandocMonad m => TextileParser m Text wordChunk = try $ do hd <- noneOf wordBoundaries tl <- many ( noneOf wordBoundaries <|> try (notFollowedBy' note *> oneOf markupChars <* lookAhead (noneOf wordBoundaries) ) ) return $ T.pack $ hd:tl -- | Any string str :: PandocMonad m => TextileParser m Inlines str = do baseStr <- hyphenedWords -- RedCloth compliance : if parsed word is uppercase and immediately -- followed by parens, parens content is unconditionally word acronym fullStr <- option baseStr $ try $ do guard $ T.all isUpper baseStr acro <- T.pack <$> enclosed (char '(') (char ')') anyChar' return $ T.concat [baseStr, " (", acro, ")"] updateLastStrPos return $ B.str fullStr -- | Some number of space chars whitespace :: PandocMonad m => TextileParser m Inlines whitespace = many1 spaceChar >> return B.space "whitespace" -- | In Textile, an isolated endline character is a line break endline :: PandocMonad m => TextileParser m Inlines endline = try $ do newline notFollowedBy blankline notFollowedBy listStart notFollowedBy rawHtmlBlock return B.linebreak rawHtmlInline :: PandocMonad m => TextileParser m Inlines rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag -- | Raw LaTeX Inline rawLaTeXInline' :: PandocMonad m => TextileParser m Inlines rawLaTeXInline' = try $ do guardEnabled Ext_raw_tex B.rawInline "latex" <$> rawLaTeXInline -- | Textile standard link syntax is "label":target. But we -- can also have ["label":target]. link :: PandocMonad m => TextileParser m Inlines link = try $ do bracketed <- (True <$ char '[') <|> return False char '"' *> notFollowedBy (oneOf " \t\n\r") attr <- attributes name <- trimInlines . mconcat <$> withQuoteContext InDoubleQuote (many1Till inline (char '"')) url <- linkUrl bracketed let name' = if B.toList name == [Str "$"] then B.str url else name return $ if attr == nullAttr then B.link url "" name' else B.spanWith attr $ B.link url "" name' linkUrl :: PandocMonad m => Bool -> TextileParser m Text linkUrl bracketed = do char ':' let stop = if bracketed then char ']' else lookAhead $ space <|> eof' <|> oneOf "[]" <|> try (oneOf "!.,;:*" *> (space <|> newline <|> eof')) rawLink <- T.pack <$> many1Till nonspaceChar stop st <- getState return $ case M.lookup (toKey rawLink) (stateKeys st) of Nothing -> rawLink Just ((src, _), _) -> src -- | image embedding image :: PandocMonad m => TextileParser m Inlines image = try $ do char '!' >> notFollowedBy space (ident, cls, kvs) <- attributes let attr = case lookup "style" kvs of Just stls -> (ident, cls, pickStylesToKVs ["width", "height"] stls) Nothing -> (ident, cls, kvs) src <- T.pack <$> many1 (noneOf " \t\n\r!(") alt <- fmap T.pack $ option "" $ try $ char '(' *> manyTill anyChar (char ')') char '!' let img = B.imageWith attr src alt (B.str alt) try (do -- image link url <- linkUrl False return (B.link url "" img)) <|> return img escapedInline :: PandocMonad m => TextileParser m Inlines escapedInline = escapedEqs <|> escapedTag escapedEqs :: PandocMonad m => TextileParser m Inlines escapedEqs = B.str . T.pack <$> try (string "==" *> manyTill anyChar' (try $ string "==")) -- | literal text escaped btw tags escapedTag :: PandocMonad m => TextileParser m Inlines escapedTag = B.str . T.pack <$> try (string "" *> manyTill anyChar' (try $ string "")) -- | Any special symbol defined in wordBoundaries symbol :: PandocMonad m => TextileParser m Inlines symbol = do c <- notFollowedBy newline *> notFollowedBy rawHtmlBlock *> oneOf wordBoundaries updateLastStrPos pure $ B.str . T.singleton $ c -- | Inline code code :: PandocMonad m => TextileParser m Inlines code = code1 <|> code2 -- any character except a newline before a blank line anyChar' :: PandocMonad m => TextileParser m Char anyChar' = satisfy (/='\n') <|> try (char '\n' <* notFollowedBy blankline) code1 :: PandocMonad m => TextileParser m Inlines code1 = B.code . T.pack <$> surrounded (char '@') anyChar' code2 :: PandocMonad m => TextileParser m Inlines code2 = do htmlTag (tagOpen (=="tt") null) B.code . T.pack <$> manyTill anyChar' (try $ htmlTag $ tagClose (=="tt")) orderedListStartAttr :: PandocMonad m => TextileParser m Int orderedListStartAttr = do digits <- many digit case readMaybe digits :: Maybe Int of Nothing -> return 1 Just n -> return n -- | Html / CSS attributes attributes :: PandocMonad m => TextileParser m Attr attributes = L.foldl' (flip ($)) ("",[],[]) <$> try (do special <- option id specialAttribute attrs <- many attribute return (special : attrs)) specialAttribute :: PandocMonad m => TextileParser m (Attr -> Attr) specialAttribute = do alignStr <- ("center" <$ char '=') <|> ("justify" <$ try (string "<>")) <|> ("right" <$ char '>') <|> ("left" <$ char '<') notFollowedBy spaceChar return $ addStyle $ T.pack $ "text-align:" ++ alignStr attribute :: PandocMonad m => TextileParser m (Attr -> Attr) attribute = try $ (classIdAttr <|> styleAttr <|> langAttr) <* notFollowedBy spaceChar classIdAttr :: PandocMonad m => TextileParser m (Attr -> Attr) classIdAttr = try $ do -- (class class #id) char '(' ws <- T.words `fmap` T.pack <$> manyTill anyChar' (char ')') case reverse ws of [] -> return $ \(_,_,keyvals) -> ("",[],keyvals) ((T.uncons -> Just ('#', ident')):classes') -> return $ \(_,_,keyvals) -> (ident',classes',keyvals) classes' -> return $ \(_,_,keyvals) -> ("",classes',keyvals) styleAttr :: PandocMonad m => TextileParser m (Attr -> Attr) styleAttr = do style <- try $ enclosed (char '{') (char '}') anyChar' return $ addStyle $ T.pack style addStyle :: Text -> Attr -> Attr addStyle style (id',classes,keyvals) = (id',classes,keyvals') where keyvals' = ("style", style') : [(k,v) | (k,v) <- keyvals, k /= "style"] style' = style <> ";" <> T.concat [v | ("style",v) <- keyvals] langAttr :: PandocMonad m => TextileParser m (Attr -> Attr) langAttr = do lang <- try $ enclosed (char '[') (char ']') alphaNum return $ \(id',classes,keyvals) -> (id',classes,("lang",T.pack lang):keyvals) -- | Parses material surrounded by a parser. surrounded :: (PandocMonad m, Show t) => ParsecT Sources st m t -- ^ surrounding parser -> ParsecT Sources st m a -- ^ content parser (to be used repeatedly) -> ParsecT Sources st m [a] surrounded border = enclosed (border *> notFollowedBy (oneOf " \t\n\r")) (try border) simpleInline :: PandocMonad m => TextileParser m t -- ^ surrounding parser -> (Inlines -> Inlines) -- ^ Inline constructor -> TextileParser m Inlines -- ^ content parser (to be used repeatedly) simpleInline border construct = try $ do notAfterString >>= guard border *> notFollowedBy (oneOf " \t\n\r") attr <- attributes body <- trimInlines . mconcat <$> withQuoteContext InSingleQuote (manyTill (((B.space <>) <$> try (whitespace *> notFollowedBy newline >> inline)) <|> try (notFollowedBy newline >> inline)) (try border <* notFollowedBy alphaNum)) return $ construct $ if attr == nullAttr then body else B.spanWith attr body groupedInlineMarkup :: PandocMonad m => TextileParser m Inlines groupedInlineMarkup = try $ do char '[' sp1 <- option mempty $ B.space <$ whitespace result <- withQuoteContext InSingleQuote inlineMarkup sp2 <- option mempty $ B.space <$ whitespace char ']' return $ sp1 <> result <> sp2 eof' :: Monad m => ParsecT Sources s m Char eof' = '\n' <$ eof ================================================ FILE: src/Text/Pandoc/Readers/TikiWiki.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.TikiWiki Copyright : Copyright (C) 2017 Robin Lee Powell License : GNU GPL, version 2 or above Maintainer : Robin Lee Powell Stability : alpha Portability : portable Conversion of TikiWiki text to 'Pandoc' document. -} module Text.Pandoc.Readers.TikiWiki ( readTikiWiki ) where import Control.Monad import Control.Monad.Except (throwError) import qualified Data.Foldable as F import Data.List (dropWhileEnd) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad (..), getVerbosity) import Text.Pandoc.Definition import Text.Pandoc.Logging (Verbosity (..)) import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed) import Text.Pandoc.Shared (safeRead) import Text.Pandoc.XML (fromEntities) import Text.Printf (printf) -- | Read TikiWiki from an input string and return a Pandoc document. readTikiWiki :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readTikiWiki opts s = do let sources = ensureFinalNewlines 2 (toSources s) res <- readWithM parseTikiWiki def{ stateOptions = opts } sources case res of Left e -> throwError e Right d -> return d type TikiWikiParser = ParsecT Sources ParserState -- -- utility functions -- tryMsg :: Text -> TikiWikiParser m a -> TikiWikiParser m a tryMsg msg p = try p T.unpack msg skip :: TikiWikiParser m a -> TikiWikiParser m () skip parser = Control.Monad.void parser -- -- main parser -- parseTikiWiki :: PandocMonad m => TikiWikiParser m Pandoc parseTikiWiki = do bs <- mconcat <$> many block spaces eof return $ B.doc bs block :: PandocMonad m => TikiWikiParser m B.Blocks block = do verbosity <- getVerbosity pos <- getPosition res <- mempty <$ skipMany1 blankline <|> blockElements <|> para skipMany blankline when (verbosity >= INFO) $ trace (T.pack $ printf "line %d: %s" (sourceLine pos) (take 60 $ show $ B.toList res)) return res blockElements :: PandocMonad m => TikiWikiParser m B.Blocks blockElements = choice [ table , hr , header , mixedList , definitionList , codeMacro ] -- top -- ---- -- bottom -- -- ---- -- hr :: PandocMonad m => TikiWikiParser m B.Blocks hr = try $ do string "----" many (char '-') newline return B.horizontalRule -- ! header -- -- !! header level two -- -- !!! header level 3 -- header :: PandocMonad m => TikiWikiParser m B.Blocks header = tryMsg "header" $ do level <- fmap length (many1 (char '!')) guard $ level <= 6 skipSpaces content <- B.trimInlines . mconcat <$> manyTill inline newline attr <- registerHeader nullAttr content return $ B.headerWith attr level content tableRow :: PandocMonad m => TikiWikiParser m [B.Blocks] tableRow = try $ do -- row <- sepBy1 (many1Till inline $ oneOf "\n|") (try $ string "|" <* notFollowedBy (oneOf "|\n")) -- return $ map (B.plain . mconcat) row row <- sepBy1 (many1 (noneOf "\n|") >>= parseColumn . T.pack) (try $ string "|" <* notFollowedBy (oneOf "|\n")) return $ map B.plain row where parseColumn x = do parsed <- parseFromString (many1 inline) x return $ mconcat parsed -- Tables: -- -- ||foo|| -- -- ||row1-column1|row1-column2||row2-column1|row2-column2|| -- -- ||row1-column1|row1-column2 -- row2-column1|row2-column2|| -- -- ||row1-column1|row1-column2 -- row2-column1|row2-column2||row3-column1|row3-column2|| -- -- || Orange | Apple | more -- Bread | Pie | more -- Butter | Ice cream | and more || -- table :: PandocMonad m => TikiWikiParser m B.Blocks table = try $ do string "||" rows <- sepBy1 tableRow (try $ string "\n" <|> (string "||" <* notFollowedBy (string "\n"))) string "||" newline -- return $ B.simpleTable (headers rows) $ trace ("rows: " ++ (show rows)) rows return $ B.simpleTable (headers rows) rows where -- The headers are as many empty strings as the number of columns -- in the first row headers [] = [] headers (r:_) = replicate (length r) ((B.plain . B.str) "") para :: PandocMonad m => TikiWikiParser m B.Blocks para = fmap (result . mconcat) ( many1Till inline endOfParaElement) where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof endOfPara = try $ blankline >> skipMany1 blankline newBlockElement = try $ blankline >> skip blockElements result content = if F.all (==Space) content then mempty else B.para $ B.trimInlines content -- ;item 1: definition 1 -- ;item 2: definition 2-1 -- + definition 2-2 -- ;item ''3'': definition ''3'' -- definitionList :: PandocMonad m => TikiWikiParser m B.Blocks definitionList = tryMsg "definitionList" $ do elements <-many1 parseDefinitionListItem return $ B.definitionList elements where parseDefinitionListItem :: PandocMonad m => TikiWikiParser m (B.Inlines, [B.Blocks]) parseDefinitionListItem = do skipSpaces >> char ';' <* skipSpaces term <- many1Till inline $ char ':' <* skipSpaces line <- listItemLine 1 return (mconcat term, [B.plain line]) data ListType = None | Numbered | Bullet deriving (Ord, Eq, Show) data ListNesting = LN { lntype :: ListType, lnnest :: Int } deriving (Ord, Eq, Show) -- The first argument is a stack (most recent == head) of our list -- nesting status; the list type and the nesting level; if we're in -- a number list in a bullet list it'd be -- [LN Numbered 2, LN Bullet 1] -- -- Mixed list example: -- -- # one -- # two -- ** two point one -- ** two point two -- # three -- # four -- mixedList :: PandocMonad m => TikiWikiParser m B.Blocks mixedList = try $ do items <- try $ many1 listItem return $ mconcat $ fixListNesting $ spanFoldUpList (LN None 0) items -- See the "Handling Lists" section of DESIGN-CODE for why this -- function exists. It's to post-process the lists and do some -- mappends. -- -- We need to walk the tree two items at a time, so we can see what -- we're going to join *to* before we get there. -- -- Because of that, it seemed easier to do it by hand than to try to -- figre out a fold or something. fixListNesting :: [B.Blocks] -> [B.Blocks] fixListNesting [] = [] fixListNesting [first] = [recurseOnList first] -- fixListNesting nestall | trace ("\n\nfixListNesting: " ++ (show nestall)) False = undefined -- fixListNesting nestall@(first:second:rest) = fixListNesting (first:second:rest) = case B.toList second of (BulletList _ : _) -> fixListNesting $ mappend (recurseOnList first) (recurseOnList second) : rest (OrderedList _ _ : _) -> fixListNesting $ mappend (recurseOnList first) (recurseOnList second) : rest _ -> recurseOnList first : fixListNesting (second:rest) -- This function walks the Block structure for fixListNesting, -- because it's a bit complicated, what with converting to and from -- lists and so on. recurseOnList :: B.Blocks -> B.Blocks -- recurseOnList item | trace ("rOL: " ++ (show $ length $ B.toList item) ++ ", " ++ (show $ B.toList item)) False = undefined recurseOnList items | length (B.toList items) == 1 = case B.toList items of (BulletList listItems : _) -> B.bulletList $ fixListNesting $ map B.fromList listItems (OrderedList _ listItems : _) -> B.orderedList $ fixListNesting $ map B.fromList listItems _ -> items -- The otherwise works because we constructed the blocks, and we -- know for a fact that no mappends have been run on them; each -- Blocks consists of exactly one Block. -- -- Anything that's not like that has already been processed by -- fixListNesting; don't bother to process it again. | otherwise = items -- Turn the list if list items into a tree by breaking off the first -- item, splitting the remainder of the list into items that are in -- the tree of the first item and those that aren't, wrapping the -- tree of the first item in its list time, and recursing on both -- sections. spanFoldUpList :: ListNesting -> [(ListNesting, B.Blocks)] -> [B.Blocks] spanFoldUpList _ [] = [] spanFoldUpList ln [first] = listWrap ln (fst first) [snd first] spanFoldUpList ln (first:rest) = let (span1, span2) = span (splitListNesting (fst first)) rest newTree1 = listWrap ln (fst first) $ snd first : spanFoldUpList (fst first) span1 newTree2 = spanFoldUpList ln span2 in newTree1 ++ newTree2 -- Decide if the second item should be in the tree of the first -- item, which is true if the second item is at a deeper nesting -- level and of the same type. splitListNesting :: ListNesting -> (ListNesting, B.Blocks) -> Bool splitListNesting ln1 (ln2, _) | lnnest ln1 < lnnest ln2 = True | ln1 == ln2 = True | otherwise = False -- If we've moved to a deeper nesting level, wrap the new level in -- the appropriate type of list. listWrap :: ListNesting -> ListNesting -> [B.Blocks] -> [B.Blocks] listWrap upperLN curLN retTree = if upperLN == curLN then retTree else case lntype curLN of None -> [] Bullet -> [B.bulletList retTree] Numbered -> [B.orderedList retTree] listItem :: PandocMonad m => TikiWikiParser m (ListNesting, B.Blocks) listItem = choice [ bulletItem , numberedItem ] -- * Start each line -- * with an asterisk (*). -- ** More asterisks gives deeper -- *** and deeper levels. -- bulletItem :: PandocMonad m => TikiWikiParser m (ListNesting, B.Blocks) bulletItem = try $ do prefix <- many1 $ char '*' many $ char ' ' content <- listItemLine (length prefix) return (LN Bullet (length prefix), B.plain content) -- # Start each line -- # with a number (1.). -- ## More number signs gives deeper -- ### and deeper -- numberedItem :: PandocMonad m => TikiWikiParser m (ListNesting, B.Blocks) numberedItem = try $ do prefix <- many1 $ char '#' many $ char ' ' content <- listItemLine (length prefix) return (LN Numbered (length prefix), B.plain content) listItemLine :: PandocMonad m => Int -> TikiWikiParser m B.Inlines listItemLine nest = lineContent >>= parseContent where lineContent = do content <- anyLine continuation <- optionMaybe listContinuation return $ filterSpaces content <> "\n" <> Data.Maybe.fromMaybe "" continuation filterSpaces = T.dropWhileEnd (== ' ') listContinuation = string (replicate nest '+') >> lineContent parseContent x = do parsed <- parseFromString (many1 inline) x return $ mconcat $ dropWhileEnd (== B.space) parsed -- Turn the CODE macro attributes into Pandoc code block attributes. mungeAttrs :: [(Text, Text)] -> (Text, [Text], [(Text, Text)]) mungeAttrs rawAttrs = ("", classes, rawAttrs) where -- "colors" is TikiWiki CODE macro for "name of language to do -- highlighting for"; turn the value into a class color = fromMaybe "" $ lookup "colors" rawAttrs -- ln = 1 means line numbering. It's also the default. So we -- emit numberLines as a class unless ln = 0 lnRaw = fromMaybe "1" $ lookup "ln" rawAttrs ln = if lnRaw == "0" then "" else "numberLines" classes = filter (/= "") [color, ln] codeMacro :: PandocMonad m => TikiWikiParser m B.Blocks codeMacro = try $ do string "{CODE(" rawAttrs <- macroAttrs string ")}" body <- T.pack <$> manyTill anyChar (try (string "{CODE}")) newline if not (null rawAttrs) then return $ B.codeBlockWith (mungeAttrs rawAttrs) body else return $ B.codeBlock body -- -- inline parsers -- inline :: PandocMonad m => TikiWikiParser m B.Inlines inline = choice [ whitespace , noparse , strong , emph , nbsp , image , htmlComment , strikeout , code , wikiLink , notExternalLink , externalLink , superTag , superMacro , subTag , subMacro , escapedChar , colored , centered , underlined , boxed , breakChars , str , symbol ] "inline" whitespace :: PandocMonad m => TikiWikiParser m B.Inlines whitespace = lb <|> regsp where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space -- UNSUPPORTED, as there doesn't seem to be any facility in calibre -- for this nbsp :: PandocMonad m => TikiWikiParser m B.Inlines nbsp = try $ do string "~hs~" return $ B.str " NOT SUPPORTED BEGIN: ~hs~ (non-breaking space) :END " -- UNSUPPORTED, as the desired behaviour (that the data be -- *retained* and stored as a comment) doesn't exist in calibre, and -- silently throwing data out seemed bad. htmlComment :: PandocMonad m => TikiWikiParser m B.Inlines htmlComment = try $ do string "~hc~" inner <- fmap T.pack $ many1 $ noneOf "~" string "~/hc~" return $ B.str $ " NOT SUPPORTED: ~hc~ (html comment opener) BEGIN: " <> inner <> " ~/hc~ :END " linebreak :: PandocMonad m => TikiWikiParser m B.Inlines linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) where lastNewline = eof >> return mempty innerNewline = return B.space between :: (Monoid c, PandocMonad m, Show b) => TikiWikiParser m a -> TikiWikiParser m b -> (TikiWikiParser m b -> TikiWikiParser m c) -> TikiWikiParser m c between start end p = mconcat <$> try (start >> notFollowedBy whitespace >> many1Till (p end) end) enclosed :: (Monoid b, PandocMonad m, Show a) => TikiWikiParser m a -> (TikiWikiParser m a -> TikiWikiParser m b) -> TikiWikiParser m b enclosed sep p = between sep (try $ sep <* endMarker) p where endMarker = lookAhead $ skip endSpace <|> skip (oneOf ".,!?:)|'_") <|> eof endSpace = (spaceChar <|> newline) >> return B.space nestedInlines :: (Show a, PandocMonad m) => TikiWikiParser m a -> TikiWikiParser m B.Inlines nestedInlines end = innerSpace <|> nestedInline where innerSpace = try $ whitespace <* notFollowedBy end nestedInline = notFollowedBy whitespace >> inline -- {img attId="39" imalign="right" link="http://info.tikiwiki.org" alt="Panama Hat"} -- -- {img attId="37", thumb="mouseover", styleimage="border", desc="150"} -- -- {img src="img/wiki_up/393px-Pears.jpg" thumb="y" imalign="center" stylebox="border" button="y" desc="Pretty pears" max="200" rel="box"} -- image :: PandocMonad m => TikiWikiParser m B.Inlines image = try $ do string "{img " rawAttrs <- sepEndBy1 imageAttr spaces string "}" let src = fromMaybe "" $ lookup "src" rawAttrs let title = fromMaybe src $ lookup "desc" rawAttrs let alt = fromMaybe title $ lookup "alt" rawAttrs let classes = map fst $ filter (\(_,b) -> b == "" || b == "y") rawAttrs if not (T.null src) then return $ B.imageWith ("", classes, rawAttrs) src title (B.str alt) else return $ B.str $ " NOT SUPPORTED: image without src attribute BEGIN: {img " <> printAttrs rawAttrs <> "} :END " where printAttrs attrs = T.unwords $ map (\(a, b) -> a <> "=\"" <> b <> "\"") attrs imageAttr :: PandocMonad m => TikiWikiParser m (Text, Text) imageAttr = try $ do key <- many1 (noneOf "=} \t\n") char '=' optional $ char '"' value <- many1 (noneOf "}\"\n") optional $ char '"' optional $ char ',' return (T.pack key, T.pack value) -- __strong__ strong :: PandocMonad m => TikiWikiParser m B.Inlines strong = try $ fmap B.strong (enclosed (string "__") nestedInlines) -- ''emph'' emph :: PandocMonad m => TikiWikiParser m B.Inlines emph = try $ fmap B.emph (enclosed (string "''") nestedInlines) -- ~246~ escapedChar :: PandocMonad m => TikiWikiParser m B.Inlines escapedChar = try $ do string "~" mNumber <- safeRead . T.pack <$> many1 digit string "~" return $ B.str $ case mNumber of Just number -> T.singleton $ toEnum (number :: Int) Nothing -> "" -- UNSUPPORTED, as there doesn't seem to be any facility in calibre -- for this centered :: PandocMonad m => TikiWikiParser m B.Inlines centered = try $ do string "::" inner <- fmap T.pack $ many1 $ noneOf ":\n" string "::" return $ B.str $ " NOT SUPPORTED: :: (centered) BEGIN: ::" <> inner <> ":: :END " -- UNSUPPORTED, as there doesn't seem to be any facility in calibre -- for this colored :: PandocMonad m => TikiWikiParser m B.Inlines colored = try $ do string "~~" inner <- fmap T.pack $ many1 $ noneOf "~\n" string "~~" return $ B.str $ " NOT SUPPORTED: ~~ (colored) BEGIN: ~~" <> inner <> "~~ :END " -- ===underlined=== underlined :: PandocMonad m => TikiWikiParser m B.Inlines underlined = try $ B.underline <$> enclosed (string "===") nestedInlines -- UNSUPPORTED, as there doesn't seem to be any facility in calibre -- for this boxed :: PandocMonad m => TikiWikiParser m B.Inlines boxed = try $ do string "^" inner <- fmap T.pack $ many1 $ noneOf "^\n" string "^" return $ B.str $ " NOT SUPPORTED: ^ (boxed) BEGIN: ^" <> inner <> "^ :END " -- --text-- strikeout :: PandocMonad m => TikiWikiParser m B.Inlines strikeout = try $ fmap B.strikeout (enclosed (string "--") nestedInlines) nestedString :: (Show a, PandocMonad m) => TikiWikiParser m a -> TikiWikiParser m Text nestedString end = innerSpace <|> countChar 1 nonspaceChar where innerSpace = try $ T.pack <$> many1 spaceChar <* notFollowedBy end breakChars :: PandocMonad m => TikiWikiParser m B.Inlines breakChars = try $ string "%%%" >> return B.linebreak -- superscript: foo{TAG(tag=>sup)}super{TAG}foo / bar{SUP()}super2{SUP}bar superTag :: PandocMonad m => TikiWikiParser m B.Inlines superTag = try $ fmap (B.superscript . B.text . fromEntities) ( between (string "{TAG(tag=>sup)}") (string "{TAG}") nestedString) superMacro :: PandocMonad m => TikiWikiParser m B.Inlines superMacro = try $ do string "{SUP(" manyTill anyChar (string ")}") body <- manyTill anyChar (string "{SUP}") return $ B.superscript $ B.text $ T.pack body -- subscript: baz{TAG(tag=>sub)}sub{TAG}qux / qux{SUB()}sub2{SUB}qux subTag :: PandocMonad m => TikiWikiParser m B.Inlines subTag = try $ fmap (B.subscript . B.text . fromEntities) ( between (string "{TAG(tag=>sub)}") (string "{TAG}") nestedString) subMacro :: PandocMonad m => TikiWikiParser m B.Inlines subMacro = try $ do string "{SUB(" manyTill anyChar (string ")}") body <- manyTill anyChar (string "{SUB}") return $ B.subscript $ B.text $ T.pack body -- -+text+- code :: PandocMonad m => TikiWikiParser m B.Inlines code = try $ fmap (B.code . fromEntities) ( between (string "-+") (string "+-") nestedString) macroAttr :: PandocMonad m => TikiWikiParser m (Text, Text) macroAttr = try $ do key <- many1 (noneOf "=)") char '=' optional $ char '"' value <- many1 (noneOf " )\"") optional $ char '"' return (T.pack key, T.pack value) macroAttrs :: PandocMonad m => TikiWikiParser m [(Text, Text)] macroAttrs = try $ sepEndBy macroAttr spaces -- ~np~ __not bold__ ~/np~ noparse :: PandocMonad m => TikiWikiParser m B.Inlines noparse = try $ do string "~np~" body <- manyTill anyChar (string "~/np~") return $ B.str $ T.pack body str :: PandocMonad m => TikiWikiParser m B.Inlines str = fmap B.str (T.pack <$> many1 alphaNum <|> characterReference) symbol :: PandocMonad m => TikiWikiParser m B.Inlines symbol = fmap B.str (countChar 1 nonspaceChar) -- [[not a link] notExternalLink :: PandocMonad m => TikiWikiParser m B.Inlines notExternalLink = try $ do start <- string "[[" body <- many (noneOf "\n[]") end <- string "]" return $ B.text $ T.pack $ start ++ body ++ end -- [http://www.somesite.org url|Some Site title] -- ((internal link)) -- -- The ((...)) wiki links and [...] external links are handled -- exactly the same; this abstracts that out makeLink :: PandocMonad m => Text -> Text -> Text -> TikiWikiParser m B.Inlines makeLink start middle end = try $ do st <- getState guard $ stateAllowLinks st setState $ st{ stateAllowLinks = False } (url, title, anchor) <- wikiLinkText start middle end parsedTitle <- parseFromString (many1 inline) title setState $ st{ stateAllowLinks = True } return $ B.link (url <> anchor) "" $ mconcat parsedTitle wikiLinkText :: PandocMonad m => Text -> Text -> Text -> TikiWikiParser m (Text, Text, Text) wikiLinkText start middle end = do string (T.unpack start) url <- T.pack <$> many1 (noneOf $ T.unpack middle ++ "\n") seg1 <- option url linkContent seg2 <- option "" linkContent string (T.unpack end) if seg2 /= "" then return (url, seg2, seg1) else return (url, seg1, "") where linkContent = do char '|' T.pack <$> many (noneOf $ T.unpack middle) externalLink :: PandocMonad m => TikiWikiParser m B.Inlines externalLink = makeLink "[" "]|" "]" -- NB: this wiki linking is unlikely to work for anyone besides me -- (rlpowell); it happens to work for me because my Hakyll code has -- post-processing that treats pandoc .md titles as valid link -- targets, so something like -- [see also this other post](My Other Page) is perfectly valid. wikiLink :: PandocMonad m => TikiWikiParser m B.Inlines wikiLink = makeLink "((" ")|" "))" ================================================ FILE: src/Text/Pandoc/Readers/Txt2Tags.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE LambdaCase #-} {- | Module : Text.Pandoc.Readers.Txt2Tags Copyright : Copyright (C) 2014 Matthew Pickering License : GNU GPL, version 2 or above Maintainer : Matthew Pickering Conversion of txt2tags formatted plain text to 'Pandoc' document. -} module Text.Pandoc.Readers.Txt2Tags ( readTxt2Tags , getT2TMeta , T2TMeta (..) ) where import Control.Monad (guard, void, when) import Control.Monad.Except (catchError, throwError) import Control.Monad.Reader (Reader, asks, runReader) import Data.Default import Data.List (intercalate, transpose) import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import Data.Time.Format (formatTime) import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.Class.PandocMonad as P import Data.Time (defaultTimeLocale) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (space, spaces, uri) import Text.Pandoc.Shared (compactify, compactifyDL) import Text.Pandoc.URI (escapeURI) type T2T = ParsecT Sources ParserState (Reader T2TMeta) -- | An object for the T2T macros meta information -- the contents of each field is simply substituted verbatim into the file data T2TMeta = T2TMeta { date :: Text -- ^ Current date , mtime :: Text -- ^ Last modification time of infile , infile :: FilePath -- ^ Input file , outfile :: FilePath -- ^ Output file } deriving Show instance Default T2TMeta where def = T2TMeta "" "" "" "" -- | Get the meta information required by Txt2Tags macros getT2TMeta :: PandocMonad m => m T2TMeta getT2TMeta = do inps <- P.getInputFiles outp <- fromMaybe "" <$> P.getOutputFile curDate <- formatTime defaultTimeLocale "%F" <$> P.getZonedTime curMtime <- catchError (mapM P.getModificationTime inps >>= (\case Nothing -> formatTime defaultTimeLocale "%T" <$> P.getZonedTime Just ts -> return $ formatTime defaultTimeLocale "%T" $ maximum ts) . nonEmpty) (const (return "")) return $ T2TMeta (T.pack curDate) (T.pack curMtime) (intercalate ", " inps) outp -- | Read Txt2Tags from an input string returning a Pandoc document readTxt2Tags :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readTxt2Tags opts s = do let sources = ensureFinalNewlines 2 (toSources s) meta <- getT2TMeta let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) sources case parsed of Right result -> return result Left e -> throwError e -- | Read Txt2Tags (ignoring all macros) from an input string returning -- a Pandoc document -- readTxt2TagsNoMacros :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -- readTxt2TagsNoMacros = readTxt2Tags parseT2T :: T2T Pandoc parseT2T = do -- Parse header if standalone flag is set standalone <- getOption readerStandalone when standalone parseHeader body <- mconcat <$> manyTill block eof meta' <- stateMeta <$> getState return $ Pandoc meta' (B.toList body) parseHeader :: T2T () parseHeader = do () <$ try blankline <|> header meta <- stateMeta <$> getState optional blanklines config <- manyTill setting (notFollowedBy setting) -- TODO: Handle settings better let settings = foldr (\(k,v) -> B.setMeta k (MetaString v)) meta config updateState (\s -> s {stateMeta = settings}) <* optional blanklines header :: T2T () header = titleline >> authorline >> dateline headerline :: B.ToMetaValue a => Text -> T2T a -> T2T () headerline field p = (() <$ try blankline) <|> (p >>= updateState . B.setMeta field) titleline :: T2T () titleline = headerline "title" (trimInlines . mconcat <$> manyTill inline newline) authorline :: T2T () authorline = headerline "author" (sepBy author (char ';') <* newline) where author = trimInlines . mconcat <$> many (notFollowedBy (char ';' <|> newline) >> inline) dateline :: T2T () dateline = headerline "date" (trimInlines . mconcat <$> manyTill inline newline) type Keyword = Text type Value = Text setting :: T2T (Keyword, Value) setting = do string "%!" keyword <- ignoreSpacesCap (many1Char alphaNum) char ':' value <- ignoreSpacesCap (manyTillChar anyChar newline) return (keyword, value) -- Blocks parseBlocks :: T2T Blocks parseBlocks = mconcat <$> manyTill block eof block :: T2T Blocks block = choice [ mempty <$ blanklines , quote , hrule -- hrule must go above title , title , commentBlock , verbatim , rawBlock , taggedBlock , list , table , para ] title :: T2T Blocks title = try $ balancedTitle '+' <|> balancedTitle '=' balancedTitle :: Char -> T2T Blocks balancedTitle c = try $ do spaces level <- length <$> many1 (char c) guard (level <= 5) -- Max header level 5 heading <- manyTillChar (noneOf "\n\r") (count level (char c)) label <- optionMaybe (enclosed (char '[') (char ']') (alphaNum <|> oneOf "_-")) many spaceChar *> newline let attr = maybe nullAttr (\x -> (T.pack x, [], [])) label return $ B.headerWith attr level (trimInlines $ B.text heading) para :: T2T Blocks para = try $ do ils <- parseInlines nl <- option False (True <$ newline) option (B.plain ils) (guard nl >> notFollowedBy listStart >> return (B.para ils)) where listStart = try bulletListStart <|> orderedListStart commentBlock :: T2T Blocks commentBlock = try (blockMarkupArea anyLine (const mempty) "%%%") <|> comment -- Separator and Strong line treated the same hrule :: T2T Blocks hrule = try $ do spaces line <- many1 (oneOf "=-_") guard (length line >= 20) B.horizontalRule <$ blankline quote :: T2T Blocks quote = try $ do lookAhead tab rawQuote <- many1 (tab *> optional spaces *> anyLine) contents <- parseFromString' parseBlocks (T.intercalate "\n" rawQuote <> "\n\n") return $ B.blockQuote contents commentLine :: T2T Inlines commentLine = comment -- List Parsing code from Org Reader list :: T2T Blocks list = choice [bulletList, orderedList, definitionList] bulletList :: T2T Blocks bulletList = B.bulletList . compactify <$> many1 (listItem bulletListStart parseBlocks) orderedList :: T2T Blocks orderedList = B.orderedList . compactify <$> many1 (listItem orderedListStart parseBlocks) definitionList :: T2T Blocks definitionList = try $ B.definitionList . compactifyDL <$> many1 (listItem definitionListStart definitionListEnd) definitionListEnd :: T2T (Inlines, [Blocks]) definitionListEnd = (,) <$> (mconcat <$> manyTill inline newline) <*> ((:[]) <$> parseBlocks) genericListStart :: T2T Char -> T2T Int genericListStart listMarker = try $ (2+) <$> (length <$> many spaceChar <* listMarker <* space <* notFollowedBy space) -- parses bullet list \start and returns its length (excl. following whitespace) bulletListStart :: T2T Int bulletListStart = genericListStart (char '-') orderedListStart :: T2T Int orderedListStart = genericListStart (char '+' ) definitionListStart :: T2T Int definitionListStart = genericListStart (char ':') -- parse raw text for one list item, excluding start marker and continuations listItem :: T2T Int -> T2T a -> T2T a listItem start end = try $ do markerLength <- try start firstLine <- anyLineNewline blank <- option "" ("\n" <$ blankline) rest <- T.concat <$> many (listContinuation markerLength) parseFromString' end $ firstLine <> blank <> rest -- continuation of a list item - indented and separated by blankline or endline. -- Note: nested lists are parsed as continuations. listContinuation :: Int -> T2T Text listContinuation markerLength = try $ notFollowedBy' (blankline >> blankline) *> (mappend <$> (T.concat <$> many1 listLine) <*> manyChar blankline) where listLine = try $ indentWith markerLength *> anyLineNewline -- Table table :: T2T Blocks table = try $ do tableHeader <- fmap snd <$> option mempty (try headerRow) rows <- many1 (many commentLine *> tableRow) let columns = transpose rows let ncolumns = length columns let aligns = map (fromMaybe AlignDefault . foldr findAlign Nothing) columns let rows' = map (map snd) rows let size = maybe 0 maximum $ nonEmpty $ map length rows' let rowsPadded = map (pad size) rows' let headerPadded = if null tableHeader then mempty else pad size tableHeader let toRow = Row nullAttr . map B.simpleCell toHeaderRow l = [toRow l | not (null l)] return $ B.table B.emptyCaption (zip aligns (replicate ncolumns ColWidthDefault)) (TableHead nullAttr $ toHeaderRow headerPadded) [TableBody nullAttr 0 [] $ map toRow rowsPadded] (TableFoot nullAttr []) pad :: (Monoid a) => Int -> [a] -> [a] pad n xs = xs ++ replicate (n - length xs) mempty findAlign :: (Alignment, a) -> Maybe Alignment -> Maybe Alignment findAlign (x,_) (Just y) | x == y = Just x | otherwise = Just AlignDefault findAlign (x,_) Nothing = Just x headerRow :: T2T [(Alignment, Blocks)] headerRow = genericRow (string "||") tableRow :: T2T [(Alignment, Blocks)] tableRow = genericRow (char '|') genericRow :: T2T a -> T2T [(Alignment, Blocks)] genericRow start = try $ do spaces *> start manyTill tableCell newline "genericRow" tableCell :: T2T (Alignment, Blocks) tableCell = try $ do leftSpaces <- length <$> lookAhead (many1 space) -- Case of empty cell means we must lookAhead content <- manyTill inline (try $ lookAhead cellEnd) rightSpaces <- length <$> many space let align = case compare leftSpaces rightSpaces of LT -> AlignLeft EQ -> AlignCenter GT -> AlignRight endOfCell return (align, B.plain (B.trimInlines $ mconcat content)) where cellEnd = void newline <|> (many1 space *> endOfCell) endOfCell :: T2T () endOfCell = try (skipMany1 $ char '|') <|> ( () <$ lookAhead newline) -- Raw area verbatim :: T2T Blocks verbatim = genericBlock anyLineNewline B.codeBlock "```" rawBlock :: T2T Blocks rawBlock = genericBlock anyLineNewline (B.para . B.str) "\"\"\"" taggedBlock :: T2T Blocks taggedBlock = do target <- getTarget genericBlock anyLineNewline (B.rawBlock target) "'''" -- Generic genericBlock :: Monoid a => T2T a -> (a -> Blocks) -> Text -> T2T Blocks genericBlock p f s = blockMarkupArea p f s <|> blockMarkupLine p f s blockMarkupArea :: Monoid a => T2T a -> (a -> Blocks) -> Text -> T2T Blocks blockMarkupArea p f s = try (do textStr s *> blankline f . mconcat <$> manyTill p (eof <|> void (textStr s *> blankline))) blockMarkupLine :: T2T a -> (a -> Blocks) -> Text -> T2T Blocks blockMarkupLine p f s = try (f <$> (textStr s *> space *> p)) -- Can be in either block or inline position comment :: Monoid a => T2T a comment = try $ do atStart notFollowedBy macro mempty <$ (char '%' *> anyLine) -- Inline parseInlines :: T2T Inlines parseInlines = trimInlines . mconcat <$> many1 inline inline :: T2T Inlines inline = choice [ endline , macro , commentLine , whitespace , url , link , image , bold , underline , code , raw , tagged , strike , italic , code , str , symbol ] bold :: T2T Inlines bold = inlineMarkup inline B.strong '*' B.str underline :: T2T Inlines underline = inlineMarkup inline B.underline '_' B.str strike :: T2T Inlines strike = inlineMarkup inline B.strikeout '-' B.str italic :: T2T Inlines italic = inlineMarkup inline B.emph '/' B.str code :: T2T Inlines code = inlineMarkup (T.singleton <$> anyChar) B.code '`' id raw :: T2T Inlines raw = inlineMarkup (T.singleton <$> anyChar) B.text '"' id tagged :: T2T Inlines tagged = do target <- getTarget inlineMarkup (T.singleton <$> anyChar) (B.rawInline target) '\'' id -- Parsec for markup indicated by a double character. -- Inline markup is greedy and glued -- Greedy meaning ***a*** = Bold [Str "*a*"] -- Glued meaning that markup must be tight to content -- Markup can't pass newlines inlineMarkup :: Monoid a => T2T a -- Content parser -> (a -> Inlines) -- Constructor -> Char -- Fence -> (Text -> a) -- Special Case to handle ****** -> T2T Inlines inlineMarkup p f c special = try $ do start <- many1Char (char c) let l = T.length start guard (l >= 2) when (l == 2) (void $ notFollowedBy space) -- We must make sure that there is no space before the start of the -- closing tags body <- optionMaybe (try $ manyTillChar (noneOf "\n\r") (try $ lookAhead (noneOf " " >> string [c,c] ))) case body of Just middle -> do lastChar <- anyChar end <- many1Char (char c) let parser inp = parseFromString' (mconcat <$> many p) inp let start' = case T.drop 2 start of "" -> mempty xs -> special xs body' <- parser (middle <> T.singleton lastChar) let end' = case T.drop 2 end of "" -> mempty xs -> special xs return $ f (start' `mappend` body' `mappend` end') Nothing -> do -- Either bad or case such as ***** guard (l >= 5) let body' = T.replicate (l - 4) $ T.singleton c return $ f (special body') link :: T2T Inlines link = try imageLink <|> titleLink -- Link with title titleLink :: T2T Inlines titleLink = try $ do char '[' notFollowedBy space tokens <- sepBy1 (manyChar $ noneOf " ]") space guard (length tokens >= 2) char ']' let link' = last tokens guard $ not $ T.null link' let tit = T.unwords (init tokens) return $ B.link link' "" (B.text tit) -- Link with image imageLink :: T2T Inlines imageLink = try $ do char '[' body <- image many1 space l <- manyTillChar (noneOf "\n\r ") (char ']') return (B.link l "" body) macro :: T2T Inlines macro = try $ do name <- string "%%" *> oneOfStringsCI (map fst commands) optional (try $ enclosed (char '(') (char ')') anyChar) lookAhead (spaceChar <|> oneOf specialChars <|> newline) maybe (return mempty) (\f -> asks (B.str . f)) (lookup name commands) where commands = [ ("date", date), ("mtime", mtime) , ("infile", T.pack . infile), ("outfile", T.pack . outfile)] -- raw URLs in text are automatically linked url :: T2T Inlines url = try $ do (rawUrl, escapedUrl) <- try uri <|> emailAddress' return $ B.link rawUrl "" (B.str escapedUrl) emailAddress' :: T2T (Text, Text) emailAddress' = do (base, mailURI) <- emailAddress query <- option "" emailQuery return (base <> query, mailURI <> query) emailQuery :: T2T Text emailQuery = do char '?' parts <- kv `sepBy1` (char '&') return $ "?" <> T.intercalate "&" parts kv :: T2T Text kv = do k <- T.pack <$> many1 alphaNum char '=' let vchar = alphaNum <|> try (oneOf "%._/~:,=$@&+-;*" <* lookAhead alphaNum) v <- T.pack <$> many1 vchar return (k <> "=" <> v) uri :: T2T (Text, Text) uri = try $ do address <- t2tURI return (address, escapeURI address) -- The definition of a URI in the T2T source differs from the -- actual definition. This is a transcription of the definition in -- the source of v2.6 --isT2TURI :: String -> Bool --isT2TURI (parse t2tURI "" -> Right _) = True --isT2TURI _ = False t2tURI :: T2T Text t2tURI = do start <- try ((<>) <$> proto <*> urlLogin) <|> guess domain <- many1Char chars sep <- manyChar (char '/') form' <- option mempty (T.cons <$> char '?' <*> many1Char form) anchor' <- option mempty (T.cons <$> char '#' <*> manyChar anchor) return (start <> domain <> sep <> form' <> anchor') where protos = ["http", "https", "ftp", "telnet", "gopher", "wais"] proto = (<>) <$> oneOfStrings protos <*> textStr "://" guess = (<>) <$> (((<>) <$> stringAnyCase "www" <*> option mempty (T.singleton <$> oneOf "23")) <|> stringAnyCase "ftp") <*> (T.singleton <$> char '.') login = alphaNum <|> oneOf "_.-" pass = manyChar (noneOf " @") chars = alphaNum <|> oneOf "%._/~:,=$@&+-" anchor = alphaNum <|> oneOf "%._0" form = chars <|> oneOf ";*" urlLogin = option mempty $ try ((\x y z -> x <> y <> T.singleton z) <$> many1Char login <*> option mempty (T.cons <$> char ':' <*> pass) <*> char '@') image :: T2T Inlines image = try $ do -- List taken from txt2tags source let extensions = [".jpg", ".jpeg", ".gif", ".png", ".eps", ".bmp"] char '[' (path, ext) <- manyUntilChar (noneOf "\n\t\r ") (oneOfStrings extensions) char ']' return $ B.image (path <> ext) "" mempty -- Characters used in markup specialChars :: [Char] specialChars = "%*-_/|:+;" tab :: T2T Char tab = char '\t' space :: T2T Char space = char ' ' spaces :: T2T Text spaces = manyChar space endline :: T2T Inlines endline = try $ do newline notFollowedBy blankline notFollowedBy hrule notFollowedBy title notFollowedBy verbatim notFollowedBy rawBlock notFollowedBy taggedBlock notFollowedBy quote notFollowedBy list notFollowedBy table return B.softbreak str :: T2T Inlines str = try $ B.str <$> many1Char (noneOf $ specialChars ++ "\n\r ") whitespace :: T2T Inlines whitespace = try $ B.space <$ spaceChar symbol :: T2T Inlines symbol = B.str . T.singleton <$> oneOf specialChars -- Utility getTarget :: T2T Text getTarget = do mv <- lookupMeta "target" . stateMeta <$> getState return $ case mv of Just (MetaString target) -> target Just (MetaInlines [Str target]) -> target _ -> "html" atStart :: T2T () atStart = getPosition >>= guard . (== 1) . sourceColumn ignoreSpacesCap :: T2T Text -> T2T Text ignoreSpacesCap p = T.toLower <$> (spaces *> p <* spaces) ================================================ FILE: src/Text/Pandoc/Readers/Typst/Math.hs ================================================ {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Text.Pandoc.Readers.Typst.Math ( pMathMany ) where import Control.Monad (MonadPlus (mplus)) import Data.Char (isAlphaNum, isDigit) import Data.List (intercalate) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Sequence (Seq) import qualified Data.Sequence as Seq import Data.Text (Text) import qualified Data.Text as T import qualified Data.Vector as V import Text.Pandoc.Parsing ( many ) import Text.Pandoc.Class ( PandocMonad ) import Text.TeXMath.Types ( Alignment (..), Exp (..), FractionType (..), TeXSymbolType (..), TextType (..), ) import Text.TeXMath.Unicode.ToTeX (getSymbolType) import Text.Pandoc.Readers.Typst.Parsing ( P, pTok, ignored, pWithContents, getField, chunks ) import Typst.Types withGroup :: [Exp] -> Exp withGroup [x] = x withGroup xs = EGrouped xs data AttachmentStyle = Limits | LimitsDisplay | Scripts deriving (Eq, Show) getAttachmentStyle :: PandocMonad m => M.Map Identifier Val -> P m (Maybe AttachmentStyle) getAttachmentStyle fields = do (base :: Seq Content) <- getField "base" fields case base of [Elt "math.op" _ fs] -> do limits <- getField "limits" fs if limits == VBoolean True then pure $ Just Limits else pure Nothing [Elt "math.limits" _ fs] -> do inl <- getField "inline" fs if inl == VBoolean False then pure $ Just LimitsDisplay else pure $ Just Limits [Elt "math.scripts" _ _] -> pure $ Just Scripts _ -> pure Nothing pMath :: PandocMonad m => P m Exp pMath = pTok (const True) >>= handleMath handleMath :: PandocMonad m => Content -> P m Exp handleMath tok = case tok of Lab t -> do ignored ("label " <> t) pure (EGrouped []) Txt t | T.any isDigit t -> pure $ ENumber t | T.length t == 1 -> case T.unpack t of [c] | not (isAlphaNum c) -> pure $ ESymbol (getSymbolType c) t _ -> pure $ EIdentifier t | otherwise -> pure $ EText TextNormal t Elt "math.dif" _ _ -> pure $ EIdentifier "d" Elt "math.Dif" _ _ -> pure $ EIdentifier "D" Elt "math.equation" _ fields -> getField "body" fields >>= pMathGrouped Elt "text" _ fields -> do body <- getField "body" fields (mbweight :: Maybe Text) <- getField "weight" fields case mbweight of Just "bold" -> EStyled TextBold <$> pMathMany body _ -> pMathGrouped body Elt "math.op" _ fields -> EMathOperator <$> getField "text" fields Elt "math.frac" _ fields -> do num <- getField "num" fields >>= pMathGrouped denom <- getField "denom" fields >>= pMathGrouped pure $ EFraction NormalFrac num denom Elt "math.accent" _ fields -> do base <- getField "base" fields >>= pMathGrouped acc <- getField "accent" fields >>= pMathGrouped let acc' = case acc of ESymbol _ "\8901" -> ESymbol Accent "\775" -- \dot ESymbol _ "\168" -> ESymbol Accent "\776" -- \ddot ESymbol _ "\8764" -> ESymbol Accent "\771" -- \tilde ESymbol _ t -> ESymbol Accent t _ -> acc pure $ EOver False base acc' Elt "math.attach" _ fields -> do base <- getField "base" fields >>= pMathGrouped t' <- getField "t" fields b' <- getField "b" fields tr' <- getField "tr" fields tl' <- getField "tl" fields br' <- getField "br" fields bl' <- getField "bl" fields attachmentStyle <- getAttachmentStyle fields let limits = case attachmentStyle of Just Limits -> True Just LimitsDisplay -> True _ -> False let convertible = attachmentStyle == Just LimitsDisplay let (mbt, mbtr) = case (t', tr') of (Just top, Just topright) -> (Just top, Just topright) (Just top, Nothing) | limits -> (Just top, Nothing) | otherwise -> (Nothing, Just top) (Nothing, Just topright) -> (Nothing, Just topright) (Nothing, Nothing) -> (Nothing, Nothing) let (mbb, mbbr) = case (b', br') of (Just bot, Just botright) -> (Just bot, Just botright) (Just bot, Nothing) | limits -> (Just bot, Nothing) | otherwise -> (Nothing, Just bot) (Nothing, Just topright) -> (Nothing, Just topright) (Nothing, Nothing) -> (Nothing, Nothing) let dummy = EGrouped [] let addPrefix x = case (tl', bl') of (Nothing, Nothing) -> pure x (Just top, Nothing) -> do res <- ESuper dummy <$> pMathGrouped top pure $ EGrouped [res, x] (Nothing, Just bot) -> do res <- ESub dummy <$> pMathGrouped bot pure $ EGrouped [res, x] (Just top, Just bot) -> do res <- ESubsup dummy <$> pMathGrouped bot <*> pMathGrouped top pure $ EGrouped [res, x] base' <- case (mbtr, mbbr) of (Nothing, Nothing) -> pure base (Nothing, Just br) -> ESub base <$> pMathGrouped br (Just tr, Nothing) -> ESuper base <$> pMathGrouped tr (Just tr, Just br) -> ESubsup base <$> pMathGrouped br <*> pMathGrouped tr suffix <- case (mbt, mbb) of (Nothing, Nothing) -> pure base' (Nothing, Just bot) -> EUnder convertible base' <$> pMathGrouped bot (Just top, Nothing) -> EOver convertible base' <$> pMathGrouped top (Just top, Just bot) -> EUnderover convertible base' <$> pMathGrouped bot <*> pMathGrouped top addPrefix suffix Elt "math.serif" _ fields -> EStyled TextNormal <$> (getField "body" fields >>= pMathMany) Elt "math.sans" _ fields -> EStyled TextSansSerif <$> (getField "body" fields >>= pMathMany) Elt "math.frak" _ fields -> EStyled TextFraktur <$> (getField "body" fields >>= pMathMany) Elt "math.mono" _ fields -> EStyled TextMonospace <$> (getField "body" fields >>= pMathMany) Elt "math.cal" _ fields -> EStyled TextScript <$> (getField "body" fields >>= pMathMany) Elt "math.bb" _ fields -> EStyled TextDoubleStruck <$> (getField "body" fields >>= pMathMany) Elt "math.upright" _ fields -> EStyled TextNormal <$> (getField "body" fields >>= pMathMany) Elt "math.bold" _ fields -> EStyled TextBold <$> (getField "body" fields >>= pMathMany) Elt "math.italic" _ fields -> EStyled TextItalic <$> (getField "body" fields >>= pMathMany) Elt "math.underline" _ fields -> EUnder False <$> (getField "body" fields >>= pMathGrouped) <*> pure (ESymbol TUnder "_") Elt "math.overline" _ fields -> EOver False <$> (getField "body" fields >>= pMathGrouped) <*> pure (ESymbol TOver "\175") Elt "math.underbrace" _ fields -> do mbAnn <- getField "annotation" fields body <- getField "body" fields >>= pMathGrouped let x = EUnder False body (ESymbol TUnder "\9183") case mbAnn of Nothing -> pure x Just ann -> EUnder False x <$> pMathGrouped ann Elt "math.overbrace" _ fields -> do mbAnn <- getField "annotation" fields body <- getField "body" fields >>= pMathGrouped let x = EOver False body (ESymbol TOver "\9182") case mbAnn of Nothing -> pure x Just ann -> EOver False x <$> pMathGrouped ann Elt "math.underbracket" _ fields -> do mbAnn <- getField "annotation" fields body <- getField "body" fields >>= pMathGrouped let x = EUnder False body (ESymbol TUnder "\9141") case mbAnn of Nothing -> pure x Just ann -> EUnder False x <$> pMathGrouped ann Elt "math.overbracket" _ fields -> do mbAnn <- getField "annotation" fields body <- getField "body" fields >>= pMathGrouped let x = EOver False body (ESymbol TOver "\9140") case mbAnn of Nothing -> pure x Just ann -> EOver False x <$> pMathGrouped ann Elt "math.underparen" _ fields -> do mbAnn <- getField "annotation" fields body <- getField "body" fields >>= pMathGrouped let x = EUnder False body (ESymbol TUnder "\9181") case mbAnn of Nothing -> pure x Just ann -> EUnder False x <$> pMathGrouped ann Elt "math.overparen" _ fields -> do mbAnn <- getField "annotation" fields body <- getField "body" fields >>= pMathGrouped let x = EOver False body (ESymbol TOver "\9180") case mbAnn of Nothing -> pure x Just ann -> EOver False x <$> pMathGrouped ann Elt "math.scripts" _ fields -> getField "body" fields >>= pMathGrouped Elt "math.limits" _ fields -> getField "body" fields >>= pMathGrouped Elt "math.root" _ fields -> do mbindex <- getField "index" fields radicand <- getField "radicand" fields >>= pMathGrouped case mbindex of Nothing -> pure $ ESqrt radicand Just index -> do index' <- pMathGrouped index pure $ ERoot index' radicand Elt "math.sqrt" _ fields -> ESqrt <$> (getField "radicand" fields >>= pMathGrouped) Elt "math.abs" _ fields -> do body <- getField "body" fields >>= pMathGrouped pure $ EDelimited "|" "|" [Right body] Elt "math.floor" _ fields -> do body <- getField "body" fields >>= pMathGrouped pure $ EDelimited "\8970" "\8971" [Right body] Elt "math.ceil" _ fields -> do body <- getField "body" fields >>= pMathGrouped pure $ EDelimited "\8968" "\8969" [Right body] Elt "math.norm" _ fields -> do body <- getField "body" fields >>= pMathGrouped pure $ EDelimited "\8214" "\8214" [Right body] Elt "math.round" _ fields -> do body <- getField "body" fields >>= pMathGrouped pure $ EDelimited "\8970" "\8969" [Right body] Elt "math.lr" _ fields -> do bodyparts <- getField "body" fields >>= mapM pMathMany . V.toList let rawbody = intercalate [ESymbol Pun ","] bodyparts let (op, rest) = case rawbody of (ESymbol _ t : xs) -> (t, xs) _ -> ("", rawbody) let (body, cl) = case reverse rest of (ESymbol _ t : _) -> (map Right (init rest), t) _ -> (map Right rest, "") pure $ EDelimited op cl body Elt "math.binom" _ fields -> do up <- getField "upper" fields >>= pMathGrouped low <- getField "lower" fields >>= pMathGrouped pure $ EDelimited "(" ")" [Right (EFraction NoLineFrac up low)] Elt "math.cases" _ fields -> do (delim :: Maybe Text) <- getField "delim" fields (children :: [Seq Content]) <- map valToContent . V.toList <$> getField "children" fields let isAlignPoint (Elt "math.alignpoint" _ _) = True isAlignPoint _ = False let formatRow vs = case Seq.breakl isAlignPoint vs of (xs, ys) -> do case Seq.viewl ys of _ Seq.:< rest -> do xs' <- pMathMany xs ys' <- pMathMany rest pure [xs', ys'] _ -> (: []) <$> pMathMany vs rows <- mapM formatRow children pure $ EDelimited (fromMaybe "{" delim) "" [Right (EArray [AlignLeft, AlignLeft] rows)] Elt "math.vec" _ fields -> do (op, cl) <- arrayDelims fields rows <- getField "children" fields >>= mapM (fmap (: []) . pMathMany) . V.toList pure $ EDelimited op cl [Right (EArray [AlignCenter] rows)] Elt "math.mat" _ fields -> do (op, cl) <- arrayDelims fields let formatCell x = do let content = valToContent x let align = case Seq.viewl content of Elt "math.alignpoint" _ _ Seq.:< _ -> AlignLeft _ -> case Seq.viewr content of _ Seq.:> Elt "math.alignpoint" _ _ -> AlignRight _ -> AlignCenter exp' <- pMathMany content pure (align, exp') let formatRow (VArray vs) = mapM formatCell (V.toList vs) formatRow _ = fail "mat expected array" (rawrows :: V.Vector Val) <- getField "rows" fields rows <- mapM formatRow (V.toList rawrows) let aligns = case rows of [] -> [] (r : _) -> map fst r pure $ EDelimited op cl [Right (EArray aligns (map (map snd) rows))] Elt "hide" _ fields -> do EPhantom <$> (getField "body" fields >>= pMathGrouped) Elt "h" _ fields -> do amount <- getField "amount" fields let em = case amount of LExact x LEm -> toRational x _ -> case amount <> LExact 0 LPt of -- force to Pt LExact x LPt -> toRational x / 12 _ -> 1 / 3 -- guess! pure $ ESpace em Elt "grid" _ fields -> do children <- getField "children" fields >>= mapM pMathMany . V.toList (columns :: Val) <- getField "columns" fields numcols <- case columns of VInteger x -> pure $ fromIntegral x VArray x -> pure $ V.length x VNone -> pure 1 _ -> fail $ "Could not determine number of columns: " <> show columns let rows = chunks numcols children pure $ EArray (replicate numcols AlignLeft) rows Elt "table" pos fields -> handleMath (Elt "grid" pos fields) Elt "link" _ fields -> do body <- getField "body" fields ignored "hyperlink in math" pMathGrouped body Elt "math.display" _ fields -> do content <- getField "content" fields ignored "display" pMathGrouped content Elt "math.inline" _ fields -> do content <- getField "content" fields ignored "inline" pMathGrouped content Elt "math.script" _ fields -> do content <- getField "content" fields ignored "script" pMathGrouped content Elt "math.sscript" _ fields -> do content <- getField "content" fields ignored "sscript" pMathGrouped content Elt (Identifier name) _ fields -> do body <- getField "body" fields `mplus` pure mempty ignored name pMathGrouped body arrayDelims :: PandocMonad m => M.Map Identifier Val -> P m (Text, Text) arrayDelims fields = do (mbdelim :: Maybe Text) <- getField "delim" fields pure $ case mbdelim of Just "(" -> ("(", ")") Just "[" -> ("[", "]") Just "{" -> ("{", "}") Just "|" -> ("|", "|") Just "||" -> ("\8741", "\8741") _ -> ("(", ")") pMathMany :: PandocMonad m => Seq Content -> P m [Exp] pMathMany cs = do -- check for "alignpoint" and "linebreak" elements -- and use an array structure for alignment let lns = splitOnLinebreaks cs case lns of [] -> pure [] [ln] | not (any isAlignpoint ln) -> pWithContents (many pMath) ln _ -> do rows <- mapM (mapM (pWithContents (many pMath)) . splitOnAlignpoints) lns let numcols = maximum $ map length rows let cols = take numcols $ AlignRight : cycle [AlignLeft, AlignRight] pure [EArray cols rows] pMathGrouped :: PandocMonad m => Seq Content -> P m Exp pMathGrouped = fmap withGroup . pMathMany splitOnLinebreaks :: Seq Content -> [Seq Content] splitOnLinebreaks xs = if Seq.null bs then if null as then [] else [as] else as : splitOnLinebreaks (Seq.drop 1 bs) where (as, bs) = Seq.breakl isLinebreak xs isLinebreak (Elt "linebreak" _ _) = True isLinebreak _ = False splitOnAlignpoints :: Seq Content -> [Seq Content] splitOnAlignpoints xs = if Seq.null bs then if null as then [] else [as] else as : splitOnAlignpoints (Seq.drop 1 bs) where (as, bs) = Seq.breakl isAlignpoint xs isAlignpoint :: Content -> Bool isAlignpoint (Elt "math.alignpoint" _ _) = True isAlignpoint _ = False ================================================ FILE: src/Text/Pandoc/Readers/Typst/Parsing.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Text.Pandoc.Readers.Typst.Parsing ( P, PState(..), defaultPState, pTok, pWithContents, ignored, getField, chunks, ) where import Control.Monad (MonadPlus) import Control.Monad.Reader (lift) import qualified Data.Foldable as F import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Sequence (Seq) import Data.Text (Text) import Text.Parsec ( ParsecT, getInput, setInput, tokenPrim ) import Typst.Types ( Identifier, Content(Elt), FromVal(..), Val(VNone) ) import Text.Pandoc.Class.PandocMonad ( PandocMonad, report ) import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Definition data PState = PState { sLabels :: [Text] , sMeta :: Meta } deriving (Show) defaultPState :: PState defaultPState = PState { sLabels = [] , sMeta = mempty } type P m a = ParsecT [Content] PState m a -- state tracks a list of labels in the document pTok :: PandocMonad m => (Content -> Bool) -> P m Content pTok f = tokenPrim show showPos match where showPos _oldpos (Elt _ (Just pos) _) _ = pos showPos oldpos _ _ = oldpos match x | f x = Just x match _ = Nothing ignored :: PandocMonad m => Text -> P m () ignored msg = lift $ report $ IgnoredElement msg pWithContents :: PandocMonad m => P m a -> Seq Content -> P m a pWithContents pa cs = do inp <- getInput setInput $ F.toList cs res <- pa setInput inp pure res -- | Get field value from element, defaulting to VNone. getField :: (MonadFail m, MonadPlus m, FromVal a) => Identifier -> M.Map Identifier Val -> m a getField name fields = fromVal $ fromMaybe VNone $ M.lookup name fields -- | Split a list into chunks of a given size. The last chunk may be smaller. chunks :: Int -> [a] -> [[a]] chunks _ [] = [] chunks n xs = take n xs : chunks n (drop n xs) ================================================ FILE: src/Text/Pandoc/Readers/Typst.hs ================================================ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Readers.Typst Copyright : Copyright (C) 2023 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Reads and evaluates a Typst document as a Pandoc AST. -} module Text.Pandoc.Readers.Typst ( readTypst ) where import Text.Pandoc.Class import Text.Pandoc.Sources import Text.Pandoc.Options import Text.Pandoc.Definition import Typst ( parseTypst, evaluateTypst ) import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Translations (Term(References), translateTerm) import Text.Pandoc.Shared (tshow, blocksToInlines) import Control.Monad.Except (throwError) import Control.Monad (MonadPlus (mplus), void, guard, foldM) import Control.Monad.Trans (lift) import qualified Data.Foldable as F import qualified Data.Map as M import Data.Maybe (catMaybes, fromMaybe, isJust) import Data.Sequence (Seq) import qualified Data.Sequence as Seq import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import Text.Pandoc.Walk import Text.Parsec import Text.TeXMath (writeTeX) import Text.TeXMath.Shared (getSpaceChars) import Text.Pandoc.Readers.Typst.Math (pMathMany) import Text.Pandoc.Readers.Typst.Parsing (pTok, ignored, getField, P, PState(..), defaultPState) import Typst.Methods (formatNumber, applyPureFunction) import Typst.Types import qualified Data.Vector as V import System.FilePath (takeDirectory) import qualified System.FilePath.Windows as Windows import qualified System.FilePath.Posix as Posix -- | Read Typst from an input string and return a Pandoc document. readTypst :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readTypst _opts inp = do let sources = toSources inp let inputName = case sources of Sources ((pos, _):_) -> sourceName pos _ -> "" case parseTypst inputName (sourcesToText sources) of Left e -> throwError $ PandocParseError $ T.pack $ show e Right parsed -> do let ops = Operations { loadBytes = readFileStrict, currentUTCTime = getCurrentTime, lookupEnvVar = fmap (fmap T.unpack) . lookupEnv . T.pack, checkExistence = fileExists } res <- evaluateTypst ops inputName parsed case res of Left e -> throwError $ PandocParseError $ tshow e Right content -> do let content' = fixNesting content let labs = findLabels [content'] runParserT pPandoc defaultPState{ sLabels = labs } inputName [content'] >>= either (throwError . PandocParseError . T.pack . show) pure pBlockElt :: PandocMonad m => P m B.Blocks pBlockElt = try $ do res <- pTok (\t -> isBlock t || not (isInline t)) -- check for following label mbident <- option Nothing $ Just <$> pLab case res of Elt name@(Identifier tname) pos fields -> do case M.lookup name blockHandlers of Nothing -> do ignored ("unknown block element " <> tname <> " at " <> tshow pos) pure mempty Just handler -> handler pos mbident fields _ -> pure mempty pInline :: PandocMonad m => P m B.Inlines pInline = try $ do res <- pTok (\t -> isInline t || not (isBlock t)) case res of Txt t -> pure $ B.text t Lab name -> pure $ B.spanWith (name, [], []) mempty Elt (Identifier tname) _ _ | "math." `T.isPrefixOf` tname , tname /= "math.equation" -> B.math . writeTeX <$> pMathMany (Seq.singleton res) Elt name@(Identifier tname) pos fields -> do labs <- sLabels <$> getState labelTarget <- (do result <- getField "target" fields case result of VLabel t | t `elem` labs -> pure True _ -> pure False) <|> pure False if tname == "ref" && not labelTarget then do -- @foo is a citation unless it links to a lab in the doc: let targetToKey (Identifier "target") = Identifier "key" targetToKey k = k case M.lookup "cite" inlineHandlers of Nothing -> do ignored ("unknown inline element " <> tname <> " at " <> tshow pos) pure mempty Just handler -> handler pos Nothing (M.mapKeys targetToKey fields) else do case M.lookup name inlineHandlers of Nothing -> do ignored ("unknown inline element " <> tname <> " at " <> tshow pos) pure mempty Just handler -> handler pos Nothing fields -- Pull block elements out of inline elements, e.g. -- Elt "smallcaps" [ Elt "heading" [..] ] -> -- Elt "heading" [ Elt "smallcaps" [..]]. See #11017. fixNesting :: Content -> Content fixNesting el@(Elt name pos fields) | Just (VContent elts) <- M.lookup "body" fields = let elts' = fmap fixNesting elts fields' = M.insert "body" (VContent elts') fields in if isInline el then case getField "body" fields' of Just ([el'@(Elt name' pos' fields'')] :: Seq Content) | isBlock el' , not (isInline el') , "body" `M.member` fields'' -> Elt name' pos' $ M.insert "body" (VContent (Seq.singleton (Elt name pos fields''))) fields' _ -> Elt name pos fields' else Elt name pos fields' fixNesting x = x pPandoc :: PandocMonad m => P m B.Pandoc pPandoc = do Elt "document" _ fields <- pTok isDocument bs <- getField "body" fields >>= pWithContents pBlocks title <- (getField "title" fields >>= pWithContents pInlines) <|> pure mempty authors <- (getField "author" fields >>= mapM (pWithContents pInlines) . V.toList) <|> ((:[]) <$> (getField "author" fields >>= (\x -> guard (not (null x)) *> pWithContents pInlines x))) <|> pure [] date <- (getField "date" fields >>= pWithContents pInlines) <|> pure mempty keywords <- (getField "keywords" fields >>= mapM (pWithContents pInlines) . V.toList) <|> pure [] meta <- sMeta <$> getState let meta' = (if title == mempty || isJust (lookupMeta "title" meta) then id else B.setMeta "title" title) . (if null authors then id else B.setMeta "author" authors) . (if null date then id else B.setMeta "date" date) . (if null keywords then id else B.setMeta "keywords" keywords) $ meta pure $ Pandoc meta' (B.toList bs) pBlocks :: PandocMonad m => P m B.Blocks pBlocks = mconcat <$> many pBlock pBlock :: PandocMonad m => P m B.Blocks pBlock = pPara <|> pBlockElt pSpace :: PandocMonad m => P m Content pSpace = pTok ( \case Txt t | T.all (== ' ') t -> True _ -> False ) pLab :: PandocMonad m => P m Text pLab = try $ do optional pSpace Lab t <- pTok ( \case Lab _ -> True _ -> False ) pure t isDocument :: Content -> Bool isDocument (Elt "document" _ _) = True isDocument _ = False isBlock :: Content -> Bool isBlock (Elt "raw" _ fields) = M.lookup "block" fields == Just (VBoolean True) isBlock (Elt name _ _) = name `Set.member` blockKeys isBlock Lab{} = True isBlock _ = False isInline :: Content -> Bool isInline (Elt "raw" _ fields) = M.lookup "block" fields /= Just (VBoolean True) isInline (Elt name _ _) = name `Set.member` inlineKeys isInline Lab{} = True isInline Txt{} = True blockKeys :: Set.Set Identifier blockKeys = Set.fromList $ M.keys (blockHandlers :: M.Map Identifier (Maybe SourcePos -> Maybe Text -> M.Map Identifier Val -> P PandocPure B.Blocks)) inlineKeys :: Set.Set Identifier inlineKeys = Set.fromList $ M.keys (inlineHandlers :: M.Map Identifier (Maybe SourcePos -> Maybe Text -> M.Map Identifier Val -> P PandocPure B.Inlines)) blockHandlers :: PandocMonad m => M.Map Identifier (Maybe SourcePos -> Maybe Text -> M.Map Identifier Val -> P m B.Blocks) blockHandlers = M.fromList [("text", \_ _ fields -> do body <- getField "body" fields -- sometimes text elements include para breaks notFollowedBy $ void $ pWithContents pInlines body pWithContents pBlocks body) ,("title", \_ _ fields -> do body <- getField "body" fields case body of VContent cs -> do ils <- pWithContents pInlines cs <|> pure mempty updateState $ \s -> s{ sMeta = B.setMeta "title" ils (sMeta s) } pure mempty _ -> pure mempty) ,("box", \_ _ fields -> do body <- getField "body" fields B.divWith ("", ["box"], []) <$> pWithContents pBlocks body) ,("heading", \_ mbident fields -> do body <- getField "body" fields lev <- getField "level" fields <|> pure 1 B.headerWith (fromMaybe "" mbident,[],[]) lev <$> pWithContents pInlines body) ,("quote", \_ _ fields -> do getField "block" fields >>= guard body <- getField "body" fields >>= pWithContents pBlocks attribution' <- getField "attribution" fields attribution <- if attribution' == mempty then pure mempty else (\x -> B.para ("\x2014\xa0" <> x)) <$> (pWithContents pInlines attribution') pure $ B.blockQuote $ body <> attribution) ,("list", \_ _ fields -> do children <- V.toList <$> getField "children" fields B.bulletList <$> mapM (pWithContents pBlocks) children) ,("list.item", \_ _ fields -> getField "body" fields >>= pWithContents pBlocks) ,("enum", \_ _ fields -> do children <- V.toList <$> getField "children" fields mbstart <- getField "start" fields start <- case mbstart of Nothing -> pure 1 Just x | x >= 0 -> pure x | otherwise -> fail "number must be positive" (numbering :: Text) <- getField "numbering" fields `mplus` pure "" let (sty, delim) = case numbering of "1." -> (B.Decimal, B.Period) "1)" -> (B.Decimal, B.OneParen) "(1)" -> (B.Decimal, B.TwoParens) "a." -> (B.LowerAlpha, B.Period) "a)" -> (B.LowerAlpha, B.OneParen) "(a)" -> (B.LowerAlpha, B.TwoParens) "A." -> (B.UpperAlpha, B.Period) "A)" -> (B.UpperAlpha, B.OneParen) "(A)" -> (B.UpperAlpha, B.TwoParens) "i." -> (B.LowerRoman, B.Period) "i)" -> (B.LowerRoman, B.OneParen) "(i)" -> (B.LowerRoman, B.TwoParens) "I." -> (B.UpperRoman, B.Period) "I)" -> (B.UpperRoman, B.OneParen) "(I)" -> (B.UpperRoman, B.TwoParens) _ -> (B.DefaultStyle, B.DefaultDelim) let listAttr = (start, sty, delim) B.orderedListWith listAttr <$> mapM (pWithContents pBlocks) children) ,("enum.item", \_ _ fields -> getField "body" fields >>= pWithContents pBlocks) ,("terms", \_ _ fields -> do children <- V.toList <$> getField "children" fields B.definitionList <$> mapM ( \case VTermItem t d -> do t' <- pWithContents pInlines t d' <- pWithContents pBlocks d pure (t', [d']) _ -> pure (mempty, []) ) children) ,("terms.item", \_ _ fields -> getField "body" fields >>= pWithContents pBlocks) ,("raw", \_ mbident fields -> do txt <- T.filter (/= '\r') <$> getField "text" fields mblang <- getField "lang" fields let attr = (fromMaybe "" mbident, maybe [] (\l -> [l]) mblang, []) pure $ B.codeBlockWith attr txt) ,("parbreak", \_ _ _ -> pure mempty) ,("block", \_ mbident fields -> maybe id (\ident -> B.divWith (ident, [], [])) mbident <$> (getField "body" fields >>= pWithContents pBlocks)) ,("place", \_ _ fields -> do ignored "parameters of place" getField "body" fields >>= pWithContents pBlocks) ,("columns", \_ _ fields -> do (cnt :: Integer) <- getField "count" fields B.divWith ("", ["columns-flow"], [("count", T.pack (show cnt))]) <$> (getField "body" fields >>= pWithContents pBlocks)) ,("rect", \_ _ fields -> B.divWith ("", ["rect"], []) <$> (getField "body" fields >>= pWithContents pBlocks)) ,("circle", \_ _ fields -> B.divWith ("", ["circle"], []) <$> (getField "body" fields >>= pWithContents pBlocks)) ,("ellipse", \_ _ fields -> B.divWith ("", ["ellipse"], []) <$> (getField "body" fields >>= pWithContents pBlocks)) ,("polygon", \_ _ fields -> B.divWith ("", ["polygon"], []) <$> (getField "body" fields >>= pWithContents pBlocks)) ,("square", \_ _ fields -> B.divWith ("", ["square"], []) <$> (getField "body" fields >>= pWithContents pBlocks)) ,("align", \_ _ fields -> do alignment <- getField "alignment" fields B.divWith ("", [], [("align", repr alignment)]) <$> (getField "body" fields >>= pWithContents pBlocks)) ,("stack", \_ _ fields -> do (dir :: Direction) <- getField "dir" fields `mplus` pure Ltr rawchildren <- getField "children" fields children <- mapM ( \case val@(VFraction {}) -> pure $ B.divWith ("", [], [("space", repr val)]) mempty val -> fromVal val >>= pWithContents pBlocks ) (V.toList rawchildren) pure $ B.divWith ("", [], [("stack", repr (VDirection dir))]) $ mconcat $ map (B.divWith ("", [], [])) children) ,("grid", \_ mbident fields -> parseTable mbident fields) ,("table", \_ mbident fields -> parseTable mbident fields) ,("figure", \_ mbident fields -> do body <- getField "body" fields >>= pWithContents pBlocks (mbCaption :: Maybe (Seq Content)) <- getField "caption" fields (caption :: B.Blocks) <- maybe mempty (pWithContents pBlocks) mbCaption pure $ case B.toList body of [B.Table attr _ colspecs thead tbodies tfoot] -> B.singleton (B.Table attr (B.Caption Nothing (B.toList caption)) colspecs thead tbodies tfoot) _ -> B.figureWith (fromMaybe "" mbident, [], []) (B.Caption Nothing (B.toList caption)) body) ,("line", \_ _ fields -> case ( M.lookup "start" fields >> M.lookup "end" fields >> M.lookup "angle" fields ) of Nothing -> pure B.horizontalRule _ -> pure mempty) ,("numbering", \_ _ fields -> do numStyle <- getField "numbering" fields (nums :: V.Vector Integer) <- getField "numbers" fields let toText v = fromMaybe "" $ fromVal v let toNum n = case numStyle of VString t -> formatNumber t (fromIntegral n) VFunction _ _ f -> case applyPureFunction f [VInteger n] of Success x -> toText x Failure _ -> "?" _ -> "?" pure $ B.plain . B.text . mconcat . map toNum $ V.toList nums) ,("footnote.entry", \_ _ fields -> getField "body" fields >>= pWithContents pBlocks) ,("pad", \_ _ fields -> -- ignore paddingy getField "body" fields >>= pWithContents pBlocks) ,("pagebreak", \_ _ _ -> pure $ B.divWith ("", ["page-break"], [("wrapper", "1")]) B.horizontalRule) ,("bibliography", \_ _ fields -> do let getSources v = case v of VString t -> MetaString t VArray xs -> MetaList $ map getSources $ V.toList xs _ -> MetaBool True -- should not occur let mbSources = getSources <$> M.lookup "sources" fields let updateBibliography x = updateState $ \s -> s{ sMeta = B.setMeta "bibliography" x (sMeta s) } maybe (pure ()) updateBibliography mbSources let title' = M.lookup "title" fields mbTitle <- case title' of Just VNone -> pure Nothing Just (VContent cs) -> Just <$> pWithContents pInlines cs Just (VString t) -> pure $ Just $ B.text t _ -> Just . B.text <$> lift (translateTerm References) let hdr = maybe mempty (B.header 1) mbTitle pure $ hdr <> B.divWith ("refs", [], []) mempty) ] inlineHandlers :: PandocMonad m => M.Map Identifier (Maybe SourcePos -> Maybe Text -> M.Map Identifier Val -> P m B.Inlines) inlineHandlers = M.fromList [("ref", \_ _ fields -> do VLabel target <- getField "target" fields supplement' <- getField "supplement" fields supplement <- case supplement' of VAuto -> -- TODO for now, until we can locate the element pure $ B.text ("[" <> target <> "]") VContent cs -> pWithContents pInlines cs VFunction _ _ _f -> -- TODO for now, until we can locate the element pure $ B.text ("[" <> target <> "]") _ -> pure mempty pure $ B.linkWith ("", ["ref"], []) ("#" <> target) "" supplement) ,("linebreak", \_ _ _ -> pure B.linebreak) ,("text", \_ _ fields -> do body <- getField "body" fields (mbweight :: Maybe Text) <- getField "weight" fields case mbweight of Just "bold" -> B.strong <$> pWithContents pInlines body _ -> pWithContents pInlines body) ,("raw", \_ _ fields -> B.code . T.filter (/= '\r') <$> getField "text" fields) ,("footnote", \_ _ fields -> B.note <$> (getField "body" fields >>= pWithContents pBlocks)) ,("cite", \_ _ fields -> do VLabel key <- getField "key" fields (form :: Text) <- getField "form" fields <|> pure "normal" let citation = B.Citation { B.citationId = key, B.citationPrefix = mempty, B.citationSuffix = mempty, B.citationMode = case form of "year" -> B.SuppressAuthor _ -> B.NormalCitation, B.citationNoteNum = 0, B.citationHash = 0 } pure $ B.cite [citation] (B.text $ "[" <> key <> "]")) ,("lower", \_ _ fields -> do body <- getField "text" fields walk (modString T.toLower) <$> pWithContents pInlines body) ,("upper", \_ _ fields -> do body <- getField "text" fields walk (modString T.toUpper) <$> pWithContents pInlines body) ,("emph", \_ _ fields -> do body <- getField "body" fields B.emph <$> pWithContents pInlines body) ,("strong", \_ _ fields -> do body <- getField "body" fields B.strong <$> pWithContents pInlines body) ,("sub", \_ _ fields -> do body <- getField "body" fields B.subscript <$> pWithContents pInlines body) ,("super", \_ _ fields -> do body <- getField "body" fields B.superscript <$> pWithContents pInlines body) ,("strike", \_ _ fields -> do body <- getField "body" fields B.strikeout <$> pWithContents pInlines body) ,("smallcaps", \_ _ fields -> do body <- getField "body" fields B.smallcaps <$> pWithContents pInlines body) ,("underline", \_ _ fields -> do body <- getField "body" fields B.underline <$> pWithContents pInlines body) ,("quote", \_ _ fields -> do (getField "block" fields <|> pure False) >>= guard . not body <- getInlineBody fields >>= pWithContents pInlines pure $ B.doubleQuoted $ B.trimInlines body) ,("link", \_ _ fields -> do dest <- getField "dest" fields src <- case dest of VString t -> pure t VLabel t -> pure $ "#" <> t VDict _ -> do ignored "link to location, linking to #" pure "#" _ -> fail "Expected string or label for dest" body <- getField "body" fields description <- if null body then pure $ B.text $ if "mailto:" `T.isPrefixOf` src then T.drop 7 src else if "tel:" `T.isPrefixOf` src then T.drop 4 src else src else pWithContents pInlines body <|> pWithContents (B.fromList . blocksToInlines . B.toList <$> pBlocks) body pure $ B.link src "" description) ,("image", \mbpos _ fields -> do path <- getField "source" fields <|> getField "path" fields alt <- (B.text <$> getField "alt" fields) `mplus` pure mempty let basedir = maybe "." (takeDirectory . sourceName) mbpos let isAbsolutePath p = Posix.isAbsolute p || Windows.isAbsolute p let path' = T.pack $ if isAbsolutePath path || basedir == "." then path else basedir Posix. path (mbwidth :: Maybe Text) <- fmap (renderLength False) <$> getField "width" fields (mbheight :: Maybe Text) <- fmap (renderLength False) <$> getField "height" fields let attr = ( "", [], maybe [] (\x -> [("width", x)]) mbwidth ++ maybe [] (\x -> [("height", x)]) mbheight ) pure $ B.imageWith attr path' "" alt) ,("box", \_ _ fields -> do body <- getField "body" fields B.spanWith ("", ["box"], []) <$> pWithContents pInlines body) ,("h", \_ _ fields -> do amount <- getField "amount" fields `mplus` pure (LExact 1 LEm) let em = case amount of LExact x LEm -> toRational x _ -> case amount <> LExact 0 LPt of -- force to Pt LExact x LPt -> toRational x / 12 _ -> 1 / 3 -- guess! pure $ B.text $ getSpaceChars em) ,("place", \_ _ fields -> do ignored "parameters of place" getField "body" fields >>= pWithContents pInlines) ,("align", \_ _ fields -> do alignment <- getField "alignment" fields B.spanWith ("", [], [("align", repr alignment)]) <$> (getField "body" fields >>= pWithContents pInlines)) ,("sys.version", \_ _ _ -> pure $ B.text "typst-hs") ,("math.equation", \_ _ fields -> do body <- getField "body" fields display <- getField "block" fields (if display then B.displayMath else B.math) . writeTeX <$> pMathMany body) ,("pad", \_ _ fields -> -- ignore paddingy getField "body" fields >>= pWithContents pInlines) ,("block", \_ mbident fields -> maybe id (\ident -> B.spanWith (ident, [], [])) mbident <$> (getField "body" fields >>= pWithContents pInlines)) ] getInlineBody :: PandocMonad m => M.Map Identifier Val -> P m (Seq Content) getInlineBody fields = parbreaksToLinebreaks <$> getField "body" fields parbreaksToLinebreaks :: Seq Content -> Seq Content parbreaksToLinebreaks = fmap go . Seq.dropWhileL isParbreak . Seq.dropWhileR isParbreak where go (Elt "parbreak" pos _) = Elt "linebreak" pos mempty go x = x isParbreak (Elt "parbreak" _ _) = True isParbreak _ = False pPara :: PandocMonad m => P m B.Blocks pPara = do ils <- B.trimInlines . collapseAdjacentCites . mconcat <$> many1 pInline optional pParBreak pure $ if ils == mempty then mempty else B.para ils pParBreak :: PandocMonad m => P m () pParBreak = void $ pTok ( \case Elt "parbreak" _ _ -> True _ -> False ) pWithContents :: PandocMonad m => P m a -> Seq Content -> P m a pWithContents pa cs = try $ do inp <- getInput setInput $ F.toList cs res <- pa eof setInput inp pure res pInlines :: PandocMonad m => P m B.Inlines pInlines = mappend <$> (collapseAdjacentCites . mconcat <$> many pInline) <*> ((B.softbreak <$ pParBreak) <|> pure mempty) collapseAdjacentCites :: B.Inlines -> B.Inlines collapseAdjacentCites = B.fromList . foldr go [] . B.toList where go (Cite cs1 ils1) (Cite cs2 ils2 : xs) = Cite (cs1 ++ cs2) (ils1 <> ils2) : xs go (Cite cs1 ils1) (Space : Cite cs2 ils2 : xs) = Cite (cs1 ++ cs2) (ils1 <> ils2) : xs go x xs = x:xs modString :: (Text -> Text) -> B.Inline -> B.Inline modString f (B.Str t) = B.Str (f t) modString _ x = x findLabels :: Seq.Seq Content -> [Text] findLabels = foldr go [] where go (Txt{}) = id go (Lab t) = (t :) go (Elt{ eltFields = fs }) = \ts -> foldr go' ts fs go' (VContent cs) = (findLabels cs ++) go' _ = id parseTable :: PandocMonad m => Maybe Text -> M.Map Identifier Val -> P m B.Blocks parseTable mbident fields = do children <- V.toList <$> getField "children" fields (columns :: Val) <- getField "columns" fields let toWidth (VFraction f) = Just (floor $ 1000 * f) toWidth _ = Nothing let normalizeWidths xs = let givenwidths = catMaybes xs (totgivenwidth :: Int) = sum givenwidths avgwidth = totgivenwidth `div` length givenwidths totwidth = avgwidth * length xs in if null givenwidths then replicate (length xs) B.ColWidthDefault else map ( \case Just x -> B.ColWidth (fromIntegral x / fromIntegral totwidth) Nothing -> B.ColWidth (fromIntegral avgwidth / fromIntegral totwidth) ) xs widths <- case columns of VInteger x -> pure $ replicate (fromIntegral x) B.ColWidthDefault VArray x -> pure $ normalizeWidths $ map toWidth (V.toList x) VNone -> pure [B.ColWidthDefault] _ -> fail $ "Could not determine number of columns: " <> show columns let numcols = length widths align <- getField "align" fields let toAlign (VAlignment (Just horiz) _) = case horiz of HorizStart -> B.AlignLeft HorizLeft -> B.AlignLeft HorizEnd -> B.AlignRight HorizRight -> B.AlignRight HorizCenter -> B.AlignCenter toAlign _ = B.AlignDefault aligns <- case align of VAlignment {} -> pure $ replicate numcols (toAlign align) VArray v -> pure $ map toAlign (V.toList v) VFunction _ _ f -> do mapM ( \colnum -> case applyPureFunction f [VInteger colnum, VInteger 0] of Success x -> pure $ toAlign x Failure e -> fail e ) [0 .. (fromIntegral numcols - 1)] _ -> pure $ replicate numcols B.AlignDefault let colspecs = zip (aligns ++ repeat B.AlignDefault) widths let addCell' cell Nothing = addCell' cell (Just ([], [])) addCell' cell@(B.Cell _ _ (B.RowSpan rowspan) (B.ColSpan colspan) _) (Just (freecols, revrows)) = let freecols' = case (rowspan + 1) - length freecols of n | n < 0 -> freecols | otherwise -> freecols ++ replicate n numcols in case freecols' of [] -> -- should not happen error "empty freecols'" x:xs | colspan <= x -- there is room on current row -> let (as, bs) = splitAt rowspan (x:xs) in Just (map (\z -> z - colspan) as ++ bs, case revrows of [] -> [[cell]] r:rs -> (cell:r):rs) | otherwise -> let (as, bs) = splitAt rowspan xs in Just (map (\z -> z - colspan) as ++ bs, [cell]:revrows) let addCell tableSection cell (TableData tdata) = TableData (M.alter (addCell' cell) tableSection tdata) let toCell tableSection tableData contents = do case contents of [Elt (Identifier "grid.cell") _pos fs] -> do bs <- B.toList <$> (getField "body" fs >>= pWithContents pBlocks) rowspan <- getField "rowspan" fs <|> pure 1 colspan <- getField "colspan" fs <|> pure 1 align' <- (toAlign <$> getField "align" fs) <|> pure B.AlignDefault pure $ addCell tableSection (B.Cell B.nullAttr align' (B.RowSpan rowspan) (B.ColSpan colspan) bs) tableData [Elt (Identifier "table.cell") pos fs] -> toCell tableSection tableData [Elt (Identifier "grid.cell") pos fs] [Elt (Identifier "table.vline") _pos _fs] -> pure tableData [Elt (Identifier "table.hline") _pos _fs] -> pure tableData [Elt (Identifier "grid.vline") _pos _fs] -> pure tableData [Elt (Identifier "grid.hline") _pos _fs] -> pure tableData [Elt (Identifier "table.header") _pos fs] -> getField "children" fs >>= foldM (toCell THeader) tableData . V.toList [Elt (Identifier "table.footer") _pos fs] -> getField "children" fs >>= foldM (toCell TFooter) tableData . V.toList _ -> do bs <- B.toList <$> pWithContents pBlocks contents pure $ addCell tableSection (B.Cell B.nullAttr B.AlignDefault (B.RowSpan 1) (B.ColSpan 1) bs) tableData tableData <- foldM (toCell TBody) (TableData mempty) children let getRows tablePart = map (B.Row B.nullAttr . reverse) . maybe [] (reverse . snd) . M.lookup tablePart . unTableData let headRows = getRows THeader tableData let bodyRows = getRows TBody tableData let footRows = getRows TFooter tableData pure $ B.tableWith (fromMaybe "" mbident, [], []) (B.Caption mempty mempty) colspecs (B.TableHead B.nullAttr headRows) [B.TableBody B.nullAttr 0 [] bodyRows] (B.TableFoot B.nullAttr footRows) data TableSection = THeader | TBody | TFooter deriving (Show, Ord, Eq) newtype TableData = TableData { unTableData :: M.Map TableSection ([Int], [[Cell]]) } deriving (Show) -- for each table section, we have a pair -- the first element indicates the number of column spaces left -- in [currentLine, nextLine, lineAfter, etc.] -- the second element is a list of rows, in reverse order, -- each of which is a list of cells, in reverse order ================================================ FILE: src/Text/Pandoc/Readers/Vimwiki.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-} {- | Module : Text.Pandoc.Readers.Vimwiki Copyright : Copyright (C) 2017-2020 Yuchen Pei License : GNU GPL, version 2 or above Maintainer : Yuchen Pei Stability : alpha Portability : portable Conversion of vimwiki text to 'Pandoc' document. -} {-- [X]: implemented [O]: not implemented * block parsers: * [X] header * [X] hrule * [X] comment * [X] blockquote * [X] preformatted -- using codeblock * [X] displaymath * [X] bulletlist / orderedlist * [X] todo lists -- using span. * [X] table * [X] centered table -- using div * [O] colspan and rowspan -- see issue #1024 * [X] paragraph * [X] definition list * inline parsers: * [X] bareURL * [X] strong * [X] emph * [X] strikeout * [X] code * [X] link * [X] image * [X] inline math * [X] tag * [X] sub- and super-scripts * misc: * [X] `TODO:` mark * [X] metadata placeholders: %title and %date * [O] control placeholders: %template and %nohtml -- ignored --} module Text.Pandoc.Readers.Vimwiki ( readVimwiki ) where import Control.Monad (guard) import Control.Monad.Except (throwError) import Data.Default import Data.List (isInfixOf) import Data.Maybe import Data.Text (Text) import qualified Data.Text as T import Safe (lastMay) import Text.Pandoc.Builder (Blocks, Inlines, fromList, toList, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.Definition (Attr, Block (BulletList, OrderedList), Inline (Space), ListNumberDelim (..), ListNumberStyle (..), Pandoc (..), nullMeta) import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Parsing (ParserState, ParsecT, blanklines, emailAddress, many1Till, orderedListMarker, readWithM, registerHeader, spaceChar, stateMeta, stateOptions, uri, manyTillChar, manyChar, textStr, many1Char, countChar, many1TillChar, alphaNum, anyChar, char, newline, noneOf, oneOf, space, spaces, string, choice, eof, lookAhead, many1, many, manyTill, notFollowedBy, skipMany1, try, option, updateState, getState, (<|>)) import Text.Pandoc.Sources (ToSources(..), Sources) import Text.Pandoc.Shared (splitTextBy, stringify, stripFirstAndLast, tshow) import Text.Pandoc.URI (isURI) readVimwiki :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readVimwiki opts s = do let sources = toSources s res <- readWithM parseVimwiki def{ stateOptions = opts } sources case res of Left e -> throwError e Right result -> return result type VwParser = ParsecT Sources ParserState -- constants specialChars :: [Char] specialChars = "=*-#[]_~{}`$|:%^," spaceChars :: [Char] spaceChars = " \t\n" -- main parser parseVimwiki :: PandocMonad m => VwParser m Pandoc parseVimwiki = do bs <- mconcat <$> many block spaces eof st <- getState let meta = stateMeta st return $ Pandoc meta (toList bs) -- block parser block :: PandocMonad m => VwParser m Blocks block = do res <- choice [ mempty <$ blanklines , header , hrule , mempty <$ comment , mixedList , preformatted , displayMath , table , mempty <$ placeholder , blockQuote , definitionList , para ] trace (T.take 60 $ tshow $ toList res) return res blockML :: PandocMonad m => VwParser m Blocks blockML = choice [preformatted, displayMath, table] header :: PandocMonad m => VwParser m Blocks header = try $ do sp <- many spaceChar eqs <- many1 (char '=') spaceChar let lev = length eqs guard $ lev <= 6 contents <- trimInlines . mconcat <$> manyTill inline (try $ spaceChar >> string eqs >> many spaceChar >> newline) attr <- registerHeader (makeId contents, ["justcenter" | not (null sp)], []) contents return $ B.headerWith attr lev contents para :: PandocMonad m => VwParser m Blocks para = try $ do contents <- trimInlines . mconcat <$> many1 inline if all (==Space) (toList contents) then return mempty else return $ B.para contents hrule :: PandocMonad m => VwParser m Blocks hrule = try $ B.horizontalRule <$ (string "----" >> many (char '-') >> newline) comment :: PandocMonad m => VwParser m () comment = try $ do many spaceChar >> string "%%" >> many (noneOf "\n") return () blockQuote :: PandocMonad m => VwParser m Blocks blockQuote = try $ do string " " contents <- trimInlines . mconcat <$> many1 inlineBQ if all (==Space) (toList contents) then return mempty else return $ B.blockQuote $ B.plain contents definitionList :: PandocMonad m => VwParser m Blocks definitionList = try $ B.definitionList <$> many1 (dlItemWithDT <|> dlItemWithoutDT) dlItemWithDT :: PandocMonad m => VwParser m (Inlines, [Blocks]) dlItemWithDT = do dt <- definitionTerm dds <- many definitionDef return (dt, dds) dlItemWithoutDT :: PandocMonad m => VwParser m (Inlines, [Blocks]) dlItemWithoutDT = do dds <- many1 definitionDef return (mempty, dds) definitionDef :: PandocMonad m => VwParser m Blocks definitionDef = try $ notFollowedBy definitionTerm >> many spaceChar >> (definitionDef1 <|> definitionDef2) definitionDef1 :: PandocMonad m => VwParser m Blocks definitionDef1 = try $ mempty <$ defMarkerE definitionDef2 :: PandocMonad m => VwParser m Blocks definitionDef2 = try $ B.plain <$> (defMarkerM >> (trimInlines . mconcat <$> many inline') <* newline) definitionTerm :: PandocMonad m => VwParser m Inlines definitionTerm = try $ do x <- definitionTerm1 <|> definitionTerm2 guard (stringify x /= "") return x definitionTerm1 :: PandocMonad m => VwParser m Inlines definitionTerm1 = try $ trimInlines . mconcat <$> manyTill inline' (try defMarkerE) definitionTerm2 :: PandocMonad m => VwParser m Inlines definitionTerm2 = try $ trimInlines . mconcat <$> manyTill inline' (try $ lookAhead (defMarkerM >> notFollowedBy hasDefMarkerM)) defMarkerM :: PandocMonad m => VwParser m Char defMarkerM = string "::" >> spaceChar defMarkerE :: PandocMonad m => VwParser m Char defMarkerE = string "::" >> newline hasDefMarkerM :: PandocMonad m => VwParser m Text hasDefMarkerM = manyTillChar (noneOf "\n") (try defMarkerM) preformatted :: PandocMonad m => VwParser m Blocks preformatted = try $ do many spaceChar >> string "{{{" attrText <- manyChar (noneOf "\n") lookAhead newline contents <- manyTillChar anyChar (try (char '\n' >> many spaceChar >> string "}}}" >> many spaceChar >> newline)) if (contents /= "") && (T.head contents == '\n') then return $ B.codeBlockWith (makeAttr attrText) (T.tail contents) else return $ B.codeBlockWith (makeAttr attrText) contents makeAttr :: Text -> Attr makeAttr s = let xs = splitTextBy (`elem` (" \t" :: String)) s in ("", syntax xs, mapMaybe nameValue xs) syntax :: [Text] -> [Text] syntax (s:_) | not $ T.isInfixOf "=" s = [s] syntax _ = [] nameValue :: Text -> Maybe (Text, Text) nameValue s = case splitTextBy (== '=') s of [a,b] | T.length b >= 2 , "\"" `T.isPrefixOf` b , "\"" `T.isSuffixOf` b -> Just (a, stripFirstAndLast b) _ -> Nothing displayMath :: PandocMonad m => VwParser m Blocks displayMath = try $ do many spaceChar >> string "{{$" mathTag <- option "" mathTagParser many space contents <- manyTillChar anyChar (try (char '\n' >> many spaceChar >> string "}}$" >> many spaceChar >> newline)) let contentsWithTags | mathTag == "" = contents | otherwise = "\\begin{" <> mathTag <> "}\n" <> contents <> "\n\\end{" <> mathTag <> "}" return $ B.para $ B.displayMath contentsWithTags mathTagLaTeX :: Text -> Text mathTagLaTeX s = case s of "equation" -> "" "equation*" -> "" "gather" -> "gathered" "gather*" -> "gathered" "multline" -> "gathered" "multline*" -> "gathered" "eqnarray" -> "aligned" "eqnarray*" -> "aligned" "align" -> "aligned" "align*" -> "aligned" "alignat" -> "aligned" "alignat*" -> "aligned" _ -> s mixedList :: PandocMonad m => VwParser m Blocks mixedList = try $ do ((bl:_), _) <- mixedList' (-1) return bl mixedList' :: PandocMonad m => Int -> VwParser m ([Blocks], Int) mixedList' prevInd = do (curInd, builder) <- option (-1, "na") (lookAhead listStart) if curInd < prevInd then return ([], curInd) else do listStart curLine <- listItemContent let listBuilder = if builder == "ul" then B.bulletList else B.orderedList (subList, lowInd) <- mixedList' curInd if lowInd >= curInd then do (sameIndList, endInd) <- mixedList' lowInd let curList = combineList curLine subList ++ sameIndList if curInd > prevInd then return ([listBuilder curList], endInd) else return (curList, endInd) else do let (curList, endInd) = (combineList curLine subList, lowInd) if curInd > prevInd then return ([listBuilder curList], endInd) else return (curList, endInd) plainInlineML' :: PandocMonad m => Inlines -> VwParser m Blocks plainInlineML' w = do xs <- many inlineML newline return $ B.plain $ trimInlines $ mconcat $ w:xs plainInlineML :: PandocMonad m => VwParser m Blocks plainInlineML = notFollowedBy listStart >> spaceChar >> plainInlineML' mempty listItemContent :: PandocMonad m => VwParser m Blocks listItemContent = try $ do w <- option mempty listTodoMarker x <- plainInlineML' w y <- many blocksThenInline z <- many blockML return $ mconcat $ x:y ++ z blocksThenInline :: PandocMonad m => VwParser m Blocks blocksThenInline = try $ do y <- many1 blockML x <- plainInlineML return $ mconcat $ y ++ [x] listTodoMarker :: PandocMonad m => VwParser m Inlines listTodoMarker = try $ do x <- (many spaceChar >> char '[') *> oneOf " .oOX" <* (char ']' >> spaceChar) return $ makeListMarkerSpan x makeListMarkerSpan :: Char -> Inlines makeListMarkerSpan x = let cl = case x of ' ' -> "done0" '.' -> "done1" 'o' -> "done2" 'O' -> "done3" 'X' -> "done4" _ -> "" in B.spanWith ("", [cl], []) mempty combineList :: Blocks -> [Blocks] -> [Blocks] combineList x [y] = case toList y of [BulletList z] -> [fromList $ toList x ++ [BulletList z]] [OrderedList attr z] -> [fromList $ toList x ++ [OrderedList attr z]] _ -> x:[y] combineList x xs = x:xs listStart :: PandocMonad m => VwParser m (Int, Text) listStart = try $ do s <- many spaceChar listType <- bulletListMarkers <|> orderedListMarkers spaceChar return (length s, listType) bulletListMarkers :: PandocMonad m => VwParser m Text bulletListMarkers = "ul" <$ (char '*' <|> char '-') orderedListMarkers :: PandocMonad m => VwParser m Text orderedListMarkers = ("ol" <$ choice (orderedListMarker Decimal Period:(($ OneParen) . orderedListMarker <$> [Decimal, LowerRoman, UpperRoman, LowerAlpha, UpperAlpha]))) <|> ("ol" <$ char '#') --many need trimInlines table :: PandocMonad m => VwParser m Blocks table = try $ do indent <- lookAhead (many spaceChar) (th, trs) <- table1 <|> table2 let tab = B.simpleTable th trs if indent == "" then return tab else return $ B.divWith ("", ["center"], []) tab -- table with header table1 :: PandocMonad m => VwParser m ([Blocks], [[Blocks]]) table1 = try $ do th <- tableRow many1 tableHeaderSeparator trs <- many tableRow return (th, trs) -- headerless table table2 :: PandocMonad m => VwParser m ([Blocks], [[Blocks]]) table2 = try $ do trs@(firstrow:_) <- many1 tableRow return (replicate (length firstrow) mempty, trs) tableHeaderSeparator :: PandocMonad m => VwParser m () tableHeaderSeparator = try $ do many spaceChar >> char '|' >> many1 (many1 (char '-') >> char '|') >> many spaceChar >> newline return () tableRow :: PandocMonad m => VwParser m [Blocks] tableRow = try $ do many spaceChar >> char '|' s <- lookAhead $ manyTill anyChar (try (char '|' >> many spaceChar >> newline)) guard $ not $ "||" `isInfixOf` ("|" ++ s ++ "|") many tableCell <* many spaceChar <* char '\n' tableCell :: PandocMonad m => VwParser m Blocks tableCell = try $ B.plain . trimInlines . mconcat <$> manyTill inline' (char '|') placeholder :: PandocMonad m => VwParser m () placeholder = try $ choice (ph <$> ["title", "date"]) <|> noHtmlPh <|> templatePh ph :: PandocMonad m => Text -> VwParser m () ph s = try $ do many spaceChar >> textStr (T.cons '%' s) >> spaceChar contents <- trimInlines . mconcat <$> manyTill inline (lookAhead newline) --use lookAhead because of placeholder in the whitespace parser let meta' = B.setMeta s contents nullMeta -- this order ensures that later values will be ignored in favor -- of earlier ones: updateState $ \st -> st { stateMeta = meta' <> stateMeta st } noHtmlPh :: PandocMonad m => VwParser m () noHtmlPh = try $ () <$ many spaceChar <* string "%nohtml" <* many spaceChar <* lookAhead newline templatePh :: PandocMonad m => VwParser m () templatePh = try $ () <$ many spaceChar <* string "%template" <* many (noneOf "\n") <* lookAhead newline -- inline parser inline :: PandocMonad m => VwParser m Inlines inline = choice $ whitespace endlineP:inlineList inlineList :: PandocMonad m => [VwParser m Inlines] inlineList = [ bareURL , todoMark , str , strong , emph , strikeout , code , link , image , inlineMath , tag , superscript , subscript , special ] -- inline parser without softbreaks or comment breaks inline' :: PandocMonad m => VwParser m Inlines inline' = choice $ whitespace':inlineList -- inline parser for blockquotes inlineBQ :: PandocMonad m => VwParser m Inlines inlineBQ = choice $ whitespace endlineBQ:inlineList -- inline parser for mixedlists inlineML :: PandocMonad m => VwParser m Inlines inlineML = choice $ whitespace endlineML:inlineList str :: PandocMonad m => VwParser m Inlines str = B.str <$> many1Char (noneOf $ spaceChars ++ specialChars) whitespace :: PandocMonad m => VwParser m () -> VwParser m Inlines whitespace endline = B.space <$ (skipMany1 spaceChar <|> try (newline >> (comment <|> placeholder))) <|> B.softbreak <$ endline whitespace' :: PandocMonad m => VwParser m Inlines whitespace' = B.space <$ skipMany1 spaceChar special :: PandocMonad m => VwParser m Inlines special = B.str <$> countChar 1 (oneOf specialChars) bareURL :: PandocMonad m => VwParser m Inlines bareURL = try $ do (orig, src) <- uri <|> emailAddress return $ B.link src "" (B.str orig) strong :: PandocMonad m => VwParser m Inlines strong = try $ do char '*' notFollowedBy (oneOf spaceChars) contents <- mconcat <$> many1Till inline' (try (char '*' *> notFollowedBy alphaNum)) guard $ lastMay (toList contents) /= Just Space return $ B.spanWith (makeId contents, [], []) mempty <> B.strong contents makeId :: Inlines -> Text makeId i = T.concat (stringify <$> toList i) emph :: PandocMonad m => VwParser m Inlines emph = try $ do char '_' notFollowedBy (oneOf spaceChars) contents <- mconcat <$> many1Till inline' (try (char '_' *> notFollowedBy alphaNum)) guard $ lastMay (toList contents) /= Just Space return $ B.emph contents strikeout :: PandocMonad m => VwParser m Inlines strikeout = try $ do string "~~" contents <- mconcat <$>many1Till inline' (string "~~") return $ B.strikeout contents code :: PandocMonad m => VwParser m Inlines code = try $ do char '`' contents <- many1TillChar (noneOf "\n") (char '`') return $ B.code contents superscript :: PandocMonad m => VwParser m Inlines superscript = try $ B.superscript . mconcat <$> (char '^' >> many1Till inline' (char '^')) subscript :: PandocMonad m => VwParser m Inlines subscript = try $ B.subscript . mconcat <$> (string ",," >> many1Till inline' (try $ string ",,")) link :: PandocMonad m => VwParser m Inlines link = try $ do string "[[" contents <- lookAhead $ manyTillChar anyChar (string "]]") if T.any (== '|') contents then do url <- manyTillChar anyChar $ char '|' lab <- mconcat <$> manyTill inline (string "]]") return $ B.linkWith (attr url) (procLink url) "" lab else do manyTill anyChar (string "]]") -- not using try here because [[hell]o]] is not rendered as a link in vimwiki return $ B.linkWith (attr contents) (procLink contents) "" (B.str contents) where attr t | isURI t = B.nullAttr | otherwise = (mempty, ["wikilink"], mempty) image :: PandocMonad m => VwParser m Inlines image = try $ do string "{{" contentText <- lookAhead $ manyTill (noneOf "\n") (try $ string "}}") images $ length $ filter (== '|') contentText images :: PandocMonad m => Int -> VwParser m Inlines images k | k == 0 = do imgurl <- manyTillChar anyChar (try $ string "}}") return $ B.image (procImgurl imgurl) "" (B.str "") | k == 1 = do imgurl <- manyTillChar anyChar (char '|') alt <- mconcat <$> manyTill inline (try $ string "}}") return $ B.image (procImgurl imgurl) "" alt | k == 2 = do imgurl <- manyTillChar anyChar (char '|') alt <- mconcat <$> manyTill inline (char '|') attrText <- manyTillChar anyChar (try $ string "}}") return $ B.imageWith (makeAttr attrText) (procImgurl imgurl) "" alt | otherwise = do imgurl <- manyTillChar anyChar (char '|') alt <- mconcat <$> manyTill inline (char '|') attrText <- manyTillChar anyChar (char '|') manyTill anyChar (try $ string "}}") return $ B.imageWith (makeAttr attrText) (procImgurl imgurl) "" alt procLink' :: Text -> Text procLink' s | T.take 6 s == "local:" = "file" <> T.drop 5 s | T.take 6 s == "diary:" = "diary/" <> T.drop 6 s | any (`T.isPrefixOf` s) [ "http:", "https:", "ftp:", "file:", "mailto:", "news:", "telnet:" ] = s | s == "" = "" | T.last s == '/' = s | otherwise = s procLink :: Text -> Text procLink s = procLink' x <> y where (x, y) = T.break (=='#') s procImgurl :: Text -> Text procImgurl s = if T.take 6 s == "local:" then "file" <> T.drop 5 s else s inlineMath :: PandocMonad m => VwParser m Inlines inlineMath = try $ B.math <$ char '$' <*> many1TillChar (noneOf "\n") (char '$') tag :: PandocMonad m => VwParser m Inlines tag = try $ do char ':' s <- manyTillChar (noneOf spaceChars) (try (char ':' >> lookAhead space)) guard $ not $ "::" `T.isInfixOf` (":" <> s <> ":") case splitTextBy (==':') s of [] -> fail "tag doesn't contain :" (x:xs) -> return $ mconcat $ makeTagSpan' x : (makeTagSpan <$> xs) todoMark :: PandocMonad m => VwParser m Inlines todoMark = try $ do string "TODO:" return $ B.spanWith ("", ["todo"], []) (B.str "TODO:") -- helper functions and parsers endlineP :: PandocMonad m => VwParser m () endlineP = () <$ try (newline <* nFBTTBSB <* notFollowedBy blockQuote) endlineBQ :: PandocMonad m => VwParser m () endlineBQ = () <$ try (newline <* nFBTTBSB <* string " ") endlineML :: PandocMonad m => VwParser m () endlineML = () <$ try (newline <* nFBTTBSB <* many1 spaceChar) --- nFBTTBSB is short for notFollowedByThingsThatBreakSoftBreaks nFBTTBSB :: PandocMonad m => VwParser m () nFBTTBSB = notFollowedBy newline <* notFollowedBy hrule <* notFollowedBy tableRow <* notFollowedBy header <* notFollowedBy listStart <* notFollowedBy preformatted <* notFollowedBy displayMath <* notFollowedBy hasDefMarker hasDefMarker :: PandocMonad m => VwParser m () hasDefMarker = () <$ manyTill (noneOf "\n") (string "::" >> oneOf spaceChars) makeTagSpan' :: Text -> Inlines makeTagSpan' s = B.spanWith (T.cons '-' s, [], []) (B.str "") <> B.spanWith (s, ["tag"], []) (B.str s) makeTagSpan :: Text -> Inlines makeTagSpan s = B.space <> makeTagSpan' s mathTagParser :: PandocMonad m => VwParser m Text mathTagParser = do s <- try $ lookAhead (char '%' >> manyTillChar (noneOf spaceChars) (try $ char '%' >> many (noneOf $ '%':spaceChars) >> space)) char '%' >> textStr s >> char '%' return $ mathTagLaTeX s ================================================ FILE: src/Text/Pandoc/Readers/XML.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -- | -- Module : Text.Pandoc.Readers.XML -- Copyright : Copyright (C) 2025- Massimiliano Farinella and John MacFarlane -- License : GNU GPL, version 2 or above -- -- Maintainer : Massimiliano Farinella -- Stability : WIP -- Portability : portable -- -- Conversion of (Pandoc specific) xml to 'Pandoc' document. module Text.Pandoc.Readers.XML (readXML) where import Control.Monad (msum) import Control.Monad.Except (throwError) import Control.Monad.State.Strict (StateT (runStateT), modify) import Data.Char (isSpace) import Data.Default (Default (..)) import qualified Data.List as L import qualified Data.Map as M import Data.Maybe (catMaybes, fromMaybe, mapMaybe) import qualified Data.Set as S (Set, fromList, member) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Lazy (fromStrict) import Data.Version (Version, makeVersion) import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Options import Text.Pandoc.Parsing (ToSources, toSources) import Text.Pandoc.Sources (sourcesToText) import Text.Pandoc.Version (pandocVersion) import Text.Pandoc.XML (lookupEntity) import Text.Pandoc.XML.Light import Text.Pandoc.XMLFormat import Text.Read (readMaybe) -- TODO: use xmlPath state to give better context when an error occurs type XMLReader m = StateT XMLReaderState m data XMLReaderState = XMLReaderState { xmlApiVersion :: Version, xmlMeta :: Meta, xmlContent :: [Content], xmlPath :: [Text] } deriving (Show) instance Default XMLReaderState where def = XMLReaderState { xmlApiVersion = pandocVersion, xmlMeta = mempty, xmlContent = [], xmlPath = ["root"] } readXML :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readXML _ inp = do let sources = toSources inp tree <- either (throwError . PandocXMLError "") return $ parseXMLContents (fromStrict . sourcesToText $ sources) (bs, st') <- flip runStateT (def {xmlContent = tree}) $ mapM parseBlock tree let blockList = toList $ concatMany bs return $ Pandoc (xmlMeta st') blockList concatMany :: [Many a] -> Many a concatMany = Many . mconcat . map unMany parseBlocks :: (PandocMonad m) => [Content] -> XMLReader m Blocks parseBlocks contents = concatMany <$> mapM parseBlock contents getBlocks :: (PandocMonad m) => Element -> XMLReader m Blocks getBlocks e = parseBlocks (elContent e) elementName :: Element -> Text elementName e = qName $ elName e attrValue :: Text -> Element -> Text attrValue attr = fromMaybe "" . maybeAttrValue attr maybeAttrValue :: Text -> Element -> Maybe Text maybeAttrValue attr elt = lookupAttrBy (\x -> qName x == attr) (elAttribs elt) parseBlock :: (PandocMonad m) => Content -> XMLReader m Blocks parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE parseBlock (Text (CData _ s _)) = if T.all isSpace s then return mempty else do throwError $ PandocXMLError "" "non-space characters out of inline context" parseBlock (CRef x) = do throwError $ PandocXMLError "" ("reference \"" <> x <> "\" out of inline context") parseBlock (Elem e) = do let name = elementName e in case (name) of "Pandoc" -> parsePandoc "?xml" -> return mempty "blocks" -> getBlocks e "meta" -> let entry_els = childrenNamed tgNameMetaMapEntry e in do entries <- catMaybes <$> mapM parseMetaMapEntry entry_els mapM_ (uncurry addMeta) entries return mempty "Para" -> para <$> getInlines (elContent e) "Plain" -> do ils <- getInlines (elContent e) return $ singleton . Plain . toList $ ils "Header" -> (headerWith attr level) <$> getInlines (elContent e) where level = textToInt (attrValue atNameLevel e) 1 attr = filterAttrAttributes [atNameLevel] $ attrFromElement e "HorizontalRule" -> return horizontalRule "BlockQuote" -> do contents <- getBlocks e return $ blockQuote contents "Div" -> do contents <- getBlocks e return $ divWith (attrFromElement e) contents "BulletList" -> do items <- getListItems e return $ bulletList items "OrderedList" -> do items <- getListItems e return $ orderedListWith (getListAttributes e) items "DefinitionList" -> do let items_contents = getContentsOfElements (isElementNamed tgNameDefListItem) (elContent e) items <- mapM parseDefinitionListItem items_contents return $ definitionList items "Figure" -> do let attr = attrFromElement e (maybe_caption_el, contents) = partitionFirstChildNamed "Caption" $ elContent e figure_caption <- case (maybe_caption_el) of Just (caption_el) -> parseCaption $ elContent caption_el Nothing -> pure emptyCaption blocks <- parseBlocks contents return $ figureWith attr figure_caption blocks "CodeBlock" -> do let attr = attrFromElement e return $ codeBlockWith attr $ strContentRecursive e "RawBlock" -> do let format = (attrValue atNameFormat e) return $ rawBlock format $ strContentRecursive e "LineBlock" -> do lins <- mapM getInlines (contentsOfChildren tgNameLineItem (elContent e)) return $ lineBlock lins "Table" -> do -- TODO: check unexpected items let attr = attrFromElement e (maybe_caption_el, after_caption) = partitionFirstChildNamed "Caption" $ elContent e children = elementsWithNames (S.fromList [tgNameColspecs, "TableHead", "TableBody", "TableFoot"]) after_caption is_element tag el = tag == elementName el colspecs <- getColspecs $ L.find (is_element tgNameColspecs) children tbs <- getTableBodies $ filter (is_element "TableBody") children th <- getTableHead $ L.find (is_element "TableHead") children tf <- getTableFoot $ L.find (is_element "TableFoot") children capt <- parseMaybeCaptionElement maybe_caption_el case colspecs of Nothing -> return mempty Just cs -> return $ fromList [Table attr capt cs th tbs tf] _ -> do throwError $ PandocXMLError "" ("unexpected element \"" <> name <> "\" in blocks context") where parsePandoc = do let version = maybeAttrValue atNameApiVersion e apiversion = case (version) of Just (v) -> makeVersion $ map (read . T.unpack) $ T.splitOn "," v Nothing -> pandocVersion in modify $ \st -> st {xmlApiVersion = apiversion} getBlocks e getListItems :: (PandocMonad m) => Element -> XMLReader m [Blocks] getListItems e = let items_els = childrenNamed tgNameListItem e in do mapM getBlocks items_els getContentsOfElements :: (Content -> Bool) -> [Content] -> [[Content]] getContentsOfElements filter_element contents = mapMaybe element_contents $ filter filter_element contents where element_contents :: Content -> Maybe [Content] element_contents c = case (c) of Elem e -> Just (elContent e) _ -> Nothing strContentRecursive :: Element -> Text strContentRecursive = strContent . (\e' -> e' {elContent = map elementToStr $ elContent e'}) elementToStr :: Content -> Content elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing elementToStr x = x textToInt :: Text -> Int -> Int textToInt t deflt = let safe_to_int :: Text -> Maybe Int safe_to_int s = readMaybe $ T.unpack s in case (safe_to_int t) of Nothing -> deflt Just (n) -> n parseInline :: (PandocMonad m) => Content -> XMLReader m Inlines parseInline (Text (CData _ s _)) = return $ text s parseInline (CRef ref) = return $ maybe (text $ T.toUpper ref) text $ lookupEntity ref parseInline (Elem e) = let name = elementName e in case (name) of "Space" -> let count = textToInt (attrValue atNameSpaceCount e) 1 in return $ fromList $ replicate count Space "Str" -> return $ fromList [Str $ attrValue atNameStrContent e] "Emph" -> innerInlines emph "Strong" -> innerInlines strong "Strikeout" -> innerInlines strikeout "Subscript" -> innerInlines subscript "Superscript" -> innerInlines superscript "Underline" -> innerInlines underline "SoftBreak" -> return softbreak "LineBreak" -> return linebreak "SmallCaps" -> innerInlines smallcaps "Quoted" -> case (attrValue atNameQuoteType e) of "SingleQuote" -> innerInlines singleQuoted _ -> innerInlines doubleQuoted "Math" -> case (attrValue atNameMathType e) of "DisplayMath" -> pure $ displayMath $ strContentRecursive e _ -> pure $ math $ strContentRecursive e "Span" -> innerInlines $ spanWith (attrFromElement e) "Code" -> do let attr = attrFromElement e return $ codeWith attr $ strContentRecursive e "Link" -> innerInlines $ linkWith attr url title where url = attrValue atNameLinkUrl e title = attrValue atNameTitle e attr = filterAttrAttributes [atNameLinkUrl, atNameTitle] $ attrFromElement e "Image" -> innerInlines $ imageWith attr url title where url = attrValue atNameImageUrl e title = attrValue atNameTitle e attr = filterAttrAttributes [atNameImageUrl, atNameTitle] $ attrFromElement e "RawInline" -> do let format = (attrValue atNameFormat e) return $ rawInline format $ strContentRecursive e "Note" -> do contents <- getBlocks e return $ note contents "Cite" -> let (maybe_citations_el, contents) = partitionFirstChildNamed tgNameCitations $ elContent e in case (maybe_citations_el) of Just citations_el -> do citations <- parseCitations $ elContent citations_el (innerInlines' contents) $ cite citations Nothing -> getInlines contents _ -> do throwError $ PandocXMLError "" ("unexpected element \"" <> name <> "\" in inline context") where innerInlines' contents f = f . concatMany <$> mapM parseInline contents innerInlines f = innerInlines' (elContent e) f getInlines :: (PandocMonad m) => [Content] -> XMLReader m Inlines getInlines contents = concatMany <$> mapM parseInline contents getListAttributes :: Element -> ListAttributes getListAttributes e = (start, style, delim) where start = textToInt (attrValue atNameStart e) 1 style = case (attrValue atNameNumberStyle e) of "Example" -> Example "Decimal" -> Decimal "LowerRoman" -> LowerRoman "UpperRoman" -> UpperRoman "LowerAlpha" -> LowerAlpha "UpperAlpha" -> UpperAlpha _ -> DefaultStyle delim = case (attrValue atNameNumberDelim e) of "Period" -> Period "OneParen" -> OneParen "TwoParens" -> TwoParens _ -> DefaultDelim contentsOfChildren :: Text -> [Content] -> [[Content]] contentsOfChildren tag contents = mapMaybe childrenElementWithTag contents where childrenElementWithTag :: Content -> Maybe [Content] childrenElementWithTag c = case (c) of (Elem e) -> if tag == elementName e then Just (elContent e) else Nothing _ -> Nothing alignmentFromText :: Text -> Alignment alignmentFromText t = case t of "AlignLeft" -> AlignLeft "AlignRight" -> AlignRight "AlignCenter" -> AlignCenter _ -> AlignDefault getColWidth :: Text -> ColWidth getColWidth txt = case reads (T.unpack txt) of [(value, "")] -> if value == 0.0 then ColWidthDefault else ColWidth value _ -> ColWidthDefault getColspecs :: (PandocMonad m) => Maybe Element -> XMLReader m (Maybe [ColSpec]) getColspecs Nothing = pure Nothing getColspecs (Just cs) = do return $ Just $ map elementToColSpec (childrenNamed "ColSpec" cs) where elementToColSpec e = (alignmentFromText $ attrValue atNameAlignment e, getColWidth $ attrValue atNameColWidth e) getTableBody :: (PandocMonad m) => Element -> XMLReader m (Maybe TableBody) getTableBody body_el = do let attr = filterAttrAttributes [atNameRowHeadColumns] $ attrFromElement body_el bh = childrenNamed tgNameBodyHeader body_el bb = childrenNamed tgNameBodyBody body_el headcols = textToInt (attrValue atNameRowHeadColumns body_el) 0 hrows <- mconcat <$> mapM getRows bh brows <- mconcat <$> mapM getRows bb return $ Just $ TableBody attr (RowHeadColumns headcols) hrows brows getTableBodies :: (PandocMonad m) => [Element] -> XMLReader m [TableBody] getTableBodies body_elements = do catMaybes <$> mapM getTableBody body_elements getTableHead :: (PandocMonad m) => Maybe Element -> XMLReader m TableHead getTableHead maybe_e = case maybe_e of Just e -> do let attr = attrFromElement e rows <- getRows e return $ TableHead attr rows Nothing -> return $ TableHead nullAttr [] getTableFoot :: (PandocMonad m) => Maybe Element -> XMLReader m TableFoot getTableFoot maybe_e = case maybe_e of Just e -> do let attr = attrFromElement e rows <- getRows e return $ TableFoot attr rows Nothing -> return $ TableFoot nullAttr [] getCell :: (PandocMonad m) => Element -> XMLReader m Cell getCell c = do let alignment = alignmentFromText $ attrValue atNameAlignment c rowspan = RowSpan $ textToInt (attrValue atNameRowspan c) 1 colspan = ColSpan $ textToInt (attrValue atNameColspan c) 1 attr = filterAttrAttributes [atNameAlignment, atNameRowspan, atNameColspan] $ attrFromElement c blocks <- getBlocks c return $ Cell attr alignment rowspan colspan (toList blocks) getRows :: (PandocMonad m) => Element -> XMLReader m [Row] getRows e = mapM getRow $ childrenNamed "Row" e where getRow r = do cells <- mapM getCell (childrenNamed "Cell" r) return $ Row (attrFromElement r) cells parseCitations :: (PandocMonad m) => [Content] -> XMLReader m [Citation] parseCitations contents = do maybecitations <- mapM getCitation contents return $ catMaybes maybecitations where getCitation :: (PandocMonad m) => Content -> XMLReader m (Maybe Citation) getCitation content = case (content) of (Elem e) -> if qName (elName e) == "Citation" then do p <- inlinesOfChildrenNamed tgNameCitationPrefix e s <- inlinesOfChildrenNamed tgNameCitationSuffix e return $ Just ( Citation { citationId = attrValue "id" e, citationPrefix = toList p, citationSuffix = toList s, citationMode = case (attrValue atNameCitationMode e) of "AuthorInText" -> AuthorInText "SuppressAuthor" -> SuppressAuthor _ -> NormalCitation, citationNoteNum = textToInt (attrValue atNameCitationNoteNum e) 0, citationHash = textToInt (attrValue atNameCitationHash e) 0 } ) else do return Nothing _ -> do return Nothing where inlinesOfChildrenNamed tag e = getInlines $ concatMap (\e' -> elContent e') (childrenNamed tag e) parseMaybeCaptionElement :: (PandocMonad m) => Maybe Element -> XMLReader m Caption parseMaybeCaptionElement Nothing = pure emptyCaption parseMaybeCaptionElement (Just e) = parseCaption $ elContent e parseCaption :: (PandocMonad m) => [Content] -> XMLReader m Caption parseCaption contents = let (maybe_shortcaption_el, caption_contents) = partitionFirstChildNamed tgNameShortCaption contents in do blocks <- parseBlocks caption_contents case (maybe_shortcaption_el) of Just shortcaption_el -> do short_caption <- getInlines (elContent shortcaption_el) return $ caption (Just $ toList short_caption) blocks Nothing -> return $ caption Nothing blocks parseDefinitionListItem :: (PandocMonad m) => [Content] -> XMLReader m (Inlines, [Blocks]) parseDefinitionListItem contents = do let term_contents = getContentsOfElements (isElementNamed tgNameDefListTerm) contents defs_elements = elementContents $ filter (isElementNamed tgNameDefListDef) contents term_inlines <- getInlines (concat term_contents) defs <- mapM getBlocks defs_elements return (term_inlines, defs) elementContents :: [Content] -> [Element] elementContents contents = mapMaybe toElement contents where toElement :: Content -> Maybe Element toElement (Elem e) = Just e toElement _ = Nothing isElementNamed :: Text -> Content -> Bool isElementNamed t c = case (c) of Elem e -> t == elementName e _ -> False childrenNamed :: Text -> Element -> [Element] childrenNamed tag e = elementContents $ filter (isElementNamed tag) (elContent e) elementsWithNames :: S.Set Text -> [Content] -> [Element] elementsWithNames tags contents = mapMaybe isElementWithNameInSet contents where isElementWithNameInSet c = case (c) of Elem el -> if (elementName el) `S.member` tags then Just el else Nothing _ -> Nothing partitionFirstChildNamed :: Text -> [Content] -> (Maybe Element, [Content]) partitionFirstChildNamed tag contents = case (contents) of (Text (CData _ s _) : rest) -> if T.all isSpace s then partitionFirstChildNamed tag rest else (Nothing, contents) (Elem e : rest) -> if tag == elementName e then (Just e, rest) else (Nothing, contents) _ -> (Nothing, contents) type PandocAttr = (Text, [Text], [(Text, Text)]) filterAttributes :: S.Set Text -> [(Text, Text)] -> [(Text, Text)] filterAttributes to_be_removed a = filter keep_attr a where keep_attr (k, _) = not (k `S.member` to_be_removed) filterAttrAttributes :: [Text] -> PandocAttr -> PandocAttr filterAttrAttributes to_be_removed (idn, classes, a) = (idn, classes, filtered) where filtered = filterAttributes (S.fromList to_be_removed) a attrFromElement :: Element -> PandocAttr attrFromElement e = filterAttrAttributes ["id", "class"] (idn, classes, attributes) where idn = attrValue "id" e classes = T.words $ attrValue "class" e attributes = map (\a -> (qName $ attrKey a, attrVal a)) $ elAttribs e addMeta :: (PandocMonad m) => (ToMetaValue a) => Text -> a -> XMLReader m () addMeta field val = modify (setMeta field val) instance HasMeta XMLReaderState where setMeta field v s = s {xmlMeta = setMeta field v (xmlMeta s)} deleteMeta field s = s {xmlMeta = deleteMeta field (xmlMeta s)} parseMetaMapEntry :: (PandocMonad m) => Element -> XMLReader m (Maybe (Text, MetaValue)) parseMetaMapEntry e = let key = attrValue atNameMetaMapEntryKey e in case (key) of "" -> pure Nothing k -> do maybe_value <- parseMetaMapEntryContents $ elContent e case (maybe_value) of Nothing -> return Nothing Just v -> return $ Just (k, v) parseMetaMapEntryContents :: (PandocMonad m) => [Content] -> XMLReader m (Maybe MetaValue) parseMetaMapEntryContents cs = msum <$> mapM parseMeta cs getElementText :: Element -> T.Text getElementText el = T.concat [cdData c | Text c <- elContent el] parseMeta :: (PandocMonad m) => Content -> XMLReader m (Maybe MetaValue) parseMeta (Text (CData CDataRaw _ _)) = return Nothing parseMeta (Text (CData _ s _)) = if T.all isSpace s then return Nothing else do throwError $ PandocXMLError "" "non-space characters out of inline context in metadata" parseMeta (CRef x) = throwError $ PandocXMLError "" ("reference \"" <> x <> "\" out of inline context") parseMeta (Elem e) = do let name = elementName e in case (name) of "MetaBool" -> case (attrValue atNameMetaBoolValue e) of "true" -> return $ Just $ MetaBool True _ -> return $ Just $ MetaBool False "MetaString" -> return $ Just $ MetaString (getElementText e) "MetaInlines" -> do inlines <- getInlines (elContent e) return $ Just $ MetaInlines $ toList inlines "MetaBlocks" -> do blocks <- getBlocks e return $ Just $ MetaBlocks $ toList blocks "MetaList" -> do maybe_items <- mapM parseMeta $ elContent e let items = catMaybes maybe_items in -- TODO: report empty MetaList? return $ Just $ MetaList items "MetaMap" -> let entry_els = childrenNamed tgNameMetaMapEntry e in do entries <- catMaybes <$> mapM parseMetaMapEntry entry_els if null entries then -- TODO: report empty MetaMap return Nothing else return $ Just $ MetaMap $ M.fromList entries _ -> do throwError $ PandocXMLError "" ("unexpected element \"" <> name <> "\" in metadata") ================================================ FILE: src/Text/Pandoc/Readers/Xlsx/Cells.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Xlsx.Cells Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Cell types and parsing for XLSX. -} module Text.Pandoc.Readers.Xlsx.Cells ( CellRef(..) , XlsxCell(..) , CellValue(..) , parseCellRef ) where import qualified Data.Text as T import Data.Text (Text) import Data.Char (ord, isAlpha) import Text.Read (readMaybe) -- | Cell reference (A1 notation) data CellRef = CellRef { cellRefCol :: Int -- 1-based (A=1, B=2, ..., AA=27) , cellRefRow :: Int -- 1-based } deriving (Show, Eq, Ord) -- | Cell value types data CellValue = TextValue Text | NumberValue Double | EmptyValue deriving (Show, Eq) -- | Parsed cell data XlsxCell = XlsxCell { cellRef :: CellRef , cellValue :: CellValue , cellBold :: Bool , cellItalic :: Bool } deriving (Show) -- | Parse cell reference (A1 → CellRef) parseCellRef :: Text -> Either Text CellRef parseCellRef ref = do let (colStr, rowStr) = T.span isAlpha ref row <- case readMaybe (T.unpack rowStr) of Just r | r > 0 -> Right r _ -> Left $ "Invalid row: " <> rowStr col <- parseColumn colStr return $ CellRef col row -- | Parse column (A=1, Z=26, AA=27, etc.) parseColumn :: Text -> Either Text Int parseColumn colStr | T.null colStr = Left "Empty column" | otherwise = Right $ T.foldl' (\acc c -> acc * 26 + (ord c - ord 'A' + 1)) 0 colStr ================================================ FILE: src/Text/Pandoc/Readers/Xlsx/Parse.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Xlsx.Parse Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Parsing of XLSX archive to intermediate representation. -} module Text.Pandoc.Readers.Xlsx.Parse ( Xlsx(..) , XlsxWorkbook(..) , XlsxSheet(..) , SheetId(..) , SharedStrings , Styles(..) , FontInfo(..) , archiveToXlsx ) where import Codec.Archive.Zip (Archive, Entry, findEntryByPath, fromEntry) import Data.List (find) import qualified Data.Map.Strict as M import Data.Maybe (mapMaybe, fromMaybe) import qualified Data.Text as T import qualified Data.Text.Lazy.Encoding as TL import Data.Text (Text) import qualified Data.Vector as V import System.FilePath (splitFileName) import Text.Pandoc.Readers.OOXML.Shared import Text.Pandoc.Readers.Xlsx.Cells import Text.Pandoc.XML.Light import Text.Read (readMaybe) -- | Sheet identifier newtype SheetId = SheetId Int deriving (Show, Eq, Ord) -- | Shared strings table (Vector for O(1) lookup) type SharedStrings = V.Vector Text -- | Font information data FontInfo = FontInfo { fontBold :: Bool , fontItalic :: Bool , fontUnderline :: Bool } deriving (Show) -- | Style information data Styles = Styles { styleFonts :: V.Vector FontInfo } deriving (Show) -- | Complete XLSX document data Xlsx = Xlsx { xlsxWorkbook :: XlsxWorkbook , xlsxSheets :: [XlsxSheet] , xlsxSharedStrings :: SharedStrings , xlsxStyles :: Styles } deriving (Show) -- | Workbook information data XlsxWorkbook = XlsxWorkbook { workbookSheetNames :: [(SheetId, Text, Text)] -- (id, name, relId) } deriving (Show) -- | Individual worksheet data XlsxSheet = XlsxSheet { sheetId :: SheetId , sheetName :: Text , sheetCells :: M.Map CellRef XlsxCell } deriving (Show) -- | Parse XLSX archive archiveToXlsx :: Archive -> Either Text Xlsx archiveToXlsx archive = do -- Find and parse workbook.xml workbookPath <- getWorkbookXmlPath archive workbookElem <- loadXMLFromArchive archive workbookPath workbook <- parseWorkbook workbookElem `addContext` ("Parsing workbook.xml from: " <> T.pack workbookPath) -- Load workbook relationships workbookRels <- loadRelationships archive (relsPathFor workbookPath) -- Parse shared strings (look for sharedStrings relationship) sharedStrings <- case findRelWithTarget workbookRels "sharedStrings" of Just (_, target) -> do let path = "xl/" ++ T.unpack target el <- loadXMLFromArchive archive path parseSharedStrings el Nothing -> Right V.empty -- Parse styles styles <- case findRelWithTarget workbookRels "styles" of Just (_, target) -> do let path = "xl/" ++ T.unpack target el <- loadXMLFromArchive archive path parseStyles el Nothing -> Right $ Styles V.empty -- Parse worksheets sheets <- mapM (\sheetInfo -> parseSheet archive workbookRels sharedStrings styles sheetInfo) (workbookSheetNames workbook) return $ Xlsx workbook sheets sharedStrings styles -- | Find workbook.xml via root relationships getWorkbookXmlPath :: Archive -> Either Text FilePath getWorkbookXmlPath archive = do relsEntry <- maybeToEither "Missing _rels/.rels" $ findEntryByPath "_rels/.rels" archive relsElem <- parseXMLFromEntry relsEntry let relElems = onlyElems $ elContent relsElem case find isOfficeDocRel relElems of Nothing -> Left "No workbook.xml relationship found" Just rel -> do target <- maybeToEither "Missing Target" $ findAttr (unqual "Target") rel return $ T.unpack target where isOfficeDocRel el = case (findAttr (unqual "Type") el, findAttr (unqual "Target") el) of (Just relType, Just target) -> "officeDocument" `T.isInfixOf` relType && "workbook" `T.isInfixOf` target _ -> False -- | Parse workbook.xml parseWorkbook :: Element -> Either Text XlsxWorkbook parseWorkbook wbElem = do let ns = elemToNameSpaces wbElem -- Find sheets element (match by local name only) sheets <- maybeToEither "Missing " $ find (\e -> qName (elName e) == "sheets") (onlyElems $ elContent wbElem) let sheetElems = filter (\e -> qName (elName e) == "sheet") (onlyElems $ elContent sheets) sheetRefs <- mapM (parseSheetRef ns) (zip [1..] sheetElems) return $ XlsxWorkbook sheetRefs parseSheetRef :: NameSpaces -> (Int, Element) -> Either Text (SheetId, Text, Text) parseSheetRef ns (idx, sheetElem) = do let name = fromMaybe ("Sheet" <> T.pack (show idx)) $ findAttr (unqual "name") sheetElem relId <- maybeToEither "Missing r:id" $ findAttrByName ns "r" "id" sheetElem return (SheetId idx, name, relId) -- | Parse shared strings parseSharedStrings :: Element -> Either Text SharedStrings parseSharedStrings sstElem = do let siElems = filter (\e -> qName (elName e) == "si") (onlyElems $ elContent sstElem) strings = map extractString siElems return $ V.fromList strings where extractString siElem = case find (\e -> qName (elName e) == "t") (onlyElems $ elContent siElem) of Just tElem -> strContent tElem Nothing -> getAllText siElem -- | Parse styles (fonts only for MVP) parseStyles :: Element -> Either Text Styles parseStyles stylesElem = do -- Parse fonts (match by local name) let fontsElem = find (\e -> qName (elName e) == "fonts") (onlyElems $ elContent stylesElem) fontElems = maybe [] (\fe -> filter (\e -> qName (elName e) == "font") (onlyElems $ elContent fe)) fontsElem fonts = V.fromList $ map (parseFont mempty) fontElems return $ Styles fonts parseFont :: NameSpaces -> Element -> FontInfo parseFont _ns fontElem = FontInfo { fontBold = any (\e -> qName (elName e) == "b") (onlyElems $ elContent fontElem) , fontItalic = any (\e -> qName (elName e) == "i") (onlyElems $ elContent fontElem) , fontUnderline = any (\e -> qName (elName e) == "u") (onlyElems $ elContent fontElem) } -- | Parse individual worksheet parseSheet :: Archive -> [(Text, Text)] -> SharedStrings -> Styles -> (SheetId, Text, Text) -> Either Text XlsxSheet parseSheet archive rels sharedStrings styles (sid, name, relId) = do target <- maybeToEither ("Sheet relationship not found: " <> relId) $ lookup relId rels let sheetPath = "xl/" ++ T.unpack target sheetElem <- loadXMLFromArchive archive sheetPath cells <- parseSheetCells sheetElem sharedStrings styles return $ XlsxSheet sid name cells -- | Parse sheet cells parseSheetCells :: Element -> SharedStrings -> Styles -> Either Text (M.Map CellRef XlsxCell) parseSheetCells sheetElem sharedStrings styles = do -- Find sheetData by local name case find (\e -> qName (elName e) == "sheetData") (onlyElems $ elContent sheetElem) of Nothing -> return M.empty Just sheetData -> do let rowElems = filter (\e -> qName (elName e) == "row") (onlyElems $ elContent sheetData) cellElems = concatMap (\r -> filter (\e -> qName (elName e) == "c") (onlyElems $ elContent r)) rowElems cells = mapMaybe (parseCell sharedStrings styles) cellElems return $ M.fromList [(cellRef c, c) | c <- cells] -- | Parse individual cell parseCell :: SharedStrings -> Styles -> Element -> Maybe XlsxCell parseCell sharedStrings styles cElem = do -- Get cell reference refText <- findAttr (unqual "r") cElem cellRefParsed <- either (const Nothing) Just $ parseCellRef refText -- Get cell type (default to number if missing) let cellType = fromMaybe "" $ findAttr (unqual "t") cElem styleIdx = findAttr (unqual "s") cElem >>= readMaybe . T.unpack -- Get value (match by local name) let vElem = find (\e -> qName (elName e) == "v") (onlyElems $ elContent cElem) vText = maybe "" strContent vElem -- Parse value based on type let value = if cellType == "s" then -- Shared string case readMaybe (T.unpack vText) of Just idx | idx >= 0 && idx < V.length sharedStrings -> TextValue (sharedStrings V.! idx) _ -> EmptyValue else if T.null vText then EmptyValue else -- Number case readMaybe (T.unpack vText) of Just n -> NumberValue n Nothing -> TextValue vText -- Get formatting from style let (bold, italic) = case styleIdx of Just idx | idx >= 0 && idx < V.length (styleFonts styles) -> let font = styleFonts styles V.! idx in (fontBold font, fontItalic font) _ -> (False, False) return $ XlsxCell cellRefParsed value bold italic -- Helper functions loadXMLFromArchive :: Archive -> FilePath -> Either Text Element loadXMLFromArchive archive path = do entry <- maybeToEither ("Entry not found: " <> T.pack path) $ findEntryByPath path archive parseXMLFromEntry entry parseXMLFromEntry :: Entry -> Either Text Element parseXMLFromEntry entry = let lazyText = TL.decodeUtf8 $ fromEntry entry in parseXMLElement lazyText loadRelationships :: Archive -> FilePath -> Either Text [(Text, Text)] loadRelationships archive relsPath = case findEntryByPath relsPath archive of Nothing -> Right [] Just entry -> do relsElem <- parseXMLFromEntry entry let relElems = onlyElems $ elContent relsElem return $ mapMaybe extractRel relElems where extractRel el = do relId <- findAttr (unqual "Id") el target <- findAttr (unqual "Target") el return (relId, target) relsPathFor :: FilePath -> FilePath relsPathFor path = let (dir, file) = splitFileName path in dir ++ "/_rels/" ++ file ++ ".rels" findRelWithTarget :: [(Text, Text)] -> Text -> Maybe (Text, Text) findRelWithTarget rels targetName = find (\(_, target) -> targetName `T.isInfixOf` target) rels maybeToEither :: Text -> Maybe a -> Either Text a maybeToEither err Nothing = Left err maybeToEither _ (Just x) = Right x getAllText :: Element -> Text getAllText el = let textFromContent (Text cdata) = cdData cdata textFromContent (Elem e) = getAllText e textFromContent _ = "" texts = map textFromContent (elContent el) in T.unwords $ filter (not . T.null) texts addContext :: Either Text a -> Text -> Either Text a addContext (Right x) _ = Right x addContext (Left err) ctx = Left (err <> " (context: " <> ctx <> ")") ================================================ FILE: src/Text/Pandoc/Readers/Xlsx/Sheets.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Xlsx.Sheets Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Conversion of XLSX sheets to Pandoc AST. -} module Text.Pandoc.Readers.Xlsx.Sheets ( xlsxToOutput ) where import qualified Data.Map.Strict as M import qualified Data.Text as T import Data.List (sort, dropWhileEnd) import Data.Char (isSpace) import Text.Pandoc.Definition import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Readers.Xlsx.Parse import Text.Pandoc.Readers.Xlsx.Cells import qualified Text.Pandoc.Builder as B -- | Convert XLSX to Pandoc output xlsxToOutput :: ReaderOptions -> Xlsx -> (Meta, [Block]) xlsxToOutput _opts xlsx = let sheets = xlsxSheets xlsx sheetBlocks = concatMap sheetToBlocks sheets in (mempty, sheetBlocks) -- | Convert sheet to blocks (header + table) sheetToBlocks :: XlsxSheet -> [Block] sheetToBlocks sheet = let SheetId n = sheetId sheet name = sheetName sheet sheetIdent = "sheet-" <> T.pack (show n) header = Header 2 (sheetIdent, [], []) (B.toList (B.text name)) -- Convert cells to table tableBlock = case cellsToTable sheet of Just tbl -> [tbl] Nothing -> [] -- Empty sheet in header : tableBlock -- | Convert cells to Pandoc Table cellsToTable :: XlsxSheet -> Maybe Block cellsToTable sheet | M.null (sheetCells sheet) = Nothing | otherwise = let cells = sheetCells sheet -- Get bounds refs = sort $ M.keys cells minCol = minimum $ map cellRefCol refs maxCol = maximum $ map cellRefCol refs minRow = minimum $ map cellRefRow refs maxRow = maximum $ map cellRefRow refs -- Build dense grid grid = [ [ M.lookup (CellRef col row) cells | col <- [minCol..maxCol] ] | row <- [minRow..maxRow] ] -- First row is header (simple heuristic) (headerRow, bodyRows) = case grid of (h:bs) -> (h, bs) [] -> ([], []) -- Filter out trailing empty rows (rows with only whitespace) filteredBodyRows = dropWhileEnd isEmptyRow bodyRows makeCell mcell = case mcell of Just cell -> let inlines = cellToInlines cell in Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) [Plain inlines] Nothing -> Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) [Plain []] numCols = length headerRow colSpec = replicate numCols (AlignDefault, ColWidthDefault) thead = TableHead nullAttr [Row nullAttr $ map makeCell headerRow] tbody = [TableBody nullAttr 0 [] $ map (Row nullAttr . map makeCell) filteredBodyRows] tfoot = TableFoot nullAttr [] in Just $ Table nullAttr (Caption Nothing []) colSpec thead tbody tfoot -- | Check if a row contains only whitespace or empty cells isEmptyRow :: [Maybe XlsxCell] -> Bool isEmptyRow = all isEmptyCell where isEmptyCell Nothing = True isEmptyCell (Just cell) = case cellValue cell of EmptyValue -> True TextValue t -> T.all isSpace t NumberValue _ -> False -- | Convert cell to Pandoc inlines cellToInlines :: XlsxCell -> [Inline] cellToInlines cell = let base = case cellValue cell of TextValue t -> B.toList $ B.text t NumberValue n -> [Str $ T.pack $ show n] EmptyValue -> [] applyBold inls = if cellBold cell then [Strong inls] else inls applyItalic inls = if cellItalic cell then [Emph inls] else inls in applyItalic $ applyBold base ================================================ FILE: src/Text/Pandoc/Readers/Xlsx.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Readers.Xlsx Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Conversion of XLSX (Excel spreadsheet) documents to 'Pandoc' document. -} module Text.Pandoc.Readers.Xlsx (readXlsx) where import qualified Data.ByteString.Lazy as B import qualified Data.Text as T import Codec.Archive.Zip (toArchiveOrFail) import Control.Monad.Except (throwError) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition (Pandoc(..)) import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Readers.Xlsx.Parse (archiveToXlsx) import Text.Pandoc.Readers.Xlsx.Sheets (xlsxToOutput) -- | Read XLSX file into Pandoc AST readXlsx :: PandocMonad m => ReaderOptions -> B.ByteString -> m Pandoc readXlsx opts bytes = case toArchiveOrFail bytes of Right archive -> case archiveToXlsx archive of Right xlsx -> do let (meta, blocks) = xlsxToOutput opts xlsx return $ Pandoc meta blocks Left err -> throwError $ PandocParseError $ "Failed to parse XLSX: " <> err Left err -> throwError $ PandocParseError $ "Failed to unpack XLSX archive: " <> T.pack err ================================================ FILE: src/Text/Pandoc/Readers.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE MonoLocalBinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.Readers Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable This helper module exports the readers. Note: all of the readers assume that the input text has @'\n'@ line endings. So if you get your input text from a web form, you should remove @'\r'@ characters using @filter (/='\r')@. -} module Text.Pandoc.Readers ( -- * Readers: converting /to/ Pandoc format Reader (..) , readers , readAsciiDoc , readDocx , readPptx , readXlsx , readODT , readMarkdown , readCommonMark , readCreole , readDokuWiki , readMediaWiki , readVimwiki , readRST , readOrg , readLaTeX , readHtml , readJATS , readJira , readTextile , readDocBook , readOPML , readHaddock , readNative , readJSON , readTWiki , readTikiWiki , readTxt2Tags , readEPUB , readMuse , readMan , readMdoc , readFB2 , readIpynb , readCSV , readTSV , readCslJson , readBibTeX , readBibLaTeX , readEndNoteXML , readRIS , readRTF , readTypst , readDjot , readPod , readXML -- * Miscellaneous , getReader , getDefaultExtensions ) where import Control.Monad.Except (throwError) import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Extensions import qualified Text.Pandoc.Format as Format import Text.Pandoc.Options import Text.Pandoc.Readers.AsciiDoc import Text.Pandoc.Readers.CommonMark import Text.Pandoc.Readers.Markdown import Text.Pandoc.Readers.Creole import Text.Pandoc.Readers.DocBook import Text.Pandoc.Readers.Docx import Text.Pandoc.Readers.Pptx import Text.Pandoc.Readers.Xlsx import Text.Pandoc.Readers.DokuWiki import Text.Pandoc.Readers.EPUB import Text.Pandoc.Readers.FB2 import Text.Pandoc.Readers.Ipynb import Text.Pandoc.Readers.Haddock import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.JATS (readJATS) import Text.Pandoc.Readers.Jira (readJira) import Text.Pandoc.Readers.LaTeX import Text.Pandoc.Readers.MediaWiki import Text.Pandoc.Readers.Muse import Text.Pandoc.Readers.Native import Text.Pandoc.Readers.ODT import Text.Pandoc.Readers.OPML import Text.Pandoc.Readers.Org import Text.Pandoc.Readers.Pod import Text.Pandoc.Readers.RST import Text.Pandoc.Readers.Textile import Text.Pandoc.Readers.TikiWiki import Text.Pandoc.Readers.TWiki import Text.Pandoc.Readers.Txt2Tags import Text.Pandoc.Readers.Vimwiki import Text.Pandoc.Readers.Man import Text.Pandoc.Readers.Mdoc import Text.Pandoc.Readers.CSV import Text.Pandoc.Readers.CslJson import Text.Pandoc.Readers.BibTeX import Text.Pandoc.Readers.EndNote import Text.Pandoc.Readers.RIS import Text.Pandoc.Readers.RTF import Text.Pandoc.Readers.Typst import Text.Pandoc.Readers.Djot import Text.Pandoc.Readers.XML import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Sources (ToSources(..), sourcesToText) data Reader m = TextReader (forall a . ToSources a => ReaderOptions -> a -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) -- | Association list of formats and readers. readers :: PandocMonad m => [(Text, Reader m)] readers = [("native" , TextReader readNative) ,("json" , TextReader readJSON) ,("markdown" , TextReader readMarkdown) ,("markdown_strict" , TextReader readMarkdown) ,("markdown_phpextra" , TextReader readMarkdown) ,("markdown_github" , TextReader readMarkdown) ,("markdown_mmd", TextReader readMarkdown) ,("commonmark" , TextReader readCommonMark) ,("commonmark_x" , TextReader readCommonMark) ,("asciidoc" , TextReader readAsciiDoc) ,("creole" , TextReader readCreole) ,("dokuwiki" , TextReader readDokuWiki) ,("gfm" , TextReader readCommonMark) ,("rst" , TextReader readRST) ,("mediawiki" , TextReader readMediaWiki) ,("vimwiki" , TextReader readVimwiki) ,("docbook" , TextReader readDocBook) ,("opml" , TextReader readOPML) ,("org" , TextReader readOrg) ,("textile" , TextReader readTextile) -- TODO : textile+lhs ,("html" , TextReader readHtml) ,("bits" , TextReader readJATS) ,("jats" , TextReader readJATS) ,("jira" , TextReader readJira) ,("latex" , TextReader readLaTeX) ,("haddock" , TextReader readHaddock) ,("twiki" , TextReader readTWiki) ,("tikiwiki" , TextReader readTikiWiki) ,("docx" , ByteStringReader readDocx) ,("pptx" , ByteStringReader readPptx) ,("xlsx" , ByteStringReader readXlsx) ,("odt" , ByteStringReader readODT) ,("t2t" , TextReader readTxt2Tags) ,("epub" , ByteStringReader readEPUB) ,("muse" , TextReader readMuse) ,("man" , TextReader readMan) ,("fb2" , TextReader readFB2) ,("ipynb" , TextReader readIpynb) ,("csv" , TextReader readCSV) ,("tsv" , TextReader readTSV) ,("csljson" , TextReader readCslJson) ,("bibtex" , TextReader readBibTeX) ,("biblatex" , TextReader readBibLaTeX) ,("endnotexml" , TextReader readEndNoteXML) ,("ris" , TextReader readRIS) ,("rtf" , TextReader readRTF) ,("typst" , TextReader readTypst) ,("djot" , TextReader readDjot) ,("mdoc" , TextReader readMdoc) ,("pod" , TextReader readPod) ,("xml" , TextReader readXML) ] -- | Retrieve reader, extensions based on format spec (format+extensions). getReader :: PandocMonad m => Format.FlavoredFormat -> m (Reader m, Extensions) getReader flvrd = do let readerName = Format.formatName flvrd case lookup readerName readers of Nothing -> throwError $ PandocUnknownReaderError readerName Just r -> (r,) <$> Format.applyExtensionsDiff (Format.getExtensionsConfig readerName) flvrd -- | Read pandoc document from JSON format. readJSON :: (PandocMonad m, ToSources a) => ReaderOptions -> a -> m Pandoc readJSON _ s = case eitherDecode' . BL.fromStrict . UTF8.fromText . sourcesToText . toSources $ s of Right doc -> return doc Left e -> throwError $ PandocParseError ("JSON parse error: " <> T.pack e) ================================================ FILE: src/Text/Pandoc/RoffChar.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.RoffChar Copyright : Copyright (C) 2007-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Roff character escaping/unescaping. -} module Text.Pandoc.RoffChar ( standardEscapes , characterCodes , combiningAccents ) where import qualified Data.Text as T -- | These are the escapes specifically mentioned in groff_man(7), -- plus @ and ellipsis. We use the \(aq form when possible (with -- two-letter escapes), because these are compatible with all forms -- of roff (#10716). standardEscapes :: [(Char, T.Text)] standardEscapes = [ ('\160', "\\ ") , ('\'', "\\(aq") , ('‘', "\\(oq") , ('’', "\\(cq") , ('"', "\\(dq") , ('“', "\\(lq") , ('”', "\\(rq") , ('—', "\\(em") , ('–', "\\(en") , ('`', "\\(ga") , ('^', "\\(ha") , ('~', "\\(ti") , ('\\', "\\(rs") , ('@', "\\(at") -- because we use @ as a table and math delimiter , ('\x2026', "\\&...") -- because u2026 doesn't render on tty ] characterCodes :: [(Char, T.Text)] characterCodes = [ ('Ð', "-D") , ('ð', "Sd") , ('Þ', "TP") , ('þ', "Tp") , ('ß', "ss") , ('ff', "ff") , ('fi', "fi") , ('fl', "fl") , ('ffi', "Fi") , ('ffl', "Fl") , ('Ł', "/L") , ('ł', "/l") , ('Ø', "/O") , ('ø', "/o") , ('Æ', "AE") , ('æ', "ae") , ('Œ', "OE") , ('œ', "oe") , ('IJ', "IJ") , ('ij', "ij") , ('ı', ".i") , ('ȷ', ".j") , ('Á', "'A") , ('Ć', "'C") , ('É', "'E") , ('Í', "'I") , ('Ó', "'O") , ('Ú', "'U") , ('Ý', "'Y") , ('á', "'a") , ('ć', "'c") , ('é', "'e") , ('í', "'i") , ('ó', "'o") , ('ú', "'u") , ('ý', "'y") , ('Ä', ":A") , ('Ë', ":E") , ('Ï', ":I") , ('Ö', ":O") , ('Ü', ":U") , ('Ÿ', ":Y") , ('ä', ":a") , ('ë', ":e") , ('ï', ":i") , ('ö', ":o") , ('ü', ":u") , ('ÿ', ":y") , ('Â', "^A") , ('Ê', "^E") , ('Î', "^I") , ('Ô', "^O") , ('Û', "^U") , ('â', "^a") , ('ê', "^e") , ('î', "^i") , ('ô', "^o") , ('û', "^u") , ('À', "`A") , ('È', "`E") , ('Ì', "`I") , ('Ò', "`O") , ('Ù', "`U") , ('à', "`a") , ('è', "`e") , ('ì', "`i") , ('ò', "`o") , ('ù', "`u") , ('Ã', "~A") , ('Ñ', "~N") , ('Õ', "~O") , ('ã', "~a") , ('ñ', "~n") , ('õ', "~o") , ('Š', "vS") , ('š', "vs") , ('Ž', "vZ") , ('ž', "vz") , ('Ç', ",C") , ('ç', ",c") , ('Å', "oA") , ('å', "oa") , ('˝', "a\"") , ('¯', "a-") , ('˙', "a.") , ('^', "a^") , ('´', "aa") , ('`', "ga") , ('˘', "ab") , ('¸', "ac") , ('¨', "ad") , ('ˇ', "ah") , ('˚', "ao") , ('~', "a~") , ('˛', "ho") , ('^', "ha") , ('~', "ti") , ('„', "Bq") , ('‚', "bq") , ('“', "lq") , ('”', "rq") , ('‘', "oq") , ('’', "cq") , ('\'', "aq") , ('"', "dq") , ('«', "Fo") , ('»', "Fc") , ('‹', "fo") , ('›', "fc") , ('¡', "r!") , ('¿', "r?") , ('—', "em") , ('–', "en") , ('‐', "hy") , ('[', "lB") , (']', "rB") , ('{', "lC") , ('}', "rC") , ('⟨', "la") , ('⟩', "ra") , ('⎪', "bv") , ('⎪', "braceex") , ('⎡', "bracketlefttp") , ('⎣', "bracketleftbt") , ('⎢', "bracketleftex") , ('⎤', "bracketrighttp") , ('⎦', "bracketrightbt") , ('⎥', "bracketrightex") , ('╭', "lt") , ('⎧', "bracelefttp") , ('┥', "lk") , ('⎨', "braceleftmid") , ('╰', "lb") , ('⎩', "braceleftbt") , ('⎪', "braceleftex") , ('╮', "rt") , ('⎫', "bracerighttp") , ('┝', "rk") , ('⎬', "bracerightmid") , ('╯', "rb") , ('⎭', "bracerightbt") , ('⎪', "bracerightex") , ('⎛', "parenlefttp") , ('⎝', "parenleftbt") , ('⎜', "parenleftex") , ('⎞', "parenrighttp") , ('⎠', "parenrightbt") , ('⎟', "parenrightex") , ('←', "<-") , ('→', "->") , ('↔', "<>") , ('↓', "da") , ('↑', "ua") , ('↕', "va") , ('⇐', "lA") , ('⇒', "rA") , ('⇔', "hA") , ('⇓', "dA") , ('⇑', "uA") , ('⇕', "vA") , ('⎯', "an") , ('|', "ba") , ('│', "br") , ('_', "ul") , ('‾', "rn") , ('_', "ru") , ('¦', "bb") , ('/', "sl") , ('\\', "rs") , ('○', "ci") , ('·', "bu") , ('‡', "dd") , ('†', "dg") , ('◊', "lz") , ('□', "sq") , ('¶', "ps") , ('§', "sc") , ('☜', "lh") , ('☞', "rh") , ('@', "at") , ('#', "sh") , ('↵', "CR") , ('✓', "OK") , ('©', "co") , ('®', "rg") , ('™', "tm") , ('$', "Do") , ('¢', "ct") , ('€', "eu") , ('€', "Eu") , ('¥', "Ye") , ('£', "Po") , ('¤', "Cs") , ('ƒ', "Fn") , ('°', "de") , ('‰', "%0") , ('′', "fm") , ('″', "sd") , ('µ', "mc") , ('ª', "Of") , ('º', "Om") , ('∧', "AN") , ('∨', "OR") , ('¬', "no") , ('¬', "tno") , ('∃', "te") , ('∀', "fa") , ('∋', "st") , ('∴', "3d") , ('∴', "tf") , ('|', "or") , ('½', "12") , ('¼', "14") , ('¾', "34") , ('⅛', "18") , ('⅜', "38") , ('⅝', "58") , ('⅞', "78") , ('¹', "S1") , ('²', "S2") , ('³', "S3") , ('+', "pl") , ('−', "mi") , ('∓', "-+") , ('±', "+-") , ('±', "t+-") , ('·', "pc") , ('⋅', "md") , ('×', "mu") , ('×', "tmu") , ('⊗', "c*") , ('⊕', "c+") , ('÷', "di") , ('÷', "tdi") , ('⁄', "f/") , ('∗', "**") , ('≤', "<=") , ('≥', ">=") , ('≪', "<<") , ('≫', ">>") , ('=', "eq") , ('≠', "!=") , ('≡', "==") , ('≢', "ne") , ('≅', "=~") , ('≃', "|=") , ('∼', "ap") , ('≈', "~~") , ('≈', "~=") , ('∝', "pt") , ('∅', "es") , ('∈', "mo") , ('∉', "nm") , ('⊂', "sb") , ('⊄', "nb") , ('⊃', "sp") , ('⊅', "nc") , ('⊆', "ib") , ('⊇', "ip") , ('∩', "ca") , ('∪', "cu") , ('∠', "/_") , ('⊥', "pp") , ('∫', "is") , ('∫', "integral") , ('∑', "sum") , ('∏', "product") , ('∐', "coproduct") , ('∇', "gr") , ('√', "sr") , ('√', "sqrt") -- , "radicalex" -- "sqrtex" , ('⌈', "lc") , ('⌉', "rc") , ('⌊', "lf") , ('⌋', "rf") , ('∞', "if") , ('ℵ', "Ah") , ('ℑ', "Im") , ('ℜ', "Re") , ('℘', "wp") , ('∂', "pd") , ('ℏ', "-h") , ('ℏ', "hbar") , ('Α', "*A") , ('Β', "*B") , ('Γ', "*G") , ('Δ', "*D") , ('Ε', "*E") , ('Ζ', "*Z") , ('Η', "*Y") , ('Θ', "*H") , ('Ι', "*I") , ('Κ', "*K") , ('Λ', "*L") , ('Μ', "*M") , ('Ν', "*N") , ('Ξ', "*C") , ('Ο', "*O") , ('Π', "*P") , ('Ρ', "*R") , ('Σ', "*S") , ('Τ', "*T") , ('Υ', "*U") , ('Φ', "*F") , ('Χ', "*X") , ('Ψ', "*Q") , ('Ω', "*W") , ('α', "*a") , ('β', "*b") , ('γ', "*g") , ('δ', "*d") , ('ε', "*e") , ('ζ', "*z") , ('η', "*y") , ('θ', "*h") , ('ι', "*i") , ('κ', "*k") , ('λ', "*l") , ('μ', "*m") , ('ν', "*n") , ('ξ', "*c") , ('ο', "*o") , ('π', "*p") , ('ρ', "*r") , ('ς', "ts") , ('σ', "*s") , ('τ', "*t") , ('υ', "*u") , ('ϕ', "*f") , ('χ', "*x") , ('ψ', "*q") , ('ω', "*w") , ('ϑ', "+h") , ('φ', "+f") , ('ϖ', "+p") , ('ϵ', "+e") , ('♣', "CL") , ('♠', "SP") , ('♥', "HE") , ('♦', "DI") , ('˝' , "a\"") , ('¯', "a-") , ('˙', "a.") , ('^', "a^") , ('´', "aa") , ('`', "ga") , ('˘', "ab") , ('¸', "ac") , ('¨', "ad") , ('ˇ', "ah") , ('˚', "ao") , ('~', "a~") , ('˛', "ho") , ('^', "ha") , ('~', "ti") ] -- use like: \\[E a^ aa] combiningAccents :: [(Char, T.Text)] combiningAccents = [ ('\779' , "a\"") , ('\772', "a-") , ('\775', "a.") , ('\770', "a^") , ('\769', "aa") , ('\768', "ga") , ('\774', "ab") , ('\807', "ac") , ('\776', "ad") , ('\780', "ah") , ('\778', "ao") , ('\771', "a~") , ('\808', "ho") , ('\770', "ha") , ('\771', "ti") ] ================================================ FILE: src/Text/Pandoc/Scripting.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ImpredicativeTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Scripting Copyright : © 2022-2024 Albert Krewinkel License : GPL-2.0-or-later Maintainer : Albert Krewinkel Central data structure for scripting engines. -} module Text.Pandoc.Scripting ( ScriptingEngine (..) , CustomComponents(..) , noEngine ) where import Control.Monad.Except (throwError) import Control.Monad.IO.Class (MonadIO) import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Error (PandocError (PandocNoScriptingEngine)) import Text.Pandoc.Filter.Environment (Environment) import Text.Pandoc.Format (ExtensionsConfig) import Text.Pandoc.Readers (Reader) import Text.Pandoc.Writers (Writer) -- | A component of a custom reader/writer: a custom reader, -- a custom writer, a template for a custom writer, or a specification -- of the extensions used by a script and their default values. -- Note that a single script can contain all of these. data CustomComponents m = CustomComponents { customReader :: Maybe (Reader m) , customWriter :: Maybe (Writer m) , customTemplate :: Maybe Text , customExtensions :: Maybe ExtensionsConfig } -- | Structure to define a scripting engine. data ScriptingEngine = ScriptingEngine { engineName :: Text -- ^ Name of the engine. , engineApplyFilter :: forall m. (PandocMonad m, MonadIO m) => Environment -> [String] -> FilePath -> Pandoc -> m Pandoc -- ^ Use the scripting engine to run a filter. , engineLoadCustom :: forall m. (PandocMonad m, MonadIO m) => FilePath -> m (CustomComponents m) -- ^ Function to load a custom reader/writer from a script. } noEngine :: ScriptingEngine noEngine = ScriptingEngine { engineName = "none" , engineApplyFilter = \_env _args _fp _doc -> throwError PandocNoScriptingEngine , engineLoadCustom = \_fp -> throwError PandocNoScriptingEngine } ================================================ FILE: src/Text/Pandoc/SelfContained.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.SelfContained Copyright : Copyright (C) 2011-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Functions for converting an HTML file into one that can be viewed offline, by incorporating linked images, CSS, and scripts into the HTML using data URIs. -} module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where import Codec.Compression.GZip as Gzip import Control.Applicative ((<|>)) import Data.ByteString (ByteString) import Data.ByteString.Base64 (encode) import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as L import qualified Data.Text as T import Data.Char (isAlphaNum, isAscii) import Crypto.Hash (hashWith, SHA1(SHA1)) import Network.URI (escapeURIString) import System.FilePath (takeDirectory, takeExtension, ()) import Text.HTML.TagSoup import Text.Pandoc.Class.PandocMonad (PandocMonad (..), fetchItem, getInputFiles, report, setInputFiles) import Text.Pandoc.Logging import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Shared (renderTags', trim, tshow, safeRead) import Text.Pandoc.URI (isURI) import Text.Pandoc.UTF8 (toString, toText, fromText) import Text.Pandoc.Parsing (ParsecT, runParserT) import qualified Text.Pandoc.Parsing as P import Control.Monad.Except (throwError, catchError) import Data.Either (lefts, rights) import Data.Maybe (isNothing) import qualified Data.Map as M import Control.Monad.State isOk :: Char -> Bool isOk c = isAscii c && isAlphaNum c makeDataURI :: (MimeType, ByteString) -> T.Text makeDataURI (mime, raw) = if textual then "data:" <> mime' <> "," <> T.pack (escapeURIString isOk (toString raw)) else "data:" <> mime' <> ";base64," <> toText (encode raw') where textual = "text/" `T.isPrefixOf` mime raw' = if "+xml" `T.isSuffixOf` mime then B.filter (/= '\r') raw -- strip off CRs else raw mime' = if textual && T.any (== ';') mime then mime <> ";charset=utf-8" else mime -- mime type already has charset isSourceAttribute :: T.Text -> (T.Text, T.Text) -> Bool isSourceAttribute tagname (x,_) = x == "src" || x == "data-src" || (x == "href" && tagname == "link") || x == "poster" || x == "data-background-image" data ConvertState = ConvertState { isHtml5 :: Bool , svgMap :: M.Map T.Text (T.Text, [Attribute T.Text]) -- map from hash to (id, svg attributes) } deriving (Show) convertTags :: PandocMonad m => [Tag T.Text] -> StateT ConvertState m [Tag T.Text] convertTags [] = return [] convertTags (t@TagOpen{}:ts) | fromAttrib "data-external" t == "1" = (t:) <$> convertTags ts convertTags (t@(TagOpen "style" _):ts) = case span isTagText ts of (xs,rest) -> do xs' <- mapM (\case TagText s -> TagText . toText <$> cssURLs "" (fromText s) tag -> return tag) xs ((t:xs') ++) <$> convertTags rest convertTags (t@(TagOpen "script" as):tc@(TagClose "script"):ts) = case fromAttrib "src" t of "" -> ([t, tc] ++) <$> convertTags ts src -> do let typeAttr = fromAttrib "type" t res <- getData typeAttr src rest <- convertTags ts case res of AlreadyDataURI dataUri -> return $ TagOpen "script" (("src",dataUri) : [(x,y) | (x,y) <- as, x /= "src"]) : TagClose "script" : rest Fetched (mime, bs) | ("text/javascript" `T.isPrefixOf` mime || "application/javascript" `T.isPrefixOf` mime || "application/x-javascript" `T.isPrefixOf` mime) && not (" return $ TagOpen "script" [(k,v) | (k,v) <- as , k == "type" || "data-" `T.isPrefixOf` k] : TagText (toText bs) : TagClose "script" : rest | otherwise -> return $ TagOpen "script" (("src",makeDataURI (mime, bs)) : [(x,y) | (x,y) <- as, x /= "src"]) : TagClose "script" : rest CouldNotFetch _ -> return $ t:tc:rest convertTags (t@(TagOpen "link" as):ts) = case fromAttrib "href" t of "" -> (t:) <$> convertTags ts src -> do res <- getData (fromAttrib "type" t) src case res of AlreadyDataURI dataUri -> do rest <- convertTags ts return $ TagOpen "link" (("href",dataUri) : [(x,y) | (x,y) <- as, x /= "href"]) : rest Fetched (mime, bs) | ("text/css" `T.isPrefixOf` mime || fromAttrib "rel" t == "stylesheet") && T.null (fromAttrib "media" t) && not (" do rest <- convertTags $ dropWhile (==TagClose "link") ts return $ TagOpen "style" [("type", "text/css")] -- see #5725 : TagText (toText bs) : TagClose "style" : rest | otherwise -> do rest <- convertTags ts return $ TagOpen "link" (("href",makeDataURI (mime, bs)) : [(x,y) | (x,y) <- as, x /= "href"]) : rest CouldNotFetch _ -> do rest <- convertTags ts return $ t:rest convertTags (t@(TagOpen tagname as):ts) | any (isSourceAttribute tagname) as = do let inlineSvgs = tagname == "img" && case T.words <$> lookup "class" as of Nothing -> False Just cs -> "inline-svg" `elem` cs as' <- mapM (processAttribute inlineSvgs) as let attrs = addRole "img" $ addAriaLabel $ rights as' let svgContents = lefts as' rest <- convertTags ts case svgContents of [] -> return $ TagOpen tagname attrs : rest ((hash, tags) : _) -> do -- drop "" if present let rest' = case rest of TagClose tn : xs | tn == tagname -> xs _ -> rest svgmap <- gets svgMap case M.lookup hash svgmap of Just (svgid, svgattrs) -> do let attrs' = [(k,v) | (k,v) <- combineSvgAttrs svgattrs attrs , k /= "id"] return $ TagOpen "svg" attrs' : TagOpen "use" [("href", "#" <> svgid), ("width", "100%"), ("height", "100%")] : TagClose "use" : TagClose "svg" : rest' Nothing -> case dropWhile (not . isTagOpenName "svg") tags of TagOpen "svg" svgattrs : tags' -> do let attrs' = combineSvgAttrs svgattrs attrs let svgid = case lookup "id" attrs' of Just id' -> id' Nothing -> "svg_" <> hash let attrs'' = ("id", svgid) : [(k,v) | (k,v) <- attrs', k /= "id"] modify $ \st -> st{ svgMap = M.insert hash (svgid, attrs'') (svgMap st) } let fixUrl x = case T.breakOn "url(#" x of (_,"") -> x (before, after) -> before <> "url(#" <> svgid <> "_" <> T.drop 5 after let addIdPrefix ("id", x) = ("id", svgid <> "_" <> x) addIdPrefix (k, x) | k == "xlink:href" || k == "href" = case T.uncons x of Just ('#', x') -> (k, "#" <> svgid <> "_" <> x') _ -> (k, x) -- this clause handles things like -- style="fill:url(#radialGradient46);stroke:none", -- adding the svg id prefix to the anchor: addIdPrefix (k, x) = (k, fixUrl x) let ensureUniqueId (TagOpen tname ats) = TagOpen tname (map addIdPrefix ats) ensureUniqueId x = x return $ TagOpen "svg" attrs'' : map ensureUniqueId tags' ++ rest' _ -> return $ TagOpen tagname attrs : rest where processAttribute inlineSvgs (x,y) = if isSourceAttribute tagname (x,y) then do res <- getData (fromAttrib "type" t) y case res of AlreadyDataURI enc -> return $ Right (x, enc) Fetched ("image/svg+xml", bs) | inlineSvgs -> do -- we filter CR in the hash to ensure that Windows -- and non-Windows tests agree: let hash = T.pack $ take 20 $ show $ hashWith SHA1 $ B.filter (/='\r') bs return $ Left (hash, getSvgTags (toText bs)) Fetched (mt,bs) -> return $ Right (x, makeDataURI (mt,bs)) CouldNotFetch _ -> return $ Right (x, y) else return $ Right (x,y) convertTags (t:ts) = (t:) <$> convertTags ts addRole :: T.Text -> [(T.Text, T.Text)] -> [(T.Text, T.Text)] addRole role attrs = case lookup "role" attrs of Nothing -> ("role", role) : attrs Just _ -> attrs addAriaLabel :: [(T.Text, T.Text)] -> [(T.Text, T.Text)] addAriaLabel attrs = case lookup "aria-label" attrs of Just _ -> attrs Nothing -> case lookup "alt" attrs of Just alt -> ("aria-label", alt) : attrs Nothing -> attrs -- we want to drop spaces, , and comments before -- and anything after : getSvgTags :: T.Text -> [Tag T.Text] getSvgTags t = case takeWhile (not . isTagCloseName "svg") . dropWhile (not . isTagOpenName "svg") $ parseTags t of [] -> [] xs -> xs ++ [TagClose "svg"] combineSvgAttrs :: [(T.Text, T.Text)] -> [(T.Text, T.Text)] -> [(T.Text, T.Text)] combineSvgAttrs svgAttrs imgAttrs = case (mbViewBox, mbHeight, mbWidth) of (Nothing, Just h, Just w) -> -- calculate viewBox combinedAttrs ++ [("viewBox", T.unwords ["0", "0", tshow w, tshow h])] (Just (_minx,_miny,w,h), Nothing, Nothing) -> combinedAttrs ++ [ ("width", dropPointZero (tshow w)) | isNothing (lookup "width" combinedAttrs) ] ++ [ ("height", dropPointZero (tshow h)) | isNothing (lookup "height" combinedAttrs) ] _ -> combinedAttrs where dropPointZero t = case T.stripSuffix ".0" t of Nothing -> t Just t' -> t' combinedAttrs = [(k, v) | (k, v) <- imgAttrs , k /= "class"] ++ [(k, v) | (k, v) <- svgAttrs , isNothing (lookup k imgAttrs) , k `notElem` ["xmlns", "xmlns:xlink", "version", "class"]] ++ mergedClasses parseViewBox t = case map (safeRead . addZero) $ T.words t of [Just llx, Just lly, Just urx, Just ury] -> Just (llx, lly, urx, ury) _ -> Nothing addZero t = if "-." `T.isPrefixOf` t then "-0." <> T.drop 2 t -- safeRead fails on -.33, needs -0.33 else t (mbViewBox :: Maybe (Double, Double, Double, Double)) = lookup "viewBox" svgAttrs >>= parseViewBox (mbHeight :: Maybe Int) = lookup "height" combinedAttrs >>= safeRead (mbWidth :: Maybe Int) = lookup "width" combinedAttrs >>= safeRead mergedClasses = case (lookup "class" imgAttrs, lookup "class" svgAttrs) of (Just c1, Just c2) -> [("class", c1 <> " " <> c2)] _ -> [] cssURLs :: PandocMonad m => FilePath -> ByteString -> m ByteString cssURLs d orig = do res <- runParserT (parseCSSUrls d) () "css" orig case res of Left e -> do report $ CouldNotParseCSS $ T.pack $ show e return orig Right bs -> return bs parseCSSUrls :: PandocMonad m => FilePath -> ParsecT ByteString () m ByteString parseCSSUrls d = B.concat <$> P.many (pCSSWhite <|> pCSSComment <|> pCSSImport d <|> pCSSUrl d <|> pCSSOther) pCSSImport :: PandocMonad m => FilePath -> ParsecT ByteString () m ByteString pCSSImport d = P.try $ do P.string "@import" P.spaces res <- (pQuoted <|> pUrl) >>= handleCSSUrl d P.spaces P.char ';' P.spaces case res of Left b -> return $ B.pack "@import " <> b Right (_, b) -> return b -- Note: some whitespace in CSS is significant, so we can't collapse it! pCSSWhite :: PandocMonad m => ParsecT ByteString () m ByteString pCSSWhite = B.singleton <$> P.space <* P.spaces pCSSComment :: PandocMonad m => ParsecT ByteString () m ByteString pCSSComment = P.try $ do P.string "/*" P.manyTill P.anyChar (P.try (P.string "*/")) return B.empty pCSSOther :: PandocMonad m => ParsecT ByteString () m ByteString pCSSOther = (B.pack <$> P.many1 (P.noneOf "u/ \n\r\t")) <|> (B.singleton <$> P.char 'u') <|> (B.singleton <$> P.char '/') pCSSUrl :: PandocMonad m => FilePath -> ParsecT ByteString () m ByteString pCSSUrl d = P.try $ do res <- pUrl >>= handleCSSUrl d case res of Left b -> return b Right (mt,b) -> do let enc = makeDataURI (mt, b) return $ fromText $ "url(" <> enc <> ")" pQuoted :: PandocMonad m => ParsecT ByteString () m (T.Text, ByteString) pQuoted = P.try $ do quote <- P.oneOf "\"'" url <- T.pack <$> P.manyTill P.anyChar (P.char quote) let fallback = fromText $ T.singleton quote <> trim url <> T.singleton quote return (url, fallback) pUrl :: PandocMonad m => ParsecT ByteString () m (T.Text, ByteString) pUrl = P.try $ do P.string "url(" P.spaces quote <- P.option Nothing (Just <$> P.oneOf "\"'") url <- T.pack <$> P.manyTill P.anyChar (maybe (P.lookAhead (P.char ')')) P.char quote) P.spaces P.char ')' let fallback = fromText ("url(" <> maybe "" T.singleton quote <> trim url <> maybe "" T.singleton quote <> ")") return (url, fallback) handleCSSUrl :: PandocMonad m => FilePath -> (T.Text, ByteString) -> ParsecT ByteString () m (Either ByteString (MimeType, ByteString)) handleCSSUrl d (url, fallback) = case escapeURIString (/='|') (T.unpack $ trim url) of '#':_ -> return $ Left fallback 'd':'a':'t':'a':':':_ -> return $ Left fallback u -> do let url' = if isURI (T.pack u) then T.pack u else T.pack (d u) res <- lift $ getData "" url' case res of AlreadyDataURI uri -> return $ Left (fromText $ "url(" <> uri <> ")") Fetched (mt', raw) -> do -- note that the downloaded CSS may -- itself contain url(...). (mt, b) <- if "text/css" `T.isPrefixOf` mt' -- see #5725: in HTML5, content type -- isn't allowed on style type attribute then ("text/css",) <$> cssURLs d raw else return (mt', raw) return $ Right (mt, b) CouldNotFetch _ -> return $ Left fallback data GetDataResult = AlreadyDataURI T.Text | CouldNotFetch PandocError | Fetched (MimeType, ByteString) deriving (Show) getData :: PandocMonad m => MimeType -> T.Text -> m GetDataResult getData mimetype src | "data:" `T.isPrefixOf` src = return $ AlreadyDataURI src -- already data: uri | otherwise = catchError fetcher handler where fetcher = do let ext = T.toLower $ T.pack $ takeExtension $ T.unpack src (raw, respMime) <- fetchItem src let raw' = if ext `elem` [".gz", ".svgz"] then B.concat $ L.toChunks $ Gzip.decompress $ L.fromChunks [raw] else raw let mime = case (mimetype, respMime) of ("",Nothing) -> "application/octet-stream" (x, Nothing) -> x (_, Just x ) -> x result <- if "text/css" `T.isPrefixOf` mime then do oldInputs <- getInputFiles setInputFiles [T.unpack src] res <- cssURLs (takeDirectory $ T.unpack src) raw' setInputFiles oldInputs return res else return raw' return $ Fetched (mime, result) handler e -- If fetch failed and we have a fragment and/or query, -- try the fetch again without these, since the resource -- may be local (see #1477, #11021) | T.any (\c -> c == '?' || c == '#') src && not (isURI src) = getData mimetype (removeQueryAndFragment src) | otherwise = case e of PandocResourceNotFound r -> do report $ CouldNotFetchResource r "" return $ CouldNotFetch e PandocHttpError u er -> do report $ CouldNotFetchResource u er return $ CouldNotFetch e _ -> throwError e removeQueryAndFragment = T.takeWhile (\c -> c /= '#' && c /= '?') -- | Convert HTML into self-contained HTML, incorporating images, -- scripts, and CSS using data: URIs. makeSelfContained :: PandocMonad m => T.Text -> m T.Text makeSelfContained inp = do let tags = parseTags inp let html5 = case tags of (TagOpen "!DOCTYPE" [("html","")]:_) -> True _ -> False let convertState = ConvertState { isHtml5 = html5, svgMap = mempty } out' <- evalStateT (convertTags tags) convertState return $ renderTags' out' ================================================ FILE: src/Text/Pandoc/Shared.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Shared Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Utility functions and definitions used by the various Pandoc modules. -} module Text.Pandoc.Shared ( -- * List processing splitBy, splitTextBy, splitTextByIndices, -- * Text processing inquotes, tshow, stripTrailingNewlines, trim, triml, trimr, trimMath, stripFirstAndLast, camelCaseToHyphenated, camelCaseStrToHyphenated, toRomanNumeral, tabFilter, -- * Date/time normalizeDate, -- * Pandoc block and inline list processing addPandocAttributes, orderedListMarkers, extractSpaces, removeFormatting, deNote, stringify, capitalize, compactify, compactifyDL, linesToPara, figureDiv, makeSections, makeSectionsWithOffsets, combineAttr, uniqueIdent, inlineListToIdentifier, textToIdentifier, isHeaderBlock, hasLineBreaks, onlySimpleTableCells, isTightList, taskListItemFromAscii, taskListItemToAscii, handleTaskListItem, addMetaField, htmlSpanLikeElements, formatCode, -- * TagSoup HTML handling renderTags', -- * File handling inDirectory, makeCanonical, collapseFilePath, filteredFilesFromArchive, -- * for squashing blocks blocksToInlines, blocksToInlines', blocksToInlinesWithSep, defaultBlocksSeparator, -- * Safe read safeRead, safeStrRead ) where import Codec.Archive.Zip import qualified Control.Exception as E import Control.Monad (MonadPlus (..), msum, unless) import qualified Control.Monad.State.Strict as S import qualified Data.ByteString.Lazy as BL import Data.Containers.ListUtils (nubOrd) import Data.Char (isAlpha, isLower, isSpace, isUpper, toLower, isAlphaNum, generalCategory, GeneralCategory(NonSpacingMark, SpacingCombiningMark, EnclosingMark, ConnectorPunctuation)) 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 (..) ) import Data.Semigroup (Min (..)) import Data.Sequence (ViewL (..), ViewR (..), viewl, viewr) import qualified Data.Set as Set import qualified Data.Text as T import qualified Text.Emoji as Emoji import System.Directory import System.FilePath (isPathSeparator, splitDirectories) import qualified System.FilePath.Posix as Posix import Text.HTML.TagSoup (RenderOptions (..), Tag (..), renderOptions, renderTagsOptions) import Text.Pandoc.Builder (Blocks, Inlines, ToMetaValue (..)) import qualified Text.Pandoc.Builder as B import Data.Time import Text.Pandoc.Asciify (toAsciiText) import Text.Pandoc.Definition import Text.Pandoc.Extensions (Extensions, Extension(..), extensionEnabled) import Text.DocLayout (charWidth) import Text.Pandoc.Walk -- for addPandocAttributes: import Commonmark.Pandoc (Cm(..)) import Commonmark (HasAttributes(..)) -- -- List processing -- -- | Split list by groups of one or more sep. splitBy :: (a -> Bool) -> [a] -> [[a]] splitBy _ [] = [] splitBy isSep lst = let (first, rest) = break isSep lst in first:splitBy isSep (dropWhile isSep rest) -- | Split text by groups of one or more separator. splitTextBy :: (Char -> Bool) -> T.Text -> [T.Text] splitTextBy isSep t | T.null t = [] | otherwise = let (first, rest) = T.break isSep t in first : splitTextBy isSep (T.dropWhile isSep rest) -- | Split text at the given widths. Note that the break points are -- /not/ indices but text widths, which will be different for East Asian -- characters, emojis, etc. splitTextByIndices :: [Int] -> T.Text -> [T.Text] splitTextByIndices ns = splitTextByRelIndices (zipWith (-) ns (0:ns)) . T.unpack where splitTextByRelIndices [] cs = [T.pack cs] splitTextByRelIndices (x:xs) cs = let (first, rest) = splitAt' x cs in T.pack first : splitTextByRelIndices xs rest -- | Returns a pair whose first element is a prefix of @t@ and that has -- width @n@, and whose second is the remainder of the string. -- -- Note: Do *not* replace this with 'T.splitAt', which is not sensitive -- to character widths! splitAt' :: Int {-^ n -} -> [Char] {-^ t -} -> ([Char],[Char]) splitAt' _ [] = ([],[]) splitAt' n xs | n <= 0 = ([],xs) splitAt' n (x:xs) = (x:ys,zs) where (ys,zs) = splitAt' (n - charWidth x) xs -- -- Text processing -- -- | Wrap double quotes around a Text inquotes :: T.Text -> T.Text inquotes txt = T.cons '\"' (T.snoc txt '\"') -- | Like @'show'@, but returns a 'T.Text' instead of a 'String'. tshow :: Show a => a -> T.Text tshow = T.pack . show -- | Strip trailing newlines from string. stripTrailingNewlines :: T.Text -> T.Text stripTrailingNewlines = T.dropWhileEnd (== '\n') -- | Returns 'True' for an ASCII whitespace character, viz. space, -- carriage return, newline, and horizontal tab. isWS :: Char -> Bool isWS ' ' = True isWS '\r' = True isWS '\n' = True isWS '\t' = True isWS _ = False -- | Remove leading and trailing space (including newlines) from string. trim :: T.Text -> T.Text trim = T.dropAround isWS -- | Remove leading space (including newlines) from string. triml :: T.Text -> T.Text triml = T.dropWhile isWS -- | Remove trailing space (including newlines) from string. trimr :: T.Text -> T.Text trimr = T.dropWhileEnd isWS -- | Trim leading space and trailing space unless after \. trimMath :: T.Text -> T.Text trimMath = triml . T.reverse . stripBeginSpace . T.reverse -- no Text.spanEnd where stripBeginSpace t | T.null pref = t | Just ('\\', _) <- T.uncons suff = T.cons (T.last pref) suff | otherwise = suff where (pref, suff) = T.span isWS t -- | Strip leading and trailing characters from string stripFirstAndLast :: T.Text -> T.Text stripFirstAndLast t = case T.uncons t of Just (_, t') -> case T.unsnoc t' of Just (t'', _) -> t'' _ -> t' _ -> "" -- | Change CamelCase word to hyphenated lowercase (e.g., camel-case). camelCaseToHyphenated :: T.Text -> T.Text camelCaseToHyphenated = T.pack . camelCaseStrToHyphenated . T.unpack -- This may not work as expected on general Unicode, if it contains -- letters with a longer lower case form than upper case. I don't know -- what the camel case practices of affected scripts are, though. camelCaseStrToHyphenated :: String -> String camelCaseStrToHyphenated [] = "" camelCaseStrToHyphenated (a:b:rest) | isLower a , isUpper b = a:'-':toLower b:camelCaseStrToHyphenated rest -- handle ABCDef = abc-def camelCaseStrToHyphenated (a:b:c:rest) | isUpper a , isUpper b , isLower c = toLower a:'-':toLower b:camelCaseStrToHyphenated (c:rest) camelCaseStrToHyphenated (a:rest) = toLower a:camelCaseStrToHyphenated rest -- | Convert number < 4000 to uppercase roman numeral. toRomanNumeral :: Int -> T.Text toRomanNumeral x | x >= 4000 || x < 0 = "?" | x >= 1000 = "M" <> toRomanNumeral (x - 1000) | x >= 900 = "CM" <> toRomanNumeral (x - 900) | x >= 500 = "D" <> toRomanNumeral (x - 500) | x >= 400 = "CD" <> toRomanNumeral (x - 400) | x >= 100 = "C" <> toRomanNumeral (x - 100) | x >= 90 = "XC" <> toRomanNumeral (x - 90) | x >= 50 = "L" <> toRomanNumeral (x - 50) | x >= 40 = "XL" <> toRomanNumeral (x - 40) | x >= 10 = "X" <> toRomanNumeral (x - 10) | x == 9 = "IX" | x >= 5 = "V" <> toRomanNumeral (x - 5) | x == 4 = "IV" | x >= 1 = "I" <> toRomanNumeral (x - 1) | otherwise = "" -- | Convert tabs to spaces. Tabs will be preserved if tab stop is set to 0. tabFilter :: Int -- ^ Tab stop -> T.Text -- ^ Input -> T.Text tabFilter 0 = id tabFilter tabStop = T.unlines . map go . T.lines where go s = let (s1, s2) = T.break (== '\t') s in if T.null s2 then s1 else s1 <> T.replicate (tabStop - (T.length s1 `mod` tabStop)) (T.pack " ") <> go (T.drop 1 s2) -- -- Date/time -- -- | Parse a date and convert (if possible) to "YYYY-MM-DD" format. We -- limit years to the range 1601-9999 (ISO 8601 accepts greater than -- or equal to 1583, but MS Word only accepts dates starting 1601). normalizeDate :: T.Text -> Maybe T.Text normalizeDate = fmap T.pack . normalizeDate' . T.unpack -- | Like @'normalizeDate'@, but acts on 'String' instead of 'T.Text'. normalizeDate' :: String -> Maybe String normalizeDate' s = fmap (formatTime defaultTimeLocale "%F") (msum $ map (\fs -> parsetimeWith fs s >>= rejectBadYear) formats :: Maybe Day) where rejectBadYear day = case toGregorian day of (y, _, _) | y >= 1601 && y <= 9999 -> Just day _ -> Nothing parsetimeWith = parseTimeM True defaultTimeLocale formats = ["%x","%m/%d/%Y", "%D","%F", "%d %b %Y", "%e %B %Y", "%b. %e, %Y", "%B %e, %Y", "%Y%m%d", "%Y%m", "%Y"] -- -- Pandoc block and inline list processing -- -- | Add key-value attributes to a pandoc element. If the element -- does not have a slot for attributes, create an enclosing Span -- (for Inlines) or Div (for Blocks). Note that both 'Cm () Inlines' -- and 'Cm () Blocks' are instances of 'HasAttributes'. addPandocAttributes :: forall b . HasAttributes (Cm () b) => [(T.Text, T.Text)] -> b -> b addPandocAttributes [] bs = bs addPandocAttributes kvs bs = unCm . addAttributes kvs $ (Cm bs :: Cm () b) -- | Generate infinite lazy list of markers for an ordered list, -- depending on list attributes. orderedListMarkers :: (Int, ListNumberStyle, ListNumberDelim) -> [T.Text] orderedListMarkers (start, numstyle, numdelim) = let nums = case numstyle of DefaultStyle -> map tshow [start..] Example -> map tshow [start..] Decimal -> map tshow [start..] UpperAlpha -> drop (start - 1) $ cycle $ map T.singleton ['A'..'Z'] LowerAlpha -> drop (start - 1) $ cycle $ map T.singleton ['a'..'z'] UpperRoman -> map toRomanNumeral [start..] LowerRoman -> map (T.toLower . toRomanNumeral) [start..] inDelim str = case numdelim of DefaultDelim -> str <> "." Period -> str <> "." OneParen -> str <> ")" TwoParens -> "(" <> str <> ")" in map inDelim nums -- | Extract the leading and trailing spaces from inside an inline element -- and place them outside the element. SoftBreaks count as Spaces for -- these purposes. extractSpaces :: (Inlines -> Inlines) -> Inlines -> Inlines extractSpaces f is = let contents = B.unMany is left = case viewl contents of (Space :< _) -> B.space (SoftBreak :< _) -> B.softbreak _ -> mempty right = case viewr contents of (_ :> Space) -> B.space (_ :> SoftBreak) -> B.softbreak _ -> mempty in (left <> f (B.trimInlines . B.Many $ contents) <> right) -- | Extract inlines, removing formatting. removeFormatting :: Walkable Inline a => a -> [Inline] removeFormatting = query go . walk (deNote . deQuote) where go :: Inline -> [Inline] go (Str xs) = [Str xs] go Space = [Space] go SoftBreak = [SoftBreak] go (Code _ x) = [Str x] go (Math _ x) = [Str x] go LineBreak = [Space] go _ = [] -- | Replaces 'Note' elements with empty strings. deNote :: Inline -> Inline deNote (Note _) = Str "" deNote x = x -- | Convert pandoc structure to a string with formatting removed. -- Footnotes are skipped (since we don't want their contents in link -- labels). stringify :: Walkable Inline a => a -> T.Text stringify = query go . walk fixInlines where go :: Inline -> T.Text go Space = " " go SoftBreak = " " go (Str x) = x go (Code _ x) = x go (Math _ x) = x go (RawInline (Format "html") (T.unpack -> ('<':'b':'r':_))) = " " -- see #2105 go LineBreak = " " go _ = "" fixInlines :: Inline -> Inline fixInlines (Cite _ ils) = Cite [] ils fixInlines (Note _) = Note [] fixInlines (q@Quoted{}) = deQuote q fixInlines x = x -- | Unwrap 'Quoted' inline elements, enclosing the contents with -- English-style Unicode quotes instead. deQuote :: Inline -> Inline deQuote (Quoted SingleQuote xs) = Span ("",[],[]) (Str "\8216" : xs ++ [Str "\8217"]) deQuote (Quoted DoubleQuote xs) = Span ("",[],[]) (Str "\8220" : xs ++ [Str "\8221"]) deQuote x = x -- | Bring all regular text in a pandoc structure to uppercase. -- -- This function correctly handles cases where a lowercase character doesn't -- match to a single uppercase character – e.g. “Straße” would be converted -- to “STRASSE”, not “STRAßE”. capitalize :: Walkable Inline a => a -> a capitalize = walk go where go :: Inline -> Inline go (Str s) = Str $ T.toUpper s go x = x -- | Change final list item from @Para@ to @Plain@ if the list contains -- no other @Para@ blocks. Otherwise (if the list items contain @Para@ -- blocks besides possibly at the end), turn any @Plain@s into @Para@s (#5285). compactify :: [Blocks] -- ^ List of list items (each a list of blocks) -> [Blocks] compactify [] = [] compactify items = let (others, final) = (init items, last items) in case reverse (B.toList final) of (Para a:xs) | null [Para x | Para x <- xs ++ concatMap B.toList others] -> others ++ [B.fromList (reverse (Plain a : xs))] _ | null [Para x | Para x <- concatMap B.toList items] -> items _ -> map (fmap plainToPara) items plainToPara :: Block -> Block plainToPara (Plain ils) = Para ils plainToPara x = x -- | Like @compactify@, but acts on items of definition lists. compactifyDL :: [(Inlines, [Blocks])] -> [(Inlines, [Blocks])] compactifyDL items = case reverse items of ((t,ds):ys) -> case reverse (map (reverse . B.toList) ds) of ((Para x:xs) : zs) | not (any isPara xs) -> reverse ys ++ [(t, reverse (map B.fromList zs) ++ [B.fromList (reverse (Plain x:xs))])] _ -> items _ -> items -- | Combine a list of lines by adding hard linebreaks. combineLines :: [[Inline]] -> [Inline] combineLines = intercalate [LineBreak] -- | Convert a list of lines into a paragraph with hard line breaks. This is -- useful e.g. for rudimentary support of LineBlock elements in writers. linesToPara :: [[Inline]] -> Block linesToPara = Para . combineLines -- | Creates a Div block from figure components. The intended use is in -- writers of formats that do not have markup support for figures. -- -- The resulting div is given the class @figure@ and contains the figure -- body and the figure caption. The latter is wrapped in a 'Div' of -- class @caption@, with the stringified @short-caption@ as attribute. figureDiv :: Attr -> Caption -> [Block] -> Block figureDiv (ident, classes, kv) (Caption shortcapt longcapt) body = let divattr = ( ident , ["figure"] `union` classes , kv ) captkv = maybe mempty (\s -> [("short-caption", stringify s)]) shortcapt capt = [Div ("", ["caption"], captkv) longcapt | not (null longcapt)] in Div divattr (body ++ capt) -- | Returns 'True' iff the given element is a 'Para'. isPara :: Block -> Bool isPara (Para _) = True isPara _ = False -- | Convert Pandoc inline list to plain text identifier. inlineListToIdentifier :: Extensions -> [Inline] -> T.Text inlineListToIdentifier exts = textToIdentifier exts . stringify . unEmojify where unEmojify :: [Inline] -> [Inline] unEmojify | extensionEnabled Ext_gfm_auto_identifiers exts || extensionEnabled Ext_ascii_identifiers exts = walk unEmoji | otherwise = id unEmoji (Span ("",["emoji"],[("data-emoji",ename)]) _) = Str ename unEmoji (Str t) = Str (Emoji.replaceEmojis emojisToAliases t) unEmoji x = x emojisToAliases t [] = t emojisToAliases _ (a:_) = a -- | Convert string to plain text identifier. textToIdentifier :: Extensions -> T.Text -> T.Text textToIdentifier exts = dropNonLetter . filterAscii . toIdent where dropNonLetter | extensionEnabled Ext_gfm_auto_identifiers exts = id | otherwise = T.dropWhile (not . isAlpha) filterAscii | extensionEnabled Ext_ascii_identifiers exts = toAsciiText | otherwise = id toIdent | extensionEnabled Ext_gfm_auto_identifiers exts = filterPunct . spaceToDash . T.toLower | otherwise = T.intercalate "-" . T.words . filterPunct . T.toLower filterPunct = T.filter (\c -> isSpace c || isAlphaNum c || isAllowedPunct c) isAllowedPunct c | extensionEnabled Ext_gfm_auto_identifiers exts = c == '-' || c == '_' || generalCategory c `elem` [NonSpacingMark, SpacingCombiningMark, EnclosingMark, ConnectorPunctuation] | otherwise = c == '_' || c == '-' || c == '.' spaceToDash = T.map (\c -> if isSpace c then '-' else c) -- | Put a list of Pandoc blocks into a hierarchical structure: -- a list of sections (each a Div with class "section" and first -- element a Header). If the 'numbering' parameter is True, Header -- numbers are added via the number attribute on the header. -- If the baseLevel parameter is Just n, Header levels are -- adjusted so that the lowest header level is n. -- (There may still be gaps in header level if the author leaves them.) makeSections :: Bool -> Maybe Int -> [Block] -> [Block] makeSections = makeSectionsWithOffsets [] -- | Like 'makeSections', but with a parameter for number offsets -- (a list of 'Int's, the first of which is added to the level 1 -- section number, the second to the level 2, and so on). makeSectionsWithOffsets :: [Int] -> Bool -> Maybe Int -> [Block] -> [Block] makeSectionsWithOffsets numoffsets numbering mbBaseLevel bs = S.evalState (go bs) numoffsets where getLevel (Header level _ _) = Min level getLevel _ = Min 99 minLevel = if all (== 0) numoffsets then getMin $ query getLevel bs else 1 -- see #5071, for backwards compatibility go :: [Block] -> S.State [Int] [Block] go (Header level (ident,classes,kvs) title':xs) = do lastnum <- S.get let level' = maybe level (\n -> n + level - minLevel) mbBaseLevel let adjustNum lev numComponent | lev < level = numComponent | lev == level = numComponent + 1 | otherwise = 0 let newnum = zipWith adjustNum [minLevel..level] (lastnum ++ repeat 0) unless (null newnum || "unnumbered" `elem` classes) $ S.put newnum let (sectionContents, rest) = break (headerLtEq level) xs sectionContents' <- go sectionContents rest' <- go rest let kvs' = -- don't touch number if already present case lookup "number" kvs of Nothing | numbering , "unnumbered" `notElem` classes -> ("number", T.intercalate "." (map tshow newnum)) : kvs _ -> kvs let divattr = (ident, "section":classes, kvs') let isHeadingAttr ("epub:type",_) = False isHeadingAttr ("role",v) = v `elem` ["tab", "presentation", "none", "treeitem", "menuitem", "button", "heading"] isHeadingAttr _ = True let hattr = ("",classes, filter isHeadingAttr kvs') return $ Div divattr (Header level' hattr title' : sectionContents') : rest' go (Div divattr@(dident,dclasses,_) (Header level hattr title':ys) : xs) | all (\case Header level' _ _ -> level' > level _ -> True) ys , "column" `notElem` dclasses , "columns" `notElem` dclasses , "fragment" `notElem` dclasses = do inner <- go (Header level hattr title':ys) rest <- go xs return $ case inner of [Div divattr'@(dident',_,_) zs] | T.null dident || T.null dident' || dident == dident' -> Div (combineAttr divattr' divattr) zs : rest _ -> Div divattr inner : rest go (Div attr xs : rest) = do xs' <- go xs rest' <- go rest return $ Div attr xs' : rest' go (x:xs) = (x :) <$> go xs go [] = return [] -- | Combine two 'Attr'. Classes are concatenated. For the id and key-value -- attributes, the first one takes precedence in case of duplicates. combineAttr :: Attr -> Attr -> Attr combineAttr (id1, classes1, kvs1) (id2, classes2, kvs2) = (if T.null id1 then id2 else id1, nubOrd (classes1 ++ classes2), foldr (\(k,v) kvs -> case lookup k kvs of Nothing -> (k,v):kvs Just _ -> kvs) kvs1 kvs2) headerLtEq :: Int -> Block -> Bool headerLtEq level (Header l _ _) = l <= level headerLtEq level (Div _ (b:_)) = headerLtEq level b headerLtEq _ _ = False -- | Generate a unique identifier from a list of inlines. -- Second argument is a list of already used identifiers. uniqueIdent :: Extensions -> [Inline] -> Set.Set T.Text -> T.Text uniqueIdent exts title' usedIdents = if baseIdent `Set.member` usedIdents then maybe baseIdent numIdent $ find (\x -> numIdent x `Set.notMember` usedIdents) ([1..60000] :: [Int]) -- if we have more than 60,000, allow repeats else baseIdent where baseIdent = case inlineListToIdentifier exts title' of "" -> "section" 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 isHeaderBlock _ = False -- | Detect if table rows contain only cells consisting of a single -- paragraph that has no @LineBreak@. onlySimpleTableCells :: [[[Block]]] -> Bool onlySimpleTableCells = all isSimpleCell . concat where isSimpleCell [Plain ils] = not (hasLineBreak ils) isSimpleCell [Para ils ] = not (hasLineBreak ils) isSimpleCell [] = True isSimpleCell _ = False hasLineBreak = getAny . query isLineBreak isLineBreak LineBreak = Any True isLineBreak _ = Any False -- | Detect if a list is tight. isTightList :: [[Block]] -> Bool isTightList = all isPlainItem where isPlainItem [] = True isPlainItem (Plain _ : _) = True isPlainItem [BulletList xs] = isTightList xs isPlainItem [OrderedList _ xs] = isTightList xs isPlainItem _ = False -- | Convert a list item containing tasklist syntax (e.g. @[x]@) -- to using @U+2610 BALLOT BOX@ or @U+2612 BALLOT BOX WITH X@. taskListItemFromAscii :: Extensions -> [Block] -> [Block] taskListItemFromAscii = handleTaskListItem fromMd where fromMd (Str "[" : Space : Str "]" : Space : is) = Str "☐" : Space : is fromMd (Str "[x]" : Space : is) = Str "☒" : Space : is fromMd (Str "[X]" : Space : is) = Str "☒" : Space : is fromMd is = is -- | Convert a list item containing text starting with @U+2610 BALLOT BOX@ -- or @U+2612 BALLOT BOX WITH X@ to tasklist syntax (e.g. @[x]@). taskListItemToAscii :: Extensions -> [Block] -> [Block] taskListItemToAscii = handleTaskListItem toMd where toMd (Str "☐" : Space : is) = rawMd "[ ]" : Space : is toMd (Str "☒" : Space : is) = rawMd "[x]" : Space : is toMd (Str "❏" : Space : is) = rawMd "[ ]" : Space : is toMd (Str "✓" : Space : is) = rawMd "[x]" : Space : is toMd is = is rawMd = RawInline (Format "markdown") handleTaskListItem :: ([Inline] -> [Inline]) -> Extensions -> [Block] -> [Block] handleTaskListItem handleInlines exts bls = if Ext_task_lists `extensionEnabled` exts then handleItem bls else bls where handleItem (Plain is : bs) = Plain (handleInlines is) : bs handleItem (Para is : bs) = Para (handleInlines is) : bs handleItem bs = bs -- | Set a field of a 'Meta' object. If the field already has a value, -- convert it into a list with the new value appended to the old value(s). addMetaField :: ToMetaValue a => T.Text -> a -> Meta -> Meta addMetaField key val (Meta meta) = Meta $ M.insertWith combine key (toMetaValue val) meta where combine newval (MetaList xs) = MetaList (xs ++ tolist newval) combine newval x = MetaList [x, newval] tolist (MetaList ys) = ys tolist y = [y] -- | Set of HTML elements that are represented as Span with a class equal as -- the element tag itself. htmlSpanLikeElements :: Set.Set T.Text htmlSpanLikeElements = Set.fromList ["kbd", "mark", "dfn", "abbr"] -- | Reformat 'Inlines' as code, putting the stringlike parts in 'Code' -- elements while bringing other inline formatting outside. -- The idea is that e.g. `[Str "a",Space,Strong [Str "b"]]` should turn -- into `[Code ("",[],[]) "a ", Strong [Code ("",[],[]) "b"]]`. -- This helps work around the limitation that pandoc's Code element can -- only contain string content (see issue #7525). formatCode :: Attr -> Inlines -> Inlines formatCode attr = B.fromList . walk fmt . B.toList where isPlaintext (Str _) = True isPlaintext Space = True isPlaintext SoftBreak = True isPlaintext (Quoted _ _) = True isPlaintext _ = False fmt = concatMap go . groupBy (\a b -> isPlaintext a && isPlaintext b) where go xs | all isPlaintext xs = B.toList $ B.codeWith attr $ stringify xs | otherwise = xs -- -- TagSoup HTML handling -- -- | Render HTML tags. renderTags' :: [Tag T.Text] -> T.Text renderTags' = renderTagsOptions renderOptions{ optMinimize = matchTags ["hr", "br", "img", "meta", "link", "col", "use", "path", "rect"] , optRawTag = matchTags ["script", "style"] } where matchTags tags = flip elem tags . T.toLower -- -- File handling -- -- | Perform an IO action in a directory, returning to starting directory. inDirectory :: FilePath -> IO a -> IO a inDirectory path action = E.bracket getCurrentDirectory setCurrentDirectory (const $ setCurrentDirectory path >> action) -- | Canonicalizes a file path by removing redundant @.@ and @..@. makeCanonical :: FilePath -> FilePath makeCanonical = Posix.joinPath . transformPathParts . splitDirectories where transformPathParts = reverse . L.foldl' go [] go as "." = as go ("..":as) ".." = ["..", ".."] <> as go (_:as) ".." = as go as x = x : as -- | Remove intermediate "." and ".." directories from a path. -- -- > collapseFilePath "./foo" == "foo" -- > collapseFilePath "/bar/../baz" == "/baz" -- > collapseFilePath "/../baz" == "/../baz" -- > collapseFilePath "parent/foo/baz/../bar" == "parent/foo/bar" -- > collapseFilePath "parent/foo/baz/../../bar" == "parent/bar" -- > collapseFilePath "parent/foo/.." == "parent" -- > collapseFilePath "/parent/foo/../../bar" == "/bar" collapseFilePath :: FilePath -> FilePath collapseFilePath = Posix.joinPath . reverse . L.foldl' go [] . splitDirectories where go rs "." = rs go r@(p:rs) ".." = case p of ".." -> "..":r (checkPathSeperator -> Just True) -> "..":r _ -> rs go _ (checkPathSeperator -> Just True) = [[Posix.pathSeparator]] go rs x = x:rs isSingleton [] = Nothing isSingleton [x] = Just x isSingleton _ = Nothing checkPathSeperator = fmap isPathSeparator . isSingleton -- -- File selection from the archive -- filteredFilesFromArchive :: Archive -> (FilePath -> Bool) -> [(FilePath, BL.ByteString)] filteredFilesFromArchive zf f = mapMaybe (fileAndBinary zf) (filter f (filesInArchive zf)) where fileAndBinary :: Archive -> FilePath -> Maybe (FilePath, BL.ByteString) fileAndBinary a fp = findEntryByPath fp a >>= \e -> Just (fp, fromEntry e) --- --- Squash blocks into inlines --- blockToInlines :: Block -> Inlines blockToInlines (Plain ils) = B.fromList ils blockToInlines (Para ils) = B.fromList ils blockToInlines (LineBlock lns) = B.fromList $ combineLines lns blockToInlines (CodeBlock attr str) = B.codeWith attr str blockToInlines (RawBlock (Format fmt) str) = B.rawInline fmt str blockToInlines (BlockQuote blks) = blocksToInlines' blks blockToInlines (OrderedList _ blkslst) = mconcat $ map blocksToInlines' blkslst blockToInlines (BulletList blkslst) = mconcat $ map blocksToInlines' blkslst blockToInlines (DefinitionList pairslst) = mconcat $ map f pairslst where f (ils, blkslst) = B.fromList ils <> B.str ":" <> B.space <> mconcat (map blocksToInlines' blkslst) blockToInlines (Header _ _ ils) = B.fromList ils blockToInlines HorizontalRule = mempty blockToInlines (Table _ _ _ (TableHead _ hbd) bodies (TableFoot _ fbd)) = mconcat $ intersperse B.linebreak $ map (mconcat . map blocksToInlines') (plainRowBody <$> hbd <> unTableBodies bodies <> fbd) where plainRowBody (Row _ body) = cellBody <$> body cellBody (Cell _ _ _ _ body) = body unTableBody (TableBody _ _ hd bd) = hd <> bd unTableBodies = concatMap unTableBody blockToInlines (Div _ blks) = blocksToInlines' blks blockToInlines (Figure _ _ body) = blocksToInlines' body blocksToInlinesWithSep :: Inlines -> [Block] -> Inlines blocksToInlinesWithSep sep = mconcat . intersperse sep . map blockToInlines blocksToInlines' :: [Block] -> Inlines blocksToInlines' = blocksToInlinesWithSep defaultBlocksSeparator blocksToInlines :: [Block] -> [Inline] blocksToInlines = B.toList . blocksToInlines' -- | Inline elements used to separate blocks when squashing blocks into -- inlines. defaultBlocksSeparator :: Inlines defaultBlocksSeparator = -- This is used in the pandoc.utils.blocks_to_inlines function. Docs -- there should be updated if this is changed. B.linebreak -- -- Safe read -- safeRead :: (MonadPlus m, Read a) => T.Text -> m a safeRead = safeStrRead . T.unpack safeStrRead :: (MonadPlus m, Read a) => String -> m a safeStrRead s = case reads s of (d,x):_ | all isSpace x -> return d _ -> mzero ================================================ FILE: src/Text/Pandoc/Slides.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Slides Copyright : Copyright (C) 2012-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Utility functions for splitting documents into slides for slide show formats (dzslides, revealjs, s5, slidy, slideous, beamer). -} module Text.Pandoc.Slides ( getSlideLevel, prepSlides ) where import Text.Pandoc.Definition -- | Find level of header that starts slides (defined as the least header -- level that occurs before a non-header/non-hrule in the blocks). getSlideLevel :: [Block] -> Int getSlideLevel = go 6 where go least (Header n _ _ : x : xs) | n < least && nonHOrHR x = go n xs | otherwise = go least (x:xs) go least (Div _ bs : xs) = min (go least bs) (go least xs) go least (_ : xs) = go least xs go least [] = least nonHOrHR Header{} = False nonHOrHR HorizontalRule = False nonHOrHR _ = True -- | Prepare a block list to be passed to makeSections. prepSlides :: Int -> [Block] -> [Block] prepSlides slideLevel = ensureStartWithH . splitHrule . extractRefsHeader where splitHrule (HorizontalRule : Header n attr xs : ys) | n == slideLevel = Header slideLevel attr xs : splitHrule ys splitHrule (HorizontalRule : xs) = Header slideLevel nullAttr [Str "\0"] : splitHrule xs splitHrule (x : xs) = x : splitHrule xs splitHrule [] = [] extractRefsHeader bs = case reverse bs of (Div ("refs",classes,kvs) (Header n attrs xs : ys) : zs) -> reverse zs ++ [Header n attrs xs, Div ("refs",classes,kvs) ys] _ -> bs ensureStartWithH bs@(Header n _ _:_) | n <= slideLevel = bs ensureStartWithH bs@(Div _ (Header n _ _:_) : _) | n <= slideLevel = bs ensureStartWithH bs = Header slideLevel nullAttr [Str "\0"] : bs ================================================ FILE: src/Text/Pandoc/Sources.hs ================================================ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Sources Copyright : Copyright (C) 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Defines Sources object to be used as input to pandoc parsers and redefines Char parsers so they get source position information from it. -} module Text.Pandoc.Sources ( Sources(..) , ToSources(..) , UpdateSourcePos(..) , sourcesToText , initialSourceName , addToSources , ensureFinalNewlines , addToInput , satisfy , oneOf , noneOf , anyChar , char , string , newline , space , spaces , letter , digit , hexDigit , alphaNum ) where import qualified Text.Parsec as P import Text.Parsec (Stream(..), ParsecT) import Text.Parsec.Pos as P import Data.Text (Text) import qualified Data.Text as T import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL import Data.Char (isSpace, isLetter, isAlphaNum, isDigit, isHexDigit) import Data.String (IsString(..)) import qualified Data.List.NonEmpty as NonEmpty -- | A list of inputs labeled with source positions. It is assumed -- that the 'Text's have @\n@ line endings. newtype Sources = Sources { unSources :: [(SourcePos, Text)] } deriving (Show, Semigroup, Monoid) instance Monad m => Stream Sources m Char where uncons (Sources []) = return Nothing uncons (Sources ((pos,t):rest)) = case T.uncons t of Nothing -> uncons (Sources rest) Just (c,t') -> return $ Just (c, Sources ((pos,t'):rest)) instance IsString Sources where fromString s = Sources [(P.initialPos "", T.pack (filter (/='\r') s))] class ToSources a where toSources :: a -> Sources instance ToSources Text where toSources t = Sources [(P.initialPos "", T.filter (/='\r') t)] instance ToSources [(FilePath, Text)] where toSources = Sources . map (\(fp,t) -> (P.initialPos fp, T.snoc (T.filter (/='\r') t) '\n')) instance ToSources Sources where toSources = id sourcesToText :: Sources -> Text sourcesToText (Sources xs) = mconcat $ map snd xs addToSources :: Monad m => SourcePos -> Text -> ParsecT Sources u m () addToSources pos t = do curpos <- P.getPosition Sources xs <- P.getInput let xs' = case xs of [] -> [] ((_,t'):rest) -> (curpos,t'):rest P.setInput $ Sources ((pos, T.filter (/='\r') t):xs') ensureFinalNewlines :: Int -- ^ number of trailing newlines -> Sources -> Sources ensureFinalNewlines n (Sources xs) = case NonEmpty.nonEmpty xs of Nothing -> Sources [(initialPos "", T.replicate n "\n")] Just lst -> case NonEmpty.last lst of (spos, t) -> case T.length (T.takeWhileEnd (=='\n') t) of len | len >= n -> Sources xs | otherwise -> Sources (NonEmpty.init lst ++ [(spos, t <> T.replicate (n - len) "\n")]) class UpdateSourcePos s c where updateSourcePos :: SourcePos -> c -> s -> SourcePos instance UpdateSourcePos Text Char where updateSourcePos pos c _ = updatePosChar pos c instance UpdateSourcePos [Char] Char where updateSourcePos pos c _ = updatePosChar pos c instance UpdateSourcePos BS.ByteString Char where updateSourcePos pos c _ = updatePosChar pos c instance UpdateSourcePos BL.ByteString Char where updateSourcePos pos c _ = updatePosChar pos c instance UpdateSourcePos Sources Char where updateSourcePos pos c sources = case sources of Sources [] -> updatePosChar pos c Sources ((_,t):(pos',_):_) | T.null t -> pos' Sources _ -> case c of '\n' -> incSourceLine (setSourceColumn pos 1) 1 '\t' -> incSourceColumn pos (4 - ((sourceColumn pos - 1) `mod` 4)) _ -> incSourceColumn pos 1 -- | Get name of first source in 'Sources'. initialSourceName :: Sources -> FilePath initialSourceName (Sources []) = "" initialSourceName (Sources ((pos,_):_)) = sourceName pos -- | Add some text to the beginning of the input sources. -- This simplifies code that expands macros. addToInput :: Monad m => Text -> ParsecT Sources u m () addToInput t = do Sources xs <- P.getInput case xs of [] -> P.setInput $ Sources [(initialPos "",t)] (pos,t'):rest -> P.setInput $ Sources ((pos, t <> t'):rest) -- We need to redefine the parsers in Text.Parsec.Char so that they -- update source positions properly from the Sources stream. satisfy :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => (Char -> Bool) -> ParsecT s u m Char satisfy f = P.tokenPrim show updateSourcePos matcher where matcher !c = if f c then Just c else Nothing oneOf :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => [Char] -> ParsecT s u m Char oneOf cs = satisfy (`elem` cs) noneOf :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => [Char] -> ParsecT s u m Char noneOf cs = satisfy (`notElem` cs) anyChar :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m Char anyChar = satisfy (const True) char :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => Char -> ParsecT s u m Char char c = satisfy (== c) string :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => [Char] -> ParsecT s u m [Char] string = mapM char newline :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m Char newline = satisfy (== '\n') space :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m Char space = satisfy isSpace spaces :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m () spaces = P.skipMany space P. "white space" letter :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m Char letter = satisfy isLetter alphaNum :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m Char alphaNum = satisfy isAlphaNum digit :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m Char digit = satisfy isDigit hexDigit :: (Monad m, Stream s m Char, UpdateSourcePos s Char) => ParsecT s u m Char hexDigit = satisfy isHexDigit ================================================ FILE: src/Text/Pandoc/TeX.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {- | Module : Text.Pandoc.TeX Copyright : Copyright (C) 2017-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Types for TeX tokens and macros. -} module Text.Pandoc.TeX ( Tok(..) , TokType(..) , Macro(..) , ArgSpec(..) , ExpansionPoint(..) , MacroScope(..) , SourcePos ) where import Data.Text (Text) import Text.Parsec (SourcePos, sourceName) import Text.Pandoc.Sources import Data.List (groupBy) data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment | Esc1 | Esc2 | Arg Int | DeferredArg Int deriving (Eq, Ord, Show) data Tok = Tok SourcePos TokType Text deriving (Eq, Ord, Show) instance ToSources [Tok] where toSources = Sources . map (\ts -> case ts of Tok p _ _ : _ -> (p, mconcat $ map tokToText ts) _ -> error "toSources [Tok] encountered empty group") . groupBy (\(Tok p1 _ _) (Tok p2 _ _) -> sourceName p1 == sourceName p2) tokToText :: Tok -> Text tokToText (Tok _ _ t) = t data ExpansionPoint = ExpandWhenDefined | ExpandWhenUsed deriving (Eq, Ord, Show) data MacroScope = GlobalScope | GroupScope deriving (Eq, Ord, Show) data Macro = Macro MacroScope ExpansionPoint [ArgSpec] (Maybe [Tok]) [Tok] deriving Show data ArgSpec = ArgNum Int | Pattern [Tok] deriving Show ================================================ FILE: src/Text/Pandoc/Templates.hs ================================================ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Templates Copyright : Copyright (C) 2009-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Utility functions for working with pandoc templates. 'WithDefaultPartials' and 'WithPartials' are Monad wrappers. Wrapping these around an instance of 'PandocMonad' gives different instances of 'TemplateMonad', with different search behaviors when retrieving partials. To compile a template and limit partial search to pandoc’s data files, use @runWithDefaultPartials (compileTemplate ...)@. To compile a template and allow partials to be found locally (either on the file system or via HTTP, in the event that the main template has an absolute URL), ue @runWithPartials (compileTemplate ...)@. 'getTemplate' seeks a template locally, or via HTTP if the template has an absolute URL, falling back to the data files if not found. -} module Text.Pandoc.Templates ( Template , WithDefaultPartials(..) , WithPartials(..) , compileTemplate , renderTemplate , getTemplate , getDefaultTemplate , compileDefaultTemplate ) where import System.FilePath ((<.>), (), takeFileName) import Text.DocTemplates (Template, TemplateMonad(..), compileTemplate, renderTemplate) import Text.Pandoc.Class.CommonState (CommonState(..)) import Text.Pandoc.Class.PandocMonad (PandocMonad, fetchItem, getCommonState, modifyCommonState, toTextM) import Text.Pandoc.Data (readDataFile) import Control.Monad.Except (catchError, throwError) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Error import System.IO.Error (isDoesNotExistError) -- | Wrap a Monad in this if you want partials to -- be taken only from the default data files. newtype WithDefaultPartials m a = WithDefaultPartials { runWithDefaultPartials :: m a } deriving (Functor, Applicative, Monad) -- | Wrap a Monad in this if you want partials to -- be looked for locally (or, when the main template -- is at a URL, via HTTP), falling back to default data files. newtype WithPartials m a = WithPartials { runWithPartials :: m a } deriving (Functor, Applicative, Monad) instance PandocMonad m => TemplateMonad (WithDefaultPartials m) where getPartial fp = WithDefaultPartials $ readDataFile ("templates" takeFileName fp) >>= toTextM fp instance PandocMonad m => TemplateMonad (WithPartials m) where getPartial fp = WithPartials $ getTemplate fp -- | Retrieve text for a template. getTemplate :: PandocMonad m => FilePath -> m Text getTemplate tp = ((do surl <- stSourceURL <$> getCommonState -- we don't want to look for templates remotely -- unless the full URL is specified: modifyCommonState $ \st -> st{ stSourceURL = Nothing } (bs, _) <- fetchItem $ T.pack tp modifyCommonState $ \st -> st{ stSourceURL = surl } return bs) `catchError` (\e -> case e of PandocResourceNotFound _ -> -- see #5987 on reason for takeFileName readDataFile ("templates" takeFileName tp) PandocIOError _ ioe | isDoesNotExistError ioe -> -- see #5987 on reason for takeFileName readDataFile ("templates" takeFileName tp) _ -> throwError e)) >>= toTextM tp -- | Get default template for the specified writer. getDefaultTemplate :: PandocMonad m => Text -- ^ Name of writer -> m Text getDefaultTemplate format = do case format of "native" -> return "" "csljson" -> return "" "json" -> return "" "xml" -> return "" "fb2" -> return "" "pptx" -> return "" "ipynb" -> return "" "asciidoctor" -> getDefaultTemplate "asciidoc" "asciidoc_legacy" -> getDefaultTemplate "asciidoc" "docx" -> getDefaultTemplate "openxml" "odt" -> getDefaultTemplate "opendocument" "html" -> getDefaultTemplate "html5" "docbook" -> getDefaultTemplate "docbook5" "epub" -> getDefaultTemplate "epub3" "jats" -> getDefaultTemplate "jats_archiving" "markdown_strict" -> getDefaultTemplate "markdown" "multimarkdown" -> getDefaultTemplate "markdown" "markdown_github" -> getDefaultTemplate "markdown" "markdown_mmd" -> getDefaultTemplate "markdown" "markdown_phpextra" -> getDefaultTemplate "markdown" "gfm" -> getDefaultTemplate "commonmark" "commonmark_x" -> getDefaultTemplate "commonmark" "bbcode_phpbb" -> getDefaultTemplate "bbcode" "bbcode_fluxbb" -> getDefaultTemplate "bbcode" "bbcode_steam" -> getDefaultTemplate "bbcode" "bbcode_hubzilla" -> getDefaultTemplate "bbcode" "bbcode_xenforo" -> getDefaultTemplate "bbcode" _ -> do let fname = "templates" "default" <.> T.unpack format readDataFile fname >>= toTextM fname -- | Get and compile default template for the specified writer. -- Raise an error on compilation failure. compileDefaultTemplate :: PandocMonad m => Text -> m (Template Text) compileDefaultTemplate writer = do res <- getDefaultTemplate writer >>= runWithDefaultPartials . compileTemplate ("templates/default." <> T.unpack writer) case res of Left e -> throwError $ PandocTemplateError (T.pack e) Right t -> return t ================================================ FILE: src/Text/Pandoc/Transforms.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StrictData #-} {- | Module : Text.Pandoc.Transforms Copyright : © 2006-2024 John MacFarlane License : GPL-2.0-or-later Maintainer : John MacFarlane Transformation of a Pandoc document post-parsing -} module Text.Pandoc.Transforms ( Transform , applyTransforms , adjustLinksAndIds , eastAsianLineBreakFilter , filterIpynbOutput , headerShift ) where import Data.List (sortOn) import Data.Text (Text) import Network.URI (unEscapeString) import Text.DocLayout (charWidth) import Text.Pandoc.Definition ( Pandoc (..), Attr, Block (..), Format (..), Inline (..) ) import Text.Pandoc.Generic (bottomUp) import Text.Pandoc.Options (Extensions) import Text.Pandoc.Shared (stringify, textToIdentifier) import Text.Pandoc.Walk (walk) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B -- | Transformation of a Pandoc document post-parsing type Transform = Pandoc -> Pandoc -- | Apply a list of transforms to a document, in order. applyTransforms :: Monad m => [Transform] -> Pandoc -> m Pandoc applyTransforms transforms d = return $ foldr ($) d transforms -- | Prefixes identifiers with a string derived from the filepath of -- @thisfile@; fixes links to targets in @allfiles@ accordingly. adjustLinksAndIds :: Extensions -- ^ defines how IDs are generated -> Text -- ^ thisfile -> [Text] -- ^ allfiles -> Transform adjustLinksAndIds exts thisfile allfiles | length allfiles > 1 = walk fixInline . walk fixBlock | otherwise = id where -- fix ids in blocks fixBlock :: Block -> Block fixBlock (CodeBlock attr t) = CodeBlock (fixAttrs attr) t fixBlock (Header lev attr ils) = Header lev (fixAttrs attr) ils fixBlock (Table attr cap cols th tbs tf) = Table (fixAttrs attr) cap cols th tbs tf fixBlock (Div attr bs) = Div (fixAttrs attr) bs fixBlock x = x -- fix ids and links in inlines fixInline :: Inline -> Inline fixInline (Code attr t) = Code (fixAttrs attr) t fixInline (Link attr ils (url,tit)) = Link (fixAttrs attr) ils (fixURL url,tit) fixInline (Image attr ils (url,tit)) = Image (fixAttrs attr) ils (fixURL url,tit) fixInline (Span attr ils) = Span (fixAttrs attr) ils fixInline x = x -- add thisfile as prefix of identifier fixAttrs :: Attr -> Attr fixAttrs (i,cs,kvs) | T.null i = (i,cs,kvs) | otherwise = (T.intercalate "__" (filter (not . T.null) [toIdent thisfile, i]), cs, kvs) -- turns a filepath into an identifier toIdent :: Text -> Text toIdent = textToIdentifier exts . T.intercalate "__" . T.split (\c -> c == '/' || c == '\\') -- if URL begins with file from allfiles, convert to -- an internal link with the appropriate identifier fixURL :: Text -> Text fixURL u = let (a,b) = T.break (== '#') $ T.pack . unEscapeString . T.unpack $ u filepart = if T.null a then toIdent thisfile else toIdent a fragpart = T.dropWhile (== '#') b in if T.null a || a `elem` allfiles then "#" <> T.intercalate "__" (filter (not . T.null) [filepart, fragpart]) else u -- | Process ipynb output cells. If mode is Nothing, -- remove all output. If mode is Just format, select -- best output for the format. If format is not ipynb, -- strip out ANSI escape sequences from CodeBlocks (see #5633). filterIpynbOutput :: Maybe Format -> Pandoc -> Pandoc filterIpynbOutput mode = walk go where go (Div (ident, "output":os, kvs) bs) = case mode of Nothing -> Div (ident, "output":os, kvs) [] -- "best" for ipynb includes all formats: Just fmt | fmt == Format "ipynb" -> Div (ident, "output":os, kvs) bs | otherwise -> Div (ident, "output":os, kvs) $ walk removeANSI $ take 1 $ sortOn rank bs where rank (RawBlock (Format "html") _) | fmt == Format "html" = 1 :: Int | fmt == Format "markdown" = 3 | otherwise = 4 rank (RawBlock (Format "latex") _) | fmt == Format "latex" = 1 | fmt == Format "markdown" = 3 | otherwise = 4 rank (RawBlock f _) | fmt == f = 1 | otherwise = 4 rank (Para [Image{}]) = 2 rank _ = 3 removeANSI (CodeBlock attr code) = CodeBlock attr (removeANSIEscapes code) removeANSI x = x removeANSIEscapes t | Just cs <- T.stripPrefix "\x1b[" t = removeANSIEscapes $ T.drop 1 $ T.dropWhile (/='m') cs | Just (c, cs) <- T.uncons t = T.cons c $ removeANSIEscapes cs | otherwise = "" go x = x -- | Remove soft breaks between East Asian characters. eastAsianLineBreakFilter :: Pandoc -> Pandoc eastAsianLineBreakFilter = bottomUp go where go (x:SoftBreak:y:zs) | Just (_, b) <- T.unsnoc $ stringify x , Just (c, _) <- T.uncons $ stringify y , charWidth b == 2 , charWidth c == 2 = x:y:zs | otherwise = x:SoftBreak:y:zs go xs = xs -- | Shift header levels up or down. headerShift :: Int -> Pandoc -> Pandoc headerShift n (Pandoc meta (Header m _ ils : bs)) | n < 0 , m + n == 0 = headerShift n $ B.setTitle (B.fromList ils) $ Pandoc meta bs -- for this case, see #10459: headerShift n (Pandoc meta (Div attr@(_,"section":_,_) (Header m _ ils : as) : bs)) | n < 0 , m + n == 0 = headerShift n $ B.setTitle (B.fromList ils) $ Pandoc meta (Div attr as : bs) headerShift n (Pandoc meta bs) = Pandoc meta (walk shift bs) where shift :: Block -> Block shift (Header level attr inner) | level + n > 0 = Header (level + n) attr inner | otherwise = Para inner shift x = x ================================================ FILE: src/Text/Pandoc/Translations/Types.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Translations.Types Copyright : Copyright (C) 2017-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Data types for localization. Translations are stored in @data/translations/langname.trans@, where langname can be the full BCP47 language specifier, or just the language part. File format is: > # A comment, ignored > Figure: Figura > Index: Indeksi -} module Text.Pandoc.Translations.Types ( Term(..) , Translations , lookupTerm ) where import Data.Aeson.Types (Value(..), FromJSON(..)) import qualified Data.Aeson.Types as Aeson import qualified Data.Map as M import qualified Data.Text as T import GHC.Generics (Generic) import Text.Pandoc.Shared (safeRead) data Term = Abstract | Appendix | Bibliography | Cc | Chapter | Contents | Encl | Figure | Glossary | Index | Listing | ListOfFigures | ListOfTables | Page | Part | Preface | Proof | References | See | SeeAlso | Table | To deriving (Show, Eq, Ord, Generic, Enum, Read) newtype Translations = Translations (M.Map Term T.Text) deriving (Show, Generic, Semigroup, Monoid) instance FromJSON Term where parseJSON (String t) = case safeRead t of Just t' -> pure t' Nothing -> Prelude.fail $ "Invalid Term name " ++ show t parseJSON invalid = Aeson.typeMismatch "Term" invalid instance FromJSON Translations where parseJSON o@(Object{}) = do xs <- parseJSON o >>= mapM addItem . M.toList return $ Translations (M.fromList xs) where addItem (k,v) = case safeRead k of Nothing -> Prelude.fail $ "Invalid Term name " ++ show k Just t -> case v of (String s) -> return (t, T.strip s) inv -> Aeson.typeMismatch "String" inv parseJSON invalid = Aeson.typeMismatch "Translations" invalid -- | Lookup a term in a 'Translations'. lookupTerm :: Term -> Translations -> Maybe T.Text lookupTerm t (Translations tm) = M.lookup t tm ================================================ FILE: src/Text/Pandoc/Translations.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Translations Copyright : Copyright (C) 2017-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Functions for getting localized translations of terms. -} module Text.Pandoc.Translations ( module Text.Pandoc.Translations.Types , readTranslations , getTranslations , setTranslations , translateTerm ) where import Text.Pandoc.Translations.Types import Text.Pandoc.Class (PandocMonad(..), toTextM, report) import Text.Pandoc.Class.CommonState (CommonState(..)) import Text.Pandoc.Data (readDataFile) import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Logging (LogMessage(..)) import Control.Monad.Except (catchError) import qualified Data.Text as T import qualified Data.Yaml as Yaml import qualified Text.Pandoc.UTF8 as UTF8 import Data.Yaml (prettyPrintParseException) import Text.Collate.Lang (Lang(..), renderLang) -- | Parse YAML translations. readTranslations :: T.Text -> Either T.Text Translations readTranslations s = case Yaml.decodeAllEither' $ UTF8.fromText s of Left err' -> Left $ T.pack $ prettyPrintParseException err' Right (t:_) -> Right t Right [] -> Left "empty YAML document" -- | Select the language to use with 'translateTerm'. -- Note that this does not read a translation file; -- that is only done the first time 'translateTerm' is -- used. setTranslations :: PandocMonad m => Lang -> m () setTranslations lang = modifyCommonState $ \st -> st{ stTranslations = Just (lang, Nothing) } -- | Load term map. getTranslations :: PandocMonad m => m Translations getTranslations = do mbtrans <- getsCommonState stTranslations case mbtrans of Nothing -> return mempty -- no language defined Just (_, Just t) -> return t Just (lang, Nothing) -> do -- read from file let translationFile = "translations/" <> renderLang lang <> ".yaml" let fallbackFile = "translations/" <> langLanguage lang <> ".yaml" let getTrans fp = do txt <- readDataFile fp >>= toTextM fp case readTranslations txt of Left e -> do report $ CouldNotLoadTranslations (renderLang lang) (T.pack fp <> ": " <> e) -- make sure we don't try again... modifyCommonState $ \st -> st{ stTranslations = Nothing } return mempty Right t -> do modifyCommonState $ \st -> st{ stTranslations = Just (lang, Just t) } return t catchError (getTrans $ T.unpack translationFile) (\_ -> catchError (getTrans $ T.unpack fallbackFile) (\e -> do report $ CouldNotLoadTranslations (renderLang lang) $ case e of PandocCouldNotFindDataFileError _ -> "data file " <> fallbackFile <> " not found" _ -> "" -- make sure we don't try again... modifyCommonState $ \st -> st{ stTranslations = Nothing } return mempty)) -- | Get a translation from the current term map. -- Issue a warning if the term is not defined. translateTerm :: PandocMonad m => Term -> m T.Text translateTerm term = do translations <- getTranslations case lookupTerm term translations of Just s -> return s Nothing -> do report $ NoTranslation $ T.pack $ show term return "" ================================================ FILE: src/Text/Pandoc/URI.hs ================================================ {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-} {- | Module : Text.Pandoc.URI Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable -} module Text.Pandoc.URI ( urlEncode , escapeURI , isURI , schemes , uriPathToPath , pBase64DataURI ) where import qualified Network.HTTP.Types as HTTP import Data.ByteString.Base64 (decodeLenient) import Text.Pandoc.MIME (MimeType) import qualified Data.ByteString as B import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.Text as T import qualified Data.Set as Set import Data.Char (isSpace, isAscii, isHexDigit, chr) import Safe (readMay) import Network.URI (URI (uriScheme), parseURI, escapeURIString) import qualified Data.Attoparsec.Text as A import Data.Text.Encoding (encodeUtf8) import Control.Applicative (many, (<|>)) urlEncode :: T.Text -> T.Text urlEncode = UTF8.toText . HTTP.urlEncode True . UTF8.fromText -- | Escape whitespace and some punctuation characters in URI. escapeURI :: T.Text -> T.Text escapeURI = T.pack . escapeURIString (not . needsEscaping) . T.unpack where needsEscaping c = isSpace c || T.any (== c) "<>|\"{}[]^`" -- -- IANA URIs -- -- | Schemes from http://www.iana.org/assignments/uri-schemes.html plus -- the unofficial schemes doi, javascript, isbn, pmid. schemes :: Set.Set T.Text schemes = Set.fromList -- Official IANA schemes [ "aaa", "aaas", "about", "acap", "acct", "acr", "adiumxtra", "afp", "afs" , "aim", "appdata", "apt", "attachment", "aw", "barion", "beshare", "bitcoin" , "blob", "bolo", "browserext", "callto", "cap", "chrome", "chrome-extension" , "cid", "coap", "coaps", "com-eventbrite-attendee", "content", "crid", "cvs" , "data", "dav", "dict", "dis", "dlna-playcontainer", "dlna-playsingle" , "dns", "dntp", "dtn", "dvb", "ed2k", "example", "facetime", "fax", "feed" , "feedready", "file", "filesystem", "finger", "fish", "ftp", "geo", "gg" , "git", "gizmoproject", "go", "gopher", "graph", "gtalk", "h323", "ham" , "hcp", "http", "https", "hxxp", "hxxps", "hydrazone", "iax", "icap", "icon" , "im", "imap", "info", "iotdisco", "ipn", "ipp", "ipps", "irc", "irc6" , "ircs", "iris", "iris.beep", "iris.lwz", "iris.xpc", "iris.xpcs" , "isostore", "itms", "jabber", "jar", "jms", "keyparc", "lastfm", "ldap" , "ldaps", "lvlt", "magnet", "mailserver", "mailto", "maps", "market" , "message", "mid", "mms", "modem", "mongodb", "moz", "ms-access" , "ms-browser-extension", "ms-drive-to", "ms-enrollment", "ms-excel" , "ms-gamebarservices", "ms-getoffice", "ms-help", "ms-infopath" , "ms-media-stream-id", "ms-officeapp", "ms-project", "ms-powerpoint" , "ms-publisher", "ms-search-repair", "ms-secondary-screen-controller" , "ms-secondary-screen-setup", "ms-settings", "ms-settings-airplanemode" , "ms-settings-bluetooth", "ms-settings-camera", "ms-settings-cellular" , "ms-settings-cloudstorage", "ms-settings-connectabledevices" , "ms-settings-displays-topology", "ms-settings-emailandaccounts" , "ms-settings-language", "ms-settings-location", "ms-settings-lock" , "ms-settings-nfctransactions", "ms-settings-notifications" , "ms-settings-power", "ms-settings-privacy", "ms-settings-proximity" , "ms-settings-screenrotation", "ms-settings-wifi", "ms-settings-workplace" , "ms-spd", "ms-sttoverlay", "ms-transit-to", "ms-virtualtouchpad" , "ms-visio", "ms-walk-to", "ms-whiteboard", "ms-whiteboard-cmd", "ms-word" , "msnim", "msrp", "msrps", "mtqp", "mumble", "mupdate", "mvn", "news", "nfs" , "ni", "nih", "nntp", "notes", "ocf", "oid", "onenote", "onenote-cmd" , "opaquelocktoken", "pack", "palm", "paparazzi", "pkcs11", "platform", "pop" , "pres", "prospero", "proxy", "pwid", "psyc", "qb", "query", "redis" , "rediss", "reload", "res", "resource", "rmi", "rsync", "rtmfp", "rtmp" , "rtsp", "rtsps", "rtspu", "secondlife", "service", "session", "sftp", "sgn" , "shttp", "sieve", "sip", "sips", "skype", "smb", "sms", "smtp", "snews" , "snmp", "soap.beep", "soap.beeps", "soldat", "spotify", "ssh", "steam" , "stun", "stuns", "submit", "svn", "tag", "teamspeak", "tel", "teliaeid" , "telnet", "tftp", "things", "thismessage", "tip", "tn3270", "tool", "turn" , "turns", "tv", "udp", "unreal", "urn", "ut2004", "v-event", "vemmi" , "ventrilo", "videotex", "vnc", "view-source", "wais", "webcal", "wpid" , "ws", "wss", "wtai", "wyciwyg", "xcon", "xcon-userid", "xfire" , "xmlrpc.beep", "xmlrpc.beeps", "xmpp", "xri", "ymsgr", "z39.50", "z39.50r" , "z39.50s" -- Unofficial schemes , "doi", "gemini", "isbn", "javascript", "pmid" ] -- | Check if the string is a valid URL with a IANA or frequently used but -- unofficial scheme (see @schemes@). isURI :: T.Text -> Bool isURI t = -- If it's a base 64 data: URI, avoid the expensive call to parseURI: case A.parseOnly (pBase64DataURI *> A.endOfInput) t of Right () -> True Left _ -> -- we URI-escape non-ASCII characters because otherwise parseURI will choke: maybe False hasKnownScheme . parseURI . escapeURIString isAscii . T.unpack $ t where hasKnownScheme = (`Set.member` schemes) . T.toLower . T.filter (/= ':') . T.pack . uriScheme -- | Converts the path part of a file: URI to a regular path. -- On windows, @/c:/foo@ should be @c:/foo@. -- On linux, @/foo@ should be @/foo@. uriPathToPath :: T.Text -> FilePath uriPathToPath (T.unpack -> path) = #ifdef _WINDOWS case path of '/':ps -> ps ps -> ps #else path #endif pBase64DataURI :: A.Parser (B.ByteString, MimeType) pBase64DataURI = base64uri where base64uri = do A.string "data:" mime <- do n1 <- restrictedName A.char '/' n2 <- restrictedName mps <- many mediaParam pure $ n1 <> "/" <> n2 <> mconcat mps A.string ";base64," b64 <- mconcat <$> many (A.takeWhile1 (A.inClass "A-Za-z0-9/+ \t\r\n") <|> percentOctet) A.skipWhile (== '=') -- this decode should be lazy: pure (decodeLenient (encodeUtf8 b64), mime) percentOctet = do A.char '%' x <- A.satisfy isHexDigit y <- A.satisfy isHexDigit case readMay ['0','x',x,y] of Nothing -> fail $ "Could not read percent encoded byte " <> [x,y] Just d -> pure $ T.singleton $ chr d restrictedName = do c <- A.satisfy (A.inClass "A-Za-z0-9") rest <- A.takeWhile (A.inClass "A-Za-z0-9!#$&^_.+-") pure $ T.singleton c <> rest mediaParam = do A.char ';' A.skipWhile isSpace k <- restrictedName A.char '=' v <- A.takeWhile (/=';') pure $ ";" <> k <> "=" <> v ================================================ FILE: src/Text/Pandoc/UTF8.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.UTF8 Copyright : Copyright (C) 2010-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable UTF-8 aware string IO functions. -} module Text.Pandoc.UTF8 ( readFile , getContents , writeFileWith , writeFile , putStrWith , putStr , putStrLnWith , putStrLn , hPutStrWith , hPutStr , hPutStrLnWith , hPutStrLn , hGetContents , toString , toText , fromString , fromText , toStringLazy , fromTextLazy , toTextLazy , fromStringLazy , encodePath , decodeArg ) where import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as BL import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.IO as TIO import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Encoding as TL import Prelude hiding (getContents, putStr, putStrLn, readFile, writeFile) import System.IO hiding (getContents, hGetContents, hPutStr, hPutStrLn, putStr, putStrLn, readFile, writeFile) readFile :: FilePath -> IO Text readFile f = do h <- openFile (encodePath f) ReadMode hGetContents h getContents :: IO Text getContents = hGetContents stdin writeFileWith :: Newline -> FilePath -> Text -> IO () writeFileWith eol f s = withFile (encodePath f) WriteMode $ \h -> hPutStrWith eol h s writeFile :: FilePath -> Text -> IO () writeFile = writeFileWith nativeNewline putStrWith :: Newline -> Text -> IO () putStrWith eol s = hPutStrWith eol stdout s putStr :: Text -> IO () putStr = putStrWith nativeNewline putStrLnWith :: Newline -> Text -> IO () putStrLnWith eol s = hPutStrLnWith eol stdout s putStrLn :: Text -> IO () putStrLn = putStrLnWith nativeNewline hPutStrWith :: Newline -> Handle -> Text -> IO () hPutStrWith eol h s = hSetNewlineMode h (NewlineMode eol eol) >> hSetEncoding h utf8 >> TIO.hPutStr h s hPutStr :: Handle -> Text -> IO () hPutStr = hPutStrWith nativeNewline hPutStrLnWith :: Newline -> Handle -> Text -> IO () hPutStrLnWith eol h s = hSetNewlineMode h (NewlineMode eol eol) >> hSetEncoding h utf8 >> TIO.hPutStrLn h s hPutStrLn :: Handle -> Text -> IO () hPutStrLn = hPutStrLnWith nativeNewline hGetContents :: Handle -> IO Text hGetContents = fmap toText . B.hGetContents -- | Convert UTF8-encoded ByteString to Text, also -- removing '\\r' characters. toText :: B.ByteString -> Text toText = T.decodeUtf8 . filterCRs . dropBOM where dropBOM bs = if "\xEF\xBB\xBF" `B.isPrefixOf` bs then B.drop 3 bs else bs filterCRs = B.filter (/='\r') -- | Convert UTF8-encoded ByteString to String, also -- removing '\\r' characters. toString :: B.ByteString -> String toString = T.unpack . toText -- | Convert UTF8-encoded ByteString to Text, also -- removing '\\r' characters. toTextLazy :: BL.ByteString -> TL.Text toTextLazy = TL.decodeUtf8 . filterCRs . dropBOM where dropBOM bs = if "\xEF\xBB\xBF" `BL.isPrefixOf` bs then BL.drop 3 bs else bs filterCRs = BL.filter (/='\r') -- | Convert UTF8-encoded ByteString to String, also -- removing '\\r' characters. toStringLazy :: BL.ByteString -> String toStringLazy = TL.unpack . toTextLazy fromText :: Text -> B.ByteString fromText = T.encodeUtf8 fromTextLazy :: TL.Text -> BL.ByteString fromTextLazy = TL.encodeUtf8 fromString :: String -> B.ByteString fromString = fromText . T.pack fromStringLazy :: String -> BL.ByteString fromStringLazy = fromTextLazy . TL.pack encodePath :: FilePath -> FilePath encodePath = id {-# DEPRECATED decodeArg "decodeArg is now a no-op" #-} decodeArg :: String -> String decodeArg = id ================================================ FILE: src/Text/Pandoc/UUID.hs ================================================ {- | Module : Text.Pandoc.UUID Copyright : Copyright (C) 2010-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable UUID generation using Version 4 (random method) described in RFC4122. See http://tools.ietf.org/html/rfc4122 -} module Text.Pandoc.UUID ( UUID(..), getRandomUUID ) where import Data.Bits (clearBit, setBit) import Data.Word import System.Random (RandomGen, randoms) import Text.Printf (printf) import Text.Pandoc.Class.PandocMonad data UUID = UUID Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 instance Show UUID where show (UUID a b c d e f g h i j k l m n o p) = "urn:uuid:" ++ printf "%02x" a ++ printf "%02x" b ++ printf "%02x" c ++ printf "%02x" d ++ "-" ++ printf "%02x" e ++ printf "%02x" f ++ "-" ++ printf "%02x" g ++ printf "%02x" h ++ "-" ++ printf "%02x" i ++ printf "%02x" j ++ "-" ++ printf "%02x" k ++ printf "%02x" l ++ printf "%02x" m ++ printf "%02x" n ++ printf "%02x" o ++ printf "%02x" p getUUID :: RandomGen g => g -> UUID getUUID gen = case take 16 (randoms gen :: [Word8]) of [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p] -> -- set variant let i' = i `setBit` 7 `clearBit` 6 -- set version (0100 for random) g' = g `clearBit` 7 `setBit` 6 `clearBit` 5 `clearBit` 4 in UUID a b c d e f g' h i' j k l m n o p _ -> error "not enough random numbers for UUID" -- should not happen getRandomUUID :: PandocMonad m => m UUID getRandomUUID = getUUID <$> newStdGen ================================================ FILE: src/Text/Pandoc/Version.hs ================================================ {-# LANGUAGE CPP #-} {- | Module : Text.Pandoc.Version Copyright : Copyright (C) 2022-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Version information. -} module Text.Pandoc.Version ( pandocVersion, pandocVersionText ) where import Data.Version (Version, showVersion) import Paths_pandoc (version) import qualified Data.Text as T -- | Version number of pandoc library. pandocVersion :: Version pandocVersion = version -- | Text representation of the library's version number. pandocVersionText :: T.Text pandocVersionText = T.pack $ showVersion version ================================================ FILE: src/Text/Pandoc/Writers/ANSI.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.ANSI Copyright : © 2024 Evan Silberman © 2025 Pandoc contributors License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to Ansi terminal output. -} module Text.Pandoc.Writers.ANSI ( writeANSI ) where import Control.Monad.State.Strict ( StateT, gets, modify, evalStateT ) import Control.Monad (foldM) import Data.List (intersperse) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Text (Text) import Text.DocLayout ((<+>), ($$), ($+$)) import Text.DocTemplates (Context(..)) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (highlight, formatANSI) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Math(texMathToInlines) import Text.Pandoc.Writers.Shared import qualified Data.Text as T import Data.Text.Lazy (toStrict) import qualified Text.DocLayout as D import qualified Text.Pandoc.Highlighting as HL hr :: D.HasChars a => D.Doc a hr = rule 20 rule :: D.HasChars a => Int -> D.Doc a rule n = D.literal $ D.replicateChar n '─' data WriterState = WriterState { stNotes :: [D.Doc Text] -- Footnotes , stColumns :: Int -- Width of the rendered text block , stInner :: Bool -- Are we at the document's top-level or in a nested construct? , stNextFigureNum :: Int , stInFigure :: Bool , stInTable :: Bool } type TW = StateT WriterState withFewerColumns :: PandocMonad m => Int -> TW m a -> TW m a withFewerColumns n a = do cols <- gets stColumns inner <- gets stInner modify $ \s -> s{stColumns = max (cols - n) 4, stInner = True} result <- a modify $ \s -> s{stColumns = cols, stInner = inner} return result -- | Convert Pandoc to ANSI writeANSI :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeANSI opts document = evalStateT (pandocToANSI opts document) WriterState { stNotes = [], stColumns = (writerColumns opts), stInner = False, stNextFigureNum = 1, stInFigure = False, stInTable = False } -- | Return ANSI-styled version of document pandocToANSI :: PandocMonad m => WriterOptions -> Pandoc -> TW m Text pandocToANSI opts (Pandoc meta blocks) = do metadata <- metaToContext opts (blockListToANSI opts) (inlineListToANSI opts) meta let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing let title = titleBlock colwidth metadata let blocks' = makeSections (writerNumberSections opts) Nothing blocks body <- blockListToANSI opts blocks' notes <- gets $ reverse . stNotes let notemark x = D.literal (tshow (x :: Int) <> ".") <+> D.space let marks = map notemark [1..length notes] let hangWidth = foldr (max . D.offset) 0 marks let notepretty | not (null notes) = (case colwidth of Nothing -> hr Just w -> D.cblock w hr) $+$ hangMarks hangWidth marks notes | otherwise = D.empty let main = body $+$ notepretty let context = defField "body" main $ defField "titleblock" title metadata return $ case writerTemplate opts of Nothing -> toStrict $ D.renderANSI colwidth main Just tpl -> toStrict $ D.renderANSI colwidth $ renderTemplate tpl context titleBlock :: Maybe Int -> Context Text -> D.Doc Text titleBlock width meta = if null most then D.empty else (maybe id D.cblock width) $ most $+$ hr where title = D.bold (fromMaybe D.empty $ getField "title" meta) subtitle = fromMaybe D.empty $ getField "subtitle" meta author = D.vcat $ fromMaybe [] $ getField "author" meta date = D.italic (fromMaybe D.empty $ getField "date" meta) most = (title $$ subtitle) $+$ author $+$ date hangMarks :: Int -> [D.Doc Text] -> [D.Doc Text] -> D.Doc Text hangMarks width markers contents = D.vsep (zipWith hangMark markers contents) where hangMark m d = D.rblock width m <+> D.nest (width + 1) d stackMarks :: [D.Doc Text] -> [D.Doc Text] -> D.Doc Text stackMarks markers contents = D.vsep (zipWith stack markers contents) where stack m d = m $$ D.nest 4 d -- | Convert Pandoc block element to ANSI blockToANSI :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> TW m (D.Doc Text) blockToANSI opts (Div _ bs) = blockListToANSI opts bs blockToANSI opts (Plain inlines) = inlineListToANSI opts inlines blockToANSI opts (Para inlines) = inlineListToANSI opts inlines blockToANSI opts (LineBlock lns) = do let go [] = return D.blankline go xs = inlineListToANSI opts xs lns' <- mapM go lns return $ D.vcat lns' blockToANSI _ b@(RawBlock _ _) = do report $ BlockNotRendered b return D.empty blockToANSI _ HorizontalRule = return $ D.blankline $$ hr $$ D.blankline blockToANSI opts (Header level (_, classes, kvs) inlines) = do contents <- inlineListToANSI opts inlines let secnum = fromMaybe mempty $ lookup "number" kvs let doNumber = writerNumberSections opts && not (T.null secnum) && "unnumbered" `notElem` classes let number | doNumber = D.hang (D.realLength secnum + 1) (header level (D.literal secnum) <> D.space) | otherwise = id return $ number (header level contents) $$ D.blankline where header 1 = (fmap T.toUpper) . D.bold header 2 = D.bold header _ = D.italic -- The approach to code blocks and highlighting here is a best-effort with -- existing tools. The Skylighting formatANSI function produces fully-rendered -- results, and its line numbers are followed by a tab character, which can -- produce less-than-ideal results depending on your terminal's tab stops. (See -- tabs(1)). A more ambitious approach here could process SourceLines into a -- Doc Text. blockToANSI opts (CodeBlock attr str) = do table <- gets stInTable let highlightWithStyle s = do let fmt o = formatANSI o s result = highlight (writerSyntaxMap opts) fmt attr str return $ case result of Left _ -> defaultStyle str Right f -> D.literal f inner <- case (table, writerHighlightMethod opts) of (_, NoHighlighting) -> return $ defaultStyle str (True, _) -> return $ defaultStyle str (False, Skylighting s) -> highlightWithStyle s (False, DefaultHighlighting) -> highlightWithStyle HL.defaultStyle (False, IdiomaticHighlighting) -> do report $ CouldNotHighlight "no idiomatic highlighting in ANSI" return $ defaultStyle str return $ nest table inner where defaultStyle = (D.fg D.red) . D.literal nest False = D.nest 4 nest True = id blockToANSI opts (BlockQuote blocks) = do contents <- withFewerColumns 2 $ blockListToANSI opts blocks return ( D.prefixed "│ " contents $$ D.blankline) blockToANSI opts (Table _ (Caption _ caption) colSpecs (TableHead _ thead) tbody (TableFoot _ tfoot)) = do let captionInlines = blocksToInlines caption captionMarkup <- if null captionInlines then return mempty else D.nest 2 <$> inlineListToANSI opts (blocksToInlines caption) wasTable <- gets stInTable modify $ \s -> s{stInTable = True} let tw = writerColumns opts let ncol = length colSpecs let inWidths = map snd colSpecs let spaceForColumns = tw - ncol + 1 -- reserve a 1-char gutter between tcols let claimWidth ColWidthDefault = 0 claimWidth (ColWidth n) = floor (n * fromIntegral spaceForColumns) let usedSpace = sum (map claimWidth inWidths) let remaining = spaceForColumns - usedSpace let defWidth = remaining `div` length (filter (== ColWidthDefault) inWidths) let maxWidth ColWidthDefault = defWidth maxWidth k = claimWidth k let widths = map maxWidth inWidths let decor = [D.hsep $ map rule widths] head' <- (makeRows widths . map unRow) thead body' <- (makeRows widths . map unRow) (tableBodiesToRows tbody) foot' <- (makeRows widths . map unRow) tfoot modify $ \s -> s{stInTable = wasTable} return $ D.vcat (head' <> decor <> body' <> decor <> foot') $+$ captionMarkup where unRow (Row _ cs) = cs makeRows ws rows = do (docs, _) <- foldM (goRow ws) ([], M.empty) rows return $ reverse docs goRow _ (r, spans) [] = -- Empty rows are not displayed but previous row spans still apply for them. let spans' = M.map decrementPreviousRowSpans spans in return (r, spans') goRow ws (r, spans) cs = do (d, (nextPos, spans'), _) <- foldM goCell ([], (0, spans), ws) cs let spans'' = decrementTrailingRowSpans nextPos spans' -- Handle previous row spans next to the end of the current row return (D.hcat (intersperse (D.vfill " ") $ reverse d):r, spans'') goCell (r, (colPos, spans), ws) cell@(Cell _ aln (RowSpan rspan) (ColSpan cspan) inner) | Just (ColSpan previousColSpan, spans') <- takePreviousSpansAtColumn colPos spans = do (r', nextPos, ws') <- makeCell r colPos ws AlignDefault previousColSpan [] goCell (r', (nextPos, spans'), ws') cell | otherwise = do (r', nextPos, ws') <- makeCell r colPos ws aln cspan inner let spans' = insertCurrentSpansAtColumn colPos spans (RowSpan rspan) (ColSpan cspan) return (r', (nextPos, spans'), ws') decrementPreviousRowSpans spans@(RowSpan rspan, cspan) = if rspan >= 1 then (RowSpan rspan - 1, cspan) else spans makeCell r colPos ws aln cspan inner = do let (ws', render) = next ws aln cspan innerDoc <- blockListToANSI opts inner return ((render innerDoc):r, colPos + cspan, ws') tcell AlignLeft = D.lblock tcell AlignRight = D.rblock tcell AlignCenter = D.cblock tcell AlignDefault = D.lblock next ws aln cspan = let (this, ws') = splitAt cspan ws w = sum this + cspan - 1 cell = (tcell aln) w in (ws', cell) blockToANSI opts (BulletList items) = do contents <- withFewerColumns 2 $ mapM (blockListToANSI opts) items return $ D.vsep (fmap hangMark contents) where hangMark d = D.hang 2 (D.literal "• ") d blockToANSI opts (OrderedList attribs items) = do let markers = fmap D.literal $ take (length items) $ orderedListMarkers attribs let hangWidth = foldr (max . D.offset) 0 markers contents <- withFewerColumns hangWidth $ mapM (blockListToANSI opts) items return $ hangMarks hangWidth markers contents <> D.cr blockToANSI opts (DefinitionList items) = do labels <- mapM (inlineListToANSI opts . fst) items columns <- gets stColumns let hangWidth = foldr (max . D.offset) 0 labels if hangWidth > floor (toRational columns / 10 * 3) then do contents <- withFewerColumns 4 $ mapM ((mapM (blockListToANSI opts)) . snd) items return $ stackMarks (D.bold <$> labels) (D.vsep <$> contents) <> D.cr else do contents <- withFewerColumns hangWidth $ mapM ((mapM (blockListToANSI opts)) . snd) items return $ hangMarks hangWidth (D.bold <$> labels) (D.vsep <$> contents) <> D.cr blockToANSI opts (Figure _ (Caption _ caption) body) = do let captionInlines = blocksToInlines caption figState <- gets stInFigure captionMarkup <- if null captionInlines then return mempty else D.nest 2 <$> inlineListToANSI opts (blocksToInlines caption) modify $ \s -> s{stInFigure = True} contents <- blockListToANSI opts body modify $ \s -> s{stInFigure = figState} return $ contents $+$ captionMarkup -- Auxiliary functions for lists: -- | Convert list of Pandoc block elements to ANSI blockListToANSI :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements -> TW m (D.Doc Text) blockListToANSI opts blocks = D.vsep <$> mapM (blockToANSI opts) blocks -- | Convert list of Pandoc inline elements to ANSI inlineListToANSI :: PandocMonad m => WriterOptions -> [Inline] -> TW m (D.Doc Text) inlineListToANSI opts lst = D.hcat <$> mapM (inlineToANSI opts) lst -- | Convert Pandoc inline element to ANSI inlineToANSI :: PandocMonad m => WriterOptions -> Inline -> TW m (D.Doc Text) inlineToANSI opts (Span _ lst) = inlineListToANSI opts lst inlineToANSI opts (Emph lst) = do contents <- inlineListToANSI opts lst return $ D.italic contents inlineToANSI opts (Underline lst) = do contents <- inlineListToANSI opts lst return $ D.underlined contents inlineToANSI opts (Strong lst) = do contents <- inlineListToANSI opts lst return $ D.bold contents inlineToANSI opts (Strikeout lst) = do contents <- inlineListToANSI opts lst return $ D.strikeout contents inlineToANSI opts (Superscript lst) = do case traverse toSuperscriptInline lst of Just xs -> inlineListToANSI opts xs Nothing -> D.parens <$> inlineListToANSI opts lst inlineToANSI opts (Subscript lst) = do case traverse toSubscriptInline lst of Just xs -> inlineListToANSI opts xs Nothing -> D.parens <$> inlineListToANSI opts lst inlineToANSI opts (SmallCaps lst) = inlineListToANSI opts lst inlineToANSI opts (Quoted SingleQuote lst) = do contents <- inlineListToANSI opts lst return $ "‘" <> contents <> "’" inlineToANSI opts (Quoted DoubleQuote lst) = do contents <- inlineListToANSI opts lst return $ "“" <> contents <> "”" inlineToANSI opts (Cite _ lst) = inlineListToANSI opts lst -- Making a judgment call here that for ANSI-formatted output -- intended for reading, we want to reflow inline Code on spaces inlineToANSI _ (Code _ str) = return $ D.bg D.white $ D.fg D.red $ " " <> D.hcat flow <> " " where flow = intersperse D.space (D.literal <$> T.words str) inlineToANSI _ (Str str) = return $ D.literal str inlineToANSI opts (Math t str) = texMathToInlines t str >>= inlineListToANSI opts inlineToANSI _ il@RawInline{} = do report $ InlineNotRendered il return "" inlineToANSI _ LineBreak = return D.cr inlineToANSI _ SoftBreak = return D.space inlineToANSI _ Space = return D.space inlineToANSI opts (Link (_, _, _) txt (src, _)) = do label <- inlineListToANSI opts txt return $ D.underlined $ D.fg D.cyan $ D.link src label inlineToANSI opts (Image _ alt _) = do infig <- gets stInFigure if not infig then do alt' <- inlineListToANSI opts alt return $ D.brackets $ "image: " <> alt' else return $ D.brackets "image" -- by construction, we should never be lacking in superscript characters -- for the footnote number, but we'll fall back to square brackets anyway inlineToANSI opts (Note contents) = do curNotes <- gets stNotes let newnum = tshow $ length curNotes + 1 contents' <- blockListToANSI opts contents modify $ \s -> s { stNotes = contents' : curNotes } let super = T.pack <$> (traverse toSuperscript (T.unpack newnum)) return $ D.literal $ fromMaybe ("[" <> newnum <> "]") super ================================================ FILE: src/Text/Pandoc/Writers/AnnotatedTable.hs ================================================ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE UndecidableInstances #-} {- | Module : Text.Pandoc.Writers.AnnotatedTable Copyright : Copyright 2020 Christian Despres License : GNU GPL, version 2 or above Maintainer : Christian Despres Stability : alpha Portability : portable Definitions and conversion functions for an intermediate 'Table' and related types, which annotates the existing Pandoc 'B.Table' types with additional inferred information. For use in writers that need to know the details of columns that cells span, row numbers, and the cells that are in the row head. -} module Text.Pandoc.Writers.AnnotatedTable ( toTable , fromTable , Table(..) , TableHead(..) , TableBody(..) , TableFoot(..) , HeaderRow(..) , BodyRow(..) , RowNumber(..) , RowHead , RowBody , Cell(..) , ColNumber(..) ) where import Control.Monad.RWS.Strict import Data.Generics ( Data , Typeable ) import Data.List.NonEmpty ( NonEmpty(..) ) import GHC.Generics ( Generic ) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Walk ( Walkable (..) ) -- | An annotated table type, corresponding to the Pandoc 'B.Table' -- constructor and the HTML @\@ element. It records the data -- of the columns that cells span, the cells in the row head, the row -- numbers of rows, and the column numbers of cells, in addition to -- the data in a 'B.Table'. The type itself does not enforce any -- guarantees about the consistency of this data. Use 'toTable' to -- produce a 'Table' from a Pandoc 'B.Table'. data Table = Table B.Attr B.Caption [B.ColSpec] TableHead [TableBody] TableFoot deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) -- | An annotated table head, corresponding to a Pandoc 'B.TableHead' -- and the HTML @\@ element. data TableHead = TableHead B.Attr [HeaderRow] deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) -- | An annotated table body, with an intermediate head and body, -- corresponding to a Pandoc 'B.TableBody' and the HTML @\@ -- element. data TableBody = TableBody B.Attr B.RowHeadColumns [HeaderRow] [BodyRow] deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) -- | An annotated table foot, corresponding to a Pandoc 'B.TableFoot' -- and the HTML @\@ element. data TableFoot = TableFoot B.Attr [HeaderRow] deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) -- | An annotated header row, corresponding to a Pandoc 'B.Row' and -- the HTML @\@ element, and also recording the row number of the -- row. All the cells in a 'HeaderRow' are header (@\@) cells. data HeaderRow = HeaderRow B.Attr RowNumber [Cell] deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) -- | An annotated body row, corresponding to a Pandoc 'B.Row' and the -- HTML @\@ element, and also recording its row number and -- separating the row head cells from the row body cells. data BodyRow = BodyRow B.Attr RowNumber RowHead RowBody deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) -- | The row number of a row. Note that rows are numbered continuously -- from zero from the start of the table, so the first row in a table -- body, for instance, may have a large 'RowNumber'. newtype RowNumber = RowNumber Int deriving (Eq, Ord, Read, Show, Typeable, Data, Generic, Num, Enum) -- | The head of a body row; the portion of the row lying in the stub -- of the 'TableBody'. Its cells correspond to HTML @\@ cells. type RowHead = [Cell] -- | The body of a body row; the portion of the row lying after the -- stub of the 'TableBody'. Its cells correspond to HTML @\@ -- cells. type RowBody = [Cell] -- | An annotated table cell, wrapping a Pandoc 'B.Cell' with its -- 'ColNumber' and the 'B.ColSpec' data for the columns that the cell -- spans. data Cell = Cell (NonEmpty B.ColSpec) ColNumber B.Cell deriving (Eq, Ord, Read, Show, Typeable, Data, Generic) -- | The column number of a cell, meaning the column number of the -- first column that the cell spans, if the table were laid on a -- grid. Columns are numbered starting from zero. newtype ColNumber = ColNumber Int deriving (Eq, Ord, Read, Show, Typeable, Data, Generic, Num, Enum) -- | Convert a Pandoc 'B.Table' to an annotated 'Table'. This function -- also performs the same normalization that the 'B.table' builder -- does (fixing overlapping cells, cells that protrude out of their -- table section, and so on). If the input table happens to satisfy -- the conditions that 'B.table' guarantees, then the resulting -- 'Table' will be identical, save for the addition of the inferred -- table information. toTable :: B.Attr -> B.Caption -> [B.ColSpec] -> B.TableHead -> [B.TableBody] -> B.TableFoot -> Table toTable attr cap cs th tbs tf = Table attr cap cs th' tbs' tf' where (th', tbs', tf') = fst $ evalRWS (annotateTable th tbs tf) (cs, length cs) 0 -- | Internal monad for annotating a table, passing in the 'B.ColSpec' -- data for the table, the grid width, and the current 'RowNumber' to -- be referenced or updated. type AnnM a = RWS ([B.ColSpec], Int) () RowNumber a incRowNumber :: AnnM RowNumber incRowNumber = do rn <- get put $ rn + 1 return rn annotateTable :: B.TableHead -> [B.TableBody] -> B.TableFoot -> AnnM (TableHead, [TableBody], TableFoot) annotateTable th tbs tf = do th' <- annotateTableHead th tbs' <- traverse annotateTableBody tbs tf' <- annotateTableFoot tf return (th', tbs', tf') annotateTableHead :: B.TableHead -> AnnM TableHead annotateTableHead (B.TableHead attr rows) = TableHead attr <$> annotateHeaderSection rows annotateTableBody :: B.TableBody -> AnnM TableBody annotateTableBody (B.TableBody attr rhc th tb) = do twidth <- asks snd let rhc' = max 0 $ min (B.RowHeadColumns twidth) rhc th' <- annotateHeaderSection th tb' <- annotateBodySection rhc' tb return $ TableBody attr rhc' th' tb' annotateTableFoot :: B.TableFoot -> AnnM TableFoot annotateTableFoot (B.TableFoot attr rows) = TableFoot attr <$> annotateHeaderSection rows annotateHeaderSection :: [B.Row] -> AnnM [HeaderRow] annotateHeaderSection rows = do colspec <- asks fst let hangcolspec = (1, ) <$> colspec annotateHeaderSection' hangcolspec id $ B.clipRows rows where annotateHeaderSection' oldHang acc (B.Row attr cells : rs) = do let (_, newHang, cells', _) = annotateRowSection 0 oldHang $ cells <> repeat B.emptyCell n <- incRowNumber let annRow = HeaderRow attr n cells' annotateHeaderSection' newHang (acc . (annRow :)) rs annotateHeaderSection' _ acc [] = return $ acc [] annotateBodySection :: B.RowHeadColumns -> [B.Row] -> AnnM [BodyRow] annotateBodySection (B.RowHeadColumns rhc) rows = do colspec <- asks fst let colspec' = (1, ) <$> colspec let (stubspec, bodyspec) = splitAt rhc colspec' normalizeBodySection' stubspec bodyspec id $ B.clipRows rows where normalizeBodySection' headHang bodyHang acc (B.Row attr cells : rs) = do let (colnum, headHang', rowStub, cells') = annotateRowSection 0 headHang $ cells <> repeat B.emptyCell let (_, bodyHang', rowBody, _) = annotateRowSection colnum bodyHang cells' n <- incRowNumber let annRow = BodyRow attr n rowStub rowBody normalizeBodySection' headHang' bodyHang' (acc . (annRow :)) rs normalizeBodySection' _ _ acc [] = return $ acc [] -- | Lay out a section of a 'Table' row on a grid row, annotating the -- cells with the 'B.ColSpec' data for the columns that they -- span. Performs the same normalization as 'B.placeRowSection'. annotateRowSection :: ColNumber -- ^ The current column number -> [(B.RowSpan, B.ColSpec)] -- ^ The overhang of the previous grid row, -- with column data -> [B.Cell] -- ^ The cells to annotate -> (ColNumber, [(B.RowSpan, B.ColSpec)], [Cell], [B.Cell]) -- ^ The new -- column -- number, -- overhang, -- annotated -- cells, -- and -- remaining -- cells annotateRowSection !colnum oldHang cells -- If the grid has overhang at our position, try to re-lay in -- the next position. | (o, colspec) : os <- oldHang , o > 1 = let (colnum', newHang, newCell, cells') = annotateRowSection (colnum + 1) os cells in (colnum', (o - 1, colspec) : newHang, newCell, cells') -- Otherwise if there is any available width, place the cell and -- continue. | c : cells' <- cells , (h, w) <- getDim c , w' <- max 1 w , (w'', cellHang@(chStart : chRest), oldHang') <- splitCellHang h w' oldHang = let c' = setW w'' c annCell = Cell (snd <$> chStart :| chRest) colnum c' colnum' = colnum + ColNumber (getColSpan w'') (colnum'', newHang, newCells, remainCells) = annotateRowSection colnum' oldHang' cells' in (colnum'', cellHang <> newHang, annCell : newCells, remainCells) -- Otherwise there is no room in the section | otherwise = (colnum, [], [], cells) where getColSpan (B.ColSpan x) = x getDim (B.Cell _ _ h w _) = (h, w) setW w (B.Cell a b h _ c) = B.Cell a b h w c -- | In @'splitCellHang' rs cs coldata@, with @rs@ the height of a -- cell that lies at the beginning of @coldata@, and @cs@ its width -- (which is not assumed to fit in the available space), return the -- actual width of the cell (what will fit in the available space), -- the data for the columns that the cell spans (including updating -- the overhang to equal @rs@), and the remaining column data. splitCellHang :: B.RowSpan -> B.ColSpan -> [(B.RowSpan, B.ColSpec)] -> (B.ColSpan, [(B.RowSpan, B.ColSpec)], [(B.RowSpan, B.ColSpec)]) splitCellHang h n = go 0 where go acc ((1, spec) : ls) | acc < n = let (acc', hang, ls') = go (acc + 1) ls in (acc', (h, spec) : hang, ls') go acc l = (acc, [], l) -- | Convert an annotated 'Table' to a Pandoc -- 'B.Table'. This is the inverse of 'toTable' on -- well-formed tables (i.e. tables satisfying the guarantees of -- 'B.table'). fromTable :: Table -> ( B.Attr , B.Caption , [B.ColSpec] , B.TableHead , [B.TableBody] , B.TableFoot ) fromTable (Table attr cap cs th tbs tf) = (attr, cap, cs, th', tbs', tf') where th' = fromTableHead th tbs' = map fromTableBody tbs tf' = fromTableFoot tf fromTableHead :: TableHead -> B.TableHead fromTableHead (TableHead attr rows) = B.TableHead attr $ fromHeaderRow <$> rows fromTableBody :: TableBody -> B.TableBody fromTableBody (TableBody attr rhc th tb) = B.TableBody attr rhc (fromHeaderRow <$> th) (fromBodyRow <$> tb) fromTableFoot :: TableFoot -> B.TableFoot fromTableFoot (TableFoot attr rows) = B.TableFoot attr $ fromHeaderRow <$> rows fromHeaderRow :: HeaderRow -> B.Row fromHeaderRow (HeaderRow attr _ cells) = B.Row attr $ fromCell <$> cells fromBodyRow :: BodyRow -> B.Row fromBodyRow (BodyRow attr _ rh rb) = B.Row attr ((fromCell <$> rh) <> (fromCell <$> rb)) fromCell :: Cell -> B.Cell fromCell (Cell _ _ c) = c -- -- Instances -- instance Walkable a B.Cell => Walkable a Cell where walkM f (Cell colspecs colnum cell) = Cell colspecs colnum <$> walkM f cell query f (Cell _colspecs _colnum cell) = query f cell instance Walkable a B.Cell => Walkable a HeaderRow where walkM f (HeaderRow attr rownum cells) = HeaderRow attr rownum <$> walkM f cells query f (HeaderRow _attr _rownum cells) = query f cells instance Walkable a B.Cell => Walkable a TableHead where walkM f (TableHead attr rows) = TableHead attr <$> walkM f rows query f (TableHead _attr rows) = query f rows ================================================ FILE: src/Text/Pandoc/Writers/AsciiDoc.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.AsciiDoc Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to asciidoc. Note that some information may be lost in conversion, due to expressive limitations of asciidoc. Footnotes and table cells with paragraphs (or other block items) are not possible in asciidoc. If pandoc encounters one of these, it will insert a message indicating that it has omitted the construct. AsciiDoc: -} module Text.Pandoc.Writers.AsciiDoc ( writeAsciiDoc, writeAsciiDocLegacy, writeAsciiDoctor ) where import Control.Monad (foldM) import Control.Monad.State.Strict ( StateT, MonadState(get), gets, modify, evalStateT ) import Data.Char (isPunctuation, isSpace) #if MIN_VERSION_base(4,19,0) import Data.List (delete, intercalate, intersperse, mapAccumL, uncons, sortOn, unsnoc) #else import Data.List (delete, intercalate, intersperse, mapAccumL, uncons, sortOn) #endif import Data.List.NonEmpty (NonEmpty((:|)), (<|)) import qualified Data.List.NonEmpty as NonEmpty import Data.Maybe (fromMaybe, isJust, catMaybes) import qualified Data.Map as M import qualified Data.Set as Set import qualified Data.Text as T import Data.Text (Text) import Network.URI (parseURI, URI(uriScheme)) import System.FilePath (dropExtension) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, space) import Text.DocLayout import Text.Pandoc.Builder (emptyCell) import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared import Text.Pandoc.Walk (walk) #if !MIN_VERSION_base(4,19,0) unsnoc :: [a] -> Maybe ([a], a) unsnoc = foldr (\x -> Just . maybe ([], x) (\(~(a, b)) -> (x : a, b))) Nothing #endif data WriterState = WriterState { defListMarker :: Text , orderedListLevel :: Int , bulletListLevel :: Int , intraword :: Bool , autoIds :: Set.Set Text , legacy :: Bool , inList :: Bool , hasMath :: Bool -- |0 is no table -- 1 is top level table -- 2 is a table in a table , tableNestingLevel :: Int } defaultWriterState :: WriterState defaultWriterState = WriterState { defListMarker = "::" , orderedListLevel = 0 , bulletListLevel = 0 , intraword = False , autoIds = Set.empty , legacy = False , inList = False , hasMath = False , tableNestingLevel = 0 } -- | Convert Pandoc to AsciiDoc. writeAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeAsciiDoc opts document = evalStateT (pandocToAsciiDoc opts document) defaultWriterState {-# DEPRECATED writeAsciiDoctor "Use writeAsciiDoc instead" #-} -- | Deprecated synonym of 'writeAsciiDoc'. writeAsciiDoctor :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeAsciiDoctor = writeAsciiDoc -- | Convert Pandoc to legacy AsciiDoc. writeAsciiDocLegacy :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeAsciiDocLegacy opts document = evalStateT (pandocToAsciiDoc opts document) defaultWriterState{ legacy = True } type ADW = StateT WriterState -- | Return asciidoc representation of document. pandocToAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> ADW m Text pandocToAsciiDoc opts (Pandoc meta blocks) = do let titleblock = not $ null (docTitle meta) && null (docAuthors meta) && null (docDate meta) let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing metadata <- metaToContext opts (blockListToAsciiDoc opts) (fmap chomp . inlineListToAsciiDoc opts) meta main <- blockListToAsciiDoc opts $ makeSections False Nothing blocks st <- get let context = defField "body" main $ defField "toc" (writerTableOfContents opts && isJust (writerTemplate opts)) $ defField "math" (hasMath st && not (legacy st)) $ defField "titleblock" titleblock metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context data EscContext = Normal | InTable deriving (Show, Eq) -- | Escape special characters for AsciiDoc. escapeString :: EscContext -> Text -> Doc Text escapeString context t | T.any needsEscape t = literal $ case T.foldl' go (False, mempty) t of (True, x) -> x <> "++" -- close passthrough context (False, x) -> x | otherwise = literal t where -- Bool is True when we are in a ++ passthrough context go :: (Bool, Text) -> Char -> (Bool, Text) go (True, x) '+' = (False, x <> "++" <> "{plus}") -- close context go (False, x) '+' = (False, x <> "{plus}") go (True, x) '|' | context == InTable = (False, x <> "++" <> "{vbar}") -- close context go (False, x) '|' | context == InTable = (False, x <> "{vbar}") go (True, x) c | needsEscape c = (True, T.snoc x c) | otherwise = (False, T.snoc (x <> "++") c) go (False, x) c | needsEscape c = (True, x <> "++" <> T.singleton c) | otherwise = (False, T.snoc x c) needsEscape '{' = True needsEscape '+' = True needsEscape '`' = True needsEscape '*' = True needsEscape '#' = True needsEscape '_' = True needsEscape '<' = True needsEscape '>' = True needsEscape '[' = True needsEscape ']' = True needsEscape '\\' = True needsEscape '|' = True needsEscape _ = False -- | Ordered list start parser for use in Para below. olMarker :: Parsec Text ParserState Char olMarker = do (start, style', delim) <- anyOrderedListMarker if delim == Period && (style' == UpperAlpha || (style' == UpperRoman && start `elem` [1, 5, 10, 50, 100, 500, 1000])) then spaceChar >> spaceChar else spaceChar -- | True if string begins with an ordered list marker -- or would be interpreted as an AsciiDoc option command needsEscaping :: Text -> Bool needsEscaping s = beginsWithOrderedListMarker s || isBracketed s where beginsWithOrderedListMarker str = case runParser olMarker defaultParserState "para start" (T.take 10 str) of Left _ -> False Right _ -> True isBracketed t | Just ('[', t') <- T.uncons t , Just (_, ']') <- T.unsnoc t' = True | otherwise = False -- | Convert Pandoc block element to asciidoc. blockToAsciiDoc :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> ADW m (Doc Text) blockToAsciiDoc opts (Div (id',"section":_,_) (Header level (_,cls,kvs) ils : xs)) = do hdr <- blockToAsciiDoc opts (Header level (id',cls,kvs) ils) rest <- blockListToAsciiDoc opts xs return $ hdr $$ rest blockToAsciiDoc opts (Plain inlines) = do contents <- inlineListToAsciiDoc opts inlines return $ contents <> blankline blockToAsciiDoc opts (Para inlines) = do contents <- inlineListToAsciiDoc opts inlines -- escape if para starts with ordered list marker let esc = if needsEscaping (render Nothing contents) then text "{empty}" else empty return $ esc <> contents <> blankline blockToAsciiDoc opts (LineBlock lns) = do let docify line = if null line then return blankline else inlineListToAsciiDoc opts line let joinWithLinefeeds = nowrap . mconcat . intersperse cr contents <- joinWithLinefeeds <$> mapM docify lns return $ "[verse]" $$ text "--" $$ contents $$ text "--" $$ blankline blockToAsciiDoc _ b@(RawBlock f s) | f == "asciidoc" = return $ literal s | otherwise = do report $ BlockNotRendered b return empty blockToAsciiDoc _ HorizontalRule = return $ blankline <> text "'''''" <> blankline blockToAsciiDoc opts (Header level (ident,_,_) inlines) = do contents <- inlineListToAsciiDoc opts inlines ids <- gets autoIds let autoId = uniqueIdent (writerExtensions opts) inlines ids modify $ \st -> st{ autoIds = Set.insert autoId ids } let identifier = if T.null ident || (isEnabled Ext_auto_identifiers opts && ident == autoId) then empty else "[[" <> literal ident <> "]]" return $ identifier $$ nowrap (text (replicate (level + 1) '=') <> space <> contents) <> blankline blockToAsciiDoc opts (Figure attr (Caption _ longcapt) body) = do -- Images in figures all get rendered as individual block-level images -- with the given caption. Non-image elements are rendered unchanged. capt <- if null longcapt then pure mempty else ("." <>) . nowrap <$> inlineListToAsciiDoc opts (blocksToInlines longcapt) let renderFigElement = \case Plain [Image imgAttr alternate (src, tit)] -> do args <- imageArguments opts imgAttr alternate src tit let figAttributes = case attr of ("", _, _) -> empty (ident, _, _) -> literal $ "[#" <> ident <> "]" -- .Figure caption -- image::images/logo.png[Company logo, title="blah"] return $ capt $$ figAttributes $$ "image::" <> args <> blankline blk -> blockToAsciiDoc opts blk vcat <$> mapM renderFigElement body blockToAsciiDoc _ (CodeBlock (_,classes,_) str) = return $ flush ( if null classes then "...." $$ literal str $$ "...." else attrs $$ "----" $$ literal str $$ "----") <> blankline where attrs = "[" <> literal (T.intercalate "," classes') <> "]" classes' = if "numberLines" `elem` classes then "source%linesnum" : delete "numberLines" classes else "source" : classes blockToAsciiDoc opts (BlockQuote blocks) = do contents <- blockListToAsciiDoc opts blocks let isBlock (BlockQuote _) = True isBlock _ = False -- if there are nested block quotes, put in an open block let contents' = if any isBlock blocks then "--" $$ contents $$ "--" else contents let bar = text "____" return $ bar $$ chomp contents' $$ bar <> blankline blockToAsciiDoc opts block@(Table _ blkCapt specs thead@(TableHead _ originalHeaders) originalTbody tfoot@(TableFoot _ originalFooters)) = do let (caption, aligns, widths, _, _) = toLegacyTable blkCapt specs thead originalTbody tfoot let headers = adjustEmptyRows originalHeaders let rows = adjustEmptyRows $ tableBodiesToRows originalTbody let footers = adjustEmptyRows originalFooters caption' <- inlineListToAsciiDoc opts caption let caption'' = if null caption then empty else "." <> caption' <> cr let isSimple = all (== 0) widths let relativePercentWidths = if isSimple then widths else map (/ sum widths) widths let widths'' :: [Integer] widths'' = map (floor . (* 100)) relativePercentWidths -- ensure that the widths sum to 100 let widths' = case widths'' of _ | isSimple -> widths'' (w:ws) | sum (w:ws) < 100 -> (100 - sum ws) : ws ws -> ws let totalwidth :: Integer totalwidth = floor $ sum widths * 100 let alignmentOperator AlignLeft = "<" alignmentOperator AlignCenter = "^" alignmentOperator AlignRight = ">" alignmentOperator AlignDefault = "" let colspec al wi = (alignmentOperator al) ++ if wi == 0 then "" else show wi ++ "%" let optionSpecForRows rowList spec = if allRowsEmpty rowList then Nothing else Just spec let headerspec = optionSpecForRows headers "header" let footerspec = optionSpecForRows footers "footer" let optionsList = catMaybes [headerspec, footerspec] let optionsspec = if null optionsList then empty else text "options=\"" <> text (intercalate "," optionsList) <> text "\"," let widthspec = if totalwidth == 0 then empty else text "width=" <> doubleQuotes (text $ show totalwidth ++ "%") <> text "," let tablespec = text "[" <> widthspec <> text "cols=" <> doubleQuotes (text $ intercalate "," $ zipWith colspec aligns widths') <> text "," <> optionsspec <> text "]" -- construct cells and recurse in case of nested tables parentTableLevel <- gets tableNestingLevel let currentNestingLevel = parentTableLevel + 1 modify $ \st -> st{ tableNestingLevel = currentNestingLevel } let separator = text (if parentTableLevel == 0 then "|" -- top level separator else "!") -- nested separator let makeCell [Plain x] = do d <- blockListToAsciiDoc opts [Plain x] return $ separator <> chomp d makeCell [Para x] = makeCell [Plain x] makeCell [] = return separator makeCell bs = if currentNestingLevel == 2 then do --asciidoc only supports nesting once report $ BlockNotRendered block return separator else do d <- blockListToAsciiDoc opts bs return $ (text "a" <> separator) $$ d let colSpanFactor (ColSpan colSpan) = if colSpan > 1 then text $ show colSpan else empty let rowSpanFactor (RowSpan rowSpan) = if rowSpan > 1 then text $ "." ++ show rowSpan else empty let makeCellWithSpansAndAlignment (Cell _ alignment rowSpan colSpan blocks) = do let spanFactor = colSpanFactor colSpan <> rowSpanFactor rowSpan cell <- makeCell blocks let alignedCell = alignmentOperator alignment <> cell return $ if null spanFactor then alignedCell else spanFactor <> text "+" <> alignedCell let makeRow (Row attr []) = makeRow $ Row attr $ replicate (length widths') emptyCell makeRow (Row _ cells) = hsep `fmap` mapM makeCellWithSpansAndAlignment cells -- AsciiDoc only supports 1 header row and 1 footer row. let headerRow = Data.List.uncons $ adjustHeaders headers let footerRow = unsnoc $ adjustFooters footers let tailHeaderRows = if allRowsEmpty headers then [] else maybe [] snd headerRow let initFooterRows = if allRowsEmpty footers then [] else maybe [] fst footerRow rows' <- mapM makeRow $ tailHeaderRows ++ rows ++ initFooterRows head' <- case headerRow of Nothing -> return empty Just (headerRow', _) -> makeRow headerRow' foot <- case footerRow of Nothing -> return empty Just (_, footerRow') -> makeRow footerRow' modify $ \st -> st{ tableNestingLevel = parentTableLevel } let head'' = if allRowsEmpty headers then empty else head' let foot' = if allRowsEmpty footers then empty else foot let colwidth = if writerWrapText opts == WrapAuto then writerColumns opts else 100000 let maxwidth = maximum $ fmap offset (foot <| (head' :| rows')) let body = if maxwidth > colwidth then vsep rows' else vcat rows' let border = separator <> text "===" return $ caption'' $$ tablespec $$ border $$ head'' $$ body $$ foot' $$ border $$ blankline blockToAsciiDoc opts (BulletList items) = do inlist <- gets inList modify $ \st -> st{ inList = True } contents <- mapM (bulletListItemToAsciiDoc opts) items modify $ \st -> st{ inList = inlist } return $ mconcat contents <> blankline blockToAsciiDoc opts (OrderedList (start, sty, _delim) items) = do let listStyle = case sty of DefaultStyle -> [] Decimal -> ["arabic"] Example -> [] _ -> [T.toLower (tshow sty)] let listStart = ["start=" <> tshow start | start /= 1] let listoptions = case T.intercalate ", " (listStyle ++ listStart) of "" -> empty x -> brackets (literal x) inlist <- gets inList modify $ \st -> st{ inList = True } contents <- mapM (orderedListItemToAsciiDoc opts) items modify $ \st -> st{ inList = inlist } return $ listoptions $$ mconcat contents <> blankline blockToAsciiDoc opts (DefinitionList items) = do inlist <- gets inList modify $ \st -> st{ inList = True } contents <- mapM (definitionListItemToAsciiDoc opts) items modify $ \st -> st{ inList = inlist } return $ mconcat contents <> blankline -- convert admonition and sidebar divs to asicidoc blockToAsciiDoc opts (Div (ident,classes,_) bs) = do let identifier = if T.null ident then empty else "[[" <> literal ident <> "]]" let admonition_classes = ["attention","caution","danger","error","hint", "important","note","tip","warning"] let sidebar_class = "sidebar" contents <- case classes of (l:_) | l `elem` admonition_classes || T.toLower l == sidebar_class -> do let (titleBs, bodyBs) = case bs of (Div (_,["title"],_) ts : rest) -> (ts, rest) _ -> ([], bs) let fence = if l == "sidebar" then "****" else "====" elemTitle <- if null titleBs || -- If title matches class, omit (T.toLower (T.strip (stringify titleBs))) == l then return mempty else ("." <>) <$> blockListToAsciiDoc opts titleBs elemBody <- blockListToAsciiDoc opts bodyBs return $ "[" <> literal (T.toUpper l) <> "]" $$ chomp elemTitle $$ fence $$ chomp elemBody $$ fence _ -> blockListToAsciiDoc opts bs return $ identifier $$ contents $$ blankline -- | Convert bullet list item (list of blocks) to asciidoc. bulletListItemToAsciiDoc :: PandocMonad m => WriterOptions -> [Block] -> ADW m (Doc Text) bulletListItemToAsciiDoc opts blocks = do lev <- gets bulletListLevel modify $ \s -> s{ bulletListLevel = lev + 1 } isLegacy <- gets legacy let blocksWithTasks = if isLegacy then blocks else (taskListItemToAsciiDoc blocks) contents <- snd <$> foldM (addBlock opts) (False, empty) blocksWithTasks modify $ \s -> s{ bulletListLevel = lev } let marker = text (replicate (lev + 1) '*') return $ marker <> text " " <> listBegin blocksWithTasks <> contents <> cr -- | Convert a list item containing text starting with @U+2610 BALLOT BOX@ -- or @U+2612 BALLOT BOX WITH X@ to asciidoctor checkbox syntax (e.g. @[x]@). taskListItemToAsciiDoc :: [Block] -> [Block] taskListItemToAsciiDoc = handleTaskListItem toAd listExt where toAd (Str "☐" : Space : is) = RawInline (Format "asciidoc") "[ ]" : Space : is toAd (Str "☒" : Space : is) = RawInline (Format "asciidoc") "[x]" : Space : is toAd is = is listExt = extensionsFromList [Ext_task_lists] addBlock :: PandocMonad m => WriterOptions -> (Bool, Doc Text) -> Block -> ADW m (Bool, Doc Text) addBlock opts (containsContinuation, d) b = do x <- chomp <$> blockToAsciiDoc opts b return $ case b of BulletList{} | containsContinuation -> (False, d <> blankline <> x) -- see #11006 | otherwise -> (False, d <> cr <> x) OrderedList (start, sty, _) _ | containsContinuation , start == 1 , sty == DefaultStyle -> (False, d <> blankline <> x) -- see #11006 | otherwise -> (False, d <> cr <> x) Para (Math DisplayMath _:_) -> (containsContinuation, d <> cr <> x) Plain (Math DisplayMath _:_) -> (containsContinuation, d <> cr <> x) Para{} | isEmpty d -> (containsContinuation, x) Plain{} | isEmpty d -> (containsContinuation, x) _ -> (True, d <> cr <> text "+" <> cr <> x) listBegin :: [Block] -> Doc Text listBegin blocks = case blocks of Para (Math DisplayMath _:_) : _ -> "{blank}" Plain (Math DisplayMath _:_) : _ -> "{blank}" Para _ : _ -> empty Plain _ : _ -> empty _ : _ -> "{blank}" [] -> "{blank}" -- | Convert ordered list item (a list of blocks) to asciidoc. orderedListItemToAsciiDoc :: PandocMonad m => WriterOptions -- ^ options -> [Block] -- ^ list item (list of blocks) -> ADW m (Doc Text) orderedListItemToAsciiDoc opts blocks = do lev <- gets orderedListLevel modify $ \s -> s{ orderedListLevel = lev + 1 } contents <- snd <$> foldM (addBlock opts) (False, empty) blocks modify $ \s -> s{ orderedListLevel = lev } let marker = text (replicate (lev + 1) '.') return $ marker <> text " " <> listBegin blocks <> contents <> cr -- | Convert definition list item (label, list of blocks) to asciidoc. definitionListItemToAsciiDoc :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> ADW m (Doc Text) definitionListItemToAsciiDoc opts (label, defs) = do labelText <- inlineListToAsciiDoc opts label marker <- gets defListMarker if marker == "::" then modify (\st -> st{ defListMarker = ";;"}) else modify (\st -> st{ defListMarker = "::"}) let divider = cr <> text "+" <> cr let defsToAsciiDoc :: PandocMonad m => [Block] -> ADW m (Doc Text) defsToAsciiDoc ds = (vcat . intersperse divider . map chomp) `fmap` mapM (blockToAsciiDoc opts) ds defs' <- mapM defsToAsciiDoc defs modify (\st -> st{ defListMarker = marker }) let contents = nest 2 $ vcat $ intersperse divider $ map chomp defs' return $ labelText <> literal marker <> cr <> contents <> cr -- | Convert list of Pandoc block elements to asciidoc. blockListToAsciiDoc :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements -> ADW m (Doc Text) blockListToAsciiDoc opts blocks = mconcat `fmap` mapM (blockToAsciiDoc opts) blocks data SpacyLocation = End | Start -- | Convert list of Pandoc inline elements to asciidoc. inlineListToAsciiDoc :: PandocMonad m => WriterOptions -> [Inline] -> ADW m (Doc Text) inlineListToAsciiDoc opts lst = do oldIntraword <- gets intraword setIntraword False result <- go lst setIntraword oldIntraword return result where go [] = return empty go (y:x:xs) | not (isSpacy End y) = do y' <- if isSpacy Start x then inlineToAsciiDoc opts y else withIntraword $ inlineToAsciiDoc opts y x' <- withIntraword $ inlineToAsciiDoc opts x xs' <- go xs return (y' <> x' <> xs') | not (isSpacy Start x) = do y' <- withIntraword $ inlineToAsciiDoc opts y xs' <- go (x:xs) return (y' <> xs') go (x:xs) = do x' <- inlineToAsciiDoc opts x xs' <- go xs return (x' <> xs') isSpacy :: SpacyLocation -> Inline -> Bool isSpacy _ Space = True isSpacy _ LineBreak = True isSpacy _ SoftBreak = True -- Note that \W characters count as spacy in AsciiDoc -- for purposes of determining interword: isSpacy End (Str xs) = case T.unsnoc xs of Just (_, c) -> isPunctuation c || isSpace c _ -> False isSpacy Start (Str xs) | Just (c, _) <- T.uncons xs = isPunctuation c || isSpace c isSpacy End (Link{}) = True isSpacy End (Image{}) = True isSpacy _ _ = False setIntraword :: PandocMonad m => Bool -> ADW m () setIntraword b = modify $ \st -> st{ intraword = b } withIntraword :: PandocMonad m => ADW m a -> ADW m a withIntraword p = setIntraword True *> p <* setIntraword False -- | Convert Pandoc inline element to asciidoc. inlineToAsciiDoc :: PandocMonad m => WriterOptions -> Inline -> ADW m (Doc Text) inlineToAsciiDoc opts (Emph [Strong xs]) = inlineToAsciiDoc opts (Strong [Emph xs]) -- see #5565 inlineToAsciiDoc opts (Emph lst) = do contents <- inlineListToAsciiDoc opts lst isIntraword <- gets intraword let marker = if isIntraword then "__" else "_" return $ delimited marker marker contents inlineToAsciiDoc opts (Underline lst) = do contents <- inlineListToAsciiDoc opts lst return $ delimited "[.underline]#" "#" contents inlineToAsciiDoc opts (Strong lst) = do contents <- inlineListToAsciiDoc opts lst isIntraword <- gets intraword let marker = if isIntraword then "**" else "*" return $ delimited marker marker contents inlineToAsciiDoc opts (Strikeout lst) = do contents <- inlineListToAsciiDoc opts lst return $ delimited "[line-through]#" "#" contents inlineToAsciiDoc opts (Superscript lst) = do contents <- inlineListToAsciiDoc opts lst return $ delimited "^" "^" contents inlineToAsciiDoc opts (Subscript lst) = do contents <- 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 pure $ case qt of SingleQuote | isLegacy -> "`" <> contents <> "'" | otherwise -> "'`" <> contents <> "`'" DoubleQuote | isLegacy -> "``" <> contents <> "''" | otherwise -> "\"`" <> contents <> "`\"" inlineToAsciiDoc _ (Code _ str) = do isLegacy <- gets legacy let escChar '`' = "\\'" escChar c = T.singleton c parentTableLevel <- gets tableNestingLevel let content | isLegacy = literal (T.concatMap escChar str) | otherwise = escapeString (if parentTableLevel > 0 then InTable else Normal) str return $ text "`" <> content <> "`" inlineToAsciiDoc _ (Str str) = do parentTableLevel <- gets tableNestingLevel pure $ escapeString (if parentTableLevel > 0 then InTable else Normal) str inlineToAsciiDoc _ (Math InlineMath str) = do isLegacy <- gets legacy modify $ \st -> st{ hasMath = True } let content = if isLegacy then "$" <> literal str <> "$" else literal str return $ "latexmath:[" <> content <> "]" inlineToAsciiDoc _ (Math DisplayMath str) = do isLegacy <- gets legacy modify $ \st -> st{ hasMath = True } let content = if isLegacy then "\\[" <> literal str <> "\\]" else literal str inlist <- gets inList let sepline = if inlist then text "+" else blankline return $ (cr <> sepline) $$ "[latexmath]" $$ "++++" $$ content $$ "++++" <> cr inlineToAsciiDoc _ il@(RawInline f s) | f == "asciidoc" = return $ literal s | otherwise = do report $ InlineNotRendered il return empty inlineToAsciiDoc _ LineBreak = return $ " +" <> cr inlineToAsciiDoc _ Space = return space inlineToAsciiDoc opts SoftBreak = case writerWrapText opts of WrapAuto -> return space WrapPreserve -> return cr WrapNone -> return space inlineToAsciiDoc opts (Cite _ lst) = inlineListToAsciiDoc opts lst inlineToAsciiDoc opts (Link _ txt (src, _tit)) = do -- relative: link:downloads/foo.zip[download foo.zip] -- abs: http://google.cod[Google] -- or my@email.com[email john] let fixCommas (Str t) = intersperse (RawInline (Format "asciidoc") ",") $ map Str $ T.splitOn "," t -- see #8070 fixCommas x = [x] linktext <- inlineListToAsciiDoc opts $ walk (concatMap fixCommas) txt let needsLinkPrefix = case parseURI (T.unpack src) of Just u -> uriScheme u `notElem` ["http:","https:", "ftp:", "irc:", "mailto:"] _ -> True let needsPassthrough = "--" `T.isInfixOf` src let prefix = if needsLinkPrefix then text "link:" else empty let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src) let useAuto = case txt of [Str s] | escapeURI s == srcSuffix -> True _ -> False return $ if needsPassthrough then if useAuto then "link:++" <> literal srcSuffix <> "++[]" else "link:++" <> literal src <> "++[" <> linktext <> "]" else if useAuto then literal srcSuffix else prefix <> literal src <> "[" <> linktext <> "]" inlineToAsciiDoc opts (Image attr alternate (src, tit)) = ("image:" <>) <$> imageArguments opts attr alternate src tit inlineToAsciiDoc opts (Note [Para inlines]) = inlineToAsciiDoc opts (Note [Plain inlines]) inlineToAsciiDoc opts (Note [Plain inlines]) = do contents <- inlineListToAsciiDoc opts inlines return $ text "footnote:[" <> contents <> "]" -- asciidoc can't handle blank lines in notes inlineToAsciiDoc _ (Note _) = return "[multiblock footnote omitted]" inlineToAsciiDoc opts (Span (ident,classes,_) ils) = do contents <- inlineListToAsciiDoc opts ils isIntraword <- gets intraword let marker = if isIntraword then "##" else "#" case classes of [] | T.null ident -> return contents ["mark"] | T.null ident -> return $ marker <> contents <> marker _ -> do let modifier = brackets $ literal $ T.unwords $ [ "#" <> ident | not (T.null ident)] ++ map ("." <>) classes return $ modifier <> marker <> contents <> marker -- | Provides the arguments for both `image:` and `image::` -- e.g.: sunset.jpg[Sunset,300,200] imageArguments :: PandocMonad m => WriterOptions -> Attr -> [Inline] -> Text -> Text -> ADW m (Doc Text) imageArguments opts attr altText src title = do let txt = if null altText || (altText == [Str ""]) then [Str . T.pack . dropExtension $ T.unpack src] else altText linktext <- inlineListToAsciiDoc opts txt let linktitle = if T.null title then empty else ",title=\"" <> literal title <> "\"" showDim dir = case dimension dir attr of Just (Percent a) -> ["scaledwidth=" <> text (show (Percent a))] Just dim -> [text (show dir) <> "=" <> literal (showInPixel opts dim)] Nothing -> [] dimList = showDim Width ++ showDim Height dims = if null dimList then empty else "," <> mconcat (intersperse "," dimList) return $ literal src <> "[" <> linktext <> linktitle <> dims <> "]" -- | Adjust header rows for the fact that AsciiDoc only supports a single header row. -- -- The first header row will become the single header row in AsciiDoc with the -- other rows becoming the top body rows. -- All cells of the first header row with a RowSpan > 1 will be mapped to -- RowSpan 1 and the remaining RowSpans of those cells wll be added as empty -- columns into the second row beneath them to preserve the original layout. adjustHeaders :: [Row] -> [Row] adjustHeaders [] = [] adjustHeaders [row] = [row] adjustHeaders (Row attr firstHeaderCells:secondRow:remainingRows) = let ((_, emptyHeaderCells), headerRow) = mapAccumL adjustHeaderRowCell (0, []) firstHeaderCells secondRow' = applyEmptyCells secondRow emptyHeaderCells in Row attr headerRow:secondRow':remainingRows where adjustHeaderRowCell (columnPosition, emptyCells) cell@(Cell cellAttr alignment (RowSpan rowSpan) (ColSpan colSpan) blocks) = let nextColumnPosition = columnPosition + colSpan adjustedHeaderCell = Cell cellAttr alignment (RowSpan 1) (ColSpan colSpan) blocks emptyHeaderRowCell = Cell nullAttr AlignDefault (RowSpan rowSpan - 1) (ColSpan colSpan) [] emptyCellPosition = (columnPosition, emptyHeaderRowCell) in if rowSpan > 1 then ((nextColumnPosition, emptyCellPosition:emptyCells), adjustedHeaderCell) else ((nextColumnPosition, emptyCells), cell) -- | Adjust footer rows for the fact that AsciiDoc only supports a single footer row. -- -- The last footer row will become the single footer row in AsciiDoc with the -- previous footer rows becoming the bottom body rows. -- All column indices of cells whose RowSpans would reach that last footer row -- are collected and subtracted by 1. Those collected column indices will then -- be applied as empty columns into the last footer row to preserve the original -- layout. adjustFooters :: [Row] -> [Row] adjustFooters [] = [] adjustFooters [row] = [row] adjustFooters rows = adjustFooters' [] (0, length rows) M.empty rows where adjustFooters' _ _ _ [] = [] adjustFooters' columnIndices _ _ [row] = [applyEmptyCells row columnIndices] adjustFooters' columnIndices rowInfo@(rowIndex, footerLength) previousRowSpans (row:rest) = -- Need to keep track of RowSpans from previous rows and how they occupy -- space in rows beneath them to be able to apply the correct column -- position of RowSpans that would reach the last footer row. let (previousRowSpans', row', columnIndices') = adjustFooterRow rowInfo previousRowSpans row rows' = adjustFooters' (columnIndices ++ columnIndices') (rowIndex + 1, footerLength) previousRowSpans' rest in row':rows' adjustFooterRow rowInfo previousRowSpans (Row attr cells) = let ((nextColumnPosition, previousRowSpans'), cells') = mapAccumL (adjustFooterCell rowInfo) (0, previousRowSpans) cells (cells'', columnIndices) = unzip cells' -- Apply row spans from a previous row that are next to the end of the -- current row's cells to keep track of the correct column position. previousRowSpans'' = decrementTrailingRowSpans nextColumnPosition previousRowSpans' in (previousRowSpans'', Row attr cells'', catMaybes columnIndices) -- | Adjust footer cell for the fact that AsciiDoc only supports a single footer row. -- -- Collects cells whose RowSpan would reach to the last footer row and applies -- them as empty cells to that last footer row. adjustFooterCell :: (Int, Int) -> (Int, M.Map Int (RowSpan, ColSpan)) -> Cell -> ((Int, M.Map Int (RowSpan, ColSpan)), (Cell, Maybe (Int, Cell))) adjustFooterCell rowInfo@(rowIndex, footerLength) (columnPosition, previousSpans) cell@(Cell _ _ (RowSpan rowSpan) (ColSpan colSpan) _) | Just (ColSpan previousColSpan, previousSpans') <- takePreviousSpansAtColumn columnPosition previousSpans = -- Apply row span from a previous row that occupies this column to keep -- track of the correct column position. adjustFooterCell rowInfo (columnPosition + previousColSpan, previousSpans') cell | rowSpan > 1 && rowIndex + rowSpan >= footerLength = -- Adjust row span that would reach all the way to the last footer row and -- keep track of that to apply it to the last footer row. ((nextColumnPosition, previousRowSpans'), (decrementRowSpanInCell cell, Just (columnPosition, emptyCellWithColSpan))) | otherwise = ((nextColumnPosition, previousRowSpans'), (cell, Nothing)) where -- Keep track of this cell's RowSpan for the rows following it. previousRowSpans' = insertCurrentSpansAtColumn columnPosition previousSpans (RowSpan rowSpan) (ColSpan colSpan) nextColumnPosition = columnPosition + colSpan emptyCellWithColSpan = Cell nullAttr AlignDefault (RowSpan 1) (ColSpan colSpan) [] -- | Adjust empty rows for AsciiDoc. -- -- An empty row without any cells decrements RowSpans that cover it and is -- removed by them to adjust for being unable to express empty rows with no -- cells in AsciiDoc. adjustEmptyRows :: [Row] -> [Row] adjustEmptyRows = adjustEmptyRows' . map applyInitialRowsLeft where adjustEmptyRows' [] = [] adjustEmptyRows' (row:rest) | maxRowSpan' <- maxRowSpan row , maxRowSpan' > 1 = -- Consume empty rows within the row's span. let followingRows = take (maxRowSpan' - 1) rest rows = consumeEmptyRows (row :| []) followingRows rest' = drop (length followingRows) rest in rowFromCellsWithRowsLeft (NonEmpty.head rows) : adjustEmptyRows' (NonEmpty.tail rows ++ rest') | otherwise = rowFromCellsWithRowsLeft row : adjustEmptyRows' rest rowFromCellsWithRowsLeft (attr, cellsWithRowsLeft) = Row attr $ map fst cellsWithRowsLeft cellRowSpan (Cell _ _ (RowSpan rowSpan) _ _) = rowSpan consumeEmptyRows rows [] = NonEmpty.reverse rows consumeEmptyRows rows (followingRow:restRows) = if null (snd followingRow) && any rowHasRowSpanAndRowsLeft rows then consumeEmptyRows (fmap (subtractRowsLeft decrementRowSpanInCell) rows) restRows -- Consume empty row for RowSpan and remove it else consumeEmptyRows (followingRow <| fmap (subtractRowsLeft id) rows) restRows rowHasRowSpanAndRowsLeft (_, cells) = any cellHasRowSpanAndRowsLeft cells cellHasRowSpanAndRowsLeft (cell, rowsLeft) = cellRowSpan cell > 1 && rowsLeft >= 1 subtractRowsLeft changeCell (attr, cells) = (attr, map (subtractRowsLeftCell changeCell) cells) subtractRowsLeftCell changeCell cellPair@(cell, rowsLeft) | rowsLeft >= 1 = (changeCell cell, rowsLeft - 1) | otherwise = cellPair applyInitialRowsLeft (Row attr cells) = (attr, map applyInitialRowsLeftCell cells) applyInitialRowsLeftCell cell | rowSpan <- cellRowSpan cell, rowSpan > 1 = (cell, rowSpan - 1) -- Minus its own row | otherwise = (cell, 0) maxRowSpan (_, []) = 0 maxRowSpan (_, cells) = maximum $ map (cellRowSpan . fst) cells -- | Decrement the RowSpan of a Cell if that RowSpan > 1. decrementRowSpanInCell :: Cell -> Cell decrementRowSpanInCell cell@(Cell attr alignment (RowSpan rowSpan) colSpan blocks) = if rowSpan > 1 then Cell attr alignment (RowSpan rowSpan - 1) colSpan blocks else cell -- | Apply empty table cells at the given positions inside a Row. applyEmptyCells :: Row -> [(Int, Cell)] -> Row applyEmptyCells (Row attr cells) = Row attr . applyEmptyCells' 0 cells . sortOn fst where applyEmptyCells' _ cells' [] = cells' applyEmptyCells' currentPosition cells' ((columnPosition, columnEmptyCell@(Cell _ _ _ (ColSpan colSpan) _)):rest) | columnPosition == currentPosition = columnEmptyCell : applyEmptyCells' (currentPosition + colSpan) cells' rest applyEmptyCells' _ [] _ = [] applyEmptyCells' currentPosition (cell@(Cell _ _ _ (ColSpan currentCellColSpan) _):restCells) emptyCellList = cell : applyEmptyCells' (currentPosition + currentCellColSpan) restCells emptyCellList ================================================ FILE: src/Text/Pandoc/Writers/BBCode.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Strict #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Writers.BBCode Copyright : © 2025 Aleksey Myshko License : GNU GPL, version 2 or above Maintainer : Aleksey Myshko Stability : alpha Portability : portable Conversion of 'Pandoc' documents to various BBCode flavors. -} module Text.Pandoc.Writers.BBCode ( -- * Predefined writers -- Writers for different flavors of BBCode. 'writeBBCode' is a synonym for -- 'writeBBCode_official' writeBBCode, writeBBCodeOfficial, writeBBCodeSteam, writeBBCodePhpBB, writeBBCodeFluxBB, writeBBCodeHubzilla, writeBBCodeXenforo, -- * Extending the writer -- $extending FlavorSpec (..), WriterState (..), RR, writeBBCodeCustom, inlineToBBCode, inlineListToBBCode, blockToBBCode, blockListToBBCode, -- ** Handling attributes -- $wrapping_spans_divs attrToMap, -- * Predefined flavor specifications officialSpec, steamSpec, phpbbSpec, fluxbbSpec, hubzillaSpec, xenforoSpec, ) where import Control.Applicative (some) import Control.Monad (forM) import Control.Monad.Reader (MonadReader (..), ReaderT (..), asks) import Control.Monad.State (MonadState (..), StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.Either (isRight) import Data.Foldable (toList) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Data.Maybe (fromMaybe, isJust) import Data.Sequence (Seq, (|>)) import qualified Data.Sequence as Seq import Data.Text (Text) import qualified Data.Text as T import Text.DocLayout hiding (char, link, text) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging (LogMessage (..)) import Text.Pandoc.Options (WriterOptions (..)) import Text.Pandoc.Parsing (char, digit, eof, readWith) import Text.Pandoc.Shared (inquotes, onlySimpleTableCells, removeFormatting, trim, tshow) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.URI (escapeURI) import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable, unsmartify) import Text.Read (readMaybe) -- Type synonym to prevent haddock-generated HTML from overflowing type PandocTable = (Attr, Caption, [ColSpec], TableHead, [TableBody], TableFoot) -- $extending -- If you want to support more Pandoc elements (or render some of them -- differently) you can do so by creating your own 'FlavorSpec' -- -- The module exports the @'FlavorSpec'@s underlying @writeBBCode_*@ functions, -- namely 'officialSpec', 'steamSpec', 'phpbbSpec', 'fluxbbSpec', -- 'hubzillaSpec'. -- -- You can create and use your own renderers, for instance here we define a -- renderer for 'CodeBlock' and use it to create a derivative format: -- -- > renderCodeBlockCustom :: (PandocMonad m) => Attr -> Text -> RR m (Doc Text) -- > renderCodeBlockCustom (_, cls, _) code = do -- > let opening = case cls of -- > (lang : _) -> "[code=" <> lang <> "]" -- > ("c++" : _) -> "[code=cpp]" -- > _ -> "[code]" -- > pure $ mconcat [literal opening, literal code, cr, "[/code]"] -- > -- > specCustom = officialSpec{renderCodeBlock = renderCodeBlockCustom} -- -- Then we can use it to render 'Pandoc' document via 'writeBBCode_custom' {- | Data type that is a collection of renderers for most elements in a Pandoc AST (see 'Block' and 'Inline') The intention here is to allow inheritance between formats, for instance if format A and format @B@ differ only in rendering tables, @B@ can be implemented as @A{'renderTable' = renderTableB}@ -} data FlavorSpec = FlavorSpec { renderBlockQuote :: forall m. (PandocMonad m) => [Block] -> RR m (Doc Text) -- ^ Render 'BlockQuote' , renderBulletList :: forall m. (PandocMonad m) => [[Block]] -> RR m (Doc Text) -- ^ Render 'BulletList' , renderCodeBlock :: forall m. (PandocMonad m) => Attr -> Text -> RR m (Doc Text) -- ^ Render 'CodeBlock' , renderDefinitionList :: forall m. (PandocMonad m) => [([Inline], [[Block]])] -> RR m (Doc Text) -- ^ Render 'DefinitionList' , renderHeader :: forall m. (PandocMonad m) => Int -> Attr -> [Inline] -> RR m (Doc Text) -- ^ Render 'Header' , renderInlineCode :: forall m. (PandocMonad m) => Attr -> Text -> RR m (Doc Text) -- ^ Render 'Code' , renderLink :: forall m. (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) -- ^ Render 'Link' , renderOrderedList :: forall m. (PandocMonad m) => ListAttributes -> [[Block]] -> RR m (Doc Text) -- ^ Render 'OrderedList' , renderStrikeout :: forall m. (PandocMonad m) => [Inline] -> RR m (Doc Text) -- ^ Render 'Strikeout' , renderTable :: forall m. (PandocMonad m) => PandocTable -> RR m (Doc Text) -- ^ Render 'Table' , renderHorizontalRule :: forall m. (PandocMonad m) => RR m (Doc Text) -- ^ Render 'HorizontalRule' , renderLineBlock :: forall m. (PandocMonad m) => [[Inline]] -> RR m (Doc Text) -- ^ Render 'LineBlock' , renderPara :: forall m. (PandocMonad m) => [Inline] -> RR m (Doc Text) -- ^ Render 'Para' , renderSuperscript :: forall m. (PandocMonad m) => [Inline] -> RR m (Doc Text) -- ^ Render 'Superscript' , renderSubscript :: forall m. (PandocMonad m) => [Inline] -> RR m (Doc Text) -- ^ Render 'Subscript' , renderSmallCaps :: forall m. (PandocMonad m) => [Inline] -> RR m (Doc Text) -- ^ Render 'SmallCaps' , renderCite :: forall m. (PandocMonad m) => [Citation] -> [Inline] -> RR m (Doc Text) -- ^ Render 'Cite' , renderNote :: forall m. (PandocMonad m) => [Block] -> RR m (Doc Text) -- ^ Render 'Note' , renderFigure :: forall m. (PandocMonad m) => Attr -> Caption -> [Block] -> RR m (Doc Text) -- ^ Render 'Figure' , renderQuoted :: forall m. (PandocMonad m) => QuoteType -> [Inline] -> RR m (Doc Text) -- ^ Render 'Quoted' , renderMath :: forall m. (PandocMonad m) => MathType -> Text -> RR m (Doc Text) -- ^ Render 'Math' , renderImage :: forall m. (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) -- ^ Render 'Image' , wrapSpanDiv :: Bool -> Map Text (Maybe Text) -> Doc Text -> Doc Text -- ^ Wrap document in bbcode tags based on attributes/classes. Boolean flag -- indicates whether passed argument is a Div or a Span (True means Div) } data WriterState = WriterState { writerOptions :: WriterOptions , flavorSpec :: FlavorSpec , inList :: Bool } instance Default WriterState where def = WriterState { writerOptions = def , flavorSpec = officialSpec , inList = False } -- | The base of a renderer monad. type RR m a = StateT (Seq (Doc Text)) (ReaderT WriterState m) a pandocToBBCode :: (PandocMonad m) => Pandoc -> RR m Text pandocToBBCode (Pandoc meta body) = do opts <- asks writerOptions -- Run the rendering that mutates the state by producing footnotes bodyContents <- blockListToBBCode body -- Get the footnotes footnotes <- get -- Separate footnotes (if any) with a horizontal rule footnotesSep <- if null footnotes then pure empty else (\hr -> blankline <> hr <> blankline) <$> blockToBBCode HorizontalRule -- Put footnotes after the main text let docText = bodyContents <> footnotesSep <> vsep (toList footnotes) metadata <- metaToContext opts blockListToBBCode inlineListToBBCode meta let context = defField "body" docText metadata case writerTemplate opts of Just tpl -> pure $ render Nothing (renderTemplate tpl context) Nothing -> pure $ render Nothing docText writeBBCode , writeBBCodeOfficial , writeBBCodeSteam , writeBBCodePhpBB , writeBBCodeFluxBB , writeBBCodeHubzilla , writeBBCodeXenforo :: (PandocMonad m) => WriterOptions -> Pandoc -> m Text writeBBCode = writeBBCodeOfficial writeBBCodeOfficial = writeBBCodeCustom officialSpec writeBBCodeSteam = writeBBCodeCustom steamSpec writeBBCodePhpBB = writeBBCodeCustom phpbbSpec writeBBCodeFluxBB = writeBBCodeCustom fluxbbSpec writeBBCodeHubzilla = writeBBCodeCustom hubzillaSpec writeBBCodeXenforo = writeBBCodeCustom xenforoSpec {- | Convert a 'Pandoc' document to BBCode using the given 'FlavorSpec' and 'WriterOptions'. -} writeBBCodeCustom :: (PandocMonad m) => FlavorSpec -> WriterOptions -> Pandoc -> m Text writeBBCodeCustom spec opts document = runRR mempty def{writerOptions = opts, flavorSpec = spec} $ pandocToBBCode document where runRR :: (Monad m) => Seq (Doc Text) -> WriterState -> RR m a -> m a runRR footnotes writerState action = runReaderT (evalStateT action footnotes) writerState blockListToBBCode :: (PandocMonad m) => [Block] -> RR m (Doc Text) blockListToBBCode blocks = chomp . vsep . filter (not . null) <$> mapM blockToBBCode blocks blockToBBCode :: (PandocMonad m) => Block -> RR m (Doc Text) blockToBBCode block = do spec <- asks flavorSpec case block of Plain inlines -> inlineListToBBCode inlines Para inlines -> renderPara spec inlines LineBlock inliness -> renderLineBlock spec inliness CodeBlock attr code -> renderCodeBlock spec attr code RawBlock format raw -> case format of "bbcode" -> pure $ literal raw _ -> "" <$ report (BlockNotRendered block) BlockQuote blocks -> renderBlockQuote spec blocks OrderedList attr items -> renderOrderedList spec attr items BulletList items -> renderBulletList spec items DefinitionList items -> renderDefinitionList spec items Header level attr inlines -> renderHeader spec level attr inlines HorizontalRule -> renderHorizontalRule spec Table attr blkCapt specs thead tbody tfoot -> renderTable spec (attr, blkCapt, specs, thead, tbody, tfoot) Figure attr caption blocks -> renderFigure spec attr caption blocks Div attr blocks -> do contents <- blockListToBBCode blocks let kvcMap = attrToMap attr -- whether passed contents is a Div (Block) element -- vvvv pure $ wrapSpanDiv spec True kvcMap contents inlineToBBCode :: (PandocMonad m) => Inline -> RR m (Doc Text) inlineToBBCode inline = do spec <- asks flavorSpec case inline of Str str -> do opts <- asks writerOptions pure . literal $ unsmartify opts str Emph inlines -> do contents <- inlineListToBBCode inlines pure $ mconcat ["[i]", contents, "[/i]"] Underline inlines -> do contents <- inlineListToBBCode inlines pure $ mconcat ["[u]", contents, "[/u]"] Strong inlines -> do contents <- inlineListToBBCode inlines pure $ mconcat ["[b]", contents, "[/b]"] Strikeout inlines -> renderStrikeout spec inlines Superscript inlines -> renderSuperscript spec inlines Subscript inlines -> renderSubscript spec inlines SmallCaps inlines -> renderSmallCaps spec inlines Quoted typ inlines -> renderQuoted spec typ inlines Cite cits inlines -> renderCite spec cits inlines Code attr code -> renderInlineCode spec attr code Space -> pure space SoftBreak -> pure space LineBreak -> pure cr Math typ math -> renderMath spec typ math RawInline (Format format) text -> case format of "bbcode" -> pure $ literal text _ -> "" <$ report (InlineNotRendered inline) Link attr txt target -> renderLink spec attr txt target Image attr alt target -> renderImage spec attr alt target Note blocks -> renderNote spec blocks Span attr inlines -> do contents <- inlineListToBBCode inlines let kvcMap = attrToMap attr -- whether passed contents is a Div (Block element) -- vvvvv pure $ wrapSpanDiv spec False kvcMap contents renderImageDefault :: (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) renderImageDefault (_, _, kvList) alt (source, title) = do altText <- trim . render Nothing <$> inlineListToBBCode (removeFormatting alt) let kvMap = Map.fromList kvList -- No BBCode flavor supported by the Writer has local images support, but we -- still allow source to be plain path or anything else pure . literal $ mconcat [ "[img" , if T.null altText then "" else " alt=" <> inquotes altText , if T.null title then "" else " title=" <> inquotes title , case Map.lookup "width" kvMap of Just w | isJust (readMaybe @Int $ T.unpack w) -> " width=" <> inquotes w _ -> "" , case Map.lookup "height" kvMap of Just h | isJust (readMaybe @Int $ T.unpack h) -> " height=" <> inquotes h _ -> "" , "]" , source , "[/img]" ] renderImageOmit :: (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) renderImageOmit _ _ _ = pure "" {- | Basic phpBB doesn't support any attributes, although @[img src=https://example.com]whatever[/img]@ is supported, but text in tag has no effect -} renderImagePhpBB :: (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) renderImagePhpBB _ _ (source, _) = pure . literal $ mconcat ["[img]", source, "[/img]"] renderImageXenforo :: (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) renderImageXenforo (_, _, kvList) alt (source, title) = do altText <- trim . render Nothing <$> inlineListToBBCode (removeFormatting alt) let kvMap = Map.fromList kvList -- No BBCode flavor supported by the Writer has local images support, but we -- still allow source to be plain path or anything else pure . literal $ mconcat [ "[img" , if T.null altText then "" else " alt=" <> inquotes altText , if T.null title then "" else " title=" <> inquotes title , case Map.lookup "width" kvMap of Just w | isRight (readWith sizeP Nothing w) -> " width=" <> w _ -> "" , "]" , source , "[/img]" ] where sizeP = some digit >> char '%' >> eof {- | Check whether character is a bracket >>> T.filter notBracket "[a]b[[ó]qü]]n®" "ab\243q\252n\174" -} notBracket :: Char -> Bool notBracket = \case '[' -> False ']' -> False _ -> True -- FluxBB uses [img=alt text] instead of [img alt="alt text"] renderImageFluxBB :: (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) renderImageFluxBB _ alt (source, _) = do alt' <- T.filter notBracket . render Nothing <$> inlineListToBBCode alt pure . literal $ mconcat [ "[img" , if T.null alt' then "" else "=" <> alt' , "]" , source , "[/img]" ] inlineListToBBCode :: (PandocMonad m) => [Inline] -> RR m (Doc Text) inlineListToBBCode inlines = mconcat <$> mapM inlineToBBCode inlines -- Taken from Data.Ord clamp :: (Ord a) => (a, a) -> a -> a clamp (low, high) a = min high (max a low) renderHeaderDefault :: (PandocMonad m) => Int -> Attr -> [Inline] -> RR m (Doc Text) renderHeaderDefault level _attr inlines = case clamp (1, 4) level of 1 -> inlineToBBCode $ Underline [Strong inlines] 2 -> inlineToBBCode $ Strong inlines 3 -> inlineToBBCode $ Underline inlines _ -> inlineListToBBCode inlines -- Adapted from Text.Pandoc.Writers.Org renderLinkDefault :: (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) renderLinkDefault _ txt (src, _) = case txt of [Str x] | escapeURI x == src -> pure $ "[url]" <> literal x <> "[/url]" _ -> do contents <- inlineListToBBCode txt let suffix = if T.null src then "" else "=" <> src pure $ "[url" <> literal suffix <> "]" <> contents <> "[/url]" renderCodeBlockDefault :: (PandocMonad m) => Attr -> Text -> RR m (Doc Text) renderCodeBlockDefault (_, cls, _) code = do let opening = case cls of (lang : _) -> "[code=" <> lang <> "]" _ -> "[code]" pure $ mconcat [literal opening, literal code, cr, "[/code]"] renderCodeBlockSimple :: (PandocMonad m) => Attr -> Text -> RR m (Doc Text) renderCodeBlockSimple _ code = do pure $ mconcat [literal "[code]", literal code, cr, "[/code]"] renderInlineCodeLiteral :: (PandocMonad m) => Attr -> Text -> RR m (Doc Text) renderInlineCodeLiteral _ code = pure $ literal code renderInlineCodeNoParse :: (PandocMonad m) => Attr -> Text -> RR m (Doc Text) renderInlineCodeNoParse _ code = pure $ mconcat [literal "[noparse]", literal code, "[/noparse]"] renderInlineCodeHubzilla :: (PandocMonad m) => Attr -> Text -> RR m (Doc Text) renderInlineCodeHubzilla _ code = pure $ mconcat [literal "[code]", literal code, "[/code]"] renderInlineCodeXenforo :: (PandocMonad m) => Attr -> Text -> RR m (Doc Text) renderInlineCodeXenforo _ code = pure $ mconcat [literal "[icode]", literal code, "[/icode]"] renderStrikeoutDefault :: (PandocMonad m) => [Inline] -> RR m (Doc Text) renderStrikeoutDefault inlines = do contents <- inlineListToBBCode inlines pure $ mconcat ["[s]", contents, "[/s]"] renderStrikeoutSteam :: (PandocMonad m) => [Inline] -> RR m (Doc Text) renderStrikeoutSteam inlines = do contents <- inlineListToBBCode inlines pure $ mconcat ["[strike]", contents, "[/strike]"] renderDefinitionListDefault :: (PandocMonad m) => [([Inline], [[Block]])] -> RR m (Doc Text) renderDefinitionListDefault items = do items' <- forM items $ \(term, definitions) -> do term' <- inlineListToBBCode term definitions' <- blockToBBCode (BulletList definitions) pure $ term' $$ definitions' pure $ vcat items' renderDefinitionListHubzilla :: (PandocMonad m) => [([Inline], [[Block]])] -> RR m (Doc Text) renderDefinitionListHubzilla items = do items' <- forM items $ \(term, definitions) -> do term' <- inlineListToBBCode term let term'' = "[*= " <> term' <> "]" definitions' <- forM definitions blockListToBBCode pure $ vcat (term'' : definitions') pure $ vcat (literal "[dl terms=\"b\"]" : items' ++ [literal "[/dl]"]) listWithTags :: (PandocMonad m) => Text -> Text -> ([[Block]] -> RR m [Doc Text]) -> [[Block]] -> RR m (Doc Text) listWithTags open close renderItems items = do contents <- local (\s -> s{inList = True}) (renderItems items) pure $ vcat $ literal open : contents ++ [literal close] starListItems :: (PandocMonad m) => [[Block]] -> RR m [Doc Text] starListItems items = forM items $ \item -> do item' <- blockListToBBCode item pure $ literal "[*]" <> item' listStyleCode :: ListNumberStyle -> Maybe Text listStyleCode = \case Decimal -> Just "1" DefaultStyle -> Just "1" LowerAlpha -> Just "a" UpperAlpha -> Just "A" LowerRoman -> Just "i" UpperRoman -> Just "I" Example -> Nothing renderBulletListOfficial :: (PandocMonad m) => [[Block]] -> RR m (Doc Text) renderBulletListOfficial = listWithTags "[list]" "[/list]" starListItems renderBulletListHubzilla :: (PandocMonad m) => [[Block]] -> RR m (Doc Text) renderBulletListHubzilla = listWithTags "[ul]" "[/ul]" starListItems renderOrderedListHubzilla :: (PandocMonad m) => ListAttributes -> [[Block]] -> RR m (Doc Text) renderOrderedListHubzilla (_, style, _) = case style of DefaultStyle -> listWithTags "[ol]" "[/ol]" starListItems Example -> listWithTags "[ol]" "[/ol]" starListItems _ -> listWithTags ("[list=" <> suffix <> "]") "[/list]" starListItems where suffix = fromMaybe "1" $ listStyleCode style renderOrderedListOfficial :: (PandocMonad m) => ListAttributes -> [[Block]] -> RR m (Doc Text) renderOrderedListOfficial (_, style, _) = do let suffix = maybe "" ("=" <>) (listStyleCode style) listWithTags ("[list" <> suffix <> "]") "[/list]" starListItems renderOrderedListSteam :: (PandocMonad m) => ListAttributes -> [[Block]] -> RR m (Doc Text) renderOrderedListSteam _ = listWithTags "[olist]" "[/olist]" starListItems renderHeaderSteam :: (PandocMonad m) => Int -> Attr -> [Inline] -> RR m (Doc Text) renderHeaderSteam level _ inlines = do body <- inlineListToBBCode inlines let capped = clamp (1, 3) level open = "[h" <> tshow capped <> "]" close = "[/h" <> tshow capped <> "]" pure $ literal open <> body <> literal close renderHeaderHubzilla :: (PandocMonad m) => Int -> Attr -> [Inline] -> RR m (Doc Text) renderHeaderHubzilla level _ inlines = do body <- inlineListToBBCode inlines let capped = clamp (1, 6) level open = "[h" <> tshow capped <> "]" close = "[/h" <> tshow capped <> "]" pure $ literal open <> body <> literal close -- xenForo supports levels 1--3, but levels other than 1--3 become div with -- .bbHeading class which can be linked to. renderHeaderXenforo :: (PandocMonad m) => Int -> Attr -> [Inline] -> RR m (Doc Text) renderHeaderXenforo level _ inlines = do body <- inlineListToBBCode inlines let capped = max 1 level open = "[heading=" <> tshow capped <> "]" close = "[/heading]" pure $ literal open <> body <> literal close renderTableGeneric :: (PandocMonad m) => Text -> Text -> Text -> (Attr, Caption, [ColSpec], TableHead, [TableBody], TableFoot) -> RR m (Doc Text) renderTableGeneric tableTag headerCellTag bodyCellTag table = do caption' <- inlineListToBBCode caption table' <- if not simpleCells then "" <$ report (BlockNotRendered tableBlock) else do headerDocs <- if null headers then pure [] else pure <$> renderTableRow headerCellTag headers rowDocs <- mapM (renderTableRow bodyCellTag) rows pure $ renderTable' headerDocs rowDocs pure $ caption' $$ table' where (attr, blkCapt, specs, thead, tbody, tfoot) = table (caption, _, _, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot tableBlock = Table attr blkCapt specs thead tbody tfoot simpleCells = onlySimpleTableCells (headers : rows) renderTable' headerDocs rowDocs = vcat [ literal ("[" <> tableTag <> "]") , vcat headerDocs , vcat rowDocs , literal ("[/" <> tableTag <> "]") ] renderCell cellTag cellDoc = mconcat [ literal ("[" <> cellTag <> "]") , cellDoc , literal ("[/" <> cellTag <> "]") ] renderTableRow cellTag cells = do renderedCells <- mapM blockListToBBCode cells let cellsDoc = mconcat $ map (renderCell cellTag) renderedCells pure $ literal "[tr]" <> cellsDoc <> literal "[/tr]" renderTableDefault :: (PandocMonad m) => ( Attr , Caption , [ColSpec] , TableHead , [TableBody] , TableFoot ) -> RR m (Doc Text) renderTableDefault = renderTableGeneric "table" "th" "td" renderTableOmit :: (PandocMonad m) => ( Attr , Caption , [ColSpec] , TableHead , [TableBody] , TableFoot ) -> RR m (Doc Text) renderTableOmit (_, blkCapt, specs, thead, tbody, tfoot) = do let (caption, _, _, _, _) = toLegacyTable blkCapt specs thead tbody tfoot caption' <- inlineListToBBCode caption pure $ caption' $$ "(TABLE)" -- $wrapping_spans_divs -- Consider attribute a key-value pair with a Just value, and respectively -- class is key-value pair with Nothing value. -- For instance, given @("", ["cl1"], [("k", "v")]) :: 'Attr'@, respective Map -- should look like @'Map.fromList' [("cl1", 'Nothing'), ("k", 'Just' "v")]@ -- -- This transformation is handled by 'attrToMap' -- -- Example definition of a wrapSpanDiv: -- -- > {-# LANGUAGE OverloadedStrings #-} -- > import Data.Map (Map) -- > import qualified Data.Map as Map -- > import Text.DocLayout -- > import Data.Text (Text) -- > import qualified Data.Text as T -- > -- > wrapSpanDivSteam :: Bool -> Map Text (Maybe Text) -> Doc Text -> Doc Text -- > wrapSpanDivSteam isDiv kvc doc = Map.foldrWithKey wrap doc kvc -- > where -- > wrap "spoiler" (Just _) acc | isDiv = "[spoiler]" <> acc <> "[/spoiler]" -- > wrap "spoiler" Nothing acc | isDiv = "[spoiler]" <> acc <> "[/spoiler]" -- > wrap _ _ acc = acc -- -- To verify it works, wrap some text in unnamed spoiler -- -- >>> render Nothing $ wrapSpanDivSteam True (attrToMap ("", ["spoiler"], [])) "I am text" -- "[spoiler]I am text[/spoiler]" {- | The goal of the transformation is to treat classes and key-value pairs uniformly. Class list becomes Map where all values are Nothing, and list of key-value pairs is converted to Map via 'Map.toList'. Both Maps are then merged. -} attrToMap :: Attr -> Map Text (Maybe Text) attrToMap (_, classes, kvList) = Map.fromList kvList' `Map.union` Map.fromList classes' where kvList' = map (\(k, v) -> (k, Just v)) kvList classes' = map (\k -> (k, Nothing)) classes wrapSpanDivOfficial :: Bool -> Map Text (Maybe Text) -> Doc Text -> Doc Text wrapSpanDivOfficial isDiv kvc doc = Map.foldrWithKey wrap doc kvc where wrap "left" Nothing acc | isDiv = "[left]" <> acc <> "[/left]" wrap "center" Nothing acc | isDiv = "[center]" <> acc <> "[/center]" wrap "right" Nothing acc | isDiv = "[right]" <> acc <> "[/right]" wrap "spoiler" Nothing acc | isDiv = "[spoiler]" <> acc <> "[/spoiler]" wrap "spoiler" (Just v) acc | isDiv = literal ("[spoiler=" <> T.filter notBracket v <> "]") <> acc <> "[/spoiler]" wrap "size" (Just v) acc | Just v' <- readMaybe @Int (T.unpack v) , v' > 0 = literal ("[size=" <> v <> "]") <> acc <> "[/size]" wrap "color" (Just v) acc = literal ("[color=" <> v <> "]") <> acc <> "[/color]" wrap _ _ acc = acc wrapSpanDivSteam :: Bool -> Map Text (Maybe Text) -> Doc Text -> Doc Text wrapSpanDivSteam isDiv kvc doc = Map.foldrWithKey wrap doc kvc where wrap "spoiler" (Just _) acc | isDiv = "[spoiler]" <> acc <> "[/spoiler]" wrap "spoiler" Nothing acc | isDiv = "[spoiler]" <> acc <> "[/spoiler]" wrap _ _ acc = acc wrapSpanDivPhpBB :: Bool -> Map Text (Maybe Text) -> Doc Text -> Doc Text wrapSpanDivPhpBB _ kvc doc = Map.foldrWithKey wrap doc kvc where wrap "color" (Just v) acc = literal ("[color=" <> v <> "]") <> acc <> "[/color]" wrap _ _ acc = acc wrapSpanDivFluxBB :: Bool -> Map Text (Maybe Text) -> Doc Text -> Doc Text wrapSpanDivFluxBB _ kvc doc = Map.foldrWithKey wrap doc kvc where wrap "color" (Just v) acc = literal ("[color=" <> v <> "]") <> acc <> "[/color]" wrap _ _ acc = acc wrapSpanDivHubzilla :: Bool -> Map Text (Maybe Text) -> Doc Text -> Doc Text wrapSpanDivHubzilla isDiv kvc doc = Map.foldrWithKey wrap doc kvc where wrap "center" Nothing acc | isDiv = "[center]" <> acc <> "[/center]" wrap "spoiler" Nothing acc | isDiv = "[spoiler]" <> acc <> "[/spoiler]" wrap "spoiler" (Just v) acc | isDiv = literal ("[spoiler=" <> T.filter notBracket v <> "]") <> acc <> "[/spoiler]" wrap "size" (Just v) acc | Just v' <- readMaybe @Int (T.unpack v) , v' > 0 = literal ("[size=" <> v <> "]") <> acc <> "[/size]" wrap "color" (Just v) acc = literal ("[color=" <> v <> "]") <> acc <> "[/color]" wrap "font" (Just v) acc = literal ("[font=" <> v <> "]") <> acc <> "[/font]" wrap _ _ acc = acc wrapSpanDivXenforo :: Bool -> Map Text (Maybe Text) -> Doc Text -> Doc Text wrapSpanDivXenforo isDiv kvc doc = Map.foldrWithKey wrap doc kvc where wrap "left" Nothing acc | isDiv = "[left]" <> acc <> "[/left]" wrap "center" Nothing acc | isDiv = "[center]" <> acc <> "[/center]" wrap "right" Nothing acc | isDiv = "[right]" <> acc <> "[/right]" wrap "spoiler" _ acc | not isDiv = "[ispoiler]" <> acc <> "[/ispoiler]" wrap "spoiler" Nothing acc | isDiv = "[spoiler]" <> acc <> "[/spoiler]" wrap "spoiler" (Just v) acc | isDiv = literal ("[spoiler=" <> T.filter notBracket v <> "]") <> acc <> "[/spoiler]" wrap "size" (Just v) acc | Just v' <- readMaybe @Int (T.unpack v) , v' > 0 = literal ("[size=" <> v <> "]") <> acc <> "[/size]" wrap "color" (Just v) acc = literal ("[color=" <> v <> "]") <> acc <> "[/color]" wrap "font" (Just v) acc = literal ("[font=" <> v <> "]") <> acc <> "[/font]" wrap _ _ acc = acc renderOrderedListFluxbb :: (PandocMonad m) => ListAttributes -> [[Block]] -> RR m (Doc Text) renderOrderedListFluxbb (_, style, _) = let suffix = case style of LowerAlpha -> "=a" UpperAlpha -> "=a" _ -> "=1" in listWithTags ("[list" <> suffix <> "]") "[/list]" starListItems renderOrderedListXenforo :: (PandocMonad m) => ListAttributes -> [[Block]] -> RR m (Doc Text) renderOrderedListXenforo _ = listWithTags "[list=1]" "[/list]" starListItems renderLinkEmailAware :: (PandocMonad m) => Attr -> [Inline] -> Target -> RR m (Doc Text) renderLinkEmailAware attr txt target@(src, _) = do case T.stripPrefix "mailto:" src of Just address -> do linkText <- inlineListToBBCode txt let isAutoEmail = case txt of [Str x] -> x == address _ -> False pure $ if isAutoEmail then literal "[email]" <> literal address <> "[/email]" else literal ("[email=" <> address <> "]") <> linkText <> "[/email]" Nothing -> renderLinkDefault attr txt target renderBlockQuoteDefault :: (PandocMonad m) => [Block] -> RR m (Doc Text) renderBlockQuoteDefault blocks = do contents <- blockListToBBCode blocks pure $ vcat ["[quote]", contents, "[/quote]"] renderBlockQuoteFluxBB :: (PandocMonad m) => [Block] -> RR m (Doc Text) renderBlockQuoteFluxBB blocks = do contents <- blockListToBBCode blocks isInList <- asks inList if isInList then "" <$ report (BlockNotRendered $ BlockQuote blocks) else pure $ vcat ["[quote]", contents, "[/quote]"] renderHorizontalRuleDefault :: (PandocMonad m) => RR m (Doc Text) renderHorizontalRuleDefault = pure "* * *" renderHorizontalRuleHR :: (PandocMonad m) => RR m (Doc Text) renderHorizontalRuleHR = pure "[hr]" renderLineBlockDefault :: (PandocMonad m) => [[Inline]] -> RR m (Doc Text) renderLineBlockDefault inliness = vcat <$> mapM inlineListToBBCode inliness renderParaDefault :: (PandocMonad m) => [Inline] -> RR m (Doc Text) renderParaDefault inlines = inlineListToBBCode inlines renderSuperscriptDefault :: (PandocMonad m) => [Inline] -> RR m (Doc Text) renderSuperscriptDefault = inlineListToBBCode renderSubscriptDefault :: (PandocMonad m) => [Inline] -> RR m (Doc Text) renderSubscriptDefault = inlineListToBBCode renderSmallCapsDefault :: (PandocMonad m) => [Inline] -> RR m (Doc Text) renderSmallCapsDefault = inlineListToBBCode renderCiteDefault :: (PandocMonad m) => [Citation] -> [Inline] -> RR m (Doc Text) renderCiteDefault _ = inlineListToBBCode renderNoteDefault :: (PandocMonad m) => [Block] -> RR m (Doc Text) renderNoteDefault blocks = do -- NOTE: no BBCode flavor has native syntax for footnotes. newN <- gets (succ . Seq.length) contents <- blockListToBBCode blocks let pointer = "(" <> tshow newN <> ")" let contents' = literal pointer <> space <> contents modify (|> contents') pure $ literal pointer renderFigureDefault :: (PandocMonad m) => Attr -> Caption -> [Block] -> RR m (Doc Text) renderFigureDefault _ (Caption _ caption) blocks = do caption' <- blockListToBBCode caption contents <- blockListToBBCode blocks pure $ contents $$ caption' renderQuotedDefault :: (PandocMonad m) => QuoteType -> [Inline] -> RR m (Doc Text) renderQuotedDefault typ inlines = do let quote = case typ of SingleQuote -> "'"; DoubleQuote -> "\"" contents <- inlineListToBBCode inlines pure $ mconcat [quote, contents, quote] renderMathDefault :: (PandocMonad m) => MathType -> Text -> RR m (Doc Text) renderMathDefault typ math = case typ of InlineMath -> inlineToBBCode $ Code ("", ["latex"], []) ("$" <> math <> "$") DisplayMath -> blockToBBCode $ CodeBlock ("", ["latex"], []) ("$$" <> math <> "$$") {- | Format documentation: There is no such thing as «Official» bbcode format, nonetheless this spec implements what is described on bbcode.org, which is a reasonable base that can be extended/contracted as needed. -} officialSpec :: FlavorSpec officialSpec = FlavorSpec { renderOrderedList = renderOrderedListOfficial , renderBulletList = renderBulletListOfficial , renderDefinitionList = renderDefinitionListDefault , renderHeader = renderHeaderDefault , renderTable = renderTableDefault , renderLink = renderLinkEmailAware , renderCodeBlock = renderCodeBlockDefault , renderInlineCode = renderInlineCodeLiteral , renderStrikeout = renderStrikeoutDefault , renderBlockQuote = renderBlockQuoteDefault , renderHorizontalRule = renderHorizontalRuleDefault , renderLineBlock = renderLineBlockDefault , renderPara = renderParaDefault , renderSuperscript = renderSuperscriptDefault , renderSubscript = renderSubscriptDefault , renderSmallCaps = renderSmallCapsDefault , renderCite = renderCiteDefault , renderNote = renderNoteDefault , renderFigure = renderFigureDefault , renderMath = renderMathDefault , renderQuoted = renderQuotedDefault , renderImage = renderImageDefault , wrapSpanDiv = wrapSpanDivOfficial } {- | Format documentation: Used at: Quirks: - There seems to be no way to show external images on steam. https://steamcommunity.com/sharedfiles/filedetails/?id=2807121939 shows [img] and [previewimg] can (could?) be used to show images, although it is likely reserved for steam urls only. -} steamSpec :: FlavorSpec steamSpec = officialSpec { renderOrderedList = renderOrderedListSteam , renderHeader = renderHeaderSteam , renderLink = renderLinkDefault , renderInlineCode = renderInlineCodeNoParse , renderStrikeout = renderStrikeoutSteam , renderImage = renderImageOmit , wrapSpanDiv = wrapSpanDivSteam , renderHorizontalRule = renderHorizontalRuleHR } {- | Format documentation: Used at: Quirks: - PhpBB docs don't mention strikeout support, but their [support forum](https://www.phpbb.com/community) does support it. - Same for named code blocks. - @[email=example\@example.com]the email[/url]@ is a valid use of [email] tag on the phpBB community forum despite not being in the docs. -} phpbbSpec :: FlavorSpec phpbbSpec = officialSpec { renderTable = renderTableOmit , renderImage = renderImagePhpBB , wrapSpanDiv = wrapSpanDivPhpBB } {- | Format documentation: Used at: https://bbs.archlinux.org -} fluxbbSpec :: FlavorSpec fluxbbSpec = officialSpec { renderOrderedList = renderOrderedListFluxbb , renderCodeBlock = renderCodeBlockSimple , renderTable = renderTableOmit , renderBlockQuote = renderBlockQuoteFluxBB , renderImage = renderImageFluxBB , wrapSpanDiv = wrapSpanDivFluxBB } {- | Format documentation: Used at: (see [other hubs](https://hubzilla.org/pubsites)) Quirks: - If link target is not a URI, it simply points to https://$BASEURL/ when rendered by a hub. -} hubzillaSpec :: FlavorSpec hubzillaSpec = officialSpec { renderOrderedList = renderOrderedListHubzilla , renderBulletList = renderBulletListHubzilla , renderDefinitionList = renderDefinitionListHubzilla , renderHeader = renderHeaderHubzilla , renderInlineCode = renderInlineCodeHubzilla , renderLink = renderLinkDefault , wrapSpanDiv = wrapSpanDivHubzilla , renderHorizontalRule = renderHorizontalRuleHR } {- | Format documentation: Used at: see -} xenforoSpec :: FlavorSpec xenforoSpec = officialSpec { wrapSpanDiv = wrapSpanDivXenforo , renderHeader = renderHeaderXenforo , renderInlineCode = renderInlineCodeXenforo , renderHorizontalRule = renderHorizontalRuleHR , renderOrderedList = renderOrderedListXenforo , renderImage = renderImageXenforo } ================================================ FILE: src/Text/Pandoc/Writers/BibTeX.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.BibTeX Copyright : Copyright (C) 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Writes a BibTeX or BibLaTeX bibliographies based on the 'references' metadata in a Pandoc document. -} module Text.Pandoc.Writers.BibTeX ( writeBibTeX , writeBibLaTeX ) where import Text.Pandoc.Options import Text.Pandoc.Definition import Data.Text (Text) import Data.Maybe (mapMaybe) import Citeproc (parseLang) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Citeproc.BibTeX as BibTeX import Text.Pandoc.Citeproc.MetaValue (metaValueToReference) import Text.Pandoc.Writers.Shared (lookupMetaString, defField, addVariablesToContext) import Text.DocLayout (render, vcat) import Text.DocTemplates (Context(..)) import Text.Pandoc.Templates (renderTemplate) -- | Write BibTeX based on the references metadata from a Pandoc document. writeBibTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeBibTeX = writeBibTeX' BibTeX.Bibtex -- | Write BibLaTeX based on the references metadata from a Pandoc document. writeBibLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeBibLaTeX = writeBibTeX' BibTeX.Biblatex writeBibTeX' :: PandocMonad m => Variant -> WriterOptions -> Pandoc -> m Text writeBibTeX' variant opts (Pandoc meta _) = do let mblang = case lookupMetaString "lang" meta of "" -> Nothing t -> either (const Nothing) Just $ parseLang t let refs = case lookupMeta "references" meta of Just (MetaList xs) -> mapMaybe metaValueToReference xs _ -> [] let main = vcat $ map (BibTeX.writeBibtexString opts variant mblang) refs let context = defField "body" main $ addVariablesToContext opts (mempty :: Context Text) let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context ================================================ FILE: src/Text/Pandoc/Writers/Blaze.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.Blaze Copyright : Copyright (C) 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Render blaze-html Html to DocLayout document (so it can be wrapped). -} module Text.Pandoc.Writers.Blaze ( layoutMarkup ) where import Text.Blaze import qualified Data.ByteString as S import Data.List (isInfixOf) import Data.Text.Encoding (decodeUtf8) import qualified Data.Text as T import Data.Text (Text) import Text.DocLayout hiding (Text, Empty) import Text.Blaze.Internal (ChoiceString(..), getText, MarkupM(..)) layoutMarkup :: Markup -> Doc T.Text layoutMarkup = go True mempty where go :: Bool -> Doc T.Text -> MarkupM b -> Doc T.Text go wrap attrs (Parent _ open close content) = let open' = getText open in literal open' <> attrs <> char '>' <> (case open' of " go False mempty content t | t == " flush $ go False mempty content | otherwise -> go wrap mempty content) <> literal (getText close) go wrap attrs (CustomParent tag content) = char '<' <> fromChoiceString wrap tag <> attrs <> char '>' <> go wrap mempty content <> literal " fromChoiceString wrap tag <> char '>' go _wrap attrs (Leaf _ begin end _) = literal (getText begin) <> attrs <> literal (getText end) go wrap attrs (CustomLeaf tag close _) = char '<' <> fromChoiceString wrap tag <> attrs <> (if close then literal " />" else char '>') go wrap attrs (AddAttribute rawkey _ value h) = go wrap (space' wrap <> literal (getText rawkey) <> char '=' <> doubleQuotes (fromChoiceString False value) <> attrs) h go wrap attrs (AddCustomAttribute key value h) = go wrap (space' wrap <> fromChoiceString wrap key <> char '=' <> doubleQuotes (fromChoiceString False value) <> attrs) h go wrap _ (Content content _) = fromChoiceString wrap content go wrap _ (Comment comment _) = literal "" go wrap attrs (Append h1 h2) = go wrap attrs h1 <> go wrap attrs h2 go _ _ (Empty _) = mempty space' wrap = if wrap then space else char ' ' fromChoiceString :: Bool -- ^ Allow wrapping -> ChoiceString -- ^ String to render -> Doc Text -- ^ Resulting builder fromChoiceString wrap (Static s) = withWrap wrap $ getText s fromChoiceString wrap (String s) = withWrap wrap $ escapeMarkupEntities $ T.pack s fromChoiceString wrap (Text s) = withWrap wrap $ escapeMarkupEntities s fromChoiceString wrap (ByteString s) = withWrap wrap $ decodeUtf8 s fromChoiceString _wrap (PreEscaped x) = -- don't wrap! case x of String s -> literal $ T.pack s Text s -> literal s s -> fromChoiceString False s fromChoiceString wrap (External x) = case x of -- Check that the sequence " if " if " if " fromChoiceString wrap s fromChoiceString wrap (AppendChoiceString x y) = fromChoiceString wrap x <> fromChoiceString wrap y fromChoiceString _ EmptyChoiceString = mempty withWrap :: Bool -> Text -> Doc Text withWrap wrap | wrap = mconcat . toChunks | otherwise = literal toChunks :: Text -> [Doc Text] toChunks = map toDoc . T.groupBy sameStatus where toDoc t | t == " " = space | t == "\n" = cr | otherwise = literal t sameStatus c d = (c == ' ' && d == ' ') || (c == '\n' && d == '\n') || (c /= ' ' && d /= ' ' && c /= '\n' && d /= '\n') -- | Escape predefined XML entities in a text value -- escapeMarkupEntities :: Text -- ^ Text to escape -> Text -- ^ Resulting Doc escapeMarkupEntities = T.concatMap escape where escape :: Char -> Text escape '<' = "<" escape '>' = ">" escape '&' = "&" escape '"' = """ escape '\'' = "'" escape x = T.singleton x ================================================ FILE: src/Text/Pandoc/Writers/ChunkedHTML.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.ChunkedHTML Copyright : Copyright (C) 2023 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to "chunked" HTML (a folder of linked HTML documents, split by sections. -} module Text.Pandoc.Writers.ChunkedHTML ( writeChunkedHTML ) where import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions(..)) import Text.Pandoc.Shared (stringify, tshow) import Text.Pandoc.Class (PandocMonad, getPOSIXTime, runPure, fetchItem, insertMedia, getMediaBag) import Text.Pandoc.MediaBag (mediaItems) import qualified Data.ByteString.Lazy as BL import Text.Pandoc.Chunks (splitIntoChunks, Chunk(..), ChunkedDoc(..), SecInfo(..), tocToList) import Text.Pandoc.URI (isURI) import Data.Text (Text) import Data.Tree import qualified Data.Text as T import qualified Data.Text.Encoding as TE import Text.Pandoc.Writers.HTML (writeHtml5String) import Codec.Archive.Zip (Entry, addEntryToArchive, emptyArchive, toEntry, fromArchive) import qualified Data.Map as M import Text.DocTemplates (Context(..), Val(..)) import Text.DocLayout (literal) import Text.Pandoc.Writers.Shared (defField) import Data.Aeson (toJSON, encode) import System.FilePath (isRelative, normalise) import Data.List (isInfixOf) import Text.Pandoc.Walk (walkM) import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Templates (compileTemplate, WithDefaultPartials(..)) import Control.Monad.Except (throwError) import Text.Pandoc.Error -- | Splits document into HTML chunks, dividing them by section, -- and returns a zip archive of a folder of files. writeChunkedHTML :: PandocMonad m => WriterOptions -> Pandoc -> m BL.ByteString writeChunkedHTML opts (Pandoc meta blocks) = do walkM addMedia (Pandoc meta blocks) epochtime <- floor <$> getPOSIXTime let toMediaEntry (fp, _mt, bs) = toEntry fp epochtime bs mediaEntries <- map toMediaEntry . mediaItems <$> getMediaBag let chunkedDoc = splitIntoChunks (writerChunkTemplate opts) True (Just 1) (writerSplitLevel opts) (Pandoc meta blocks) let topChunk = Chunk { chunkHeading = docTitle meta , chunkId = "top" , chunkLevel = 0 , chunkNumber = 0 , chunkSectionNumber = Nothing , chunkPath = "index.html" , chunkUp = Nothing , chunkPrev = Nothing , chunkNext = case chunkedChunks chunkedDoc of [] -> Nothing (x:_) -> Just x , chunkUnlisted = True , chunkContents = mempty } let chunks = map (\x -> case chunkUp x of Nothing -> x{ chunkUp = Just topChunk } _ -> x) $ case chunkedChunks chunkedDoc of [] -> [] (x:xs) -> x{ chunkPrev = Just topChunk } : xs let Node secinfo secs = chunkedTOC chunkedDoc let tocTree = Node secinfo{ secTitle = docTitle meta, secPath = "index.html" } secs let tree = buildTOC opts tocTree renderedTOC <- writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [tree]) -- see #8915 -- we need to set the math variable in the top chunk: res <- runWithDefaultPartials $ compileTemplate "mathvar" "$math$" mathVar <- case res of Left e -> throwError $ PandocTemplateError (T.pack e) Right t -> return t tocMathVariable <- writeHtml5String opts{ writerTemplate = Just mathVar } (Pandoc meta (tree:blocks)) let opts' = opts{ writerVariables = defField "table-of-contents" renderedTOC . defField "math" tocMathVariable $ writerVariables opts } entries <- mapM (chunkToEntry opts' meta topChunk) (topChunk : chunks) let sitemap = toEntry "sitemap.json" epochtime (encode $ toJSON $ tocTreeToContext tocTree) let archive = foldr addEntryToArchive emptyArchive (sitemap : entries ++ mediaEntries) return $ fromArchive archive -- We include in the zip only local media that is in the working directory -- or below. addMedia :: PandocMonad m => Inline -> m Inline addMedia il@(Image _ _ (src,_)) | not (isURI src) , fp <- normalise (T.unpack src) , isRelative fp , not (".." `isInfixOf` fp) = do (bs, mbMime) <- fetchItem (T.pack fp) insertMedia fp mbMime (BL.fromStrict bs) return il addMedia il = return il buildTOC :: WriterOptions -> Tree SecInfo -> Block buildTOC opts = tocToList (writerNumberSections opts) (writerTOCDepth opts) chunkToEntry :: PandocMonad m => WriterOptions -> Meta -> Chunk -> Chunk -> m Entry chunkToEntry opts meta topChunk chunk = do html <- writeHtml5String opts' (Pandoc meta' blocks) epochtime <- floor <$> getPOSIXTime let htmlLBS = BL.fromStrict $ TE.encodeUtf8 html return $ toEntry (chunkPath chunk) epochtime htmlLBS where opts' = opts{ writerVariables = addContextVars opts' topChunk chunk $ writerVariables opts } meta' = setMeta "pagetitle" (MetaString (stringify $ chunkHeading chunk)) meta blocks = chunkContents chunk tocTreeToContext :: Tree SecInfo -> Context Text tocTreeToContext (Node secinfo subs) = Context $ M.fromList [ ("section", MapVal $ secInfoToContext secinfo) , ("subsections", ListVal $ map (MapVal . tocTreeToContext) subs) ] secInfoToContext :: SecInfo -> Context Text secInfoToContext sec = Context $ M.fromList [ ("title", SimpleVal $ literal $ stringify $ secTitle sec) , ("number", maybe NullVal (SimpleVal . literal) (secNumber sec)) , ("id", SimpleVal $ literal $ secId sec) , ("path", SimpleVal $ literal $ secPath sec) , ("level", SimpleVal $ literal $ tshow $ secLevel sec) ] addContextVars :: WriterOptions -> Chunk -> Chunk -> Context Text -> Context Text addContextVars opts topChunk chunk context = maybe id (defField "next" . navlinks) (chunkNext chunk) . maybe id (defField "previous" . navlinks) (chunkPrev chunk) . maybe id (defField "up" . navlinks) (chunkUp chunk) . maybe id (defField "top" . navlinks) (if chunk == topChunk then Nothing else Just topChunk) . defField "toc" (chunk == topChunk && writerTableOfContents opts) $ context where navlinks ch = toMapVal [("url", formatPath ch), ("title", formatHeading ch)] toMapVal = MapVal . Context . M.fromList formatPath = SimpleVal . literal . T.pack . chunkPath formatHeading ch = SimpleVal . literal . either (const "") id . runPure $ writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain $ chunkHeading ch]) ================================================ FILE: src/Text/Pandoc/Writers/CommonMark.hs ================================================ {- | Module : Text.Pandoc.Writers.CommonMark Copyright : Copyright (C) 2015-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to CommonMark. CommonMark: -} module Text.Pandoc.Writers.CommonMark (writeCommonMark) where import Text.Pandoc.Writers.Markdown (writeCommonMark) ================================================ FILE: src/Text/Pandoc/Writers/ConTeXt.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.ConTeXt Copyright : Copyright (C) 2007-2025 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' format into ConTeXt. -} module Text.Pandoc.Writers.ConTeXt ( writeConTeXt ) where import Control.Monad (liftM, unless) import Control.Monad.State.Strict ( StateT, MonadState(put, get), gets, modify, evalStateT ) import Data.Char (ord, isDigit) import Data.List (intersperse) import Data.List.NonEmpty (NonEmpty ((:|))) import Data.Maybe (mapMaybe, catMaybes) import Data.Monoid (Any (Any, getAny)) import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) import Text.Collate.Lang (Lang(..)) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatConTeXtBlock, formatConTeXtInline, highlight, styleToConTeXt) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI (isURI) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Walk (query) import Text.Pandoc.Writers.Shared import Text.Printf (printf) import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Map.Strict as Map import qualified Text.Pandoc.Writers.AnnotatedTable as Ann data WriterState = WriterState { stCslHangingIndent :: Bool -- CSL hanging indent , stHasCslRefs :: Bool -- has CSL citations , stHighlighting :: Bool -- has syntax-highlighted code blocks , stNextRef :: Int -- number of next URL reference , stOptions :: WriterOptions -- writer options , stOrderedListLevel :: Int -- level of ordered list , stEmphasisCommands :: Map.Map Text (Doc Text) } -- | Table type data Tabl = Xtb -- ^ Extreme tables | Ntb -- ^ Natural tables deriving (Show, Eq) -- | Whether a heading belongs to a section environment or is standalone. data HeadingType = SectionHeading | NonSectionHeading orderedListStyles :: [Char] orderedListStyles = cycle "narg" -- | Convert Pandoc to ConTeXt. writeConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeConTeXt options document = let defaultWriterState = WriterState { stCslHangingIndent = False , stHasCslRefs = False , stHighlighting = False , stNextRef = 1 , stOptions = options , stOrderedListLevel = 0 , stEmphasisCommands = mempty } in evalStateT (pandocToConTeXt options document) defaultWriterState type WM = StateT WriterState pandocToConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> WM m Text pandocToConTeXt options (Pandoc meta blocks) = do let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing metadata <- metaToContext options blockListToConTeXt (fmap chomp . inlineListToConTeXt) meta main <- blockListToConTeXt $ makeSections False Nothing blocks let layoutFromMargins = mconcat $ intersperse ("," :: Doc Text) $ mapMaybe (\(x,y) -> ((x <> "=") <>) <$> getField y metadata) [("leftmargin","margin-left") ,("rightmargin","margin-right") ,("top","margin-top") ,("bottom","margin-bottom") ] mblang <- fromBCP47 (getLang options meta) st <- get let context = defField "toc" (writerTableOfContents options) $ defField "lof" (writerListOfFigures options) $ defField "lot" (writerListOfTables options) $ defField "placelist" (mconcat . intersperse ("," :: Doc Text) $ take (writerTOCDepth options + case writerTopLevelDivision options of TopLevelPart -> 0 TopLevelChapter -> 0 _ -> 1) ["chapter","section","subsection","subsubsection", "subsubsubsection","subsubsubsubsection"]) $ defField "body" main $ defField "layout" layoutFromMargins $ defField "tagging" (isEnabled Ext_tagging options) $ defField "number-sections" (writerNumberSections options) $ defField "csl-refs" (stHasCslRefs st) $ defField "csl-hanging-indent" (stCslHangingIndent st) $ maybe id (\l -> defField "context-lang" (literal l :: Doc Text)) mblang $ (case T.unpack . render Nothing <$> getField "papersize" metadata of Just (('a':d:ds) :: String) | all isDigit (d:ds) -> resetField "papersize" (T.pack ('A':d:ds)) _ -> id) $ defField "emphasis-commands" (mconcat $ Map.elems (stEmphasisCommands st)) $ (case writerHighlightMethod options of Skylighting sty | stHighlighting st -> defField "highlighting-commands" (styleToConTeXt sty) _ -> id) $ (case T.toLower $ lookupMetaString "pdfa" meta of "true" -> resetField "pdfa" (T.pack "1b:2005") _ -> id) metadata let context' = defField "context-dir" (maybe mempty toContextDir $ getField "dir" context) context return $ render colwidth $ case writerTemplate options of Nothing -> main Just tpl -> renderTemplate tpl context' -- change rtl to r2l, ltr to l2r toContextDir :: Doc Text -> Doc Text toContextDir = fmap (\t -> case t of "ltr" -> "l2r" "rtl" -> "r2l" _ -> t) -- | escape things as needed for ConTeXt escapeCharForConTeXt :: WriterOptions -> Char -> Text escapeCharForConTeXt opts ch = let ligatures = isEnabled Ext_smart opts in case ch of '{' -> "\\{" '}' -> "\\}" '\\' -> "\\letterbackslash{}" '$' -> "\\$" '|' -> "\\letterbar{}" '%' -> "\\letterpercent{}" '~' -> "\\lettertilde{}" '#' -> "\\#" '[' -> "{[}" ']' -> "{]}" '\160' -> "~" '\x2014' | ligatures -> "---" '\x2013' | ligatures -> "--" '\x2019' | ligatures -> "'" '\x2026' -> "\\ldots{}" x -> T.singleton x -- | Escape string for ConTeXt stringToConTeXt :: WriterOptions -> Text -> Text stringToConTeXt opts = T.concatMap (escapeCharForConTeXt opts) -- | Sanitize labels toLabel :: Text -> Text toLabel z = T.concatMap go z where go x | x `elem` ("\\#[]\",{}%()|=" :: String) = "ux" <> T.pack (printf "%x" (ord x)) | otherwise = T.singleton x -- | Convert Pandoc block element to ConTeXt. blockToConTeXt :: PandocMonad m => Block -> WM m (Doc Text) blockToConTeXt (Div attr@(_,"section":_,_) (Header level _ title' : xs)) = do header' <- sectionHeader attr level title' SectionHeading footer' <- sectionFooter attr level innerContents <- blockListToConTeXt xs return $ header' $$ innerContents $$ footer' blockToConTeXt (Plain lst) = do opts <- gets stOptions contents <- inlineListToConTeXt lst return $ if isEnabled Ext_tagging opts then "\\bpar{}" <> contents <> "\\epar{}" else contents blockToConTeXt (Para lst) = do opts <- gets stOptions contents <- inlineListToConTeXt lst return $ if isEnabled Ext_tagging opts then "\\bpar" $$ contents $$ "\\epar" <> blankline else contents <> blankline blockToConTeXt (LineBlock lns) = do let emptyToBlankline doc = if isEmpty doc then blankline else doc doclines <- mapM inlineListToConTeXt lns let contextLines = vcat . map emptyToBlankline $ doclines return $ "\\startlines" $$ contextLines $$ "\\stoplines" <> blankline blockToConTeXt (BlockQuote lst) = do contents <- blockListToConTeXt lst return $ "\\startblockquote" $$ nest 0 contents $$ "\\stopblockquote" <> blankline blockToConTeXt (CodeBlock (_ident, classes, kv) str) = do opts <- gets stOptions let syntaxMap = writerSyntaxMap opts let attr' = ("", classes, kv) let unhighlighted = vcat ["\\starttyping", literal str, "\\stoptyping"] let highlighted = case highlight syntaxMap formatConTeXtBlock attr' str of Left msg -> do unless (T.null msg) $ report (CouldNotHighlight msg) return unhighlighted Right h -> do modify (\s -> s{ stHighlighting = True }) return (literal h) -- blankline because \stoptyping can't have anything after it, inc. '}' ($$ blankline) . flush <$> case writerHighlightMethod opts of Skylighting _ | not (null classes) -> pure unhighlighted _ -> highlighted blockToConTeXt b@(RawBlock f str) | f == Format "context" || f == Format "tex" = return $ literal str <> blankline | otherwise = empty <$ report (BlockNotRendered b) blockToConTeXt (Div ("refs",classes,_) bs) = do modify $ \st -> st{ stHasCslRefs = True , stCslHangingIndent = "hanging-indent" `elem` classes } inner <- blockListToConTeXt bs return $ "\\startcslreferences" $$ inner $$ "\\stopcslreferences" blockToConTeXt (Div (ident,_,kvs) bs) = do let align dir txt = "\\startalignment[" <> dir <> "]" $$ txt $$ "\\stopalignment" mblang <- fromBCP47 (lookup "lang" kvs) let wrapRef txt = if T.null ident then txt else ("\\reference" <> brackets (literal $ toLabel ident) <> braces empty <> "%") $$ txt wrapDir = case lookup "dir" kvs of Just "rtl" -> align "righttoleft" Just "ltr" -> align "lefttoright" _ -> id wrapLang txt = case mblang of Just lng -> "\\start\\language[" <> literal lng <> "]" $$ txt $$ "\\stop" Nothing -> txt wrapBlank txt = blankline <> txt <> blankline wrapBlank . wrapLang . wrapDir . wrapRef <$> blockListToConTeXt bs blockToConTeXt (BulletList lst) = do contents <- mapM listItemToConTeXt lst return $ ("\\startitemize" <> if isTightList lst then brackets "packed" else empty) $$ vcat contents $$ literal "\\stopitemize" <> blankline blockToConTeXt (OrderedList (start, style', delim) lst) = do st <- get let level = stOrderedListLevel st put st {stOrderedListLevel = level + 1} contents <- mapM listItemToConTeXt lst put st {stOrderedListLevel = level} let start' = if start == 1 then "" else "start=" <> tshow start let delim' = case delim of DefaultDelim -> "" Period -> "stopper=." OneParen -> "stopper=)" TwoParens -> "left=(,stopper=)" let specs2Items = filter (not . T.null) [start', delim'] let specs2 = if null specs2Items then "" else "[" <> T.intercalate "," specs2Items <> "]" let style'' = '[': (case style' of DefaultStyle -> orderedListStyles !! level Decimal -> 'n' Example -> 'n' LowerRoman -> 'r' UpperRoman -> 'R' LowerAlpha -> 'a' UpperAlpha -> 'A') : if isTightList lst then ",packed]" else "]" let specs = T.pack style'' <> specs2 return $ "\\startenumerate" <> literal specs $$ vcat contents $$ "\\stopenumerate" <> blankline blockToConTeXt (DefinitionList lst) = liftM vcat $ mapM defListItemToConTeXt lst blockToConTeXt HorizontalRule = return $ "\\thinrule" <> blankline -- If this is ever executed, provide a default for the reference identifier. blockToConTeXt (Header level attr lst) = sectionHeader attr level lst NonSectionHeading blockToConTeXt (Table attr caption colspecs thead tbody tfoot) = tableToConTeXt (Ann.toTable attr caption colspecs thead tbody tfoot) blockToConTeXt (Figure (ident, _, _) (Caption cshort clong) body) = do title <- inlineListToConTeXt (blocksToInlines clong) list <- maybe (pure empty) inlineListToConTeXt cshort content <- blockListToConTeXt body let options = ["reference=" <> literal (toLabel ident) | not (T.null ident)] ++ ["title=" <> braces title | not (isEmpty title)] ++ ["list=" <> braces list | not (isEmpty list)] let hasSubfigures = getAny $ query (Any . \case {Figure {} -> True; _ -> False}) body return $ "\\startplacefigure" <> brackets (mconcat $ intersperse "," options) $$ (if hasSubfigures then "\\startfloatcombination" else empty) $$ content $$ (if hasSubfigures then "\\stopfloatcombination" else empty) $$ "\\stopplacefigure" $$ blankline tableToConTeXt :: PandocMonad m => Ann.Table -> WM m (Doc Text) tableToConTeXt (Ann.Table attr caption colspecs thead tbodies tfoot) = do opts <- gets stOptions let tabl = if isEnabled Ext_ntb opts then Ntb else Xtb captionText <- case caption of Caption _ [] -> return mempty Caption _ longCapt -> blockListToConTeXt longCapt head' <- tableHeadToConTeXt tabl thead bodies <- mapM (tableBodyToConTeXt tabl) tbodies foot' <- tableFootToConTeXt tabl tfoot let body = case tabl of Xtb -> "\\startxtable" $$ head' $$ "\\startxtablebody[body]" $$ vcat bodies $$ "\\stopxtablebody" $$ foot' $$ "\\stopxtable" Ntb -> setupCols colspecs $$ "\\bTABLE" $$ head' $$ "\\bTABLEbody" $$ vcat bodies $$ "\\eTABLEbody" $$ foot' $$ "\\eTABLE" let (ident, _classes, _attribs) = attr let tblopts = filter (not . isEmpty) [ if isEmpty captionText then "location=none" else "title=" <> braces captionText , if T.null ident then empty else "reference=" <> braces (literal (toLabel ident)) ] return $ vcat [ "\\startplacetable" <> brackets (mconcat $ intersperse "," tblopts) , body , "\\stopplacetable" <> blankline ] setupCols :: [ColSpec] -> Doc Text setupCols = vcat . zipWith toColSetup [1::Int ..] where toColSetup i (align, width) = let opts = filter (not . isEmpty) [ case align of AlignLeft -> "align=right" AlignRight -> "align=left" AlignCenter -> "align=middle" AlignDefault -> "align=left" , case width of ColWidthDefault -> empty ColWidth w -> ("width=" <>) . braces . text $ printf "%.2f\\textwidth" w ] in "\\setupTABLE[column]" <> brackets (text $ show i) <> brackets (mconcat $ intersperse "," opts) tableBodyToConTeXt :: PandocMonad m => Tabl -> Ann.TableBody -> WM m (Doc Text) tableBodyToConTeXt tabl (Ann.TableBody _attr _rowHeadCols inthead rows) = do intermediateHead <- if null inthead then return mempty else headerRowsToConTeXt tabl Thead inthead bodyRows <- bodyRowsToConTeXt tabl rows return $ intermediateHead <> bodyRows tableHeadToConTeXt :: PandocMonad m => Tabl -> Ann.TableHead -> WM m (Doc Text) tableHeadToConTeXt tabl (Ann.TableHead attr rows) = tablePartToConTeXt tabl Thead attr rows tableFootToConTeXt :: PandocMonad m => Tabl -> Ann.TableFoot -> WM m (Doc Text) tableFootToConTeXt tbl (Ann.TableFoot attr rows) = tablePartToConTeXt tbl Tfoot attr rows tablePartToConTeXt :: PandocMonad m => Tabl -> TablePart -> Attr -> [Ann.HeaderRow] -> WM m (Doc Text) tablePartToConTeXt tabl tblpart _attr rows = do let (startCmd, stopCmd) = case (tabl, tblpart) of (Ntb, Thead) -> ("\\bTABLEhead", "\\eTABLEhead") (Ntb, Tfoot) -> ("\\bTABLEfoot", "\\eTABLEfoot") (Xtb, Thead) -> ("\\startxtablehead[head]", "\\stopxtablehead") (Xtb, Tfoot) -> ("\\startxtablefoot[foot]", "\\stopxtablefoot") _ -> ("", "") -- this would be unexpected contents <- headerRowsToConTeXt tabl tblpart rows return $ startCmd $$ contents $$ stopCmd -- | The part of a table; header, footer, or body. data TablePart = Thead | Tfoot | Tbody deriving (Eq) data CellType = HeaderCell | BodyCell data TableRow = TableRow TablePart Attr Ann.RowHead Ann.RowBody headerRowsToConTeXt :: PandocMonad m => Tabl -> TablePart -> [Ann.HeaderRow] -> WM m (Doc Text) headerRowsToConTeXt tabl tablepart = rowListToConTeXt tabl . map toTableRow where toTableRow (Ann.HeaderRow attr _rownum rowbody) = TableRow tablepart attr [] rowbody bodyRowsToConTeXt :: PandocMonad m => Tabl -> [Ann.BodyRow] -> WM m (Doc Text) bodyRowsToConTeXt tabl = rowListToConTeXt tabl . map toTableRow where toTableRow (Ann.BodyRow attr _rownum rowhead rowbody) = TableRow Tbody attr rowhead rowbody rowListToConTeXt :: PandocMonad m => Tabl -> [TableRow] -> WM m (Doc Text) rowListToConTeXt = \case Ntb -> fmap vcat . mapM (tableRowToConTeXt Ntb) Xtb -> \rows -> do (butlast, lastrow) <- case reverse rows of [] -> pure ( [] , empty ) r:rs -> (,) <$> (mapM (tableRowToConTeXt Xtb) (reverse rs)) <*> tableRowToConTeXt Xtb r return $ vcat butlast $$ if isEmpty lastrow then empty else "\\startxrowgroup[lastrow]" $$ lastrow $$ "\\stopxrowgroup" tableRowToConTeXt :: PandocMonad m => Tabl -> TableRow -> WM m (Doc Text) tableRowToConTeXt tabl (TableRow tblpart _attr rowhead rowbody) = do let celltype = case tblpart of Thead -> HeaderCell _ -> BodyCell headcells <- mapM (tableCellToConTeXt tabl HeaderCell) rowhead bodycells <- mapM (tableCellToConTeXt tabl celltype) rowbody let cells = vcat headcells $$ vcat bodycells return $ case tabl of Xtb -> "\\startxrow" $$ cells $$ "\\stopxrow" Ntb -> "\\bTR" $$ cells $$ "\\eTR" tableCellToConTeXt :: PandocMonad m => Tabl -> CellType -> Ann.Cell -> WM m (Doc Text) tableCellToConTeXt tabl celltype (Ann.Cell colspecs _colnum cell) = do let Cell _attr cellalign rowspan colspan blocks = cell let (colalign, _) :| _ = colspecs let halign = alignToConTeXt $ case (cellalign, tabl) of (AlignDefault, Xtb) -> colalign _ -> cellalign let nx = case colspan of ColSpan 1 -> empty ColSpan n -> "nc=" <> literal (tshow n) let ny = case rowspan of RowSpan 1 -> empty RowSpan n -> "nr=" <> literal (tshow n) let widths = map snd (NonEmpty.toList colspecs) let mbcolwidth = flip map widths $ \case ColWidthDefault -> Nothing ColWidth w -> Just w let colwidth = case catMaybes mbcolwidth of [] -> empty ws -> ("width=" <>) . braces . text $ printf "%.2f\\textwidth" (sum ws) let keys = hcat . intersperse "," $ filter (not . isEmpty) $ case tabl of Xtb -> [halign, colwidth, nx, ny] Ntb -> [halign, nx, ny] -- no need for a column width let options = (if isEmpty keys then empty else brackets keys) <> space cellContents <- blockListToConTeXt blocks return $ case tabl of Xtb -> "\\startxcell" <> options <> cellContents <> " \\stopxcell" Ntb -> case celltype of BodyCell -> "\\bTD" <> options <> cellContents <> "\\eTD" HeaderCell -> "\\bTH" <> options <> cellContents <> "\\eTH" alignToConTeXt :: Alignment -> Doc Text alignToConTeXt = \case AlignLeft -> "align=right" AlignRight -> "align=left" AlignCenter -> "align=middle" AlignDefault -> empty --- --- Lists -- listItemToConTeXt :: PandocMonad m => [Block] -> WM m (Doc Text) listItemToConTeXt list = ("\\item" $$) . nest 2 <$> blockListToConTeXt list defListItemToConTeXt :: PandocMonad m => ([Inline], [[Block]]) -> WM m (Doc Text) defListItemToConTeXt (term, defs) = do term' <- inlineListToConTeXt term def' <- liftM vsep $ mapM blockListToConTeXt defs return $ "\\startdescription" <> braces term' $$ nest 2 def' $$ "\\stopdescription" <> blankline -- | Convert list of block elements to ConTeXt. blockListToConTeXt :: PandocMonad m => [Block] -> WM m (Doc Text) blockListToConTeXt lst = liftM vcat $ mapM blockToConTeXt lst -- | Convert list of inline elements to ConTeXt. inlineListToConTeXt :: PandocMonad m => [Inline] -- ^ Inlines to convert -> WM m (Doc Text) inlineListToConTeXt lst = liftM hcat $ mapM inlineToConTeXt $ addStruts lst -- We add a \strut after a line break that precedes a space, -- or the space gets swallowed where addStruts (LineBreak : s : xs) | isSpacey s = LineBreak : RawInline (Format "context") "\\strut " : s : addStruts xs addStruts (x:xs) = x : addStruts xs addStruts [] = [] isSpacey Space = True isSpacey (Str (T.uncons -> Just ('\160',_))) = True isSpacey _ = False highlightInlines :: PandocMonad m => Text -> (Doc Text) -> [Inline] -> WM m (Doc Text) highlightInlines name style inlines = do opts <- gets stOptions contents <- inlineListToConTeXt inlines if not (isEnabled Ext_tagging opts) then return $ braces (style <> space <> contents) else do let cmd = "\\definehighlight " <> brackets (literal name) <> brackets ("style=" <> braces style) modify (\st -> st{ stEmphasisCommands = Map.insert name cmd (stEmphasisCommands st) }) return $ "\\" <> literal name <> braces contents -- | Convert inline element to ConTeXt inlineToConTeXt :: PandocMonad m => Inline -- ^ Inline to convert -> WM m (Doc Text) inlineToConTeXt (Emph lst) = highlightInlines "emph" "\\em" lst inlineToConTeXt (Strong lst) = highlightInlines "strong" "\\bf" lst inlineToConTeXt (SmallCaps lst) = highlightInlines "smallcaps" "\\setsmallcaps" lst inlineToConTeXt (Underline lst) = do contents <- inlineListToConTeXt lst return $ "\\underbar" <> braces contents inlineToConTeXt (Strikeout lst) = do contents <- inlineListToConTeXt lst return $ "\\overstrikes" <> braces contents inlineToConTeXt (Superscript lst) = do contents <- inlineListToConTeXt lst return $ "\\high" <> braces contents inlineToConTeXt (Subscript lst) = do contents <- inlineListToConTeXt lst return $ "\\low" <> braces contents inlineToConTeXt (Code (_ident, classes, _kv) str) = do let rawCode = pure . literal $ case typeDelim str of Just (open, close) -> "\\type" <> (open `T.cons` str) `T.snoc` close Nothing -> "\\type[escape=yes]{" <> (T.replace "{" "/BTEX\\letteropenbrace /ETEX" . T.replace "}" "/BTEX\\letterclosebrace /ETEX" $ str) `T.snoc` '}' opts <- gets stOptions let syntaxMap = writerSyntaxMap opts let attr' = ("", classes, []) let highlightCode = case highlight syntaxMap formatConTeXtInline attr' str of Left msg -> do unless (T.null msg) $ report (CouldNotHighlight msg) rawCode Right h -> do modify (\st -> st{ stHighlighting = True }) return (text (T.unpack h)) case writerHighlightMethod opts of Skylighting _ | not (null classes) -> highlightCode _ -> rawCode inlineToConTeXt (Quoted SingleQuote lst) = do contents <- inlineListToConTeXt lst return $ "\\quote" <> braces contents inlineToConTeXt (Quoted DoubleQuote lst) = do contents <- inlineListToConTeXt lst return $ "\\quotation" <> braces contents inlineToConTeXt (Cite _ lst) = inlineListToConTeXt lst inlineToConTeXt (Str str) = do opts <- gets stOptions return $ literal $ stringToConTeXt opts str inlineToConTeXt (Math InlineMath str) = return $ char '$' <> literal str <> char '$' inlineToConTeXt (Math DisplayMath str) = return $ literal "\\startformula " <> literal str <> literal " \\stopformula" <> space inlineToConTeXt il@(RawInline f str) | f == Format "tex" || f == Format "context" = return $ literal str | otherwise = empty <$ report (InlineNotRendered il) inlineToConTeXt LineBreak = return $ literal "\\crlf" <> cr inlineToConTeXt SoftBreak = do wrapText <- gets (writerWrapText . stOptions) return $ case wrapText of WrapAuto -> space WrapNone -> space WrapPreserve -> cr inlineToConTeXt Space = return space inlineToConTeXt (Link _ txt (src, _)) = do let isAutolink = txt == [Str (T.pack $ unEscapeString $ T.unpack src)] let escConTeXtURL = T.concatMap $ \case '#' -> "\\#" '%' -> "\\%" c -> T.singleton c if isAutolink then do next <- gets stNextRef modify $ \st -> st {stNextRef = next + 1} let ref = "url" <> tshow next return $ mconcat [ "\\useURL" , brackets (literal ref) , brackets (literal $ escConTeXtURL src) , "\\from" , brackets (literal ref) ] else do contents <- inlineListToConTeXt txt -- Handle HTML-like internal document references to sections reference <- case T.uncons src of Just ('#', ref) -> toLabel <$> (stringToConTeXt <$> gets stOptions <*> pure ref) _ -> pure $ "url(" <> escConTeXtURL src <> ")" return $ mconcat [ "\\goto" , braces contents , brackets (literal reference) ] inlineToConTeXt (Image attr@(_,cls,_) _ (src, _)) = do opts <- gets stOptions let showDim dir = let d = literal (tshow dir) <> "=" in case dimension dir attr of Just (Pixel a) -> [d <> literal (showInInch opts (Pixel a)) <> "in"] Just (Percent a) -> [d <> literal (showFl (a / 100)) <> "\\textwidth"] Just dim -> [d <> literal (tshow dim)] Nothing -> [] dimList = showDim Width ++ showDim Height dims = if null dimList then empty else brackets $ mconcat (intersperse "," dimList) clas = case cls of [] -> empty (cl:_) -> brackets $ literal $ toLabel cl -- Use / for path separators on Windows; see #4918 fixPathSeparators = T.map $ \c -> case c of '\\' -> '/' _ -> c src' = fixPathSeparators $ if isURI src then src else T.pack $ unEscapeString $ T.unpack src return $ braces $ "\\externalfigure" <> brackets (literal src') <> clas <> dims inlineToConTeXt (Note contents) = do contents' <- blockListToConTeXt contents let codeBlock x@(CodeBlock _ _) = [x] codeBlock _ = [] let codeBlocks = query codeBlock contents return $ if null codeBlocks then literal "\\footnote{" <> nest 2 (chomp contents') <> char '}' else literal "\\startbuffer " <> nest 2 (chomp contents') <> literal "\\stopbuffer\\footnote{\\getbuffer}" inlineToConTeXt (Span (ident,_,kvs) ils) = do mblang <- fromBCP47 (lookup "lang" kvs) let wrapDir txt = case lookup "dir" kvs of Just "rtl" -> braces $ "\\righttoleft " <> txt Just "ltr" -> braces $ "\\lefttoright " <> txt _ -> txt wrapLang txt = case mblang of Just lng -> braces ("\\language" <> brackets (literal lng) <> txt) Nothing -> txt addReference = if T.null ident then id else (("\\reference" <> brackets (literal ident) <> "{}") <>) addReference . wrapLang . wrapDir <$> inlineListToConTeXt ils -- | Craft the section header, inserting the section reference, if supplied. sectionHeader :: PandocMonad m => Attr -> Int -> [Inline] -> HeadingType -> WM m (Doc Text) sectionHeader (ident,classes,kvs) hdrLevel lst secenv = do opts <- gets stOptions contents <- inlineListToConTeXt lst levelText <- sectionLevelToText opts (ident,classes,kvs) hdrLevel secenv let optsList = mconcat . filter (not . null) $ [ ["title=" <> braces contents | not (isEmpty contents)] , ["reference=" <> braces (literal (toLabel ident)) | not (T.null ident)] , ["number=no" | "unnumbered" `elem` classes] , ["incrementnumber=no" | "unnumbered" `elem` classes] ] let starter = case secenv of SectionHeading -> "\\start" NonSectionHeading -> "\\" let options = if null optsList || isEmpty levelText then empty else brackets $ hcat (intersperse "," optsList) return $ starter <> levelText <> options <> blankline -- | Craft the section footer sectionFooter :: PandocMonad m => Attr -> Int -> WM m (Doc Text) sectionFooter attr hdrLevel = do opts <- gets stOptions levelText <- sectionLevelToText opts attr hdrLevel SectionHeading return $ "\\stop" <> levelText <> blankline -- | Generate a textual representation of the section level sectionLevelToText :: PandocMonad m => WriterOptions -> Attr -> Int -> HeadingType -> WM m (Doc Text) sectionLevelToText opts (_,classes,_) hdrLevel headingType = do let unlisted = "unlisted" `elem` classes let semanticSection shift = do let (section, chapter) = if unlisted then (literal "subject", literal "title") else (literal "section", literal "chapter") return $ case hdrLevel + shift of -1 -> literal "part" 0 -> chapter n | n >= 1 -> text (concat (replicate (n - 1) "sub")) <> section _ -> empty -- cannot happen case writerTopLevelDivision opts of TopLevelPart -> semanticSection (-2) TopLevelChapter -> semanticSection (-1) TopLevelSection -> semanticSection 0 TopLevelDefault -> if unlisted then semanticSection 0 else return . literal $ case headingType of SectionHeading -> "sectionlevel" NonSectionHeading -> "" -- | Finds a pair of symbols that can be used as delimiters. typeDelim :: Text -> Maybe (Char, Char) typeDelim t = let delimChars = "{\"'`()-+=%,.:;" go delims '}' = go delims '{' go delims c = T.filter (/= c) delims in case fmap fst . T.uncons $ T.foldl' go delimChars t of Just '{' -> Just ('{', '}') Just c -> Just (c, c) Nothing -> Nothing fromBCP47 :: PandocMonad m => Maybe Text -> WM m (Maybe Text) fromBCP47 mbs = fromBCP47' <$> toLang mbs -- Takes a list of the constituents of a BCP 47 language code -- and irons out ConTeXt's exceptions -- https://tools.ietf.org/html/bcp47#section-2.1 -- http://wiki.contextgarden.net/Language_Codes fromBCP47' :: Maybe Lang -> Maybe Text fromBCP47' (Just (Lang "ar" _ (Just "SY") _ _ _)) = Just "ar-sy" fromBCP47' (Just (Lang "ar" _ (Just "IQ") _ _ _)) = Just "ar-iq" fromBCP47' (Just (Lang "ar" _ (Just "JO") _ _ _)) = Just "ar-jo" fromBCP47' (Just (Lang "ar" _ (Just "LB") _ _ _)) = Just "ar-lb" fromBCP47' (Just (Lang "ar" _ (Just "DZ") _ _ _)) = Just "ar-dz" fromBCP47' (Just (Lang "ar" _ (Just "MA") _ _ _)) = Just "ar-ma" fromBCP47' (Just (Lang "de" _ _ ["1901"] _ _)) = Just "deo" fromBCP47' (Just (Lang "de" _ (Just "DE") _ _ _)) = Just "de-de" fromBCP47' (Just (Lang "de" _ (Just "AT") _ _ _)) = Just "de-at" fromBCP47' (Just (Lang "de" _ (Just "CH") _ _ _)) = Just "de-ch" fromBCP47' (Just (Lang "el" _ _ ["poly"] _ _)) = Just "agr" fromBCP47' (Just (Lang "en" _ (Just "US") _ _ _)) = Just "en-us" fromBCP47' (Just (Lang "en" _ (Just "GB") _ _ _)) = Just "en-gb" fromBCP47' (Just (Lang "grc"_ _ _ _ _)) = Just "agr" fromBCP47' (Just (Lang "el" _ _ _ _ _)) = Just "gr" fromBCP47' (Just (Lang "eu" _ _ _ _ _)) = Just "ba" fromBCP47' (Just (Lang "he" _ _ _ _ _)) = Just "il" fromBCP47' (Just (Lang "uk" _ _ _ _ _)) = Just "ua" fromBCP47' (Just (Lang "vi" _ _ _ _ _)) = Just "vn" fromBCP47' (Just (Lang "zh" _ _ _ _ _)) = Just "cn" fromBCP47' (Just (Lang l _ _ _ _ _)) = Just l fromBCP47' Nothing = Nothing ================================================ FILE: src/Text/Pandoc/Writers/CslJson.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.CslJson Copyright : Copyright (C) 2020-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of references from 'Pandoc' metadata to CSL JSON: . Note that this writer ignores everything in the body of the document and everything in the metadata except `references`. It assumes that the `references` field is a list with the structure of a CSL JSON bibliography. -} module Text.Pandoc.Writers.CslJson ( writeCslJson ) where import Data.Text (Text) import qualified Data.Text as T import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Error import Text.Pandoc.Class import Control.Monad.Except (throwError) import Data.ByteString.Lazy (toStrict) import Data.ByteString (ByteString) import Text.Pandoc.Definition import Text.Pandoc.Builder as B import Text.Pandoc.Citeproc.MetaValue (metaValueToReference, metaValueToText) import Citeproc (parseLang, Locale, Reference(..), Lang(..)) import Control.Monad.Identity import Citeproc.Locale (getLocale) import Citeproc.CslJson import Text.Pandoc.Options (WriterOptions) import Data.Maybe (mapMaybe, fromMaybe) import Data.Aeson.Encode.Pretty (Config (..), Indent (Spaces), NumberFormat (Generic), defConfig, encodePretty') writeCslJson :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeCslJson _opts (Pandoc meta _) = do let lang = fromMaybe (Lang "en" Nothing (Just "US") [] [] []) (lookupMeta "lang" meta >>= metaValueToText >>= either (const Nothing) Just . parseLang) locale <- case getLocale lang of Left e -> throwError $ PandocCiteprocError e Right l -> return l let rs = case lookupMeta "references" meta of Just (MetaList xs) -> xs _ -> [] return $ UTF8.toText (toCslJson locale (mapMaybe metaValueToReference rs)) <> "\n" fromInlines :: [Inline] -> CslJson Text fromInlines = foldMap fromInline . B.fromList fromInline :: Inline -> CslJson Text fromInline (Str t) = CslText t fromInline (Emph ils) = CslItalic (fromInlines ils) fromInline (Strong ils) = CslBold (fromInlines ils) fromInline (Underline ils) = CslUnderline (fromInlines ils) fromInline (Strikeout ils) = fromInlines ils fromInline (Superscript ils) = CslSup (fromInlines ils) fromInline (Subscript ils) = CslSub (fromInlines ils) fromInline (SmallCaps ils) = CslSmallCaps (fromInlines ils) fromInline (Quoted _ ils) = CslQuoted (fromInlines ils) fromInline (Cite _ ils) = fromInlines ils fromInline (Code _ t) = CslText t fromInline Space = CslText " " fromInline SoftBreak = CslText " " fromInline LineBreak = CslText "\n" fromInline (Math InlineMath t) = CslText $ "$" <> t <> "$" fromInline (Math DisplayMath t) = CslText $ "$$" <> t <> "$$" fromInline (RawInline _ _) = CslEmpty fromInline (Link _ ils _) = fromInlines ils fromInline (Image _ ils _) = fromInlines ils fromInline (Note _) = CslEmpty fromInline (Span (_,[cl],_) ils) | "csl-" `T.isPrefixOf` cl = CslDiv cl (fromInlines ils) | cl == "nocase" = CslNoCase (fromInlines ils) fromInline (Span _ ils) = fromInlines ils toCslJson :: Locale -> [Reference Inlines] -> ByteString toCslJson locale = toStrict . encodePretty' defConfig{ confIndent = Spaces 2 , confCompare = compare , confNumFormat = Generic } . map (runIdentity . traverse (return . renderCslJson False locale . foldMap fromInline)) ================================================ FILE: src/Text/Pandoc/Writers/Djot.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.Djot Copyright : Copyright (C) 2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' format into Djot markup (). -} module Text.Pandoc.Writers.Djot ( writeDjot ) where import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Class ( PandocMonad , report ) import Text.Pandoc.Options ( WriterOptions(..), WrapOption(..)) import Data.Text (Text) import Data.Set (Set) import qualified Data.Set as Set import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as B8 import Data.List (intersperse) import qualified Data.Text as T import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Writers.Shared ( metaToContext, defField, toLegacyTable ) import Text.Pandoc.Shared (isTightList, tshow, stringify, onlySimpleTableCells, makeSections) import Text.DocLayout import Text.DocTemplates (renderTemplate) import Control.Monad.State (StateT(..), gets, modify) import Control.Monad (zipWithM, when) import Data.Maybe (fromMaybe) import qualified Djot.AST as D import Djot (renderDjot, RenderOptions(..), toIdentifier) import Text.Pandoc.UTF8 (fromText) -- | Convert Pandoc to Djot. writeDjot :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDjot opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing let ropts = RenderOptions{ preserveSoftBreaks = writerWrapText opts == WrapPreserve } metadata <- metaToContext opts (fmap (renderDjot ropts) . bodyToDjot opts) (fmap (chomp . renderDjot ropts) . bodyToDjot opts . (:[]) . Plain) meta main <- renderDjot ropts <$> bodyToDjot opts (makeSections False Nothing blocks) let context = defField "body" main metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context data DjotState = DjotState { footnotes :: D.NoteMap , references :: D.ReferenceMap , autoReferences :: D.ReferenceMap , autoIds :: Set B.ByteString , options :: WriterOptions } bodyToDjot :: PandocMonad m => WriterOptions -> [Block] -> m D.Doc bodyToDjot opts bls = do (bs, st) <- runStateT (blocksToDjot bls) (DjotState mempty mempty mempty mempty opts) let D.ReferenceMap autos = autoReferences st let D.ReferenceMap refs = references st pure $ D.Doc{ D.docBlocks = bs , D.docFootnotes = footnotes st , D.docReferences = D.ReferenceMap $ M.difference refs autos , D.docAutoReferences = D.ReferenceMap autos , D.docAutoIdentifiers = autoIds st } blocksToDjot :: PandocMonad m => [Block] -> StateT DjotState m D.Blocks blocksToDjot = fmap mconcat . mapM blockToDjot blockToDjot :: PandocMonad m => Block -> StateT DjotState m D.Blocks blockToDjot (Para ils) = D.para <$> inlinesToDjot ils blockToDjot (Plain ils) = D.para <$> inlinesToDjot ils blockToDjot (LineBlock ls) = D.para . mconcat . intersperse D.hardBreak <$> mapM inlinesToDjot ls blockToDjot (CodeBlock attr@(_,_,kvs) t) = do let lang = fromMaybe mempty $ lookup "lang" kvs pure $ D.addAttr (toDjotAttr attr) <$> D.codeBlock (fromText lang) (fromText t) blockToDjot (RawBlock (Format f) t) = pure $ D.rawBlock (D.Format (fromText f)) (fromText t) blockToDjot (BlockQuote bls) = D.blockQuote <$> blocksToDjot bls blockToDjot (Header lev attr ils) = fmap (D.addAttr (toDjotAttr attr)) . D.heading lev <$> inlinesToDjot ils blockToDjot HorizontalRule = pure D.thematicBreak blockToDjot (Div (ident,"section":cls,kvs) (Header lev (_,hcls,hkvs) ils : bls)) = do ilsBs <- D.inlinesToByteString <$> inlinesToDjot ils let ident' = toIdentifier ilsBs let label = D.normalizeLabel ilsBs let autoid = UTF8.toText ident' == ident when autoid $ modify $ \st -> st{ autoIds = Set.insert ident' (autoIds st) } modify $ \st -> st{ autoReferences = D.insertReference label (B8.cons '#' ident', mempty) (autoReferences st) } fmap (D.addAttr (toDjotAttr (if autoid then "" else ident, filter (/= "section") cls, filter (\(k,_) -> k /= "wrapper") kvs) <> toDjotAttr ("", [c | c <- hcls, c `notElem` cls], hkvs))) . D.section <$> blocksToDjot (Header lev mempty ils : bls) blockToDjot (Div attr@(ident,cls,kvs) bls) | Just "1" <- lookup "wrapper" kvs = fmap (D.addAttr (toDjotAttr (ident,cls,filter (\(k,_) -> k /= "wrapper") kvs))) <$> blocksToDjot bls | otherwise = fmap (D.addAttr (toDjotAttr attr)) . D.div <$> blocksToDjot bls blockToDjot (BulletList items) = D.bulletList spacing <$> mapM blocksToDjot items where spacing = if isTightList items then D.Tight else D.Loose blockToDjot (OrderedList (start, sty, delim) items) = D.orderedList listAttr spacing <$> mapM blocksToDjot items where spacing = if isTightList items then D.Tight else D.Loose listAttr = D.OrderedListAttributes { D.orderedListStyle = case sty of DefaultStyle -> D.Decimal Example -> D.Decimal Decimal -> D.Decimal LowerRoman -> D.RomanLower UpperRoman -> D.RomanUpper LowerAlpha -> D.LetterLower UpperAlpha -> D.LetterUpper, D.orderedListDelim = case delim of DefaultDelim -> D.RightPeriod Period -> D.RightPeriod OneParen -> D.RightParen TwoParens -> D.LeftRightParen, D.orderedListStart = start } blockToDjot (DefinitionList items) = D.definitionList spacing <$> mapM toDLItem items where spacing = if isTightList (map (concat . snd) items) then D.Tight else D.Loose toDLItem (term, defs) = do term' <- inlinesToDjot term def' <- mconcat <$> mapM blocksToDjot defs pure (term', def') blockToDjot (Figure attr (Caption _ capt) bls) = do content <- blocksToDjot bls caption <- fmap (D.addAttr (D.Attr [("class","caption")])) . D.div <$> blocksToDjot capt pure $ fmap (D.addAttr (toDjotAttr attr)) $ D.div $ content <> caption blockToDjot (Table attr capt' colspecs thead tbodies tfoot) = do let (capt, aligns, _, headRow, bodyRows) = toLegacyTable capt' colspecs thead tbodies tfoot if onlySimpleTableCells (headRow : bodyRows) then do let alignToAlign al = case al of AlignDefault -> D.AlignDefault AlignLeft -> D.AlignLeft AlignRight -> D.AlignRight AlignCenter -> D.AlignCenter let defAligns = map alignToAlign aligns let cellToCell isHeader bls al = D.Cell (if isHeader then D.HeadCell else D.BodyCell) al <$> case bls of [Para ils] -> inlinesToDjot ils [Plain ils] -> inlinesToDjot ils [] -> pure mempty bs -> do mapM_ (report . BlockNotRendered) bs pure $ D.str "((omitted))" let rowToRow isHeader cells = zipWithM (cellToCell isHeader) cells defAligns hrows <- if null headRow then pure [] else (:[]) <$> rowToRow True headRow rows <- mapM (rowToRow False) bodyRows caption <- case capt of [] -> pure Nothing _ -> Just . D.Caption . D.para <$> inlinesToDjot capt pure $ D.addAttr (toDjotAttr attr) <$> D.table caption (hrows <> rows) else do -- table can't be represented as a simple pipe table, use list tableList <- D.bulletList D.Loose <$> mapM (fmap (D.bulletList D.Loose) . mapM blocksToDjot) (headRow:bodyRows) pure $ D.addAttr (D.Attr [("class", "table")]) <$> tableList inlinesToDjot :: PandocMonad m => [Inline] -> StateT DjotState m D.Inlines inlinesToDjot = fmap mconcat . mapM inlineToDjot inlineToDjot :: PandocMonad m => Inline -> StateT DjotState m D.Inlines inlineToDjot (Str t) = pure $ D.str (fromText t) inlineToDjot Space = pure $ D.str " " inlineToDjot SoftBreak = pure D.softBreak inlineToDjot LineBreak = pure D.hardBreak inlineToDjot (Emph ils) = D.emph <$> inlinesToDjot ils inlineToDjot (Underline ils) = fmap (D.addAttr (D.Attr [("class","underline")])) . D.span_ <$> inlinesToDjot ils inlineToDjot (Strong ils) = D.strong <$> inlinesToDjot ils inlineToDjot (Strikeout ils) = D.delete <$> inlinesToDjot ils inlineToDjot (Subscript ils) = D.subscript <$> inlinesToDjot ils inlineToDjot (Superscript ils) = D.superscript <$> inlinesToDjot ils inlineToDjot (Span ("",["mark"],[]) ils) = D.highlight <$> inlinesToDjot ils inlineToDjot (Span attr@(ident,cls,kvs) ils) | Just "1" <- lookup "wrapper" kvs = fmap (D.addAttr (toDjotAttr (ident,cls,filter (\(k,_) -> k /= "wrapper") kvs))) <$> inlinesToDjot ils | otherwise = fmap (D.addAttr (toDjotAttr attr)) . D.span_ <$> inlinesToDjot ils inlineToDjot (SmallCaps ils) = fmap (D.addAttr (D.Attr [("class","smallcaps")])) . D.span_ <$> inlinesToDjot ils inlineToDjot (Quoted DoubleQuote ils) = D.doubleQuoted <$> inlinesToDjot ils inlineToDjot (Quoted SingleQuote ils) = D.singleQuoted <$> inlinesToDjot ils inlineToDjot (Cite _cs ils) = inlinesToDjot ils inlineToDjot (Code attr t) = pure $ D.addAttr (toDjotAttr attr) <$> D.verbatim (fromText t) inlineToDjot (Math mt t) = pure $ (if mt == InlineMath then D.inlineMath else D.displayMath) (fromText t) inlineToDjot (RawInline (Format f) t) = pure $ D.rawInline (D.Format (fromText f)) (fromText t) inlineToDjot (Link attr ils (src,tit)) = do opts <- gets options description <- inlinesToDjot ils let ilstring = stringify ils let autolink = ilstring == src let email = ("mailto:" <> ilstring) == src let removeClass name (ident, cls, kvs) = (ident, filter (/= name) cls, kvs) let attr' = D.Attr [("title", fromText tit) | not (T.null tit)] <> toDjotAttr ( (if autolink then removeClass "uri" else id) . (if email then removeClass "email" else id) $ attr) case () of _ | autolink -> pure $ D.addAttr attr' <$> D.urlLink (fromText ilstring) | email -> pure $ D.addAttr attr' <$> D.emailLink (fromText ilstring) | writerReferenceLinks opts -> do refs@(D.ReferenceMap m) <- gets references autoRefs <- gets autoReferences let lab' = D.inlinesToByteString description lab <- case D.lookupReference lab' (refs <> autoRefs) of Just _ -> pure lab' Nothing -> do let refnum = M.size m + 1 let lab = fromText $ tshow refnum modify $ \st -> st{ references = D.insertReference lab (fromText src, attr') refs } pure lab pure $ D.addAttr attr' <$> D.link description (D.Reference lab) | otherwise -> pure $ D.addAttr attr' <$> D.link description (D.Direct (fromText src)) inlineToDjot (Image attr ils (src,tit)) = do opts <- gets options description <- inlinesToDjot ils let attr' = D.Attr [("title", fromText tit) | not (T.null tit)] <> toDjotAttr attr if writerReferenceLinks opts then do refs@(D.ReferenceMap m) <- gets references let refnum = M.size m + 1 let lab = fromText $ tshow refnum modify $ \st -> st{ references = D.insertReference lab (fromText src, attr') refs } pure $ D.addAttr attr' <$> D.image description (D.Reference lab) else pure $ D.addAttr attr' <$> D.image description (D.Direct (fromText src)) inlineToDjot (Note bs) = do notes@(D.NoteMap m) <- gets footnotes let notenum = M.size m + 1 let lab = fromText $ tshow notenum contents <- blocksToDjot bs modify $ \st -> st{ footnotes = D.insertNote lab contents notes } pure $ D.footnoteReference lab toDjotAttr :: (Text, [Text], [(Text, Text)]) -> D.Attr toDjotAttr (ident, classes, kvs) = D.Attr $ [("id", fromText ident) | not (T.null ident)] ++ [("class", fromText (T.unwords classes)) | not (null classes)] ++ map (\(k,v) -> (fromText k, fromText v)) kvs ================================================ FILE: src/Text/Pandoc/Writers/DocBook.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- | Module : Text.Pandoc.Writers.DocBook Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to DocBook XML. -} module Text.Pandoc.Writers.DocBook ( writeDocBook4, writeDocBook5 ) where import Control.Monad.Reader import Data.Generics (everywhere, mkT) import Data.List (nub, partition) import Data.Maybe (isNothing) import Data.Monoid (All (..)) import Data.Text (Text) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (languages, languagesByExtension) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.XML import Text.TeXMath import qualified Text.XML.Light as Xml data DocBookVersion = DocBook4 | DocBook5 deriving (Eq, Show) type DB = ReaderT DocBookVersion -- | Get level of the top-level headers based on the configured top-level division. -- The header level can then be used to determine appropriate DocBook element -- for each subdivision associated with a header. -- The numbering here follows LaTeX's internal numbering getStartLvl :: WriterOptions -> Int getStartLvl opts = case writerTopLevelDivision opts of TopLevelPart -> -1 TopLevelChapter -> 0 TopLevelSection -> 1 TopLevelDefault -> 1 -- | Get correct name for the id attribute based on DocBook version. -- DocBook 4 used custom id attribute but DocBook 5 adopted the xml:id specification. -- https://www.w3.org/TR/xml-id/ idName :: DocBookVersion -> Text idName DocBook5 = "xml:id" idName DocBook4 = "id" -- | Convert list of authors to a docbook section authorToDocBook :: PandocMonad m => WriterOptions -> [Inline] -> DB m B.Inlines authorToDocBook opts name' = do name <- render Nothing <$> inlinesToDocBook opts name' let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing return $ B.rawInline "docbook" $ render colwidth $ inTags True "personname" [] $ if T.any (== ',') name then -- last name first let (lastname, rest) = T.break (==',') name firstname = triml rest in inTagsSimple "firstname" (literal $ escapeStringForXML firstname) <> inTagsSimple "surname" (literal $ escapeStringForXML lastname) else -- last name last let namewords = T.words name lengthname = length namewords (firstname, lastname) = case lengthname of 0 -> ("","") 1 -> ("", name) n -> (T.unwords (take (n-1) namewords), last namewords) in inTagsSimple "firstname" (literal $ escapeStringForXML firstname) $$ inTagsSimple "surname" (literal $ escapeStringForXML lastname) writeDocBook4 :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDocBook4 opts d = runReaderT (writeDocBook opts d) DocBook4 writeDocBook5 :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDocBook5 opts d = runReaderT (writeDocBook opts d) DocBook5 -- | Convert Pandoc document to string in DocBook format. writeDocBook :: PandocMonad m => WriterOptions -> Pandoc -> DB m Text writeDocBook opts doc = do let Pandoc meta blocks = ensureValidXmlIdentifiers doc let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing let startLvl = getStartLvl opts let fromBlocks = blocksToDocBook opts . makeSections False (Just startLvl) auths' <- mapM (authorToDocBook opts) $ docAuthors meta let meta' = B.setMeta "author" auths' meta metadata <- metaToContext opts fromBlocks (inlinesToDocBook opts) meta' main <- fromBlocks blocks let context = defField "body" main $ defField "mathml" (case writerHTMLMathMethod opts of MathML -> True _ -> False) metadata return $ render colwidth $ (if writerPreferAscii opts then fmap toEntities else id) $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Convert a list of Pandoc blocks to DocBook. blocksToDocBook :: PandocMonad m => WriterOptions -> [Block] -> DB m (Doc Text) blocksToDocBook opts = fmap vcat . mapM (blockToDocBook opts) -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block plainToPara (Plain x) = Para x plainToPara x = x -- | Convert a list of pairs of terms and definitions into a list of -- DocBook varlistentrys. deflistItemsToDocBook :: PandocMonad m => WriterOptions -> [([Inline],[[Block]])] -> DB m (Doc Text) deflistItemsToDocBook opts items = vcat <$> mapM (uncurry (deflistItemToDocBook opts)) items -- | Convert a term and a list of blocks into a DocBook varlistentry. deflistItemToDocBook :: PandocMonad m => WriterOptions -> [Inline] -> [[Block]] -> DB m (Doc Text) deflistItemToDocBook opts term defs = do term' <- inlinesToDocBook opts term def' <- blocksToDocBook opts $ concatMap (map plainToPara) defs return $ inTagsIndented "varlistentry" $ inTagsIndented "term" term' $$ inTagsIndented "listitem" def' -- | Convert a list of lists of blocks to a list of DocBook list items. listItemsToDocBook :: PandocMonad m => WriterOptions -> [[Block]] -> DB m (Doc Text) listItemsToDocBook opts items = vcat <$> mapM (listItemToDocBook opts) items -- | Convert a list of blocks into a DocBook list item. listItemToDocBook :: PandocMonad m => WriterOptions -> [Block] -> DB m (Doc Text) listItemToDocBook opts item = inTagsIndented "listitem" <$> blocksToDocBook opts (map plainToPara item) imageToDocBook :: WriterOptions -> Attr -> Text -> Doc Text imageToDocBook _ attr src = selfClosingTag "imagedata" $ ("fileref", src) : idAndRole attr <> dims where dims = go Width "width" <> go Height "depth" go dir dstr = case dimension dir attr of Just a -> [(dstr, tshow a)] Nothing -> [] -- | Convert a Pandoc block element to DocBook. blockToDocBook :: PandocMonad m => WriterOptions -> Block -> DB m (Doc Text) -- Add ids to paragraphs in divs with ids - this is needed for -- pandoc-citeproc to get link anchors in bibliographies: blockToDocBook opts (Div (id',"section":_classes,divattrs) (Header lvl (_,hclasses,_) ils : xs)) = do version <- ask -- DocBook doesn't allow sections with no content, so insert some if needed let bs = if null xs then [Para []] else xs tag = case lvl of -1 -> "part" 0 -> "chapter" n | n >= 1 && n <= 5 -> if version == DocBook5 then "section" else "sect" <> tshow n _ -> "simplesect" idAttr = [(idName version, writerIdentifierPrefix opts <> id') | not (T.null id')] -- We want to add namespaces to the root (top-level) element. nsAttr = if version == DocBook5 && lvl == getStartLvl opts && isNothing (writerTemplate opts) -- Though, DocBook 4 does not support namespaces and -- standalone documents will include them in the template. then [("xmlns", "http://docbook.org/ns/docbook") ,("xmlns:xlink", "http://www.w3.org/1999/xlink")] else [] -- Populate miscAttr with Header.Attr.attributes, filtering out non-valid DocBook section attributes, id, and xml:id -- Also enrich the role attribute with certain class tokens miscAttr = enrichRole (filter (isSectionAttr version) divattrs) hclasses attribs = nsAttr <> idAttr <> miscAttr title' <- inlinesToDocBook opts ils contents <- blocksToDocBook opts bs return $ inTags True tag attribs $ inTagsSimple "title" title' $$ contents blockToDocBook opts (Div (ident,classes,_) bs) = do version <- ask let identAttribs = [(idName version, ident) | not (T.null ident)] admonitions = ["caution","danger","important","note","tip","warning"] case classes of (l:_) | l `elem` admonitions -> do let (mTitleBs, bodyBs) = case bs of -- Matches AST produced by the DocBook reader → Markdown writer → Markdown reader chain. (Div (_,["title"],_) [Para ts] : rest) -> (Just (inlinesToDocBook opts ts), rest) -- Matches AST produced by the DocBook reader. (Div (_,["title"],_) ts : rest) -> (Just (blocksToDocBook opts ts), rest) _ -> (Nothing, bs) admonitionTitle <- case mTitleBs of Nothing -> return mempty -- id will be attached to the admonition so let’s pass empty identAttrs. Just titleBs -> inTagsSimple "title" <$> titleBs admonitionBody <- handleDivBody [] bodyBs return (inTags True l identAttribs (admonitionTitle $$ admonitionBody)) _ -> handleDivBody identAttribs bs where handleDivBody identAttribs [Para lst] = if hasLineBreaks lst then flush . nowrap . inTags False "literallayout" identAttribs <$> inlinesToDocBook opts lst else inTags True "para" identAttribs <$> inlinesToDocBook opts lst handleDivBody identAttribs bodyBs = do contents <- blocksToDocBook opts (map plainToPara bodyBs) return $ (if null identAttribs then mempty else selfClosingTag "anchor" identAttribs) $$ contents blockToDocBook _ h@Header{} = do -- should be handled by Div section above, except inside lists/blockquotes report $ BlockNotRendered h return empty blockToDocBook opts (Plain lst) = inlinesToDocBook opts lst blockToDocBook opts (Para lst) | hasLineBreaks lst = flush . nowrap . inTagsSimple "literallayout" <$> inlinesToDocBook opts lst | otherwise = inTagsIndented "para" <$> inlinesToDocBook opts lst blockToDocBook opts (LineBlock lns) = inTags False "literallayout" [] . vcat <$> mapM (inlinesToDocBook opts) lns blockToDocBook opts (BlockQuote blocks) = inTagsIndented "blockquote" <$> blocksToDocBook opts blocks blockToDocBook opts (CodeBlock (_,classes,_) str) = return $ literal (" lang <> ">") <> cr <> flush (literal (escapeStringForXML str) <> cr <> literal "") where lang = case langs of [] -> "" (l:_) -> " language=\"" <> escapeStringForXML l <> "\"" syntaxMap = writerSyntaxMap opts isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap) langsFrom s = if isLang s then [s] else (languagesByExtension syntaxMap) . T.toLower $ s langs = concatMap langsFrom classes blockToDocBook opts (BulletList lst) = do let attribs = [("spacing", "compact") | isTightList lst] inTags True "itemizedlist" attribs <$> listItemsToDocBook opts lst blockToDocBook _ (OrderedList _ []) = return empty blockToDocBook opts (OrderedList (start, numstyle, _) items) = do let numeration = case numstyle of DefaultStyle -> [] Decimal -> [("numeration", "arabic")] Example -> [("numeration", "arabic")] UpperAlpha -> [("numeration", "upperalpha")] LowerAlpha -> [("numeration", "loweralpha")] UpperRoman -> [("numeration", "upperroman")] LowerRoman -> [("numeration", "lowerroman")] spacing = [("spacing", "compact") | isTightList items] startnum = [("startingnumber", tshow start) | start /= 1] attribs = numeration <> spacing <> startnum inTags True "orderedlist" attribs <$> listItemsToDocBook opts items blockToDocBook opts (DefinitionList lst) = do let attribs = [("spacing", "compact") | isTightList $ concatMap snd lst] inTags True "variablelist" attribs <$> deflistItemsToDocBook opts lst blockToDocBook _ b@(RawBlock f str) | f == "docbook" = return $ literal str -- raw XML block | f == "html" = do version <- ask if version == DocBook5 then return empty -- No html in DocBook5 else return $ literal str -- allow html for backwards -- compatibility | otherwise = do report $ BlockNotRendered b return empty blockToDocBook _ HorizontalRule = return empty -- not semantic blockToDocBook opts (Table _ blkCapt specs thead tbody tfoot) = do let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot captionDoc <- if null caption then return empty else inTagsSimple "title" <$> inlinesToDocBook opts caption let tableType = if isEmpty captionDoc then "informaltable" else "table" percent w = tshow (truncate (100*w) :: Integer) <> "*" coltags = vcat $ zipWith (\w al -> selfClosingTag "colspec" ([("colwidth", percent w) | w > 0] <> [("align", alignmentToString al)])) widths aligns head' <- if all null headers then return empty else inTagsIndented "thead" <$> tableRowToDocBook opts headers body' <- inTagsIndented "tbody" . vcat <$> mapM (tableRowToDocBook opts) rows return $ inTagsIndented tableType $ captionDoc $$ inTags True "tgroup" [("cols", tshow (length aligns))] ( coltags $$ head' $$ body') blockToDocBook opts (Figure attr capt@(Caption _ caption) body) = do -- TODO: probably better to handle nested figures as mediaobject let isAcceptable = \case Table {} -> All False Figure {} -> All False _ -> All True if not . getAll $ query isAcceptable body -- Fallback to a div if the content cannot be included in a figure then blockToDocBook opts $ figureDiv attr capt body else do title <- inlinesToDocBook opts (blocksToInlines caption) let toMediaobject = \case Plain [Image imgAttr inlns (src, _)] -> do alt <- inlinesToDocBook opts inlns pure $ inTagsIndented "mediaobject" ( inTagsIndented "imageobject" (imageToDocBook opts imgAttr src) $$ if isEmpty alt then empty else inTagsSimple "textobject" (inTagsSimple "phrase" alt)) _ -> ask >>= \case DocBook4 -> pure mempty -- docbook4 requires media DocBook5 -> blocksToDocBook opts body mediaobjects <- mapM toMediaobject body return $ if isEmpty $ mconcat mediaobjects then mempty -- figures must have at least some content else inTagsIndented "figure" $ inTagsSimple "title" title $$ mconcat mediaobjects alignmentToString :: Alignment -> Text alignmentToString alignment = case alignment of AlignLeft -> "left" AlignRight -> "right" AlignCenter -> "center" AlignDefault -> "left" tableRowToDocBook :: PandocMonad m => WriterOptions -> [[Block]] -> DB m (Doc Text) tableRowToDocBook opts cols = inTagsIndented "row" . vcat <$> mapM (tableItemToDocBook opts) cols tableItemToDocBook :: PandocMonad m => WriterOptions -> [Block] -> DB m (Doc Text) tableItemToDocBook opts item = inTags True "entry" [] . vcat <$> mapM (blockToDocBook opts) item -- | Convert a list of inline elements to DocBook. inlinesToDocBook :: PandocMonad m => WriterOptions -> [Inline] -> DB m (Doc Text) inlinesToDocBook opts lst = hcat <$> mapM (inlineToDocBook opts) lst -- | Convert an inline element to DocBook. inlineToDocBook :: PandocMonad m => WriterOptions -> Inline -> DB m (Doc Text) inlineToDocBook _ (Str str) = return $ literal $ escapeStringForXML str inlineToDocBook opts (Emph lst) = inTagsSimple "emphasis" <$> inlinesToDocBook opts lst inlineToDocBook opts (Underline lst) = inTags False "emphasis" [("role", "underline")] <$> inlinesToDocBook opts lst inlineToDocBook opts (Strong lst) = inTags False "emphasis" [("role", "strong")] <$> inlinesToDocBook opts lst inlineToDocBook opts (Strikeout lst) = inTags False "emphasis" [("role", "strikethrough")] <$> inlinesToDocBook opts lst inlineToDocBook opts (Superscript lst) = inTagsSimple "superscript" <$> inlinesToDocBook opts lst inlineToDocBook opts (Subscript lst) = inTagsSimple "subscript" <$> inlinesToDocBook opts lst inlineToDocBook opts (SmallCaps lst) = inTags False "emphasis" [("role", "smallcaps")] <$> inlinesToDocBook opts lst inlineToDocBook opts (Quoted _ lst) = inTagsSimple "quote" <$> inlinesToDocBook opts lst inlineToDocBook opts (Cite _ lst) = inlinesToDocBook opts lst inlineToDocBook opts (Span (ident,_,_) ils) = do version <- ask ((if T.null ident then mempty else selfClosingTag "anchor" [(idName version, ident)]) <>) <$> inlinesToDocBook opts ils inlineToDocBook _ (Code _ str) = return $ inTagsSimple "literal" $ literal (escapeStringForXML str) inlineToDocBook opts (Math t str) | isMathML (writerHTMLMathMethod opts) = do res <- convertMath writeMathML t str case res of Right r -> return $ inTagsSimple tagtype $ literal $ T.pack $ Xml.ppcElement conf $ fixNS $ removeAttr r Left il -> inlineToDocBook opts il | otherwise = texMathToInlines t str >>= inlinesToDocBook opts where tagtype = case t of InlineMath -> "inlineequation" DisplayMath -> "informalequation" conf = Xml.useShortEmptyTags (const False) Xml.defaultConfigPP removeAttr e = e{ Xml.elAttribs = [] } fixNS' qname = qname{ Xml.qPrefix = Just "mml" } fixNS = everywhere (mkT fixNS') inlineToDocBook _ il@(RawInline f x) | f == "html" || f == "docbook" = return $ literal x | otherwise = do report $ InlineNotRendered il return empty inlineToDocBook _ LineBreak = return $ literal "\n" -- currently ignore, would require the option to add custom -- styles to the document inlineToDocBook _ Space = return space -- because we use \n for LineBreak, we can't do soft breaks: inlineToDocBook _ SoftBreak = return space inlineToDocBook opts (Link attr txt (src, _)) | Just email <- T.stripPrefix "mailto:" src = let emailLink = inTagsSimple "email" $ literal $ escapeStringForXML email in case txt of [Str s] | escapeURI s == email -> return emailLink _ -> do contents <- inlinesToDocBook opts txt return $ contents <+> char '(' <> emailLink <> char ')' | otherwise = do version <- ask (if "#" `T.isPrefixOf` src then let tag = if null txt then "xref" else "link" in inTags False tag $ ("linkend", writerIdentifierPrefix opts <> T.drop 1 src) : idAndRole attr else if version == DocBook5 then inTags False "link" $ ("xlink:href", src) : idAndRole attr else inTags False "ulink" $ ("url", src) : idAndRole attr ) <$> inlinesToDocBook opts txt inlineToDocBook opts (Image attr ils (src, tit)) = return $ let titleDoc = if T.null tit then empty else inTagsIndented "objectinfo" $ inTagsSimple "title" (literal $ escapeStringForXML tit) alt = if null ils then mempty else inTagsIndented "textobject" $ inTagsSimple "phrase" $ literal (stringify ils) in inTagsIndented "inlinemediaobject" $ inTagsIndented "imageobject" (titleDoc $$ imageToDocBook opts attr src) $$ alt inlineToDocBook opts (Note contents) = inTagsIndented "footnote" <$> blocksToDocBook opts contents isMathML :: HTMLMathMethod -> Bool isMathML MathML = True isMathML _ = False idAndRole :: Attr -> [(Text, Text)] idAndRole (id',cls,_) = ident <> role where ident = [("id", id') | not (T.null id')] role = [("role", T.unwords cls) | not (null cls)] -- Used in blockToDocBook for Header (section) to create or extend -- the role attribute with candidate class tokens enrichRole :: [(Text, Text)] -> [Text] -> [(Text, Text)] enrichRole mattrs cls = [("role", T.unwords roles) | roles /= []] <> nonRole where (roleAttr, nonRole) = partition (\(key, _v) -> key == "role") mattrs roles = nub $ filter (`elem` cand) cls <> map snd roleAttr cand = ["unnumbered"] isSectionAttr :: DocBookVersion -> (Text, Text) -> Bool isSectionAttr _ ("label",_) = True isSectionAttr _ ("status",_) = True isSectionAttr DocBook5 ("annotations",_) = True isSectionAttr DocBook5 ("dir","ltr") = True isSectionAttr DocBook5 ("dir","rtl") = True isSectionAttr DocBook5 ("dir","lro") = True isSectionAttr DocBook5 ("dir","rlo") = True isSectionAttr _ ("remap",_) = True isSectionAttr _ ("revisionflag","changed") = True isSectionAttr _ ("revisionflag","added") = True isSectionAttr _ ("revisionflag","deleted") = True isSectionAttr _ ("revisionflag","off") = True isSectionAttr _ ("role",_) = True isSectionAttr DocBook5 ("version",_) = True isSectionAttr DocBook5 ("xml:base",_) = True isSectionAttr DocBook5 ("xml:lang",_) = True isSectionAttr _ ("xreflabel",_) = True isSectionAttr DocBook5 ("linkend",_) = True isSectionAttr DocBook5 ("linkends",_) = True isSectionAttr DocBook5 ("xlink:actuate",_) = True isSectionAttr DocBook5 ("xlink:arcrole",_) = True isSectionAttr DocBook5 ("xlink:from",_) = True isSectionAttr DocBook5 ("xlink:href",_) = True isSectionAttr DocBook5 ("xlink:label",_) = True isSectionAttr DocBook5 ("xlink:role",_) = True isSectionAttr DocBook5 ("xlink:show",_) = True isSectionAttr DocBook5 ("xlink:title",_) = True isSectionAttr DocBook5 ("xlink:to",_) = True isSectionAttr DocBook5 ("xlink:type",_) = True isSectionAttr DocBook4 ("arch",_) = True isSectionAttr DocBook4 ("condition",_) = True isSectionAttr DocBook4 ("conformance",_) = True isSectionAttr DocBook4 ("lang",_) = True isSectionAttr DocBook4 ("os",_) = True isSectionAttr DocBook4 ("revision",_) = True isSectionAttr DocBook4 ("security",_) = True isSectionAttr DocBook4 ("vendor",_) = True isSectionAttr _ (_,_) = False ================================================ FILE: src/Text/Pandoc/Writers/Docx/OpenXML.hs ================================================ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {- | Module : Text.Pandoc.Writers.Docx Copyright : Copyright (C) 2012-2025 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to docx. -} module Text.Pandoc.Writers.Docx.OpenXML ( writeOpenXML, maxListLevel ) where import Control.Monad (when, unless) import Control.Applicative ((<|>)) import Control.Monad.Except (catchError) import Crypto.Hash (hashWith, SHA1(SHA1)) import qualified Data.ByteString.Lazy as BL import Data.Char (isLetter, isSpace) import Text.Pandoc.Char (isCJK) import Data.Ord (comparing) import Data.String (fromString) import qualified Data.Map as M import Data.Maybe (fromMaybe, maybeToList, isJust) import Control.Monad.State ( gets, modify, MonadTrans(lift) ) import Control.Monad.Reader ( asks, MonadReader(local) ) import qualified Data.Set as Set import qualified Data.Text as T import Data.Text (Text) import Skylighting import Text.DocLayout (hcat, vcat, literal, render) import Text.Pandoc.Class (PandocMonad, report, getMediaBag) import Text.Pandoc.Translations (Term(Abstract), translateTerm) import Text.Pandoc.MediaBag (lookupMedia, MediaItem(..)) import qualified Text.Pandoc.Translations as Term import qualified Text.Pandoc.Class.PandocMonad as P import qualified Text.Pandoc.Builder as B import Text.Pandoc.UTF8 (fromText) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (highlight) import Text.Pandoc.Templates (compileDefaultTemplate, renderTemplate) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) import Text.Pandoc.Options import Text.Pandoc.Writers.Docx.StyleMap import Text.Pandoc.Writers.Docx.Table as Table import Text.Pandoc.Writers.Docx.Types import Text.Pandoc.Shared import Text.Pandoc.Walk import qualified Text.Pandoc.Writers.GridTable as Grid import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.TeXMath import Text.Pandoc.Writers.OOXML import Text.Pandoc.XML.Light as XML import Data.List (sortBy, intercalate, groupBy) -- from wml.xsd EG_RPrBase rPrTagOrder :: M.Map Text Int rPrTagOrder = M.fromList (zip [ "rStyle" , "rFonts" , "b" , "bCs" , "i" , "iCs" , "caps" , "smallCaps" , "strike" , "dstrike" , "outline" , "shadow" , "emboss" , "imprint" , "noProof" , "snapToGrid" , "vanish" , "webHidden" , "color" , "spacing" , "w" , "kern" , "position" , "sz" , "szCs" , "highlight" , "u" , "effect" , "bdr" , "shd" , "fitText" , "vertAlign" , "rtl" , "cs" , "em" , "lang" , "eastAsianLayout" , "specVanish" , "oMath" ] [0..]) sortSquashed :: [Element] -> [Element] sortSquashed l = sortBy (comparing tagIndex) l where tagIndex :: Element -> Int tagIndex el = fromMaybe 0 (M.lookup tag rPrTagOrder) where tag = (qName . elName) el squashProps :: EnvProps -> [Element] squashProps (EnvProps Nothing es) = sortSquashed es squashProps (EnvProps (Just e) es) = sortSquashed (e : es) -- | Certain characters are invalid in XML even if escaped. -- See #1992 stripInvalidChars :: Text -> Text stripInvalidChars = T.filter isValidChar -- | See XML reference isValidChar :: Char -> Bool isValidChar '\t' = True isValidChar '\n' = True isValidChar '\r' = True isValidChar '\xFFFE' = False isValidChar '\xFFFF' = False isValidChar c = (' ' <= c && c <= '\xD7FF') || ('\xE000' <= c) -- this is the lowest number used for a list numId baseListId :: Int baseListId = 1000 getNumId :: (PandocMonad m) => WS m Int getNumId = gets (((baseListId - 1) +) . length . stLists) makeTOC :: (PandocMonad m) => WriterOptions -> WS m [Element] makeTOC opts = do let depth = "1-" <> tshow (writerTOCDepth opts) let tocCmd = "TOC \\o \"" <> depth <> "\" \\h \\z \\u" tocTitle <- gets stTocTitle title <- withParaPropM (pStyleM "TOC Heading") (blocksToOpenXML opts [Para tocTitle]) return [mknode "w:sdt" [] [ mknode "w:sdtPr" [] ( mknode "w:docPartObj" [] [mknode "w:docPartGallery" [("w:val","Table of Contents")] (), mknode "w:docPartUnique" [] ()] -- w:docPartObj ), -- w:sdtPr mknode "w:sdtContent" [] (title ++ [ Elem $ mknode "w:p" [] ( mknode "w:r" [] [ mknode "w:fldChar" [("w:fldCharType","begin"),("w:dirty","true")] (), mknode "w:instrText" [("xml:space","preserve")] tocCmd, mknode "w:fldChar" [("w:fldCharType","separate")] (), mknode "w:fldChar" [("w:fldCharType","end")] () ] -- w:r ) -- w:p ]) ]] -- w:sdt makeLOF :: (PandocMonad m) => WriterOptions -> WS m [Element] makeLOF opts = do let lofCmd = "TOC \\h \\z \\t \"Image Caption\" \\c" :: Text lofTitle <- B.toList <$> B.text <$> translateTerm Term.ListOfFigures title <- withParaPropM (pStyleM "TOC Heading") (blocksToOpenXML opts [Para lofTitle]) return [mknode "w:sdt" [] [ mknode "w:sdtPr" [] ( mknode "w:docPartObj" [] [mknode "w:docPartGallery" [("w:val","List of Figures")] (), mknode "w:docPartUnique" [] ()] -- w:docPartObj ), -- w:sdtPr mknode "w:sdtContent" [] (title ++ [ Elem $ mknode "w:p" [] ( mknode "w:r" [] [ mknode "w:fldChar" [("w:fldCharType","begin"),("w:dirty","true")] (), mknode "w:instrText" [("xml:space","preserve")] lofCmd, mknode "w:fldChar" [("w:fldCharType","separate")] (), mknode "w:fldChar" [("w:fldCharType","end")] () ] -- w:r ) -- w:p ]) -- w:sdtContent ]] -- w:sdt makeLOT :: (PandocMonad m) => WriterOptions -> WS m [Element] makeLOT opts = do let lotCmd = "TOC \\h \\z \\t \"Table Caption\" \\c" :: Text lotTitle <- B.toList <$> B.text <$> translateTerm Term.ListOfTables title <- withParaPropM (pStyleM "TOC Heading") (blocksToOpenXML opts [Para lotTitle]) return [mknode "w:sdt" [] [ mknode "w:sdtPr" [] ( mknode "w:docPartObj" [] [mknode "w:docPartGallery" [("w:val","List of Tables")] (), mknode "w:docPartUnique" [] ()] -- w:docPartObj ), -- w:sdtPr mknode "w:sdtContent" [] (title ++ [ Elem $ mknode "w:p" [] ( mknode "w:r" [] [ mknode "w:fldChar" [("w:fldCharType","begin"),("w:dirty","true")] (), mknode "w:instrText" [("xml:space","preserve")] lotCmd, mknode "w:fldChar" [("w:fldCharType","separate")] (), mknode "w:fldChar" [("w:fldCharType","end")] () ] -- w:r ) -- w:p ]) -- w:sdtContent ]] -- w:sdt -- | Separator element between sections sectionSeparator :: PandocMonad m => WS m (Maybe Content) sectionSeparator = do asks envSectPr >>= \case Just sectPrElem -> pure $ Just $ Elem (mknode "w:p" [] (mknode "w:pPr" [] [sectPrElem])) Nothing -> pure Nothing -- | Convert Pandoc document to rendered document contents plus two lists of -- OpenXML elements (footnotes and comments). writeOpenXML :: PandocMonad m => WriterOptions -> Pandoc -> WS m (Text, [Element], [Element]) writeOpenXML opts (Pandoc meta blocks) = do setupTranslations meta let includeTOC = writerTableOfContents opts || lookupMetaBool "toc" meta let includeLOF = writerListOfFigures opts || lookupMetaBool "lof" meta let includeLOT = writerListOfTables opts || lookupMetaBool "lot" meta abstractTitle <- case lookupMeta "abstract-title" meta of Just (MetaBlocks bs) -> pure $ stringify bs Just (MetaInlines ils) -> pure $ stringify ils Just (MetaString s) -> pure s _ -> translateTerm Abstract abstract <- case lookupMetaBlocks "abstract" meta of [] -> return mempty xs -> vcat . map (literal . showContent) <$> withParaPropM (pStyleM "Abstract") (blocksToOpenXML opts xs) let toInlineMeta field = hcat . map (literal . showContent) <$> inlinesToOpenXML opts (lookupMetaInlines field meta) title <- toInlineMeta "title" subtitle <- toInlineMeta "subtitle" date <- toInlineMeta "date" author <- mapM (fmap (hcat . map (literal . showContent)) . inlinesToOpenXML opts) (docAuthors meta) doc' <- setFirstPara >> blocksToOpenXML opts blocks let body = vcat $ map (literal . showContent) doc' notes' <- gets (reverse . stFootnotes) comments <- gets (reverse . stComments) let toComment (kvs, ils) = do annotation <- inlinesToOpenXML opts ils return $ mknode "w:comment" [("w:" <> k, v) | (k,v) <- kvs] [ mknode "w:p" [] $ map Elem [ mknode "w:pPr" [] [ mknode "w:pStyle" [("w:val", "CommentText")] () ] , mknode "w:r" [] [ mknode "w:rPr" [] [ mknode "w:rStyle" [("w:val", "CommentReference")] () ] , mknode "w:annotationRef" [] () ] ] ++ annotation ] comments' <- mapM toComment comments toc <- if includeTOC then makeTOC opts else return [] lof <- if includeLOF then makeLOF opts else return [] lot <- if includeLOT then makeLOT opts else return [] metadata <- metaToContext opts (fmap (vcat . map (literal . showContent)) . blocksToOpenXML opts) (fmap (hcat . map (literal . showContent)) . inlinesToOpenXML opts) meta cStyleMap <- gets (smParaStyle . stStyleMaps) let styleIdOf name = fromStyleId $ getStyleIdFromName name cStyleMap renderedSectPr <- maybe mempty ppElement <$> asks envSectPr let context = resetField "body" body . resetField "toc" (vcat (map (literal . showElement) toc)) . resetField "lof" (vcat (map (literal . showElement) lof)) . resetField "lot" (vcat (map (literal . showElement) lot)) . resetField "title" title . resetField "subtitle" subtitle . resetField "author" author . resetField "date" date . resetField "abstract-title" abstractTitle . resetField "abstract" abstract . resetField "title-style-id" (styleIdOf "Title") . resetField "subtitle-style-id" (styleIdOf "Subtitle") . resetField "author-style-id" (styleIdOf "Author") . resetField "date-style-id" (styleIdOf "Date") . resetField "abstract-title-style-id" (styleIdOf "AbstractTitle") . resetField "abstract-style-id" (styleIdOf "Abstract") . resetField "sectpr" renderedSectPr $ metadata tpl <- maybe (lift $ compileDefaultTemplate "openxml") pure $ writerTemplate opts let rendered = render Nothing $ renderTemplate tpl context return (rendered, notes', comments') -- | Convert a list of Pandoc blocks to OpenXML. blocksToOpenXML :: (PandocMonad m) => WriterOptions -> [Block] -> WS m [Content] blocksToOpenXML opts = fmap concat . mapM (blockToOpenXML opts) . separateTables . filter (not . isForeignRawBlock) isForeignRawBlock :: Block -> Bool isForeignRawBlock (RawBlock format _) = format /= "openxml" isForeignRawBlock _ = False -- Word combines adjacent tables unless you put an empty paragraph between -- them. See #4315. separateTables :: [Block] -> [Block] separateTables [] = [] separateTables (x@Table{}:xs@(Table{}:_)) = x : RawBlock (Format "openxml") "" : separateTables xs separateTables (x:xs) = x : separateTables xs rStyleM :: (PandocMonad m) => CharStyleName -> WS m XML.Element rStyleM styleName = do cStyleMap <- gets (smCharStyle . stStyleMaps) let sty' = getStyleIdFromName styleName cStyleMap return $ mknode "w:rStyle" [("w:val", fromStyleId sty')] () getUniqueId :: (PandocMonad m) => WS m Text getUniqueId = do n <- gets stCurId modify $ \st -> st{stCurId = n + 1} return $ tshow n -- | Key for specifying user-defined docx styles. dynamicStyleKey :: Text dynamicStyleKey = "custom-style" -- | Convert a Pandoc block element to OpenXML. blockToOpenXML :: (PandocMonad m) => WriterOptions -> Block -> WS m [Content] blockToOpenXML opts blk = withDirection $ blockToOpenXML' opts blk blockToOpenXML' :: (PandocMonad m) => WriterOptions -> Block -> WS m [Content] blockToOpenXML' opts (Div (ident,_classes,kvs) bs) = do stylemod <- case lookup dynamicStyleKey kvs of Just (fromString . T.unpack -> sty) -> do modify $ \s -> s{stDynamicParaProps = Set.insert sty (stDynamicParaProps s)} return $ withParaPropM (pStyleM sty) _ -> return id dirmod <- case lookup "dir" kvs of Just "rtl" -> return $ local (\env -> env { envRTL = True }) Just "ltr" -> return $ local (\env -> env { envRTL = False }) _ -> return id let (hs, bs') = if ident == "refs" then span isHeaderBlock bs else ([], bs) let bibmod = if ident == "refs" then withParaPropM (pStyleM "Bibliography") else id let langmod = case lookup "lang" kvs of Nothing -> id Just lang -> local (\env -> env{envLang = Just lang}) header <- dirmod $ stylemod $ blocksToOpenXML opts hs contents <- dirmod $ bibmod $ stylemod $ langmod $ blocksToOpenXML opts bs' wrapBookmark ident $ header <> contents blockToOpenXML' opts (Header lev (ident,_,kvs) lst) = do setFirstPara let isSection = case writerTopLevelDivision opts of TopLevelChapter -> lev == 1 TopLevelPart -> lev <= 2 _ -> False paraProps <- withParaPropM (pStyleM (fromString $ "Heading "++show lev)) $ getParaProps False number <- if writerNumberSections opts then case lookup "number" kvs of Just n -> do num <- withTextPropM (rStyleM "SectionNumber") (inlineToOpenXML opts (Str n)) return $ num ++ [Elem $ mknode "w:r" [] [mknode "w:tab" [] ()]] Nothing -> return [] else return [] contents <- (number ++) <$> inlinesToOpenXML opts lst -- Add section break before section-level headers, but not for the first one -- to avoid a blank first page (#10578, #11482). addSectionBreak <- if isSection then do isFirst <- gets stFirstSectionHeader if isFirst then do modify $ \s -> s { stFirstSectionHeader = False } pure id else sectionSeparator >>= \case Just sep -> pure (sep:) Nothing -> pure id else pure id addSectionBreak <$> if T.null ident then return [Elem $ mknode "w:p" [] (map Elem paraProps ++ contents)] else do let bookmarkName = ident modify $ \s -> s{ stSectionIds = Set.insert bookmarkName $ stSectionIds s } bookmarkedContents <- wrapBookmark bookmarkName contents return [Elem $ mknode "w:p" [] (map Elem paraProps ++ bookmarkedContents)] blockToOpenXML' opts (Plain lst) = do isInTable <- gets stInTable isInList <- gets stInList let block = blockToOpenXML opts (Para lst) prop <- pStyleM "Compact" if isInTable || isInList then withParaProp prop block else block blockToOpenXML' opts (Para lst) | null lst && not (isEnabled Ext_empty_paragraphs opts) = return [] | otherwise = do isFirstPara <- gets stFirstPara let displayMathPara = case lst of [x] -> isDisplayMath x _ -> False paraProps <- getParaProps displayMathPara bodyTextStyle <- pStyleM $ if isFirstPara then "First Paragraph" else "Body Text" let paraProps' = case paraProps of [] -> [mknode "w:pPr" [] [bodyTextStyle]] ps -> ps modify $ \s -> s { stFirstPara = False } contents <- inlinesToOpenXML opts lst return [Elem $ mknode "w:p" [] (map Elem paraProps' ++ contents)] blockToOpenXML' opts (LineBlock lns) = blockToOpenXML opts $ linesToPara lns blockToOpenXML' _ b@(RawBlock format str) | format == Format "openxml" = return [ Text (CData CDataRaw str Nothing) ] | otherwise = do report $ BlockNotRendered b return [] blockToOpenXML' opts (BlockQuote blocks) = do inNote <- asks envInNote p <- withParaPropM (pStyleM (if inNote then "Footnote Block Text" else "Block Text")) $ blocksToOpenXML opts blocks setFirstPara return p blockToOpenXML' opts (CodeBlock attrs@(ident, _, _) str) = do p <- withParaPropM (pStyleM "Source Code") (blockToOpenXML opts $ Para [Code attrs str]) setFirstPara wrapBookmark ident p blockToOpenXML' _ HorizontalRule = do setFirstPara return [ Elem $ mknode "w:p" [] $ mknode "w:r" [] $ mknode "w:pict" [] $ mknode "v:rect" [("style","width:0;height:1.5pt"), ("o:hralign","center"), ("o:hrstd","t"),("o:hr","t")] () ] blockToOpenXML' opts (Table attr caption colspecs thead tbodies tfoot) = do -- Remove extra paragraph indentation due to list items (#5947). -- This means that tables in lists will not be indented, but it -- avoids unwanted indentation in each cell. content <- tableToOpenXML opts (local (\env -> env{ envListLevel = -1 }) . blocksToOpenXML opts) (Grid.toTable attr caption colspecs thead tbodies tfoot) let (tableId, _, _) = attr wrapBookmark tableId content blockToOpenXML' opts el | BulletList lst <- el = case mapM toTaskListItem lst of Just items -> mconcat <$> mapM (\(checked, bs) -> addOpenXMLList (CheckboxMarker checked) [bs]) items Nothing -> addOpenXMLList BulletMarker lst | OrderedList (start, numstyle, numdelim) lst <- el = addOpenXMLList (NumberMarker numstyle numdelim start) lst where addOpenXMLList marker items = do addList marker numid <- getNumId exampleid <- case marker of NumberMarker Example _ _ -> gets stExampleId _ -> return Nothing l <- asList $ concat <$> mapM (listItemToOpenXML opts $ fromMaybe numid exampleid) items setFirstPara return l blockToOpenXML' opts (DefinitionList items) = do l <- concat `fmap` mapM (definitionListItemToOpenXML opts) items setFirstPara return l blockToOpenXML' opts (Figure (ident, _, _) (Caption _ longcapt) body) = do setFirstPara fignum <- gets stNextFigureNum unless (null longcapt) $ modify $ \st -> st{ stNextFigureNum = fignum + 1 } let refid = if T.null ident then "ref_fig" <> tshow fignum else "ref_" <> ident figname <- translateTerm Term.Figure prop <- pStyleM $ if null longcapt then "Figure" else "Captioned Figure" paraProps <- local (\env -> env { envParaProperties = EnvProps (Just prop) [] <> envParaProperties env }) (getParaProps False) -- Figure contents let simpleImage x = do imgXML <- inlineToOpenXML opts x pure $ Elem (mknode "w:p" [] (map Elem paraProps ++ imgXML)) contentsNode <- case body of [Plain [img@Image {}]] -> simpleImage img [Para [img@Image {}]] -> simpleImage img _ -> toFigureTable opts body -- Caption let imageCaption = withParaPropM (pStyleM "Image Caption") . blocksToOpenXML opts let fstCaptionPara inlns = Para $ if not $ isEnabled Ext_native_numbering opts then inlns else let rawfld = RawInline (Format "openxml") $ mconcat [ "" , tshow fignum , "" ] in Span (refid,[],[]) [Str (figname <> "\160") , rawfld] : Str ": " : inlns captionNode <- case longcapt of [] -> return [] (Para xs : bs) -> imageCaption (fstCaptionPara xs : bs) (Plain xs : bs) -> imageCaption (fstCaptionPara xs : bs) _ -> imageCaption longcapt wrapBookmark ident $ case writerFigureCaptionPosition opts of CaptionBelow -> contentsNode : captionNode CaptionAbove -> captionNode ++ [contentsNode] toFigureTable :: PandocMonad m => WriterOptions -> [Block] -> WS m Content toFigureTable opts blks = do modify $ \s -> s { stInTable = True } let ncols = length blks let textwidth = 7920 -- 5.5 in in twips (1 twip == 1/20 pt) let cellfrac = 1 / fromIntegral ncols let colwidth = tshow @Integer $ floor (textwidth * cellfrac) -- twips let gridCols = replicate ncols $ mknode "w:gridCol" [("w:w", colwidth)] () let scaleImage = \case Image attr@(ident, classes, attribs) alt tgt -> let dimWidth = case dimension Width attr of Nothing -> Percent (cellfrac * 100) Just d -> scaleDimension cellfrac d dimHeight = scaleDimension cellfrac <$> dimension Height attr attribs' = (tshow Width, tshow dimWidth) : (case dimHeight of Nothing -> id Just h -> ((tshow Height, tshow h) :)) [ (k, v) | (k, v) <- attribs , k `notElem` ["width", "height"] ] in Image (ident, classes, attribs') alt tgt x -> x let blockToCell = Table.OOXMLCell nullAttr AlignCenter 1 1 . (:[]) . walk scaleImage tblBody <- Table.rowToOpenXML (blocksToOpenXML opts) . Table.OOXMLRow Table.BodyRow nullAttr $ map blockToCell blks let tbl = mknode "w:tbl" [] ( mknode "w:tblPr" [] [ mknode "w:tblStyle" [("w:val","FigureTable")] (), mknode "w:tblW" [ ("w:type", "auto"), ("w:w", "0") ] (), mknode "w:jc" [("w:val","center")] (), mknode "w:tblLook" [ ("w:firstRow", "0") , ("w:lastRow", "0") , ("w:firstColumn", "0") , ("w:lastColumn", "0") ] () ] : mknode "w:tblGrid" [] gridCols : maybeToList tblBody ) modify $ \s -> s { stInTable = False } return $ Elem tbl definitionListItemToOpenXML :: (PandocMonad m) => WriterOptions -> ([Inline],[[Block]]) -> WS m [Content] definitionListItemToOpenXML opts (term,defs) = do term' <- withParaPropM (pStyleM "Definition Term") $ blockToOpenXML opts (Para term) defs' <- withParaPropM (pStyleM "Definition") $ concat `fmap` mapM (blocksToOpenXML opts) defs return $ term' ++ defs' addList :: (PandocMonad m) => ListMarker -> WS m () addList marker = do lists <- gets stLists lastExampleId <- gets stExampleId modify $ \st -> st{ stLists = lists ++ case marker of -- Use only first occurrence of Example for list declaration to avoid overhead NumberMarker Example _ _ | isJust lastExampleId -> [] _ -> [marker] , stExampleId = case marker of -- Reuse the same identifier for all other occurrences of Example NumberMarker Example _ _ -> lastExampleId <|> Just (baseListId + length lists) _ -> lastExampleId } listItemToOpenXML :: (PandocMonad m) => WriterOptions -> Int -> [Block] -> WS m [Content] listItemToOpenXML opts numid bs = do oldInList <- gets stInList modify $ \st -> st{ stInList = True } let isListBlock = \case BulletList{} -> True OrderedList{} -> True _ -> False -- Prepend an empty string if the first entry is another -- list. Otherwise the outer bullet will disappear. let bs' = case bs of [] -> [] x:xs -> if isListBlock x then Plain [Str ""]:x:xs else x:xs modify $ \st -> st{ stNumIdUsed = False } contents <- withNumId numid $ blocksToOpenXML opts bs' modify $ \st -> st{ stInList = oldInList } return contents -- | Convert a list of inline elements to OpenXML. inlinesToOpenXML :: PandocMonad m => WriterOptions -> [Inline] -> WS m [Content] inlinesToOpenXML opts lst = concat `fmap` mapM (inlineToOpenXML opts) (convertSpace lst) withNumId :: (PandocMonad m) => Int -> WS m a -> WS m a withNumId numid = local $ \env -> env{ envListNumId = numid } asList :: (PandocMonad m) => WS m a -> WS m a asList = local $ \env -> env{ envListLevel = envListLevel env + 1 } getTextProps :: (PandocMonad m) => WS m [Element] getTextProps = do props <- asks envTextProperties mblang <- asks envLang let langnode = case mblang of Nothing -> mempty Just l -> EnvProps Nothing [mknode "w:lang" [("w:val", l)] ()] let squashed = squashProps (props <> langnode) return [mknode "w:rPr" [] squashed | (not . null) squashed] withTextProp :: PandocMonad m => Element -> WS m a -> WS m a withTextProp d p = local (\env -> env {envTextProperties = ep <> envTextProperties env}) p where ep = if isStyle d then EnvProps (Just d) [] else EnvProps Nothing [d] withTextPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a withTextPropM md p = do d <- md withTextProp d p getParaProps :: PandocMonad m => Bool -> WS m [Element] getParaProps displayMathPara = do props <- asks envParaProperties listLevel <- asks envListLevel numid <- asks envListNumId numIdUsed <- gets stNumIdUsed -- clear numId after first use to support multiple paragraphs in the same bullet -- baseListId is the code for no list marker let numid' = if numIdUsed then baseListId else numid modify $ \st -> st{ stNumIdUsed = True } let listPr = [mknode "w:numPr" [] [ mknode "w:ilvl" [("w:val",tshow listLevel)] () , mknode "w:numId" [("w:val",tshow numid')] () ] | listLevel >= 0 && not displayMathPara] return $ case squashProps (EnvProps Nothing listPr <> props) of [] -> [] ps -> [mknode "w:pPr" [] ps] formattedString :: PandocMonad m => Text -> WS m [Element] formattedString str = -- properly handle soft hyphens case splitTextBy (=='\173') str of [w] -> formattedString' w ws -> do sh <- formattedRun [mknode "w:softHyphen" [] ()] intercalate [sh] <$> mapM formattedString' ws formattedString' :: PandocMonad m => Text -> WS m [Element] formattedString' str = do inDel <- asks envInDel let mkrun s = (if T.any isCJK s then withTextProp (mknode "w:rFonts" [("w:hint","eastAsia")] ()) else id) $ formattedRun [ mktnode (if inDel then "w:delText" else "w:t") [("xml:space","preserve")] $ s ] mapM mkrun $ breakIntoChunks $ stripInvalidChars str -- For motivation see #9817. breakIntoChunks :: Text -> [Text] breakIntoChunks t | T.null t = [] | T.any isCJK t = let cs = T.groupBy (\c d -> (isSpace c && isSpace d) || not (isSpace c || isSpace d)) t css = groupBy (\x y -> not (T.any isCJK x || T.any isCJK y) || (T.all isSpace x && not (T.any isCJK y)) || (T.all isSpace y && not (T.any isCJK x))) cs in map mconcat css | otherwise = [t] formattedRun :: PandocMonad m => [Element] -> WS m Element formattedRun els = do props <- getTextProps return $ mknode "w:r" [] $ props ++ els -- | Convert an inline element to OpenXML. inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Content] inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il inlineToOpenXML' :: PandocMonad m => WriterOptions -> Inline -> WS m [Content] inlineToOpenXML' _ (Str str) = map Elem <$> formattedString str inlineToOpenXML' opts Space = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts (Span ("",["mark"],[]) ils) = withTextProp (mknode "w:highlight" [("w:val","yellow")] ()) $ inlinesToOpenXML opts ils inlineToOpenXML' opts (Span ("",["csl-block"],[]) ils) = inlinesToOpenXML opts ils inlineToOpenXML' opts (Span ("",["csl-left-margin"],[]) ils) = inlinesToOpenXML opts ils inlineToOpenXML' opts (Span ("",["csl-right-inline"],[]) ils) = ([Elem $ mknode "w:r" [] (mknode "w:t" [("xml:space","preserve")] ("\t" :: Text))] ++) <$> inlinesToOpenXML opts ils inlineToOpenXML' opts (Span ("",["csl-indent"],[]) ils) = inlinesToOpenXML opts ils inlineToOpenXML' _ (Span (ident,["comment-start"],kvs) ils) = do -- prefer the "id" in kvs, since that is the one produced by the docx -- reader. let ident' = fromMaybe ident (lookup "id" kvs) kvs' = filter (("id" /=) . fst) kvs modify $ \st -> st{ stComments = (("id",ident'):kvs', ils) : stComments st } return [ Elem $ mknode "w:commentRangeStart" [("w:id", ident')] () ] inlineToOpenXML' opts (Span (ident,["comment-end"],kvs) content) = do -- prefer the "id" in kvs, since that is the one produced by the docx -- reader. let ident' = fromMaybe ident (lookup "id" kvs) -- process nested content: see #8189 nestedContent <- inlinesToOpenXML opts content let thisCommentEnd = [ mknode "w:commentRangeEnd" [("w:id", ident')] () , mknode "w:r" [] [ mknode "w:rPr" [] [ mknode "w:rStyle" [("w:val", "CommentReference")] () ] , mknode "w:commentReference" [("w:id", ident')] () ] ] return $ map Elem thisCommentEnd ++ nestedContent inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do stylemod <- case lookup dynamicStyleKey kvs of Just (fromString . T.unpack -> sty) -> do modify $ \s -> s{stDynamicTextProps = Set.insert sty (stDynamicTextProps s)} return $ withTextPropM (rStyleM sty) _ -> return id let dirmod = case lookup "dir" kvs of Just "rtl" -> local (\env -> env { envRTL = True }) Just "ltr" -> local (\env -> env { envRTL = False }) _ -> id off x = withTextProp (mknode x [("w:val","0")] ()) pmod = (if "csl-no-emph" `elem` classes then off "w:i" else id) . (if "csl-no-strong" `elem` classes then off "w:b" else id) . (if "csl-no-smallcaps" `elem` classes then off "w:smallCaps" else id) getChangeAuthorDate = do defaultAuthor <- asks envChangesAuthor let author = fromMaybe defaultAuthor (lookup "author" kvs) let mdate = lookup "date" kvs return $ ("w:author", author) : maybe [] (\date -> [("w:date", date)]) mdate insmod <- if "insertion" `elem` classes then do changeAuthorDate <- getChangeAuthorDate insId <- gets stInsId modify $ \s -> s{stInsId = insId + 1} return $ \f -> do x <- f return [Elem $ mknode "w:ins" (("w:id", tshow insId) : changeAuthorDate) x] else return id delmod <- if "deletion" `elem` classes then do changeAuthorDate <- getChangeAuthorDate delId <- gets stDelId modify $ \s -> s{stDelId = delId + 1} return $ \f -> local (\env->env{envInDel=True}) $ do x <- f return [Elem $ mknode "w:del" (("w:id", tshow delId) : changeAuthorDate) x] else return id let langmod = case lookup "lang" kvs of Nothing -> id Just lang -> local (\env -> env{envLang = Just lang}) contents <- insmod $ delmod $ dirmod $ stylemod $ pmod $ langmod $ inlinesToOpenXML opts ils wrapBookmark ident contents inlineToOpenXML' opts (Strong lst) = withTextProp (mknode "w:bCs" [] ()) $ -- needed for LTR, #6911 withTextProp (mknode "w:b" [] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' opts (Emph lst) = withTextProp (mknode "w:iCs" [] ()) $ -- needed for LTR, #6911 withTextProp (mknode "w:i" [] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' opts (Underline lst) = withTextProp (mknode "w:u" [("w:val","single")] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' opts (Subscript lst) = withTextProp (mknode "w:vertAlign" [("w:val","subscript")] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' opts (Superscript lst) = withTextProp (mknode "w:vertAlign" [("w:val","superscript")] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' opts (SmallCaps lst) = withTextProp (mknode "w:smallCaps" [] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' opts (Strikeout lst) = withTextProp (mknode "w:strike" [] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' _ LineBreak = return [Elem br] inlineToOpenXML' _ il@(RawInline f str) | f == Format "openxml" = return [Text (CData CDataRaw str Nothing)] | otherwise = do report $ InlineNotRendered il return [] inlineToOpenXML' opts (Quoted quoteType lst) = inlinesToOpenXML opts $ [Str open] ++ lst ++ [Str close] where (open, close) = case quoteType of SingleQuote -> ("\x2018", "\x2019") DoubleQuote -> ("\x201C", "\x201D") inlineToOpenXML' opts (Math mathType str) = do when (mathType == DisplayMath) setFirstPara res <- (lift . lift) (convertMath writeOMML mathType str) case res of Right r -> return [Elem $ fromXLElement r] Left il -> inlineToOpenXML' opts il inlineToOpenXML' opts (Cite _ lst) = inlinesToOpenXML opts lst inlineToOpenXML' opts (Code attrs str) = do let alltoktypes = [KeywordTok ..] tokTypesMap <- mapM (\tt -> (,) tt <$> rStyleM (fromString $ show tt)) alltoktypes let unhighlighted = (map Elem . intercalate [br]) `fmap` mapM formattedString (T.lines str) formatOpenXML _fmtOpts = intercalate [br] . map (map toHlTok) toHlTok (toktype,tok) = mknode "w:r" [] [ mknode "w:rPr" [] $ maybeToList (lookup toktype tokTypesMap) , mknode "w:t" [("xml:space","preserve")] tok ] let highlighted = case highlight (writerSyntaxMap opts) formatOpenXML attrs str of Right h -> return (map Elem h) Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg unhighlighted withTextPropM (rStyleM "Verbatim Char") $ case writerHighlightMethod opts of DefaultHighlighting -> highlighted Skylighting _ -> highlighted _ -> unhighlighted inlineToOpenXML' opts (Note bs) = do notes <- gets stFootnotes notenum <- getUniqueId footnoteStyle <- rStyleM "Footnote Reference" let notemarker = mknode "w:r" [] [ mknode "w:rPr" [] footnoteStyle , mknode "w:footnoteRef" [] () ] let notemarkerXml = RawInline (Format "openxml") $ ppElement notemarker let insertNoteRef (Plain ils : xs) = Plain (notemarkerXml : Space : ils) : xs insertNoteRef (Para ils : xs) = Para (notemarkerXml : Space : ils) : xs insertNoteRef xs = Para [notemarkerXml] : xs contents <- local (\env -> env{ envListLevel = -1 , envParaProperties = mempty , envTextProperties = mempty , envInNote = True }) (withParaPropM (pStyleM "Footnote Text") $ blocksToOpenXML opts $ insertNoteRef bs) let newnote = mknode "w:footnote" [("w:id", notenum)] contents modify $ \s -> s{ stFootnotes = newnote : notes } return [ Elem $ mknode "w:r" [] [ mknode "w:rPr" [] footnoteStyle , mknode "w:footnoteReference" [("w:id", notenum)] () ] ] -- internal link: inlineToOpenXML' opts (Link _ txt (T.uncons -> Just ('#', xs),_)) = do contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt return [ Elem $ mknode "w:hyperlink" [("w:anchor", toBookmarkName xs)] contents ] -- external link: inlineToOpenXML' opts (Link _ txt (src,_)) = do contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt extlinks <- gets stExternalLinks id' <- case M.lookup src extlinks of Just i -> return i Nothing -> do i <- ("rId" <>) <$> getUniqueId modify $ \st -> st{ stExternalLinks = M.insert src i extlinks } return i return [ Elem $ mknode "w:hyperlink" [("r:id",id')] contents ] inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do pageWidth <- asks envPrintWidth imgs <- gets stImages let stImage = M.lookup (T.unpack src) imgs generateImgElt (ident, _fp, mt, img) = do docprid <- getUniqueId nvpicprid <- getUniqueId (blipAttrs, blipContents) <- case T.takeWhile (/=';') <$> mt of Just "image/svg+xml" -> do -- get fallback png mediabag <- getMediaBag mbFallback <- case lookupMedia (T.unpack (src <> ".png")) mediabag of Just item -> do id' <- T.unpack . ("rId" <>) <$> getUniqueId let fp' = "media/" <> id' <> ".png" let imgdata = (id', fp', Just (mediaMimeType item), BL.toStrict $ mediaContents item) modify $ \st -> st { stImages = M.insert fp' imgdata $ stImages st } return $ Just id' Nothing -> return Nothing let extLst = mknode "a:extLst" [] [ mknode "a:ext" [("uri","{28A0092B-C50C-407E-A947-70E740481C1C}")] [ mknode "a14:useLocalDpi" [("xmlns:a14","http://schemas.microsoft.com/office/drawing/2010/main"), ("val","0")] () ] , mknode "a:ext" [("uri","{96DAC541-7B7A-43D3-8B79-37D633B846F1}")] [ mknode "asvg:svgBlip" [("xmlns:asvg", "http://schemas.microsoft.com/office/drawing/2016/SVG/main"), ("r:embed",T.pack ident)] () ] ] return (maybe [] (\id'' -> [("r:embed", T.pack id'')]) mbFallback, [extLst]) _ -> return ([("r:embed", T.pack ident)], []) let (xpt,ypt) = desiredSizeInPoints opts attr (either (const def) id (imageSize opts img)) -- 12700 emu = 1 pt pageWidthPt = case dimension Width attr of Just (Percent a) -> pageWidth * floor (a * 127) _ -> pageWidth * 12700 (xemu,yemu) = fitToPage (xpt * 12700, ypt * 12700) pageWidthPt cNvPicPr = mknode "pic:cNvPicPr" [] $ mknode "a:picLocks" [("noChangeArrowheads","1") ,("noChangeAspect","1")] () nvPicPr = mknode "pic:nvPicPr" [] [ mknode "pic:cNvPr" [("descr",src) ,("id", nvpicprid) ,("name","Picture")] () , cNvPicPr ] blipFill = mknode "pic:blipFill" [] [ mknode "a:blip" blipAttrs blipContents , mknode "a:stretch" [] $ mknode "a:fillRect" [] () ] xfrm = mknode "a:xfrm" [] [ mknode "a:off" [("x","0"),("y","0")] () , mknode "a:ext" [("cx",tshow xemu) ,("cy",tshow yemu)] () ] prstGeom = mknode "a:prstGeom" [("prst","rect")] $ mknode "a:avLst" [] () ln = mknode "a:ln" [("w","9525")] [ mknode "a:noFill" [] () , mknode "a:headEnd" [] () , mknode "a:tailEnd" [] () ] spPr = mknode "pic:spPr" [("bwMode","auto")] [xfrm, prstGeom, mknode "a:noFill" [] (), ln] graphic = mknode "a:graphic" [] $ mknode "a:graphicData" [("uri","http://schemas.openxmlformats.org/drawingml/2006/picture")] [ mknode "pic:pic" [] [ nvPicPr , blipFill , spPr ] ] imgElt = mknode "w:r" [] $ mknode "w:drawing" [] $ mknode "wp:inline" [] [ mknode "wp:extent" [("cx",tshow xemu),("cy",tshow yemu)] () , mknode "wp:effectExtent" [("b","0"),("l","0"),("r","0"),("t","0")] () , mknode "wp:docPr" [ ("descr", stringify alt) , ("title", title) , ("id", docprid) , ("name","Picture") ] () , graphic ] return [Elem imgElt] wrapBookmark imgident =<< case stImage of Just imgData -> generateImgElt imgData Nothing -> ( do --try (img, mt) <- P.fetchItem src ident <- ("rId" <>) <$> getUniqueId let imgext = case mt >>= extensionFromMimeType of Just x -> "." <> x Nothing -> case imageType img of Just Png -> ".png" Just Jpeg -> ".jpeg" Just Gif -> ".gif" Just Pdf -> ".pdf" Just Eps -> ".eps" Just Svg -> ".svg" Just Emf -> ".emf" Just Tiff -> ".tiff" Just Webp -> ".webp" Just Avif -> ".avif" Nothing -> "" imgpath = "media/" <> ident <> imgext mbMimeType = mt <|> getMimeType (T.unpack imgpath) imgData = (T.unpack ident, T.unpack imgpath, mbMimeType, img) if T.null imgext then -- without an extension there is no rule for content type inlinesToOpenXML opts alt -- return alt to avoid corrupted docx else do -- insert mime type to use in constructing [Content_Types].xml modify $ \st -> st { stImages = M.insert (T.unpack src) imgData $ stImages st } generateImgElt imgData ) `catchError` ( \e -> do report $ CouldNotFetchResource src $ T.pack (show e) -- emit alt text inlinesToOpenXML opts alt ) br :: Element br = mknode "w:r" [] [mknode "w:br" [] ()] withDirection :: PandocMonad m => WS m a -> WS m a withDirection x = do isRTL <- asks envRTL paraProps <- asks envParaProperties textProps <- asks envTextProperties -- We want to clean all bidirection (bidi) and right-to-left (rtl) -- properties from the props first. This is because we don't want -- them to stack up. let paraProps' = filter (\e -> (qName . elName) e /= "bidi") (otherElements paraProps) textProps' = filter (\e -> (qName . elName) e /= "rtl") (otherElements textProps) paraStyle = styleElement paraProps textStyle = styleElement textProps if isRTL -- if we are going right-to-left, we (re?)add the properties. then flip local x $ \env -> env { envParaProperties = EnvProps paraStyle $ mknode "w:bidi" [] () : paraProps' , envTextProperties = EnvProps textStyle $ mknode "w:rtl" [] () : textProps' } else flip local x $ \env -> env { envParaProperties = EnvProps paraStyle paraProps' , envTextProperties = EnvProps textStyle textProps' } wrapBookmark :: (PandocMonad m) => Text -> [Content] -> WS m [Content] wrapBookmark "" contents = return contents wrapBookmark ident contents = do id' <- getUniqueId let bookmarkStart = mknode "w:bookmarkStart" [("w:id", id') ,("w:name", toBookmarkName ident)] () bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] () return $ Elem bookmarkStart : contents ++ [Elem bookmarkEnd] -- Word imposes a 40 character limit on bookmark names and requires -- that they begin with a letter. So we just use a hash of the -- identifier when otherwise we'd have an illegal bookmark name. toBookmarkName :: Text -> Text toBookmarkName s | Just (c, _) <- T.uncons s , isLetter c , T.length s <= 40 = s | otherwise = T.pack $ 'X' : drop 1 (show (hashWith SHA1 (fromText s))) maxListLevel :: Int maxListLevel = 8 convertSpace :: [Inline] -> [Inline] convertSpace (Str x : Space : Str y : xs) = convertSpace (Str (x <> " " <> y) : xs) convertSpace (Str x : Str y : xs) = convertSpace (Str (x <> y) : xs) convertSpace (x:xs) = x : convertSpace xs convertSpace [] = [] ================================================ FILE: src/Text/Pandoc/Writers/Docx/StyleMap.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {- | Module : Text.Pandoc.Writers.Docx.StyleMap Copyright : © 2014-2020 Jesse Rosenthal , 2014-2024 John MacFarlane , 2015-2019 Nikolay Yakimov License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal Stability : alpha Portability : portable Mappings of element styles (word to pandoc-internal). -} module Text.Pandoc.Writers.Docx.StyleMap ( StyleMaps(..) , ParaStyleName , CharStyleName , getStyleMaps , getStyleIdFromName , hasStyleName , fromStyleId , fromStyleName ) where import Text.Pandoc.Readers.Docx.Parse.Styles import Codec.Archive.Zip import qualified Data.Map as M import qualified Data.Text as T import Data.String import Data.Char (isSpace) data StyleMaps = StyleMaps { smCharStyle :: CharStyleNameMap, smParaStyle :: ParaStyleNameMap } type ParaStyleNameMap = M.Map ParaStyleName ParStyle type CharStyleNameMap = M.Map CharStyleName CharStyle getStyleIdFromName :: (Ord sn, FromStyleName sn, IsString (StyleId sty), HasStyleId sty) => sn -> M.Map sn sty -> StyleId sty getStyleIdFromName s = maybe (fallback s) getStyleId . M.lookup s where fallback = fromString . T.unpack . T.filter (not . isSpace) . fromStyleName hasStyleName :: (Ord sn, HasStyleId sty) => sn -> M.Map sn sty -> Bool hasStyleName styleName = M.member styleName getStyleMaps :: Archive -> StyleMaps getStyleMaps = uncurry StyleMaps . archiveToStyles' getStyleName getStyleName ================================================ FILE: src/Text/Pandoc/Writers/Docx/Table.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.Docx.Table Copyright : Copyright (C) 2012-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Conversion of table blocks to docx. -} module Text.Pandoc.Writers.Docx.Table ( tableToOpenXML , rowToOpenXML , OOXMLRow (..) , OOXMLCell (..) , RowType (..) ) where import Control.Monad.State.Strict ( modify, gets ) import Control.Monad ( unless , zipWithM ) import Control.Monad.Except ( throwError ) import Data.Array ( elems, (!), assocs, indices ) import Data.Text (Text) import Data.Maybe (catMaybes, fromMaybe) import Text.Pandoc.Definition ( ColSpec, Caption(Caption), Format(Format), Attr, Block(Para, Plain), Inline(Str, Span, RawInline), Alignment(..), RowSpan(..), ColSpan(..), ColWidth(..) ) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Translations (translateTerm) import Text.Pandoc.Writers.Docx.Types ( WS, WriterState(stNextTableNum, stInTable), WriterEnv(..), setFirstPara, pStyleM, withParaProp, withParaPropM ) import Control.Monad.Reader (asks) import Text.Pandoc.Shared ( tshow, stringify ) import Text.Pandoc.Options (WriterOptions(..), isEnabled, CaptionPosition(..)) import Text.Pandoc.Extensions (Extension(Ext_native_numbering)) import Text.Pandoc.Error (PandocError(PandocSomeError)) import Text.Printf (printf) import Text.Pandoc.Writers.GridTable ( rowArray, ColIndex, GridCell(..), Part(Part, partCellArray, partRowAttrs), RowIndex ) import Text.Pandoc.Writers.OOXML ( mknode ) import Text.Pandoc.XML.Light.Proc ( onlyElems ) import Text.Pandoc.XML.Light.Types ( Content(Elem), Element(elName), QName(qName) ) import qualified Data.Text as T import qualified Text.Pandoc.Translations as Term import qualified Text.Pandoc.Writers.GridTable as Grid tableToOpenXML :: PandocMonad m => WriterOptions -> ([Block] -> WS m [Content]) -> Grid.Table -> WS m [Content] tableToOpenXML opts blocksToOpenXML gridTable = do setFirstPara let (Grid.Table (ident,_,tableAttr) caption colspecs _rowheads thead tbodies tfoot) = gridTable let (Caption _maybeShortCaption captionBlocks) = caption tablenum <- gets stNextTableNum unless (null captionBlocks) $ modify $ \st -> st{ stNextTableNum = tablenum + 1 } let tableid = if T.null ident then "table" <> tshow tablenum else ident tablename <- translateTerm Term.Table let captionStr = stringify captionBlocks let aligns = map fst $ elems colspecs captionXml <- if null captionBlocks then return [] else withParaPropM (pStyleM "Table Caption") $ blocksToOpenXML $ if isEnabled Ext_native_numbering opts then addLabel tableid tablename tablenum captionBlocks else captionBlocks -- We set "in table" after processing the caption, because we don't -- want the "Table Caption" style to be overwritten with "Compact". modify $ \s -> s { stInTable = True } head' <- cellGridToOpenXML blocksToOpenXML HeadRow aligns thead bodies <- mapM (cellGridToOpenXML blocksToOpenXML BodyRow aligns) tbodies foot' <- cellGridToOpenXML blocksToOpenXML FootRow aligns tfoot let hasHeader = not . null . indices . partRowAttrs $ thead let hasFooter = not . null . indices . partRowAttrs $ tfoot -- for compatibility with Word <= 2007, we include a val with a bitmask -- 0×0020 Apply first row conditional formatting -- 0×0040 Apply last row conditional formatting -- 0×0080 Apply first column conditional formatting -- 0×0100 Apply last column conditional formatting -- 0×0200 Do not apply row banding conditional formatting -- 0×0400 Do not apply column banding conditional formattin let tblLookVal = if hasHeader then (0x20 :: Int) else 0 let (gridCols, tblWattr) = tableLayout (elems colspecs) listLevel <- asks envListLevel let tblStyle = fromMaybe "Table" (lookup "custom-style" tableAttr) let indent = (listLevel + 1) * 720 let hasWidths = not $ all ((== ColWidthDefault) . snd) colspecs let tbl = mknode "w:tbl" [] ( mknode "w:tblPr" [] ( [ mknode "w:tblStyle" [("w:val",tblStyle)] (), mknode "w:tblW" tblWattr () ] ++ [ mknode "w:jc" [("w:val","left")] () | indent > 0 ] ++ [ mknode "w:tblInd" [("w:w", tshow indent),("w:type","dxa")] () | indent > 0 ] ++ [ mknode "w:tblLayout" [("w:type", "fixed")] () | hasWidths ] ++ [ mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0") ,("w:lastRow",if hasFooter then "1" else "0") ,("w:firstColumn","0") ,("w:lastColumn","0") ,("w:noHBand","0") ,("w:noVBand","0") ,("w:val", T.pack $ printf "%04x" tblLookVal) ] () ] ++ [ mknode "w:tblCaption" [("w:val", captionStr)] () | not (T.null captionStr) ] ) : mknode "w:tblGrid" [] gridCols : head' ++ mconcat bodies ++ foot' ) modify $ \s -> s { stInTable = False } return $ case writerTableCaptionPosition opts of CaptionAbove -> captionXml ++ [Elem tbl] CaptionBelow -> Elem tbl : captionXml addLabel :: Text -> Text -> Int -> [Block] -> [Block] addLabel tableid tablename tablenum bs = case bs of (Para ils : rest) -> Para (label : Str ": " : ils) : rest (Plain ils : rest) -> Plain (label : Str ": " : ils) : rest _ -> Para [label] : bs where label = Span (tableid,[],[]) [Str (tablename <> "\160"), RawInline (Format "openxml") (" " \\* ARABIC \">" <> tshow tablenum <> "")] -- | Parts of a table data RowType = HeadRow | BodyRow | FootRow alignmentToString :: Alignment -> Text alignmentToString = \case AlignLeft -> "left" AlignRight -> "right" AlignCenter -> "center" AlignDefault -> "" tableLayout :: [ColSpec] -> ([Element], [(Text, Text)]) tableLayout specs = let textwidth = 7920 -- 5.5 in in twips (1 twip == 1/20 pt) fullrow = 5000 -- 100% specified in pct (1 pct == 1/50th of a percent) ncols = length specs getWidth = \case ColWidth n -> n _ -> 0 widths = map (getWidth . snd) specs rowwidth = round (fullrow * sum widths) :: Int widthToTwips w = floor (textwidth * w) :: Int mkGridCol w = mknode "w:gridCol" [("w:w", tshow (widthToTwips w))] () in if all (== 0) widths then ( replicate ncols $ mkGridCol (1.0 / fromIntegral ncols) , [ ("w:type", "auto"), ("w:w", "0")]) else ( map mkGridCol widths , [ ("w:type", "pct"), ("w:w", tshow rowwidth) ]) cellGridToOpenXML :: PandocMonad m => ([Block] -> WS m [Content]) -> RowType -> [Alignment] -> Part -> WS m [Element] cellGridToOpenXML blocksToOpenXML rowType aligns part@(Part _ cellArray _) = if null (elems cellArray) then return mempty else partToRows rowType aligns part >>= fmap catMaybes . mapM (rowToOpenXML blocksToOpenXML) data OOXMLCell = OOXMLCell Attr Alignment RowSpan ColSpan [Block] | OOXMLCellMerge ColSpan data OOXMLRow = OOXMLRow RowType Attr [OOXMLCell] partToRows :: PandocMonad m => RowType -> [Alignment] -> Part -> WS m [OOXMLRow] partToRows rowType aligns part = do let toOOXMLCell :: PandocMonad m => Alignment -> RowIndex -> ColIndex -> GridCell -> WS m [OOXMLCell] toOOXMLCell columnAlign ridx cidx = \case UnassignedCell -> throwError $ PandocSomeError "Encountered unassigned table cell" ContentCell attr align rowspan colspan blocks -> do -- Respect non-default, cell specific alignment. let align' = case align of AlignDefault -> columnAlign _ -> align return [OOXMLCell attr align' rowspan colspan blocks] ContinuationCell idx'@(ridx',cidx') | ridx /= ridx', cidx == cidx' -> do case (partCellArray part)!idx' of (ContentCell _ _ _ colspan _) -> return [OOXMLCellMerge colspan] x -> error $ "Content cell expected, got, " ++ show x ++ " at index " ++ show idx' _ -> return mempty let mkRow :: PandocMonad m => (RowIndex, Attr) -> WS m OOXMLRow mkRow (ridx, attr) = do cs <- zipWithM (\align -> uncurry $ toOOXMLCell align ridx) aligns (assocs . rowArray ridx $ partCellArray part) return $ OOXMLRow rowType attr . mconcat $ cs mapM mkRow $ assocs (partRowAttrs part) rowToOpenXML :: PandocMonad m => ([Block] -> WS m [Content]) -> OOXMLRow -> WS m (Maybe Element) rowToOpenXML blocksToOpenXML (OOXMLRow rowType _attr cells) | null cells = return Nothing | otherwise = do xmlcells <- mapM (ooxmlCellToOpenXML blocksToOpenXML) cells let addTrPr = case rowType of HeadRow -> (mknode "w:trPr" [] [mknode "w:tblHeader" [("w:val", "on")] ()] :) BodyRow -> id FootRow -> id return $ Just $ mknode "w:tr" [] (addTrPr xmlcells) ooxmlCellToOpenXML :: PandocMonad m => ([Block] -> WS m [Content]) -> OOXMLCell -> WS m Element ooxmlCellToOpenXML blocksToOpenXML = \case OOXMLCellMerge (ColSpan colspan) -> do return $ mknode "w:tc" [] [ mknode "w:tcPr" [] [ mknode "w:gridSpan" [("w:val", tshow colspan)] () , mknode "w:vMerge" [("w:val", "continue")] () ] , mknode "w:p" [] [mknode "w:pPr" [] ()]] OOXMLCell _attr align rowspan (ColSpan colspan) contents -> do compactStyle <- pStyleM "Compact" es <- maybe id withParaProp (alignmentFor align) $ blocksToOpenXML contents -- Table cells require a element, even an empty one! -- Not in the spec but in Word 2007, 2010. See #4953. And -- apparently the last element must be a , see #6983. return . mknode "w:tc" [] $ Elem (mknode "w:tcPr" [] ([ mknode "w:gridSpan" [("w:val", tshow colspan)] () | colspan > 1] ++ [ mknode "w:vMerge" [("w:val", "restart")] () | rowspan > RowSpan 1 ])) : if null contents then [Elem $ mknode "w:p" [] [mknode "w:pPr" [] [compactStyle]]] else case reverse (onlyElems es) of b:e:_ | qName (elName b) == "bookmarkEnd" -- y tho? , qName (elName e) == "p" -> es e:_ | qName (elName e) == "p" -> es _ -> es ++ [Elem $ mknode "w:p" [] ()] alignmentFor :: Alignment -> Maybe Element alignmentFor AlignDefault = Nothing alignmentFor al = Just $ mknode "w:jc" [("w:val",alignmentToString al)] () ================================================ FILE: src/Text/Pandoc/Writers/Docx/Types.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.Docx Copyright : Copyright (C) 2012-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Conversion of table blocks to docx. -} module Text.Pandoc.Writers.Docx.Types ( EnvProps (..) , WriterEnv (..) , defaultWriterEnv , WriterState (..) , defaultWriterState , WS , ListMarker (..) , listMarkerToId , pStyleM , isStyle , setFirstPara , withParaProp , withParaPropM ) where import Control.Applicative ((<|>)) import Control.Monad.Reader import Control.Monad.State.Strict import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Writers.Docx.StyleMap import Text.Pandoc.Writers.OOXML import Text.Pandoc.XML.Light as XML import qualified Data.ByteString as B import qualified Data.Map as M import qualified Data.Set as Set import qualified Data.Text as T data ListMarker = NoMarker | BulletMarker | CheckboxMarker Bool | NumberMarker ListNumberStyle ListNumberDelim Int deriving (Show, Read, Eq, Ord) listMarkerToId :: ListMarker -> Text listMarkerToId NoMarker = "990" listMarkerToId BulletMarker = "991" listMarkerToId (CheckboxMarker False) = "992" listMarkerToId (CheckboxMarker True) = "993" listMarkerToId (NumberMarker sty delim n) = T.pack $ '9' : '9' : styNum : delimNum : show n where styNum = case sty of DefaultStyle -> '2' Example -> '3' Decimal -> '4' LowerRoman -> '5' UpperRoman -> '6' LowerAlpha -> '7' UpperAlpha -> '8' delimNum = case delim of DefaultDelim -> '0' Period -> '1' OneParen -> '2' TwoParens -> '3' data EnvProps = EnvProps{ styleElement :: Maybe Element , otherElements :: [Element] } instance Semigroup EnvProps where EnvProps s es <> EnvProps s' es' = EnvProps (s <|> s') (es ++ es') instance Monoid EnvProps where mempty = EnvProps Nothing [] mappend = (<>) data WriterEnv = WriterEnv { envTextProperties :: EnvProps , envParaProperties :: EnvProps , envRTL :: Bool , envListLevel :: Int , envListNumId :: Int , envInDel :: Bool , envInNote :: Bool , envChangesAuthor :: Text , envChangesDate :: Text , envPrintWidth :: Integer , envLang :: Maybe Text , envSectPr :: Maybe Element } defaultWriterEnv :: WriterEnv defaultWriterEnv = WriterEnv { envTextProperties = mempty , envParaProperties = mempty , envRTL = False , envListLevel = -1 , envListNumId = 1 , envInDel = False , envInNote = False , envChangesAuthor = "unknown" , envChangesDate = "1969-12-31T19:00:00Z" , envPrintWidth = 1 , envLang = Nothing , envSectPr = Nothing } data WriterState = WriterState{ stFootnotes :: [Element] , stComments :: [([(Text, Text)], [Inline])] , stSectionIds :: Set.Set Text , stExternalLinks :: M.Map Text Text , stImages :: M.Map FilePath (String, String, Maybe MimeType, B.ByteString) , stLists :: [ListMarker] , stExampleId :: Maybe Int , stInsId :: Int , stDelId :: Int , stStyleMaps :: StyleMaps , stFirstPara :: Bool , stFirstSectionHeader :: Bool -- ^ True until first section header is processed , stNumIdUsed :: Bool -- ^ True if the current numId (envListNumId) has been used. -- Should only be used once, for the first paragraph. , stInTable :: Bool , stInList :: Bool , stTocTitle :: [Inline] , stDynamicParaProps :: Set.Set ParaStyleName , stDynamicTextProps :: Set.Set CharStyleName , stCurId :: Int , stNextFigureNum :: Int , stNextTableNum :: Int } defaultWriterState :: WriterState defaultWriterState = WriterState{ stFootnotes = defaultFootnotes , stComments = [] , stSectionIds = Set.empty , stExternalLinks = M.empty , stImages = M.empty , stLists = [NoMarker] , stExampleId = Nothing , stInsId = 1 , stDelId = 1 , stStyleMaps = StyleMaps M.empty M.empty , stFirstPara = False , stFirstSectionHeader = True , stNumIdUsed = False , stInTable = False , stInList = False , stTocTitle = [Str "Table of Contents"] , stDynamicParaProps = Set.empty , stDynamicTextProps = Set.empty , stCurId = 20 , stNextFigureNum = 1 , stNextTableNum = 1 } setFirstPara :: PandocMonad m => WS m () setFirstPara = modify $ \s -> s { stFirstPara = True } type WS m = ReaderT WriterEnv (StateT WriterState m) -- Word will insert these footnotes into the settings.xml file -- (whether or not they're visible in the document). If they're in the -- file, but not in the footnotes.xml file, it will produce -- problems. So we want to make sure we insert them into our document. defaultFootnotes :: [Element] defaultFootnotes = [ mknode "w:footnote" [("w:type", "separator"), ("w:id", "-1")] [ mknode "w:p" [] [mknode "w:r" [] [ mknode "w:separator" [] ()]]] , mknode "w:footnote" [("w:type", "continuationSeparator"), ("w:id", "0")] [ mknode "w:p" [] [ mknode "w:r" [] [ mknode "w:continuationSeparator" [] ()]]]] pStyleM :: (PandocMonad m) => ParaStyleName -> WS m XML.Element pStyleM styleName = do pStyleMap <- gets (smParaStyle . stStyleMaps) let sty' = getStyleIdFromName styleName pStyleMap return $ mknode "w:pStyle" [("w:val", fromStyleId sty')] () withParaProp :: PandocMonad m => Element -> WS m a -> WS m a withParaProp d p = local (\env -> env {envParaProperties = ep <> envParaProperties env}) p where ep = if isStyle d then EnvProps (Just d) [] else EnvProps Nothing [d] withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a withParaPropM md p = do d <- md withParaProp d p isStyle :: Element -> Bool isStyle e = isElem [] "w" "rStyle" e || isElem [] "w" "pStyle" e ================================================ FILE: src/Text/Pandoc/Writers/Docx.hs ================================================ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.Docx Copyright : Copyright (C) 2012-2025 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to docx. -} module Text.Pandoc.Writers.Docx ( writeDocx ) where import Codec.Archive.Zip ( Archive(zEntries), addEntryToArchive, emptyArchive, findEntryByPath, fromArchive, toArchive, toEntry, Entry(eRelativePath) ) import Control.Monad (MonadPlus(mplus), foldM) import Control.Monad.Except (throwError) import Control.Monad.Reader ( ReaderT(runReaderT) ) import Control.Monad.State.Strict ( StateT(runStateT) ) 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.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) unrelativize ('/':xs) = '/':xs unrelativize xs = "/word/" ++ xs 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 "" (unrelativize . T.unpack) (extractTarget x), "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml")) headers ++ map (\x -> (maybe "" (unrelativize . T.unpack) (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" let unrelativize ('/':xs) = xs unrelativize xs = "word/" ++ xs headerFooterEntries <- mapM (entryFromArchive refArchive . unrelativize) $ 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 extractTarget :: Element -> Maybe Text extractTarget = findAttr (QName "Target" Nothing Nothing) ================================================ FILE: src/Text/Pandoc/Writers/DokuWiki.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.DokuWiki Copyright : Copyright (C) 2008-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Clare Macrae Stability : alpha Portability : portable Conversion of 'Pandoc' documents to DokuWiki markup. DokuWiki: -} {- [x] Implement nested blockquotes (currently only ever does one level) [x] Implement alignment of text in tables [ ] Implement comments [ ] Work through the Dokuwiki spec, and check I've not missed anything out [ ] Remove dud/duplicate code -} module Text.Pandoc.Writers.DokuWiki ( writeDokuWiki ) where import Control.Monad (zipWithM) import Control.Monad.Reader (ReaderT, asks, local, runReaderT) import Control.Monad.State.Strict (StateT, evalStateT) import Data.Default (Default (..)) import Data.List (transpose) import Data.List.NonEmpty (nonEmpty) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Extensions import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText), isEnabled) import Text.Pandoc.Shared (figureDiv, linesToPara, removeFormatting, trimr) import Text.Pandoc.URI (escapeURI, isURI) import Text.Pandoc.Templates (renderTemplate) import Text.DocLayout (render, literal) import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable) import Data.Maybe (fromMaybe) import qualified Data.Map as M data WriterState = WriterState { } data WriterEnvironment = WriterEnvironment { stIndent :: Text -- Indent after the marker at the beginning of list items , stBackSlashLB :: Bool -- True if we should produce formatted strings with newlines (as in a table cell) , stBlockQuoteLevel :: Int -- Block quote level } instance Default WriterState where def = WriterState {} instance Default WriterEnvironment where def = WriterEnvironment { stIndent = "" , stBackSlashLB = False , stBlockQuoteLevel = 0 } type DokuWiki m = ReaderT WriterEnvironment (StateT WriterState m) -- | Convert Pandoc to DokuWiki. writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDokuWiki opts document = runDokuWiki (pandocToDokuWiki opts document) runDokuWiki :: PandocMonad m => DokuWiki m a -> m a runDokuWiki = flip evalStateT def . flip runReaderT def -- | Return DokuWiki representation of document. pandocToDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> DokuWiki m Text pandocToDokuWiki opts (Pandoc meta blocks) = do metadata <- metaToContext opts (fmap (literal . trimr) . blockListToDokuWiki opts) (fmap (literal . trimr) . inlineListToDokuWiki opts) meta body <- blockListToDokuWiki opts blocks let context = defField "body" body $ defField "toc" (writerTableOfContents opts) metadata return $ case writerTemplate opts of Nothing -> body Just tpl -> render Nothing $ renderTemplate tpl context -- | Escape special characters for DokuWiki. escapeString :: Text -> Text escapeString = T.replace "__" "%%__%%" . T.replace "**" "%%**%%" . T.replace "//" "%%//%%" -- | Convert Pandoc block element to DokuWiki. blockToDokuWiki :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> DokuWiki m Text blockToDokuWiki opts (Div _attrs bs) = do contents <- blockListToDokuWiki opts bs indent <- asks stIndent return $ contents <> if T.null indent then "\n" else "" blockToDokuWiki opts (Plain inlines) = inlineListToDokuWiki opts inlines blockToDokuWiki opts (Para inlines) = do bqLevel <- asks stBlockQuoteLevel let bqPrefix = case bqLevel of 0 -> "" n -> T.replicate n ">" <> " " indent <- asks stIndent contents <- inlineListToDokuWiki opts inlines return $ bqPrefix <> contents <> if T.null indent then "\n" else "" blockToDokuWiki opts (LineBlock lns) = blockToDokuWiki opts $ linesToPara lns blockToDokuWiki opts b@(RawBlock f str) | f == Format "dokuwiki" = return str -- See https://www.dokuwiki.org/wiki:syntax -- use uppercase HTML tag for block-level content: | f == Format "html" , isEnabled Ext_raw_html opts = return $ "\n" <> str <> "\n" | otherwise = "" <$ report (BlockNotRendered b) blockToDokuWiki _ HorizontalRule = return "\n----\n" blockToDokuWiki opts (Header level _ inlines) = do -- emphasis, links etc. not allowed in headers, apparently, -- so we remove formatting: contents <- inlineListToDokuWiki opts $ removeFormatting inlines let eqs = T.replicate ( 7 - level ) "=" return $ eqs <> " " <> contents <> " " <> eqs <> "\n" blockToDokuWiki _ (CodeBlock (_,classes,_) str) = do bqLevel <- asks stBlockQuoteLevel let bqPrefix = case bqLevel of 0 -> "" n -> T.replicate n ">" <> " " return $ bqPrefix <> " (case classes of [] -> "" (x:_) -> " " <> fromMaybe x (M.lookup x languageNames)) <> ">\n" <> str <> (if "\n" `T.isSuffixOf` str then "" else "\n") <> "
    \n" blockToDokuWiki opts (BlockQuote blocks) = local (\st -> st{ stBlockQuoteLevel = stBlockQuoteLevel st + 1 }) (blockListToDokuWiki opts blocks) blockToDokuWiki opts (Table _ blkCapt specs thead tbody tfoot) = do let (capt, aligns, _, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot captionDoc <- if null capt then return "" else do c <- inlineListToDokuWiki opts capt return $ "" <> c <> "\n" headers' <- if all null headers then return [] else zipWithM (tableItemToDokuWiki opts) aligns headers rows' <- mapM (zipWithM (tableItemToDokuWiki opts) aligns) rows let widths = map (maybe 0 maximum . nonEmpty . map T.length) $ transpose (headers':rows') let padTo (width, al) s = case width - T.length s of x | x > 0 -> if al == AlignLeft || al == AlignDefault then s <> T.replicate x " " else if al == AlignRight then T.replicate x " " <> s else T.replicate (x `div` 2) " " <> s <> T.replicate (x - x `div` 2) " " | otherwise -> s let renderRow sep cells = sep <> T.intercalate sep (zipWith padTo (zip widths aligns) cells) <> sep return $ captionDoc <> (if null headers' then "" else renderRow "^" headers' <> "\n") <> T.unlines (map (renderRow "|") rows') blockToDokuWiki opts (BulletList items) = do indent <- asks stIndent backSlash <- asks stBackSlashLB contents <- local (\s -> s { stIndent = stIndent s <> " " , stBackSlashLB = backSlash}) (mapM (listItemToDokuWiki opts) items) return $ vcat contents <> if T.null indent then "\n" else "" blockToDokuWiki opts (OrderedList _attribs items) = do indent <- asks stIndent backSlash <- asks stBackSlashLB contents <- local (\s -> s { stIndent = stIndent s <> " " , stBackSlashLB = backSlash}) (mapM (orderedListItemToDokuWiki opts) items) return $ vcat contents <> if T.null indent then "\n" else "" blockToDokuWiki opts (Figure attr capt body) = blockToDokuWiki opts $ figureDiv attr capt body -- TODO Need to decide how to make definition lists work on dokuwiki - I don't think there -- is a specific representation of them. -- TODO This creates double '; ; ' if there is a bullet or ordered list inside a definition list blockToDokuWiki opts (DefinitionList items) = do indent <- asks stIndent backSlash <- asks stBackSlashLB contents <- local (\s -> s { stIndent = stIndent s <> " " , stBackSlashLB = backSlash}) (mapM (definitionListItemToDokuWiki opts) items) return $ vcat contents <> if T.null indent then "\n" else "" -- Auxiliary functions for lists: -- | Convert bullet list item (list of blocks) to DokuWiki. listItemToDokuWiki :: PandocMonad m => WriterOptions -> [Block] -> DokuWiki m Text listItemToDokuWiki opts items = do bqLevel <- asks stBlockQuoteLevel let bqPrefix = case bqLevel of 0 -> "" n -> T.replicate n ">" <> " " let useWrap = not (isSimpleListItem items) bs <- mapM (blockToDokuWiki opts) items let contents = case items of [_, CodeBlock _ _] -> T.concat bs _ -> vcat bs indent <- asks stIndent backSlash <- asks stBackSlashLB let indent' = if backSlash then T.drop 2 indent else indent return $ bqPrefix <> indent' <> "* " <> if useWrap then "\n" <> contents <> "\n" else contents -- | Convert ordered list item (list of blocks) to DokuWiki. -- | TODO Emiminate dreadful duplication of text from listItemToDokuWiki orderedListItemToDokuWiki :: PandocMonad m => WriterOptions -> [Block] -> DokuWiki m Text orderedListItemToDokuWiki opts items = do bqLevel <- asks stBlockQuoteLevel let bqPrefix = case bqLevel of 0 -> "" n -> T.replicate n ">" <> " " let useWrap = not (isSimpleListItem items) contents <- local (\st -> st{ stBlockQuoteLevel = 0 }) (blockListToDokuWiki opts items) indent <- asks stIndent backSlash <- asks stBackSlashLB let indent' = if backSlash then T.drop 2 indent else indent return $ bqPrefix <> indent' <> "- " <> if useWrap then "\n" <> contents <> "\n" else contents -- | Convert definition list item (label, list of blocks) to DokuWiki. definitionListItemToDokuWiki :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> DokuWiki m Text definitionListItemToDokuWiki opts (label, items) = do let useWrap = not (all isSimpleListItem items) bqLevel <- asks stBlockQuoteLevel let bqPrefix = case bqLevel of 0 -> "" n -> T.replicate n ">" <> " " labelText <- inlineListToDokuWiki opts label contents <- local (\st -> st{ stBlockQuoteLevel = 0 }) (mapM (blockListToDokuWiki opts) items) indent <- asks stIndent backSlash <- asks stBackSlashLB let indent' = if backSlash then T.drop 2 indent else indent return $ bqPrefix <> indent' <> "* **" <> labelText <> "** " <> if useWrap then "\n" <> vcat contents <> "\n" else T.intercalate "; " contents -- | True if list item can be handled with the simple wiki syntax. False if -- WRAP tags will be needed. isSimpleListItem :: [Block] -> Bool isSimpleListItem [] = True isSimpleListItem [x, CodeBlock{}] | isPlainOrPara x = True isSimpleListItem (Div _ bs : ys) = -- see #8920 isSimpleListItem bs && all isSimpleList ys isSimpleListItem (x:ys) | isPlainOrPara x = all isSimpleList ys isSimpleListItem _ = False --- | True if the list can be handled by simple wiki markup, False if HTML tags will be needed. isSimpleList :: Block -> Bool isSimpleList x = case x of BulletList items -> all isSimpleListItem items OrderedList (1, _, _) items -> all isSimpleListItem items DefinitionList items -> all (all isSimpleListItem . snd) items _ -> False isPlainOrPara :: Block -> Bool isPlainOrPara (Plain _) = True isPlainOrPara (Para _) = True isPlainOrPara _ = False -- | Concatenates strings with line breaks between them. vcat :: [Text] -> Text vcat = T.intercalate "\n" -- | For each string in the input list, convert all newlines to -- dokuwiki escaped newlines. Then concat the list using double linebreaks. backSlashLineBreaks :: [Text] -> Text backSlashLineBreaks ls = vcatBackSlash $ map (T.pack . escape . T.unpack) ls where vcatBackSlash = T.intercalate "\\\\ \\\\ " -- simulate paragraphs. escape ['\n'] = "" -- remove trailing newlines escape ('\n':cs) = "\\\\ " <> escape cs escape (c:cs) = c : escape cs escape [] = [] -- Auxiliary functions for tables: tableItemToDokuWiki :: PandocMonad m => WriterOptions -> Alignment -> [Block] -> DokuWiki m Text tableItemToDokuWiki opts align' item = do let mkcell x = (if align' == AlignRight || align' == AlignCenter then " " else "") <> x <> (if align' == AlignLeft || align' == AlignCenter then " " else "") contents <- local (\s -> s { stBackSlashLB = True , stBlockQuoteLevel = 0 }) $ blockListToDokuWiki opts item return $ mkcell contents -- | Convert list of Pandoc block elements to DokuWiki. blockListToDokuWiki :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements -> DokuWiki m Text blockListToDokuWiki opts blocks = do backSlash <- asks stBackSlashLB let blocks' = consolidateRawBlocks blocks if backSlash then backSlashLineBreaks <$> mapM (blockToDokuWiki opts) blocks' else vcat <$> mapM (blockToDokuWiki opts) blocks' consolidateRawBlocks :: [Block] -> [Block] consolidateRawBlocks [] = [] consolidateRawBlocks (RawBlock f1 b1 : RawBlock f2 b2 : xs) | f1 == f2 = consolidateRawBlocks (RawBlock f1 (b1 <> "\n" <> b2) : xs) consolidateRawBlocks (x:xs) = x : consolidateRawBlocks xs -- | Convert list of Pandoc inline elements to DokuWiki. inlineListToDokuWiki :: PandocMonad m => WriterOptions -> [Inline] -> DokuWiki m Text inlineListToDokuWiki opts lst = T.concat <$> mapM (inlineToDokuWiki opts) lst -- | Convert Pandoc inline element to DokuWiki. inlineToDokuWiki :: PandocMonad m => WriterOptions -> Inline -> DokuWiki m Text inlineToDokuWiki opts (Span _attrs ils) = inlineListToDokuWiki opts ils inlineToDokuWiki opts (Emph lst) = do contents <- inlineListToDokuWiki opts lst return $ "//" <> contents <> "//" inlineToDokuWiki opts (Underline lst) = do contents <- inlineListToDokuWiki opts lst return $ "__" <> contents <> "__" inlineToDokuWiki opts (Strong lst) = do contents <- inlineListToDokuWiki opts lst return $ "**" <> contents <> "**" inlineToDokuWiki opts (Strikeout lst) = do contents <- inlineListToDokuWiki opts lst return $ "" <> contents <> "" inlineToDokuWiki opts (Superscript lst) = do contents <- inlineListToDokuWiki opts lst return $ "" <> contents <> "" inlineToDokuWiki opts (Subscript lst) = do contents <- inlineListToDokuWiki opts lst return $ "" <> contents <> "" inlineToDokuWiki opts (SmallCaps lst) = inlineListToDokuWiki opts lst inlineToDokuWiki opts (Quoted SingleQuote lst) = do contents <- inlineListToDokuWiki opts lst return $ "\8216" <> contents <> "\8217" inlineToDokuWiki opts (Quoted DoubleQuote lst) = do contents <- inlineListToDokuWiki opts lst return $ "\8220" <> contents <> "\8221" inlineToDokuWiki opts (Cite _ lst) = inlineListToDokuWiki opts lst inlineToDokuWiki _ (Code _ str) = -- In dokuwiki, text surrounded by '' is really just a font statement, i.e. , -- and so other formatting can be present inside. -- However, in pandoc, and markdown, inlined code doesn't contain formatting. -- So I have opted for using %% to disable all formatting inside inline code blocks. -- This gives the best results when converting from other formats to dokuwiki, even if -- the resultand code is a little ugly, for short strings that don't contain formatting -- characters. -- It does mean that if pandoc could ever read dokuwiki, and so round-trip the format, -- any formatting inside inlined code blocks would be lost, or presented incorrectly. return $ "''%%" <> str <> "%%''" inlineToDokuWiki _ (Str str) = return $ escapeString str inlineToDokuWiki _ (Math mathType str) = return $ delim <> str <> delim -- note: str should NOT be escaped where delim = case mathType of DisplayMath -> "$$" InlineMath -> "$" inlineToDokuWiki opts il@(RawInline f str) | f == Format "dokuwiki" = return str | f == Format "html" , isEnabled Ext_raw_html opts = return $ "" <> str <> "" | otherwise = "" <$ report (InlineNotRendered il) inlineToDokuWiki _ LineBreak = do backSlash <- asks stBackSlashLB return $ if backSlash then "\n" else "\\\\\n" inlineToDokuWiki opts SoftBreak = case writerWrapText opts of WrapNone -> return " " WrapAuto -> return " " WrapPreserve -> return "\n" inlineToDokuWiki _ Space = return " " inlineToDokuWiki opts (Link _ txt (src, _)) = do label <- inlineListToDokuWiki opts txt case txt of [Str s] | "mailto:" `T.isPrefixOf` src -> return $ "<" <> s <> ">" | escapeURI s == src -> return src _ -> if isURI src then return $ "[[" <> src <> "|" <> label <> "]]" else return $ "[[" <> src' <> "|" <> label <> "]]" where src' = case T.uncons src of Just ('/',xs) -> xs -- with leading / it's a _ -> src -- link to a help page inlineToDokuWiki opts (Image attr alt (source, tit)) = do alt' <- inlineListToDokuWiki opts alt let txt = case (tit, alt) of ("", []) -> "" ("", _ ) -> "|" <> alt' (_ , _ ) -> "|" <> tit return $ "{{" <> source <> imageDims opts attr <> txt <> "}}" inlineToDokuWiki opts (Note contents) = do contents' <- local (\st -> st{ stBlockQuoteLevel = 0 }) (blockListToDokuWiki opts contents) return $ "((" <> contents' <> "))" -- note - may not work for notes with multiple blocks imageDims :: WriterOptions -> Attr -> Text imageDims opts attr = go (toPx $ dimension Width attr) (toPx $ dimension Height attr) where toPx = fmap (showInPixel opts) . checkPct checkPct (Just (Percent _)) = Nothing checkPct maybeDim = maybeDim go (Just w) Nothing = "?" <> w go (Just w) (Just h) = "?" <> w <> "x" <> h go Nothing (Just h) = "?0x" <> h go Nothing Nothing = "" languageNames :: M.Map Text Text languageNames = M.fromList [("cs", "csharp") ,("coffee", "cofeescript") ,("commonlisp", "lisp") ,("gcc", "c") ,("html", "html5") ,("makefile", "make") ,("objectivec", "objc") ,("r", "rsplus") ,("sqlmysql", "mysql") ,("sqlpostgresql", "postgresql") ,("sci", "scilab") ,("xorg", "xorgconf") ] ================================================ FILE: src/Text/Pandoc/Writers/EPUB.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.EPUB Copyright : Copyright (C) 2010-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to EPUB. -} module Text.Pandoc.Writers.EPUB ( writeEPUB2, writeEPUB3 ) where import Codec.Archive.Zip (Entry, addEntryToArchive, eRelativePath, emptyArchive, fromArchive, fromEntry, toEntry) import Control.Applicative ( (<|>) ) import Control.Monad (mplus, unless, when, zipWithM) import Control.Monad.Except (catchError, throwError) import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get, gets, lift, modify) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 import Data.Char (isAlphaNum, isAscii, isDigit) import Data.List (isInfixOf, isPrefixOf) import qualified Data.Map as M import Data.Maybe (fromMaybe, isNothing, mapMaybe, isJust, catMaybes) import qualified Data.Set as Set import qualified Data.Text as T import Data.Text (Text) import qualified Data.Text.Lazy as TL import System.FilePath (takeExtension, takeFileName, makeRelative) import Text.HTML.TagSoup (Tag (TagOpen), fromAttrib, parseTags) import Text.Pandoc.Builder (fromList, setMeta) import Text.Pandoc.Writers.Shared (ensureValidXmlIdentifiers) import Data.Tree (Tree(..)) import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class.PandocPure as P import Text.Pandoc.Data (readDataFile) import qualified Text.Pandoc.Class.PandocMonad as P import Data.Time import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.MIME (MimeType, extensionFromMimeType, getMimeType) import Text.Pandoc.URI (urlEncode) import Text.Pandoc.Options (EPUBVersion (..), HTMLMathMethod (..), ObfuscationMethod (NoObfuscation), WrapOption (..), WriterOptions (..)) import Text.Pandoc.Shared (normalizeDate, renderTags', stringify, uniqueIdent, tshow) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.UUID (getRandomUUID) import Text.Pandoc.Walk (walk, walkM) import Text.Pandoc.Writers.HTML (writeHtmlStringForEPUB) import Text.Printf (printf) import Text.Pandoc.XML.Light import Text.Pandoc.XML (escapeStringForXML) import Text.DocTemplates (FromContext(lookupContext), Context(..), ToContext(toVal), Val(..)) import Text.Pandoc.Chunks (splitIntoChunks, Chunk(..), ChunkedDoc(..), SecInfo(..)) -- A Chapter includes a list of blocks. newtype Chapter = Chapter [Block] deriving (Show) data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] , stMediaNextId :: Int , stEpubSubdir :: FilePath } type E m = StateT EPUBState m data EPUBMetadata = EPUBMetadata{ epubIdentifier :: [Identifier] , epubTitle :: [Title] , epubDate :: [Date] , epubLanguage :: Text , epubCreator :: [Creator] , epubContributor :: [Creator] , epubSubject :: [Subject] , epubDescription :: Maybe Text , epubType :: Maybe Text , epubFormat :: Maybe Text , epubPublisher :: Maybe Text , epubSource :: Maybe Text , epubRelation :: Maybe Text , epubCoverage :: Maybe Text , epubRights :: Maybe Text , epubBelongsToCollection :: Maybe Text , epubGroupPosition :: Maybe Text , epubCoverImage :: Maybe FilePath , epubStylesheets :: [FilePath] , epubPageDirection :: Maybe ProgressionDirection , epubIbooksFields :: [(Text, Text)] , epubCalibreFields :: [(Text, Text)] , epubAccessModes :: [Text] -- https://kb.daisy.org/publishing/docs/metadata/schema.org/accessMode.html , epubAccessModeSufficient :: [Text] -- https://kb.daisy.org/publishing/docs/metadata/schema.org/accessModeSufficient.html , epubAccessibilityFeatures :: [Text] -- https://kb.daisy.org/publishing/docs/metadata/schema.org/accessibilityFeature.html , epubAccessibilityHazards :: [Text] -- https://kb.daisy.org/publishing/docs/metadata/schema.org/accessibilityHazard.html , epubAccessibilitySummary :: Maybe Text -- https://kb.daisy.org/publishing/docs/metadata/schema.org/accessibilitySummary.html } deriving Show data Date = Date{ dateText :: Text , dateEvent :: Maybe Text } deriving Show data Creator = Creator{ creatorText :: Text , creatorRole :: Maybe Text , creatorFileAs :: Maybe Text } deriving Show data Identifier = Identifier{ identifierText :: Text , identifierScheme :: Maybe Text } deriving Show data Title = Title{ titleText :: Text , titleFileAs :: Maybe Text , titleType :: Maybe Text } deriving Show data ProgressionDirection = LTR | RTL deriving Show data Subject = Subject{ subjectText :: Text , subjectAuthority :: Maybe Text , subjectTerm :: Maybe Text } deriving Show dcName :: Text -> QName dcName n = QName n Nothing (Just "dc") dcNode :: Node t => Text -> t -> Element dcNode = node . dcName opfName :: Text -> QName opfName n = QName n Nothing (Just "opf") toId :: FilePath -> Text toId = T.pack . map (\x -> if isAlphaNum x || x == '-' || x == '_' then x else '_') . takeFileName removeNote :: Inline -> Inline removeNote (Note _) = Str "" removeNote x = x toVal' :: Text -> Val T.Text toVal' = toVal mkEntry :: PandocMonad m => FilePath -> B.ByteString -> E m Entry mkEntry path content = do epubSubdir <- gets stEpubSubdir let addEpubSubdir :: Entry -> Entry addEpubSubdir e = e{ eRelativePath = (if null epubSubdir then "" else epubSubdir ++ "/") ++ eRelativePath e } epochtime <- floor <$> lift P.getPOSIXTime return $ (if path == "mimetype" || "META-INF" `isPrefixOf` path then id else addEpubSubdir) $ toEntry path epochtime content getEPUBMetadata :: PandocMonad m => WriterOptions -> Meta -> E m EPUBMetadata getEPUBMetadata opts meta = do let md = metadataFromMeta opts meta elts <- case writerEpubMetadata opts of Nothing -> return [] Just t -> case parseXMLContents (TL.fromStrict t) of Left msg -> throwError $ PandocXMLError "epub metadata" msg Right ns -> return (onlyElems ns) let md' = foldr addMetadataFromXML md elts let addIdentifier m = if null (epubIdentifier m) then do randomId <- getRandomUUID return $ m{ epubIdentifier = [Identifier (tshow randomId) Nothing] } else return m let addLanguage m = if T.null (epubLanguage m) then case lookupContext "lang" (writerVariables opts) of Just x -> return m{ epubLanguage = x } Nothing -> do mLang <- lift $ P.lookupEnv "LANG" let localeLang = case mLang of Just lang -> T.map (\c -> if c == '_' then '-' else c) $ T.takeWhile (/='.') lang Nothing -> "en-US" return m{ epubLanguage = localeLang } else return m let fixDate m = if null (epubDate m) then do currentTime <- lift P.getTimestamp return $ m{ epubDate = [ Date{ dateText = showDateTimeISO8601 currentTime , dateEvent = Nothing } ] } else return m let addAuthor m = if any (\c -> creatorRole c == Just "aut") $ epubCreator m then return m else do let authors' = map stringify $ docAuthors meta let toAuthor name = Creator{ creatorText = name , creatorRole = Just "aut" , creatorFileAs = Nothing } return $ m{ epubCreator = map toAuthor authors' ++ epubCreator m } addIdentifier md' >>= fixDate >>= addAuthor >>= addLanguage addMetadataFromXML :: Element -> EPUBMetadata -> EPUBMetadata addMetadataFromXML e@(Element (QName name _ (Just "dc")) attrs _ _) md | name == "identifier" = md{ epubIdentifier = Identifier{ identifierText = strContent e , identifierScheme = lookupAttr (opfName "scheme") attrs } : epubIdentifier md } | name == "title" = md{ epubTitle = Title{ titleText = strContent e , titleFileAs = getAttr "file-as" , titleType = getAttr "type" } : epubTitle md } | name == "date" = md{ epubDate = Date{ dateText = fromMaybe "" $ normalizeDate' $ strContent e , dateEvent = getAttr "event" } : epubDate md } | name == "language" = md{ epubLanguage = strContent e } | name == "creator" = md{ epubCreator = Creator{ creatorText = strContent e , creatorRole = getAttr "role" , creatorFileAs = getAttr "file-as" } : epubCreator md } | name == "contributor" = md{ epubContributor = Creator { creatorText = strContent e , creatorRole = getAttr "role" , creatorFileAs = getAttr "file-as" } : epubContributor md } | name == "subject" = md{ epubSubject = Subject { subjectText = strContent e , subjectAuthority = getAttr "authority" , subjectTerm = getAttr "term" } : epubSubject md } | name == "description" = md { epubDescription = Just $ strContent e } | name == "type" = md { epubType = Just $ strContent e } | name == "format" = md { epubFormat = Just $ strContent e } | name == "publisher" = md { epubPublisher = Just $ strContent e } | name == "source" = md { epubSource = Just $ strContent e } | name == "relation" = md { epubRelation = Just $ strContent e } | name == "coverage" = md { epubCoverage = Just $ strContent e } | name == "rights" = md { epubRights = Just $ strContent e } | name == "belongs-to-collection" = md { epubBelongsToCollection = Just $ strContent e } | name == "group-position" = md { epubGroupPosition = Just $ strContent e } | otherwise = md where getAttr n = lookupAttr (opfName n) attrs addMetadataFromXML e@(Element (QName "meta" _ _) attrs _ _) md = case getAttr "property" `mplus` getAttr "name" of Just s | "ibooks:" `T.isPrefixOf` s -> md{ epubIbooksFields = (T.drop 7 s, strContent e) : epubIbooksFields md } | "calibre:" `T.isPrefixOf` s -> md{ epubCalibreFields = (T.drop 8 s, fromMaybe "" $ getAttr "content") : epubCalibreFields md } _ -> md where getAttr n = lookupAttr (unqual n) attrs addMetadataFromXML _ md = md metaValueToString :: MetaValue -> Text metaValueToString (MetaString s) = s metaValueToString (MetaInlines ils) = stringify ils metaValueToString (MetaBlocks bs) = stringify bs metaValueToString (MetaBool True) = "true" metaValueToString (MetaBool False) = "false" metaValueToString _ = "" metaValueToPaths :: MetaValue -> [FilePath] metaValueToPaths (MetaList xs) = map (T.unpack . metaValueToString) xs metaValueToPaths x = [T.unpack $ metaValueToString x] getList :: T.Text -> Meta -> (MetaValue -> a) -> [a] getList s meta handleMetaValue = case lookupMeta s meta of Just (MetaList xs) -> map handleMetaValue xs Just mv -> [handleMetaValue mv] Nothing -> [] getIdentifier :: Meta -> [Identifier] getIdentifier meta = getList "identifier" meta handleMetaValue where handleMetaValue (MetaMap m) = Identifier{ identifierText = maybe "" metaValueToString $ M.lookup "text" m , identifierScheme = metaValueToString <$> M.lookup "scheme" m } handleMetaValue mv = Identifier (metaValueToString mv) Nothing getTitle :: Meta -> [Title] getTitle meta = getList "title" meta handleMetaValue where handleMetaValue (MetaMap m) = Title{ titleText = maybe "" metaValueToString $ M.lookup "text" m , titleFileAs = metaValueToString <$> M.lookup "file-as" m , titleType = metaValueToString <$> M.lookup "type" m } handleMetaValue mv = Title (metaValueToString mv) Nothing Nothing getCreator :: T.Text -> Meta -> [Creator] getCreator s meta = getList s meta handleMetaValue where handleMetaValue (MetaMap m) = Creator{ creatorText = maybe "" metaValueToString $ M.lookup "text" m , creatorFileAs = metaValueToString <$> M.lookup "file-as" m , creatorRole = metaValueToString <$> M.lookup "role" m } handleMetaValue mv = Creator (metaValueToString mv) Nothing Nothing getDate :: T.Text -> Meta -> [Date] getDate s meta = getList s meta handleMetaValue where handleMetaValue (MetaMap m) = Date{ dateText = fromMaybe "" $ M.lookup "text" m >>= normalizeDate' . metaValueToString , dateEvent = metaValueToString <$> M.lookup "event" m } handleMetaValue mv = Date { dateText = fromMaybe "" $ normalizeDate' $ metaValueToString mv , dateEvent = Nothing } getSubject :: T.Text -> Meta -> [Subject] getSubject s meta = getList s meta handleMetaValue where handleMetaValue (MetaMap m) = Subject{ subjectText = maybe "" metaValueToString $ M.lookup "text" m , subjectAuthority = metaValueToString <$> M.lookup "authority" m , subjectTerm = metaValueToString <$> M.lookup "term" m } handleMetaValue mv = Subject (metaValueToString mv) Nothing Nothing metadataFromMeta :: WriterOptions -> Meta -> EPUBMetadata metadataFromMeta opts meta = EPUBMetadata{ epubIdentifier = identifiers , epubTitle = titles , epubDate = date , epubLanguage = language , epubCreator = creators , epubContributor = contributors , epubSubject = subjects , epubDescription = description , epubType = epubtype , epubFormat = format , epubPublisher = publisher , epubSource = source , epubRelation = relation , epubCoverage = coverage , epubRights = rights , epubBelongsToCollection = belongsToCollection , epubGroupPosition = groupPosition , epubCoverImage = coverImage , epubStylesheets = stylesheets , epubPageDirection = pageDirection , epubIbooksFields = ibooksFields , epubCalibreFields = calibreFields , epubAccessModes = accessModes , epubAccessModeSufficient = accessModeSufficient , epubAccessibilityFeatures = accessibilityFeatures , epubAccessibilityHazards = accessibilityHazards , epubAccessibilitySummary = accessibilitySummary } where identifiers = getIdentifier meta titles = getTitle meta date = getDate "date" meta language = maybe "" metaValueToString $ lookupMeta "language" meta `mplus` lookupMeta "lang" meta creators = getCreator "creator" meta contributors = getCreator "contributor" meta subjects = getSubject "subject" meta description = metaValueToString <$> lookupMeta "description" meta epubtype = metaValueToString <$> lookupMeta "type" meta format = metaValueToString <$> lookupMeta "format" meta publisher = metaValueToString <$> lookupMeta "publisher" meta source = metaValueToString <$> lookupMeta "source" meta relation = metaValueToString <$> lookupMeta "relation" meta coverage = metaValueToString <$> lookupMeta "coverage" meta rights = metaValueToString <$> lookupMeta "rights" meta belongsToCollection = metaValueToString <$> lookupMeta "belongs-to-collection" meta groupPosition = metaValueToString <$> lookupMeta "group-position" meta coverImage = T.unpack <$> lookupContext "epub-cover-image" (writerVariables opts) `mplus` (metaValueToString <$> lookupMeta "cover-image" meta) mCss = lookupMeta "css" meta <|> lookupMeta "stylesheet" meta stylesheets = maybe [] metaValueToPaths mCss ++ case lookupContext "css" (writerVariables opts) of Just xs -> map T.unpack xs Nothing -> case lookupContext "css" (writerVariables opts) of Just x -> [T.unpack x] Nothing -> [] pageDirection = case T.toLower . metaValueToString <$> lookupMeta "page-progression-direction" meta of Just "ltr" -> Just LTR Just "rtl" -> Just RTL _ -> Nothing ibooksFields = case lookupMeta "ibooks" meta of Just (MetaMap mp) -> M.toList $ M.map metaValueToString mp _ -> [] calibreFields = case lookupMeta "calibre" meta of Just (MetaMap mp) -> M.toList $ M.map metaValueToString mp _ -> [] accessModes = case lookupMeta "accessModes" meta of Just (MetaList xs) -> map metaValueToString xs _ -> ["textual"] accessModeSufficient = case lookupMeta "accessModeSufficient" meta of Just (MetaList xs) -> map metaValueToString xs _ -> ["textual"] accessibilityFeatures = case lookupMeta "accessibilityFeatures" meta of Just (MetaList xs) -> map metaValueToString xs _ -> ["alternativeText", "readingOrder", "structuralNavigation", "tableOfContents"] accessibilityHazards = case lookupMeta "accessibilityHazards" meta of Just (MetaList xs) -> map metaValueToString xs _ -> ["none"] accessibilitySummary = metaValueToString <$> lookupMeta "accessibilitySummary" meta -- | Produce an EPUB2 file from a Pandoc document. writeEPUB2 :: PandocMonad m => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> m B.ByteString writeEPUB2 = writeEPUB EPUB2 -- | Produce an EPUB3 file from a Pandoc document. writeEPUB3 :: PandocMonad m => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> m B.ByteString writeEPUB3 = writeEPUB EPUB3 -- | Produce an EPUB file from a Pandoc document. writeEPUB :: PandocMonad m => EPUBVersion -> WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> m B.ByteString writeEPUB epubVersion opts doc = do let epubSubdir = writerEpubSubdirectory opts -- sanity check on epubSubdir unless (T.all (\c -> isAscii c && isAlphaNum c) epubSubdir) $ throwError $ PandocEpubSubdirectoryError epubSubdir let initState = EPUBState { stMediaPaths = [] , stMediaNextId = 0 , stEpubSubdir = T.unpack epubSubdir } evalStateT (pandocToEPUB epubVersion opts doc) initState pandocToEPUB :: PandocMonad m => EPUBVersion -> WriterOptions -> Pandoc -> E m B.ByteString pandocToEPUB version opts doc = do let doc' = ensureValidXmlIdentifiers doc -- handle pictures Pandoc meta blocks <- walkM (transformInline opts) doc' >>= walkM transformBlock picEntries <- mapMaybe (snd . snd) <$> gets stMediaPaths epubSubdir <- gets stEpubSubdir let epub3 = version == EPUB3 let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) . writeHtmlStringForEPUB version o metadata <- getEPUBMetadata opts meta -- retrieve title of document let plainTitle :: Text plainTitle = case docTitle' meta of [] -> case epubTitle metadata of [] -> "UNTITLED" (x:_) -> titleText x x -> stringify x -- stylesheet stylesheets <- case epubStylesheets metadata of [] -> (\x -> [B.fromChunks [x]]) <$> readDataFile "epub.css" fs -> mapM P.readFileLazy fs stylesheetEntries <- zipWithM (\bs n -> mkEntry ("styles/stylesheet" ++ show n ++ ".css") bs) stylesheets [(1 :: Int)..] -- writer variables let vars :: Context Text vars = Context $ M.delete "css" . M.insert "epub3" (toVal' $ if epub3 then "true" else "false") . M.insert "lang" (toVal' $ epubLanguage metadata) $ unContext $ writerVariables opts -- If True create paths relative to parent folder let cssvars :: Bool -> Context Text cssvars useprefix = Context $ M.insert "css" (ListVal $ map (\e -> toVal' $ (if useprefix then "../" else "") <> T.pack (makeRelative epubSubdir (eRelativePath e))) stylesheetEntries) mempty -- Add additional options for the writer let opts' :: WriterOptions opts' = opts{ writerEmailObfuscation = NoObfuscation , writerSectionDivs = True , writerVariables = vars , writerWrapText = WrapAuto } -- cover page (cpgEntry, cpicEntry, mbCoverImageName) <- createCoverPage meta metadata opts' vars cssvars writeHtml plainTitle -- title page tpContent <- lift $ writeHtml opts'{ writerVariables = Context (M.fromList [ ("titlepage", toVal' "true"), ("body-type", toVal' "frontmatter"), ("pagetitle", toVal $ escapeStringForXML plainTitle)]) <> cssvars True <> vars } (Pandoc meta []) tpEntry <- mkEntry "text/title_page.xhtml" tpContent -- handle fonts let matchingGlob f = do xs <- lift $ P.glob f when (null xs) $ report $ CouldNotFetchResource (T.pack f) "glob did not match any font files" return xs let mkFontEntry :: PandocMonad m => FilePath -> StateT EPUBState m Entry mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) =<< lift (P.readFileLazy f) fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts') fontEntries <- mapM mkFontEntry fontFiles -- body pages -- add level 1 header to beginning if none there let blocks' = addIdentifiers opts $ case blocks of (Div _ (Header{}:_) : _) -> blocks (Header 1 _ _ : _) -> blocks _ -> Header 1 ("",["unnumbered"],[]) (docTitle' meta) : blocks -- create the chapters let chunkedDoc = splitIntoChunks "ch%n.xhtml" (writerNumberSections opts) Nothing (writerSplitLevel opts) (Pandoc meta blocks') -- Create the chapter entries from the chapters. -- Also requires access to the extended writer options and context -- as well as the css Context and html writer chapterEntries <- createChapterEntries opts' vars cssvars writeHtml (chunkedChunks chunkedDoc) -- contents.opf -- set page progression direction attribution let progressionDirection :: [(Text, Text)] progressionDirection = case epubPageDirection metadata of Just LTR | epub3 -> [("page-progression-direction", "ltr")] Just RTL | epub3 -> [("page-progression-direction", "rtl")] _ -> [] -- incredibly inefficient (TODO): let containsMathML ent = epub3 && " [] xs -> [("properties", T.unwords xs)]) $ () let chapterRefNode ent = unode "itemref" ! [("idref", toId $ makeRelative epubSubdir $ eRelativePath ent)] $ () let pictureNode ent = unode "item" ! [("id", toId $ makeRelative epubSubdir $ eRelativePath ent), ("href", T.pack $ makeRelative epubSubdir $ eRelativePath ent), ("media-type", fromMaybe "application/octet-stream" $ mediaTypeOf $ eRelativePath ent)] $ () let fontNode ent = unode "item" ! [("id", toId $ makeRelative epubSubdir $ eRelativePath ent), ("href", T.pack $ makeRelative epubSubdir $ eRelativePath ent), ("media-type", fromMaybe "" $ getMimeType $ eRelativePath ent)] $ () -- The tocTitle is either the normal title or a specially configured title. let tocTitle = maybe plainTitle metaValueToString $ lookupMeta "toc-title" meta currentTime <- lift P.getTimestamp -- Construct the contentsData let contentsData = UTF8.fromTextLazy $ TL.fromStrict $ ppTopElement $ unode "package" ! ([("version", case version of EPUB2 -> "2.0" EPUB3 -> "3.0") ,("xmlns","http://www.idpf.org/2007/opf")] ++ [("xml:lang", epubLanguage metadata) | version == EPUB3] ++ [("unique-identifier","epub-id-1")] ++ [("prefix","ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/") | version == EPUB3]) $ [ metadataElement version metadata mbCoverImageName currentTime , unode "manifest" $ [ unode "item" ! [("id","ncx"), ("href","toc.ncx") ,("media-type","application/x-dtbncx+xml")] $ () , unode "item" ! ([("id","nav") ,("href","nav.xhtml") ,("media-type","application/xhtml+xml")] ++ [("properties","nav") | epub3 ]) $ () ] ++ [ unode "item" ! [("id","stylesheet" <> tshow n) , ("href", T.pack fp) ,("media-type","text/css")] $ () | (n :: Int, fp) <- zip [1..] (map (makeRelative epubSubdir . eRelativePath) stylesheetEntries) ] ++ map chapterNode (cpgEntry ++ [tpEntry | writerEpubTitlePage opts] ++ chapterEntries) ++ (case cpicEntry of [] -> [] (x:_) -> [add_attrs [Attr (unqual "properties") "cover-image" | epub3] (pictureNode x)]) ++ map pictureNode picEntries ++ map fontNode fontEntries , unode "spine" ! ( ("toc","ncx") : progressionDirection) $ case epubCoverImage metadata of Nothing -> [] Just _ -> [ unode "itemref" ! [("idref", "cover_xhtml")] $ () ] ++ ([unode "itemref" ! [("idref", "title_page_xhtml") ,("linear", case lookupMeta "title" meta of Just _ -> "yes" Nothing -> "no")] $ () | writerEpubTitlePage opts] ++ [unode "itemref" ! [("idref", "nav")] $ () | writerTableOfContents opts ] ++ map chapterRefNode chapterEntries) , unode "guide" $ (unode "reference" ! [("type","toc"),("title", tocTitle), ("href","nav.xhtml")] $ () ) : [ unode "reference" ! [("type","cover") ,("title","Cover") ,("href","text/cover.xhtml")] $ () | isJust (epubCoverImage metadata) ] ] -- Content should be stored in content.opf contentsEntry <- mkEntry "content.opf" contentsData -- toc.ncx -- Create the tocEntry from the metadata together with the sections and title. tocEntry <- createTocEntry opts' meta metadata plainTitle (chunkedTOC chunkedDoc) -- Create the navEntry using the metadata, all of the various writer options, -- the CSS and HTML helpers, the document and toc title as well as the epub version and all of the sections navEntry <- createNavEntry opts' meta metadata vars cssvars writeHtml tocTitle version (chunkedTOC chunkedDoc) -- mimetype mimetypeEntry <- mkEntry "mimetype" $ UTF8.fromStringLazy "application/epub+zip" -- container.xml let containerData = B.fromStrict $ UTF8.fromText $ ppTopElement $ unode "container" ! [("version","1.0") ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $ unode "rootfiles" $ unode "rootfile" ! [("full-path", (if null epubSubdir then "" else T.pack epubSubdir <> "/") <> "content.opf") ,("media-type","application/oebps-package+xml")] $ () containerEntry <- mkEntry "META-INF/container.xml" containerData -- com.apple.ibooks.display-options.xml let apple = B.fromStrict $ UTF8.fromText $ ppTopElement $ unode "display_options" $ unode "platform" ! [("name","*")] $ unode "option" ! [("name","specified-fonts")] $ ("true" :: Text) appleEntry <- mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple -- construct archive let archive = foldr addEntryToArchive emptyArchive $ [mimetypeEntry, containerEntry, appleEntry, contentsEntry, tocEntry, navEntry] ++ [tpEntry | writerEpubTitlePage opts] ++ stylesheetEntries ++ picEntries ++ cpicEntry ++ cpgEntry ++ chapterEntries ++ fontEntries return $ fromArchive archive -- | Function used during conversion from pandoc to EPUB to create the cover page. -- The first Entry list is for the cover while the second one is for the cover image. -- If no cover images are specified, empty lists will be returned. -- The third value of the returned tuple is the cover image name. createCoverPage :: PandocMonad m => Meta -> EPUBMetadata -> WriterOptions -> Context Text -> (Bool -> Context Text) -> (WriterOptions -> Pandoc -> m B8.ByteString) -> Text -> StateT EPUBState m ([Entry], [Entry], Maybe FilePath) createCoverPage meta metadata opts' vars cssvars writeHtml plainTitle = case epubCoverImage metadata of Nothing -> return ([],[], Nothing) Just img -> do let fp = takeFileName img -- retrieve cover image file coverImageName <- getMediaNextNewName (takeExtension fp) -- see #4206 -- image dimensions imgContent <- lift $ P.readFileLazy img (coverImageWidth, coverImageHeight) <- case imageSize opts' (B.toStrict imgContent) of Right sz -> return $ sizeInPixels sz Left err' -> (0, 0) <$ report (CouldNotDetermineImageSize (T.pack img) err') -- write the HTML. Use the cssvars, vars and additional writer options. cpContent <- lift $ writeHtml opts'{ writerVariables = Context (M.fromList [ ("coverpage", toVal' "true"), ("pagetitle", toVal $ escapeStringForXML plainTitle), ("cover-image", toVal' $ T.pack coverImageName), ("cover-image-width", toVal' $ tshow coverImageWidth), ("cover-image-height", toVal' $ tshow coverImageHeight)]) <> cssvars True <> vars } (Pandoc meta []) coverEntry <- mkEntry "text/cover.xhtml" cpContent coverImageEntry <- mkEntry ("media/" ++ coverImageName) imgContent return ( [ coverEntry ], [ coverImageEntry ], Just coverImageName ) -- | Converts the given chapters to entries using the writeHtml function -- and the various provided options createChapterEntries :: PandocMonad m => WriterOptions -> Context Text -> (Bool -> Context Text) -> (WriterOptions -> Pandoc -> StateT EPUBState m B8.ByteString) -> [Chunk] -> StateT EPUBState m [Entry] createChapterEntries opts' vars cssvars writeHtml chapters = do -- Create an entry from the chapter with the provided number. -- chapToEntry :: Int -> Chapter -> StateT EPUBState m Entry let chapToEntry num chunk = mkEntry ("text/" ++ chunkPath chunk) =<< -- Combine all provided options writeHtml opts'{ writerVariables = Context (M.fromList [("body-type", toVal' bodyType), ("pagetitle", toVal' $ showChapter num)]) <> cssvars True <> vars } pdoc where bs = chunkContents chunk meta' = setMeta "title" (fromList (walk removeNote (chunkHeading chunk))) nullMeta (pdoc, bodyType) = case bs of (Div (_,"section":_,kvs) _ : _) -> -- remove notes or we get doubled footnotes (Pandoc meta' bs, -- Check if the chapters belongs to the frontmatter, -- backmatter of bodymatter defaulting to the body case lookup "epub:type" kvs of Nothing -> "bodymatter" Just x | x `elem` frontMatterTypes -> "frontmatter" | x `elem` backMatterTypes -> "backmatter" | otherwise -> "bodymatter") _ -> (Pandoc meta' bs, "bodymatter") frontMatterTypes = ["prologue", "abstract", "acknowledgments", "copyright-page", "dedication", "credits", "keywords", "imprint", "contributors", "other-credits", "errata", "revision-history", "titlepage", "halftitlepage", "seriespage", "foreword", "preface", "frontispiece", "seriespage", "titlepage"] backMatterTypes = ["appendix", "colophon", "bibliography", "index"] zipWithM chapToEntry [1..] chapters createTocEntry :: PandocMonad m => WriterOptions -> Meta -> EPUBMetadata -> Text -> Tree SecInfo -> StateT EPUBState m Entry createTocEntry opts meta metadata plainTitle (Node _ secs) = do let mkNavPoint :: Tree SecInfo -> State Int (Maybe Element) mkNavPoint (Node secinfo subsecs) | secLevel secinfo > writerTOCDepth opts = return Nothing | otherwise = do n <- get modify (+ 1) subs <- catMaybes <$> mapM mkNavPoint subsecs let secnum' = case secNumber secinfo of Just t -> t <> " " Nothing -> "" let title' = secnum' <> stringify (secTitle secinfo) return $ Just $ unode "navPoint" ! [("id", "navPoint-" <> tshow n)] $ [ unode "navLabel" $ unode "text" title' , unode "content" ! [("src", "text/" <> secPath secinfo)] $ () ] ++ subs let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $ [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta) , unode "content" ! [("src", "text/title_page.xhtml")] $ () ] let navMap = evalState (catMaybes <$> mapM mkNavPoint secs) 1 uuid <- case epubIdentifier metadata of (x:_) -> return $ identifierText x -- use first identifier as UUID [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen let tocData = B.fromStrict $ UTF8.fromText $ ppTopElement $ unode "ncx" ! [("version","2005-1") ,("xmlns","http://www.daisy.org/z3986/2005/ncx/")] $ [ unode "head" $ [ unode "meta" ! [("name","dtb:uid") ,("content", uuid)] $ () , unode "meta" ! [("name","dtb:depth") ,("content", "1")] $ () , unode "meta" ! [("name","dtb:totalPageCount") ,("content", "0")] $ () , unode "meta" ! [("name","dtb:maxPageNumber") ,("content", "0")] $ () ] ++ case epubCoverImage metadata of Nothing -> [] Just img -> [unode "meta" ! [("name","cover"), ("content", toId img)] $ ()] , unode "docTitle" $ unode "text" plainTitle , unode "navMap" $ [tpNode | writerEpubTitlePage opts] ++ navMap ] mkEntry "toc.ncx" tocData createNavEntry :: PandocMonad m => WriterOptions -> Meta -> EPUBMetadata -> Context Text -> (Bool -> Context Text) -> (WriterOptions -> Pandoc -> m B8.ByteString) -> Text -> EPUBVersion -> Tree SecInfo -> StateT EPUBState m Entry createNavEntry opts meta metadata vars cssvars writeHtml tocTitle version (Node _ secs) = do let mkItem :: Tree SecInfo -> State Int (Maybe Element) mkItem (Node secinfo subsecs) | secLevel secinfo > writerTOCDepth opts = return Nothing | otherwise = do n <- get modify (+ 1) subs <- catMaybes <$> mapM mkItem subsecs let secnum' = case secNumber secinfo of Just num -> [Span ("", ["section-header-number"], []) [Str num] , Str "\160"] Nothing -> [] let title' = secnum' <> secTitle secinfo -- can't have elements inside generated links... let clean (Link _ ils _) = Span ("", [], []) ils clean (Note _) = Str "" clean x = x let titRendered = case P.runPure (writeHtmlStringForEPUB version opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain $ walk clean title'])) of Left _ -> stringify title' Right x -> x let titElements = either (const []) id $ parseXMLContents (TL.fromStrict titRendered) return $ Just $ unode "li" ! [("id", "toc-li-" <> tshow n)] $ (unode "a" ! [("href", "text/" <> secPath secinfo)] $ titElements) : case subs of [] -> [] (_:_) -> [unode "ol" ! [("class","toc")] $ subs] let navtag = if version == EPUB3 then "nav" else "div" let tocBlocks = evalState (catMaybes <$> mapM mkItem secs) 1 let navBlocks = [RawBlock (Format "html") $ showElement $ -- prettyprinting introduces bad spaces unode navtag ! ([("epub:type","toc") | version == EPUB3] ++ [("role","doc-toc") | version == EPUB3] ++ [("id","toc")]) $ [ unode "h1" ! [("id","toc-title")] $ tocTitle , unode "ol" ! [("class","toc")] $ tocBlocks ]] let landmarkItems = if version == EPUB3 then [ unode "li" [ unode "a" ! [("href", "text/title_page.xhtml") ,("epub:type", "titlepage")] $ ("Title Page" :: Text) ] | writerEpubTitlePage opts ] ++ [ unode "li" [ unode "a" ! [("href", "text/cover.xhtml") ,("epub:type", "cover")] $ ("Cover" :: Text)] | isJust (epubCoverImage metadata) ] ++ [ unode "li" [ unode "a" ! [("href", "#toc") ,("epub:type", "toc")] $ ("Table of Contents" :: Text) ] | writerTableOfContents opts ] else [] let landmarks = [RawBlock (Format "html") $ ppElement $ unode "nav" ! [("epub:type","landmarks") ,("id","landmarks") ,("hidden","hidden")] $ [ unode "ol" landmarkItems ] | not (null landmarkItems)] navData <- lift $ writeHtml opts{ writerVariables = Context (M.fromList [("navpage", toVal' "true") ,("body-type", toVal' "frontmatter") ]) <> cssvars False <> vars } (Pandoc (setMeta "title" (walk removeNote $ fromList $ docTitle' meta) nullMeta) (navBlocks ++ landmarks)) -- Return mkEntry "nav.xhtml" navData metadataElement :: EPUBVersion -> EPUBMetadata -> Maybe FilePath -> UTCTime -> Element metadataElement version md mbCoverImage currentTime = unode "metadata" ! [("xmlns:dc","http://purl.org/dc/elements/1.1/") ,("xmlns:opf","http://www.idpf.org/2007/opf")] $ mdNodes where mdNodes = identifierNodes ++ titleNodes ++ dateNodes ++ languageNodes ++ ibooksNodes ++ calibreNodes ++ creatorNodes ++ contributorNodes ++ subjectNodes ++ descriptionNodes ++ typeNodes ++ formatNodes ++ publisherNodes ++ sourceNodes ++ relationNodes ++ coverageNodes ++ rightsNodes ++ coverImageNodes ++ modifiedNodes ++ belongsToCollectionNodes ++ case version of EPUB2 -> [] EPUB3 -> accessModeNodes ++ accessModeSufficientNodes ++ accessibilityFeatureNodes ++ accessibilityHazardNodes ++ accessibilitySummaryNodes metaprop = if version == EPUB2 then "name" else "property" withIds base f = concat . zipWith f (map (\x -> base <> T.cons '-' (tshow x)) ([1..] :: [Int])) identifierNodes = withIds "epub-id" toIdentifierNode $ epubIdentifier md titleNodes = withIds "epub-title" toTitleNode $ epubTitle md dateNodes = if version == EPUB2 then withIds "epub-date" toDateNode $ epubDate md else -- epub3 allows only one dc:date -- http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-opf-dcdate case epubDate md of [] -> [] (x:_) -> [dcNode "date" ! [("id","epub-date")] $ dateText x] ibooksNodes = map ibooksNode (epubIbooksFields md) ibooksNode (k, v) = unode "meta" ! [(metaprop, "ibooks:" <> k)] $ v calibreNodes = map calibreNode (epubCalibreFields md) calibreNode (k, v) = unode "meta" ! [(metaprop, "calibre:" <> k), ("content", v)] $ () languageNodes = [dcTag "language" $ epubLanguage md] creatorNodes = withIds "epub-creator" (toCreatorNode "creator") $ epubCreator md contributorNodes = withIds "epub-contributor" (toCreatorNode "contributor") $ epubContributor md subjectNodes = withIds "subject" toSubjectNode $ epubSubject md descriptionNodes = maybe [] (dcTag' "description") $ epubDescription md typeNodes = maybe [] (dcTag' "type") $ epubType md formatNodes = maybe [] (dcTag' "format") $ epubFormat md publisherNodes = maybe [] (dcTag' "publisher") $ epubPublisher md sourceNodes = maybe [] (dcTag' "source") $ epubSource md relationNodes = maybe [] (dcTag' "relation") $ epubRelation md coverageNodes = maybe [] (dcTag' "coverage") $ epubCoverage md rightsNodes = maybe [] (dcTag' "rights") $ epubRights md coverImageNodes = maybe [] (\img -> [unode "meta" ! [("name","cover"), ("content",toId img)] $ ()]) mbCoverImage modifiedNodes = [ unode "meta" ! [(metaprop, "dcterms:modified")] $ showDateTimeISO8601 currentTime | version == EPUB3 ] belongsToCollectionNodes = maybe [] (\belongsToCollection -> (unode "meta" ! [(metaprop, "belongs-to-collection"), ("id", "epub-collection-1")] $ belongsToCollection ) : [unode "meta" ! [("refines", "#epub-collection-1"), (metaprop, "collection-type")] $ ("series" :: Text) ]) (epubBelongsToCollection md)++ maybe [] (\groupPosition -> [unode "meta" ! [("refines", "#epub-collection-1"), (metaprop, "group-position")] $ groupPosition ]) (epubGroupPosition md) schemanode k v = unode "meta" ! [(metaprop, "schema:" <> k)] $ v accessModeNodes = map (schemanode "accessMode") (epubAccessModes md) accessModeSufficientNodes = map (schemanode "accessModeSufficient") (epubAccessModeSufficient md) accessibilityFeatureNodes = map (schemanode "accessibilityFeature") (epubAccessibilityFeatures md) accessibilityHazardNodes = map (schemanode "accessibilityHazard") (epubAccessibilityHazards md) accessibilitySummaryNodes = maybe [] (\summary -> [schemanode "accessibilitySummary" summary]) $ epubAccessibilitySummary md dcTag n s = unode ("dc:" <> n) s dcTag' n s = [dcTag n s] toIdentifierNode id' (Identifier txt scheme) | version == EPUB2 = [dcNode "identifier" ! (("id",id') : maybe [] (\x -> [("opf:scheme", x)]) scheme) $ txt] | otherwise = (dcNode "identifier" ! [("id",id')] $ txt) : maybe [] ((\x -> [unode "meta" ! [ ("refines","#" <> id') , (metaprop,"identifier-type") , ("scheme","onix:codelist5") ] $ x ]) . schemeToOnix) scheme toCreatorNode s id' creator | version == EPUB2 = [dcNode s ! (("id",id') : maybe [] (\x -> [("opf:file-as",x)]) (creatorFileAs creator) ++ maybe [] (\x -> [("opf:role",x)]) (creatorRole creator >>= toRelator)) $ creatorText creator] | otherwise = [dcNode s ! [("id",id')] $ creatorText creator] ++ maybe [] (\x -> [unode "meta" ! [("refines","#" <> id'),(metaprop,"file-as")] $ x]) (creatorFileAs creator) ++ maybe [] (\x -> [unode "meta" ! [("refines","#" <> id'),(metaprop,"role"), ("scheme","marc:relators")] $ x]) (creatorRole creator >>= toRelator) toTitleNode id' title | version == EPUB2 = [dcNode "title" ! (("id",id') : -- note: EPUB2 doesn't accept opf:title-type maybe [] (\x -> [("opf:file-as",x)]) (titleFileAs title)) $ titleText title] | otherwise = [dcNode "title" ! [("id",id')] $ titleText title] ++ maybe [] (\x -> [unode "meta" ! [("refines","#" <> id'),(metaprop,"file-as")] $ x]) (titleFileAs title) ++ maybe [] (\x -> [unode "meta" ! [("refines","#" <> id'),(metaprop,"title-type")] $ x]) (titleType title) toDateNode id' date = [dcNode "date" ! (("id",id') : maybe [] (\x -> [("opf:event",x)]) (dateEvent date)) $ dateText date] toSubjectNode id' subject | version == EPUB2 = [dcNode "subject" ! [("id",id')] $ subjectText subject] | otherwise = (dcNode "subject" ! [("id",id')] $ subjectText subject) : maybe [] (\x -> (unode "meta" ! [("refines", "#" <> id'),(metaprop,"authority")] $ x) : maybe [] (\y -> [unode "meta" ! [("refines", "#" <> id'),(metaprop,"term")] $ y]) (subjectTerm subject)) (subjectAuthority subject) schemeToOnix :: Text -> Text schemeToOnix "ISBN-10" = "02" schemeToOnix "GTIN-13" = "03" schemeToOnix "UPC" = "04" schemeToOnix "ISMN-10" = "05" schemeToOnix "DOI" = "06" schemeToOnix "LCCN" = "13" schemeToOnix "GTIN-14" = "14" schemeToOnix "ISBN-13" = "15" schemeToOnix "Legal deposit number" = "17" schemeToOnix "URN" = "22" schemeToOnix "OCLC number" = "23" schemeToOnix "Co-publisher’s ISBN-13" = "24" schemeToOnix "ISMN-13" = "25" schemeToOnix "ISBN-A" = "26" schemeToOnix "JP e-code" = "27" schemeToOnix "OLCC number" = "28" schemeToOnix "JP Magazine ID" = "29" schemeToOnix "UPC-12+5" = "30" schemeToOnix "BNF Control number" = "31" schemeToOnix "ISSN-13" = "34" schemeToOnix "ARK" = "35" schemeToOnix "Digital file internal version number" = "36" schemeToOnix _ = "01" showDateTimeISO8601 :: UTCTime -> Text showDateTimeISO8601 = T.pack . formatTime defaultTimeLocale "%FT%TZ" transformTag :: PandocMonad m => Tag T.Text -> E m (Tag T.Text) transformTag tag@(TagOpen name attr) | name `elem` ["video", "source", "img", "audio"] && isNothing (lookup "data-external" attr) = do let src = fromAttrib "src" tag let poster = fromAttrib "poster" tag newsrc <- modifyMediaRef $ T.unpack src newposter <- modifyMediaRef $ T.unpack poster let attr' = filter (\(x,_) -> x /= "src" && x /= "poster") attr ++ [("src", "../" <> newsrc) | not (T.null newsrc)] ++ [("poster", "../" <> newposter) | not (T.null newposter)] return $ TagOpen name attr' transformTag tag = return tag modifyMediaRef :: PandocMonad m => FilePath -> E m T.Text modifyMediaRef "" = return "" modifyMediaRef oldsrc = do media <- gets stMediaPaths case lookup oldsrc media of Just (n,_) -> return $ T.pack n Nothing -> catchError (do (img, mbMime) <- P.fetchItem $ T.pack oldsrc let ext = maybe (takeExtension (takeWhile (/='?') oldsrc)) (T.unpack . ("." <>)) (mbMime >>= extensionFromMimeType) newName <- getMediaNextNewName ext let newPath = "media/" ++ newName entry <- mkEntry newPath (B.fromChunks . (:[]) $ img) modify $ \st -> st{ stMediaPaths = (oldsrc, (newPath, Just entry)):media} return $ T.pack newPath) (\e -> do report $ CouldNotFetchResource (T.pack oldsrc) (tshow e) return $ T.pack oldsrc) getMediaNextNewName :: PandocMonad m => FilePath -> E m FilePath getMediaNextNewName ext = do nextId <- gets stMediaNextId modify $ \st -> st { stMediaNextId = nextId + 1 } return $ "file" ++ show nextId ++ ext isHtmlFormat :: Format -> Bool isHtmlFormat (Format "html") = True isHtmlFormat (Format "html4") = True isHtmlFormat (Format "html5") = True isHtmlFormat _ = False transformBlock :: PandocMonad m => Block -> E m Block transformBlock (RawBlock fmt raw) | isHtmlFormat fmt = do let tags = parseTags raw tags' <- mapM transformTag tags return $ RawBlock fmt (renderTags' tags') transformBlock b = return b transformInline :: PandocMonad m => WriterOptions -> Inline -> E m Inline transformInline _opts (Image attr@(_,_,kvs) lab (src,tit)) | isNothing (lookup "external" kvs) = do newsrc <- modifyMediaRef $ T.unpack src return $ Image attr lab ("../" <> newsrc, tit) transformInline opts x@(Math t m) | WebTeX url <- writerHTMLMathMethod opts = do newsrc <- modifyMediaRef (T.unpack (url <> urlEncode m)) let mathclass = if t == DisplayMath then "display" else "inline" return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] ("../" <> newsrc, "")] transformInline _opts (RawInline fmt raw) | isHtmlFormat fmt = do let tags = parseTags raw tags' <- mapM transformTag tags return $ RawInline fmt (renderTags' tags') transformInline _ x = return x (!) :: (t -> Element) -> [(Text, Text)] -> t -> Element (!) f attrs n = add_attrs (map (\(k,v) -> Attr (unqual k) v) attrs) (f n) mediaTypeOf :: FilePath -> Maybe MimeType mediaTypeOf x = let mediaPrefixes = ["image", "video", "audio"] in case getMimeType x of Just y | any (`T.isPrefixOf` y) mediaPrefixes -> Just y _ -> Nothing -- Returns filename for chapter number. showChapter :: Int -> Text showChapter = T.pack . printf "ch%03d.xhtml" -- Add identifiers to any headers without them. addIdentifiers :: WriterOptions -> [Block] -> [Block] addIdentifiers opts bs = evalState (mapM go bs) Set.empty where go (Header n (ident,classes,kvs) ils) = do ids <- get let ident' = if T.null ident then uniqueIdent (writerExtensions opts) ils ids else ident modify $ Set.insert ident' return $ Header n (ident',classes,kvs) ils go x = return x -- Variant of normalizeDate that allows partial dates: YYYY, YYYY-MM normalizeDate' :: Text -> Maybe Text normalizeDate' = go . T.strip where go xs | T.length xs == 4 -- YYY , T.all isDigit xs = Just xs | (y, s) <- T.splitAt 4 xs -- YYY-MM , Just ('-', m) <- T.uncons s , T.length m == 2 , T.all isDigit y && T.all isDigit m = Just xs | otherwise = normalizeDate xs toRelator :: Text -> Maybe Text toRelator x | x `elem` relators = Just x | otherwise = lookup (T.toLower x) relatorMap relators :: [Text] relators = map snd relatorMap relatorMap :: [(Text, Text)] relatorMap = [("abridger", "abr") ,("actor", "act") ,("adapter", "adp") ,("addressee", "rcp") ,("analyst", "anl") ,("animator", "anm") ,("annotator", "ann") ,("appellant", "apl") ,("appellee", "ape") ,("applicant", "app") ,("architect", "arc") ,("arranger", "arr") ,("art copyist", "acp") ,("art director", "adi") ,("artist", "art") ,("artistic director", "ard") ,("assignee", "asg") ,("associated name", "asn") ,("attributed name", "att") ,("auctioneer", "auc") ,("author", "aut") ,("author in quotations or text abstracts", "aqt") ,("author of afterword, colophon, etc.", "aft") ,("author of dialog", "aud") ,("author of introduction, etc.", "aui") ,("autographer", "ato") ,("bibliographic antecedent", "ant") ,("binder", "bnd") ,("binding designer", "bdd") ,("blurb writer", "blw") ,("book designer", "bkd") ,("book producer", "bkp") ,("bookjacket designer", "bjd") ,("bookplate designer", "bpd") ,("bookseller", "bsl") ,("braille embosser", "brl") ,("broadcaster", "brd") ,("calligrapher", "cll") ,("cartographer", "ctg") ,("caster", "cas") ,("censor", "cns") ,("choreographer", "chr") ,("cinematographer", "cng") ,("client", "cli") ,("collection registrar", "cor") ,("collector", "col") ,("collotyper", "clt") ,("colorist", "clr") ,("commentator", "cmm") ,("commentator for written text", "cwt") ,("compiler", "com") ,("complainant", "cpl") ,("complainant-appellant", "cpt") ,("complainant-appellee", "cpe") ,("composer", "cmp") ,("compositor", "cmt") ,("conceptor", "ccp") ,("conductor", "cnd") ,("conservator", "con") ,("consultant", "csl") ,("consultant to a project", "csp") ,("contestant", "cos") ,("contestant-appellant", "cot") ,("contestant-appellee", "coe") ,("contestee", "cts") ,("contestee-appellant", "ctt") ,("contestee-appellee", "cte") ,("contractor", "ctr") ,("contributor", "ctb") ,("copyright claimant", "cpc") ,("copyright holder", "cph") ,("corrector", "crr") ,("correspondent", "crp") ,("costume designer", "cst") ,("court governed", "cou") ,("court reporter", "crt") ,("cover designer", "cov") ,("creator", "cre") ,("curator", "cur") ,("dancer", "dnc") ,("data contributor", "dtc") ,("data manager", "dtm") ,("dedicatee", "dte") ,("dedicator", "dto") ,("defendant", "dfd") ,("defendant-appellant", "dft") ,("defendant-appellee", "dfe") ,("degree granting institution", "dgg") ,("delineator", "dln") ,("depicted", "dpc") ,("depositor", "dpt") ,("designer", "dsr") ,("director", "drt") ,("dissertant", "dis") ,("distribution place", "dbp") ,("distributor", "dst") ,("donor", "dnr") ,("draftsman", "drm") ,("dubious author", "dub") ,("editor", "edt") ,("editor of compilation", "edc") ,("editor of moving image work", "edm") ,("electrician", "elg") ,("electrotyper", "elt") ,("enacting jurisdiction", "enj") ,("engineer", "eng") ,("engraver", "egr") ,("etcher", "etr") ,("event place", "evp") ,("expert", "exp") ,("facsimilist", "fac") ,("field director", "fld") ,("film director", "fmd") ,("film distributor", "fds") ,("film editor", "flm") ,("film producer", "fmp") ,("filmmaker", "fmk") ,("first party", "fpy") ,("forger", "frg") ,("former owner", "fmo") ,("funder", "fnd") ,("geographic information specialist", "gis") ,("honoree", "hnr") ,("host", "hst") ,("host institution", "his") ,("illuminator", "ilu") ,("illustrator", "ill") ,("inscriber", "ins") ,("instrumentalist", "itr") ,("interviewee", "ive") ,("interviewer", "ivr") ,("inventor", "inv") ,("issuing body", "isb") ,("judge", "jud") ,("jurisdiction governed", "jug") ,("laboratory", "lbr") ,("laboratory director", "ldr") ,("landscape architect", "lsa") ,("lead", "led") ,("lender", "len") ,("libelant", "lil") ,("libelant-appellant", "lit") ,("libelant-appellee", "lie") ,("libelee", "lel") ,("libelee-appellant", "let") ,("libelee-appellee", "lee") ,("librettist", "lbt") ,("licensee", "lse") ,("licensor", "lso") ,("lighting designer", "lgd") ,("lithographer", "ltg") ,("lyricist", "lyr") ,("manufacture place", "mfp") ,("manufacturer", "mfr") ,("marbler", "mrb") ,("markup editor", "mrk") ,("metadata contact", "mdc") ,("metal-engraver", "mte") ,("moderator", "mod") ,("monitor", "mon") ,("music copyist", "mcp") ,("musical director", "msd") ,("musician", "mus") ,("narrator", "nrt") ,("onscreen presenter", "osp") ,("opponent", "opn") ,("organizer of meeting", "orm") ,("originator", "org") ,("other", "oth") ,("owner", "own") ,("panelist", "pan") ,("papermaker", "ppm") ,("patent applicant", "pta") ,("patent holder", "pth") ,("patron", "pat") ,("performer", "prf") ,("permitting agency", "pma") ,("photographer", "pht") ,("plaintiff", "ptf") ,("plaintiff-appellant", "ptt") ,("plaintiff-appellee", "pte") ,("platemaker", "plt") ,("praeses", "pra") ,("presenter", "pre") ,("printer", "prt") ,("printer of plates", "pop") ,("printmaker", "prm") ,("process contact", "prc") ,("producer", "pro") ,("production company", "prn") ,("production designer", "prs") ,("production manager", "pmn") ,("production personnel", "prd") ,("production place", "prp") ,("programmer", "prg") ,("project director", "pdr") ,("proofreader", "pfr") ,("provider", "prv") ,("publication place", "pup") ,("publisher", "pbl") ,("publishing director", "pbd") ,("puppeteer", "ppt") ,("radio director", "rdd") ,("radio producer", "rpc") ,("recording engineer", "rce") ,("recordist", "rcd") ,("redaktor", "red") ,("renderer", "ren") ,("reporter", "rpt") ,("repository", "rps") ,("research team head", "rth") ,("research team member", "rtm") ,("researcher", "res") ,("respondent", "rsp") ,("respondent-appellant", "rst") ,("respondent-appellee", "rse") ,("responsible party", "rpy") ,("restager", "rsg") ,("restorationist", "rsr") ,("reviewer", "rev") ,("rubricator", "rbr") ,("scenarist", "sce") ,("scientific advisor", "sad") ,("screenwriter", "aus") ,("scribe", "scr") ,("sculptor", "scl") ,("second party", "spy") ,("secretary", "sec") ,("seller", "sll") ,("set designer", "std") ,("setting", "stg") ,("signer", "sgn") ,("singer", "sng") ,("sound designer", "sds") ,("speaker", "spk") ,("sponsor", "spn") ,("stage director", "sgd") ,("stage manager", "stm") ,("standards body", "stn") ,("stereotyper", "str") ,("storyteller", "stl") ,("supporting host", "sht") ,("surveyor", "srv") ,("teacher", "tch") ,("technical director", "tcd") ,("television director", "tld") ,("television producer", "tlp") ,("thesis advisor", "ths") ,("transcriber", "trc") ,("translator", "trl") ,("type designer", "tyd") ,("typographer", "tyg") ,("university place", "uvp") ,("videographer", "vdg") ,("witness", "wit") ,("wood engraver", "wde") ,("woodcutter", "wdc") ,("writer of accompanying material", "wam") ,("writer of added commentary", "wac") ,("writer of added lyrics", "wal") ,("writer of added text", "wat") ] docTitle' :: Meta -> [Inline] docTitle' meta = maybe [] go $ lookupMeta "title" meta where go (MetaString s) = [Str s] go (MetaInlines xs) = xs go (MetaBlocks [Para xs]) = xs go (MetaBlocks [Plain xs]) = xs go (MetaMap m) = case M.lookup "type" m of Just x | stringify x == "main" -> maybe [] go $ M.lookup "text" m _ -> [] go (MetaList xs) = concatMap go xs go _ = [] ================================================ FILE: src/Text/Pandoc/Writers/FB2.hs ================================================ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.FB2 Copyright : Copyright (C) 2011-2012 Sergey Astanin 2012-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' documents to FB2 (FictionBook2) format. FictionBook is an XML-based e-book format. For more information see: -} module Text.Pandoc.Writers.FB2 (writeFB2) where import Control.Monad (zipWithM, liftM) import Control.Monad.Except (catchError, throwError) import Control.Monad.State.Strict (StateT, evalStateT, get, gets, lift, modify) import Data.ByteString.Base64 (encode) import Data.Char (isAscii, isControl, isSpace) import Data.Either (lefts, rights) import Data.List (intercalate) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Encoding as TE import Text.Pandoc.URI (urlEncode, isURI) import Text.Pandoc.XML.Light as X import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Logging import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def) import Text.Pandoc.Shared (blocksToInlines, capitalize, orderedListMarkers, makeSections, tshow, stringify) import Text.Pandoc.Walk (walk) import Text.Pandoc.Writers.Shared (lookupMetaString, toLegacyTable, ensureValidXmlIdentifiers) import Data.Generics (everywhere, mkT) -- | Data to be written at the end of the document: -- (foot)notes, URLs, references, images. data FbRenderState = FbRenderState { footnotes :: [ (Int, Text, [Content]) ] -- ^ #, ID, text , imagesToFetch :: [ (Text, Text) ] -- ^ filename, URL or path , parentListMarker :: Text -- ^ list marker of the parent ordered list , writerOptions :: WriterOptions } deriving (Show) -- | FictionBook building monad. type FBM m = StateT FbRenderState m newFB :: FbRenderState newFB = FbRenderState { footnotes = [], imagesToFetch = [] , parentListMarker = "" , writerOptions = def } data ImageMode = NormalImage | InlineImage deriving (Eq) instance Show ImageMode where show NormalImage = "imageType" show InlineImage = "inlineImageType" -- | Produce an FB2 document from a 'Pandoc' document. writeFB2 :: PandocMonad m => WriterOptions -- ^ conversion options -> Pandoc -- ^ document to convert -> m Text -- ^ FictionBook2 document (not encoded yet) writeFB2 opts doc = flip evalStateT newFB $ pandocToFB2 opts doc pandocToFB2 :: PandocMonad m => WriterOptions -> Pandoc -> FBM m Text pandocToFB2 opts doc = do let Pandoc meta blocks = ensureValidXmlIdentifiers doc modify (\s -> s { writerOptions = opts }) desc <- description meta title <- cMapM toXml . docTitle $ meta secs <- renderSections 1 blocks let body = el "body" $ el "title" (el "p" title) : secs notes <- renderFootnotes (imgs,missing) <- get >>= (lift . fetchImages . imagesToFetch) let body' = replaceImagesWithAlt missing body let fb2_xml = el "FictionBook" (fb2_attrs, [desc, body'] ++ notes ++ imgs) return $ xml_head <> showContent fb2_xml <> "\n" where xml_head = "\n" fb2_attrs = let xmlns = "http://www.gribuser.ru/xml/fictionbook/2.0" xlink = "http://www.w3.org/1999/xlink" in [ uattr "xmlns" xmlns , attr ("xmlns", "l") xlink ] description :: PandocMonad m => Meta -> FBM m Content description meta' = do let genre = case lookupMetaString "genre" meta' of "" -> el "genre" ("unrecognised" :: Text) s -> el "genre" s bt <- booktitle meta' let as = authors meta' dd <- docdate meta' annotation <- case lookupMeta "abstract" meta' of Just (MetaBlocks bs) -> list . el "annotation" <$> cMapM blockToXml (map unPlain bs) _ -> pure mempty let lang = case lookupMeta "lang" meta' of Just (MetaInlines [Str s]) -> [el "lang" $ iso639 s] Just (MetaString s) -> [el "lang" $ iso639 s] _ -> [] where iso639 = T.takeWhile (/= '-') -- Convert BCP 47 to ISO 639 let coverimage url = do let img = Image nullAttr mempty (url, "") im <- insertImage InlineImage img return [el "coverpage" im] coverpage <- case lookupMeta "cover-image" meta' of Just (MetaInlines ils) -> coverimage (stringify ils) Just (MetaString s) -> coverimage s _ -> return [] return $ el "description" [ el "title-info" (genre : (as ++ bt ++ annotation ++ dd ++ coverpage ++ lang)) , el "document-info" [el "program-used" ("pandoc" :: Text)] ] booktitle :: PandocMonad m => Meta -> FBM m [Content] booktitle meta' = do t <- cMapM toXml . docTitle $ meta' return $ [el "book-title" t | not (null t)] authors :: Meta -> [Content] authors meta' = cMap author (docAuthors meta') author :: [Inline] -> [Content] author ss = let ws = T.words $ mconcat $ map plain ss email = el "email" <$> take 1 (filter (T.any (=='@')) ws) ws' = filter (not . T.any (== '@')) ws names = case ws' of [nickname] -> [ el "nickname" nickname ] [fname, lname] -> [ el "first-name" fname , el "last-name" lname ] (fname:rest) -> [ el "first-name" fname , el "middle-name" (T.concat . init $ rest) , el "last-name" (last rest) ] [] -> [] in list $ el "author" (names ++ email) docdate :: PandocMonad m => Meta -> FBM m [Content] docdate meta' = do let ss = docDate meta' d <- cMapM toXml ss return $ [el "date" d | not (null d)] -- | Divide the stream of blocks into sections and convert to XML -- representation. renderSections :: PandocMonad m => Int -> [Block] -> FBM m [Content] renderSections level blocks = do let blocks' = makeSections False Nothing blocks let isSection (Div (_,"section":_,_) (Header{}:_)) = True isSection _ = False let (initialBlocks, secs) = break isSection blocks' let blocks'' = if null initialBlocks then blocks' else Div ("",["section"],[]) (Header 1 nullAttr mempty : initialBlocks) : secs cMapM (renderSection level) blocks'' renderSection :: PandocMonad m => Int -> Block -> FBM m [Content] renderSection lvl (Div (id',"section":_,_) (Header _ _ title : xs)) = do title' <- if null title then return [] else list . el "title" <$> formatTitle title content <- cMapM (renderSection (lvl + 1)) xs let sectionContent = if T.null id' then el "section" (title' ++ content) else el "section" ([uattr "id" id'], title' ++ content) return [sectionContent] renderSection lvl (Div _attr bs) = cMapM (renderSection lvl) bs renderSection _ b = blockToXml b -- | Only

    and are allowed within in FB2. formatTitle :: PandocMonad m => [Inline] -> FBM m [Content] formatTitle inlines = cMapM (blockToXml . Para) $ split (== LineBreak) inlines split :: (a -> Bool) -> [a] -> [[a]] split _ [] = [] split cond xs = let (b,a) = break cond xs in (b:split cond (drop 1 a)) isLineBreak :: Inline -> Bool isLineBreak LineBreak = True isLineBreak _ = False -- | Make another FictionBook body with footnotes. renderFootnotes :: PandocMonad m => FBM m [Content] renderFootnotes = do fns <- footnotes `liftM` get if null fns then return [] -- no footnotes else return . list $ el "body" ([uattr "name" "notes"], map renderFN (reverse fns)) where renderFN (n, idstr, cs) = let fn_texts = el "title" (el "p" (tshow n)) : cs in el "section" ([uattr "id" idstr], fn_texts) -- | Fetch images and encode them for the FictionBook XML. -- Return image data and a list of hrefs of the missing images. fetchImages :: PandocMonad m => [(Text,Text)] -> m ([Content],[Text]) fetchImages links = do imgs <- mapM (uncurry fetchImage) links return (rights imgs, lefts imgs) -- | Fetch image data from disk or from network and make a <binary> XML section. -- Return either (Left hrefOfMissingImage) or (Right xmlContent). fetchImage :: PandocMonad m => Text -> Text -> m (Either Text Content) fetchImage href link = do mbimg <- case (isURI link, readDataURI link) of (True, Just (mime,_,True,base64)) -> let mime' = T.toLower mime in if mime' == "image/png" || mime' == "image/jpeg" then return (Just (mime',base64)) else return Nothing (True, Just _) -> return Nothing -- not base64-encoded _ -> catchError (do (bs, mbmime) <- P.fetchItem link case mbmime of Nothing -> do report $ CouldNotDetermineMimeType link return Nothing Just mime -> return $ Just (mime, TE.decodeUtf8 $ encode bs)) (\e -> do report $ CouldNotFetchResource link (tshow e) return Nothing) case mbimg of Just (imgtype, imgdata) -> return . Right $ el "binary" ( [uattr "id" href , uattr "content-type" imgtype] , txt imgdata ) _ -> return (Left ("#" <> href)) -- | Extract mime type and encoded data from the Data URI. readDataURI :: Text -- ^ URI -> Maybe (Text,Text,Bool,Text) -- ^ Maybe (mime,charset,isBase64,data) readDataURI uri = case T.stripPrefix "data:" uri of Nothing -> Nothing Just rest -> let meta = T.takeWhile (/= ',') rest -- without trailing ',' uridata = T.drop (T.length meta + 1) rest parts = T.split (== ';') meta (mime,cs,enc)=foldr upd ("text/plain","US-ASCII",False) parts in Just (mime,cs,enc,uridata) where upd str m@(mime,cs,enc) | isMimeType str = (str,cs,enc) | Just str' <- T.stripPrefix "charset=" str = (mime,str',enc) | str == "base64" = (mime,cs,True) | otherwise = m -- Without parameters like ;charset=...; see RFC 2045, 5.1 isMimeType :: Text -> Bool isMimeType s = case T.split (=='/') s of [mtype,msubtype] -> (T.toLower mtype `elem` types || "x-" `T.isPrefixOf` T.toLower mtype) && T.all valid mtype && T.all valid msubtype _ -> False where types = ["text","image","audio","video","application","message","multipart"] valid c = isAscii c && not (isControl c) && not (isSpace c) && c `notElem` ("()<>@,;:\\\"/[]?=" :: [Char]) footnoteID :: Int -> Text footnoteID i = "n" <> tshow i mkitem :: PandocMonad m => Text -> [Block] -> FBM m [Content] mkitem mrk bs = do pmrk <- gets parentListMarker let nmrk = pmrk <> mrk <> " " modify (\s -> s { parentListMarker = nmrk}) item <- cMapM blockToXml $ plainToPara $ indentBlocks nmrk bs modify (\s -> s { parentListMarker = pmrk }) -- old parent marker return item -- | Convert a block-level Pandoc's element to FictionBook XML representation. blockToXml :: PandocMonad m => Block -> FBM m [Content] blockToXml (Plain [img@Image {}]) = insertImage NormalImage img blockToXml (Plain ss) = cMapM toXml ss -- FIXME: can lead to malformed FB2 -- Special handling for singular images and display math elements blockToXml (Para [Math DisplayMath formula]) = insertMath NormalImage formula blockToXml (Para [img@(Image {})]) = insertImage NormalImage img blockToXml (Para ss) = list . el "p" <$> cMapM toXml ss blockToXml (CodeBlock _ s) = return . spaceBeforeAfter . map (el "p" . el "code") . T.lines $ s blockToXml (RawBlock f str) = if f == Format "fb2" then case parseXMLContents (TL.fromStrict str) of Left msg -> throwError $ PandocXMLError "" msg Right nds -> return nds else return [] blockToXml (Div _ bs) = cMapM blockToXml bs blockToXml (BlockQuote bs) = list . el "cite" <$> cMapM blockToXml bs blockToXml (LineBlock lns) = list . el "poem" <$> mapM stanza (split null lns) where v xs = el "v" <$> cMapM toXml xs stanza xs = el "stanza" <$> mapM v xs blockToXml (OrderedList a bss) = concat <$> zipWithM mkitem markers bss where markers = orderedListMarkers a blockToXml (BulletList bss) = cMapM (mkitem "•") bss blockToXml (DefinitionList defs) = cMapM mkdef defs where mkdef (term, bss) = do items <- cMapM (cMapM blockToXml . plainToPara . indentBlocks (T.replicate 4 " ")) bss t <- wrap "strong" term return (el "p" t : items) blockToXml h@Header{} = do -- should not occur after makeSections, except inside lists/blockquotes report $ BlockNotRendered h return [] blockToXml HorizontalRule = return [ el "empty-line" () ] blockToXml (Table _ blkCapt specs thead tbody tfoot) = do let (caption, aligns, _, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot hd <- if null headers then pure [] else (:[]) <$> mkrow "th" headers aligns bd <- mapM (\r -> mkrow "td" r aligns) rows c <- el "emphasis" <$> cMapM toXml caption return [el "table" (hd <> bd), el "p" c] where mkrow :: PandocMonad m => Text -> [[Block]] -> [Alignment] -> FBM m Content mkrow tag cells aligns' = el "tr" <$> mapM (mkcell tag) (zip cells aligns') -- mkcell :: PandocMonad m => Text -> ([Block], Alignment) -> FBM m Content mkcell tag (cell, align) = do cblocks <- cMapM blockToXml cell return $ el tag ([align_attr align], cblocks) -- align_attr a = Attr (QName "align" Nothing Nothing) (align_str a) align_str AlignLeft = "left" align_str AlignCenter = "center" align_str AlignRight = "right" align_str AlignDefault = "left" blockToXml (Figure _attr (Caption _ longcapt) body) = let alt = blocksToInlines longcapt addAlt (Image imgattr [] tgt) = Image imgattr alt tgt addAlt inln = inln in cMapM blockToXml (walk addAlt body) -- Replace plain text with paragraphs and add line break after paragraphs. -- It is used to convert plain text from tight list items to paragraphs. plainToPara :: [Block] -> [Block] plainToPara [] = [] plainToPara (Plain inlines : rest) = Para inlines : plainToPara rest plainToPara (Para inlines : rest) = Para inlines : HorizontalRule : plainToPara rest -- HorizontalRule will be converted to <empty-line /> plainToPara (p:rest) = p : plainToPara rest -- Replace plain text with paragraphs unPlain :: Block -> Block unPlain (Plain inlines) = Para inlines unPlain x = x -- Simulate increased indentation level. Will not really work -- for multi-line paragraphs. indentPrefix :: Text -> Block -> Block indentPrefix spacer = indentBlock where indentBlock (Plain ins) = Plain (Str spacer:ins) indentBlock (Para ins) = Para (Str spacer:ins) indentBlock (CodeBlock a s) = let s' = T.unlines . map (spacer<>) . T.lines $ s in CodeBlock a s' indentBlock (BlockQuote bs) = BlockQuote (map indent bs) indentBlock (Header l attr' ins) = Header l attr' (indentLines ins) indentBlock everythingElse = everythingElse -- indent every (explicit) line indentLines :: [Inline] -> [Inline] indentLines ins = let lns = split isLineBreak ins :: [[Inline]] in intercalate [LineBreak] $ map (Str spacer:) lns indent :: Block -> Block indent = indentPrefix spacer where -- indentation space spacer :: Text spacer = T.replicate 4 " " indentBlocks :: Text -> [Block] -> [Block] indentBlocks _ [] = [] indentBlocks prefix (x:xs) = indentPrefix prefix x : map (indentPrefix $ T.replicate (T.length prefix) " ") xs -- | Convert a Pandoc's Inline element to FictionBook XML representation. toXml :: PandocMonad m => Inline -> FBM m [Content] toXml (Str s) = return [txt s] toXml (Span _ ils) = cMapM toXml ils toXml (Emph ss) = list `liftM` wrap "emphasis" ss toXml (Underline ss) = list `liftM` wrap "underline" ss toXml (Strong ss) = list `liftM` wrap "strong" ss toXml (Strikeout ss) = list `liftM` wrap "strikethrough" ss toXml (Superscript ss) = list `liftM` wrap "sup" ss toXml (Subscript ss) = list `liftM` wrap "sub" ss toXml (SmallCaps ss) = cMapM toXml $ capitalize ss toXml (Quoted SingleQuote ss) = do -- FIXME: should be language-specific inner <- cMapM toXml ss return $ [txt "‘"] ++ inner ++ [txt "’"] toXml (Quoted DoubleQuote ss) = do inner <- cMapM toXml ss return $ [txt "“"] ++ inner ++ [txt "”"] toXml (Cite _ ss) = cMapM toXml ss -- FIXME: support citation styles toXml (Code _ s) = return [el "code" s] toXml Space = return [txt " "] toXml SoftBreak = return [txt "\n"] toXml LineBreak = return [txt "\n"] toXml (Math _ formula) = insertMath InlineImage formula toXml il@(RawInline _ _) = do report $ InlineNotRendered il return [] -- raw TeX and raw HTML are suppressed toXml (Link _ text (url,_)) = do ln_text <- cMapM toXml text return [ el "a" ( [ attr ("l","href") url ], ln_text) ] toXml img@Image{} = insertImage InlineImage img toXml (Note bs) = do fns <- footnotes `liftM` get let n = 1 + length fns let fn_id = footnoteID n fn_desc <- cMapM blockToXml bs modify (\s -> s { footnotes = (n, fn_id, fn_desc) : fns }) let fn_ref = txt $ "[" <> tshow n <> "]" return . list $ el "a" ( [ attr ("l","href") ("#" <> fn_id) , uattr "type" "note" ] , fn_ref ) insertMath :: PandocMonad m => ImageMode -> Text -> FBM m [Content] insertMath immode formula = do htmlMath <- fmap (writerHTMLMathMethod . writerOptions) get case htmlMath of WebTeX url -> do let alt = [Code nullAttr formula] let imgurl = url <> urlEncode formula let img = Image nullAttr alt (imgurl, "") insertImage immode img _ -> return [el "code" formula] insertImage :: PandocMonad m => ImageMode -> Inline -> FBM m [Content] insertImage immode (Image _ alt (url,ttl)) = do images <- imagesToFetch `liftM` get let n = 1 + length images let fname = "image" <> tshow n modify (\s -> s { imagesToFetch = (fname, url) : images }) let ttlattr = case (immode, T.null ttl) of (NormalImage, False) -> [ uattr "title" ttl ] _ -> [] return . list $ el "image" $ [ attr ("l","href") ("#" <> fname) , attr ("l","type") (tshow immode) , uattr "alt" (mconcat $ map plain alt) ] ++ ttlattr insertImage _ _ = error "unexpected inline instead of image" replaceImagesWithAlt :: [Text] -> Content -> Content replaceImagesWithAlt missingHrefs = everywhere (mkT go) where go c = if isMissing c then replaceNode c else c isMissing (Elem img@Element{}) = let imgAttrs = elAttribs img badAttrs = map (attr ("l","href")) missingHrefs in any (`elem` imgAttrs) badAttrs isMissing _ = False -- replaceNode :: Content -> Content replaceNode n@(Elem img@Element{}) = let attrs = elAttribs img alt = getAttrVal attrs (unqual "alt") imtype = getAttrVal attrs (qname "l" "type") in case (alt, imtype) of (Just alt', Just imtype') -> if imtype' == tshow NormalImage then el "p" alt' else txt alt' (Just alt', Nothing) -> txt alt' -- no type attribute _ -> n -- don't replace if alt text is not found replaceNode n = n -- getAttrVal :: [X.Attr] -> QName -> Maybe Text getAttrVal attrs name = case filter ((name ==) . attrKey) attrs of (a:_) -> Just (attrVal a) _ -> Nothing -- | Wrap all inlines with an XML tag (given its unqualified name). wrap :: PandocMonad m => Text -> [Inline] -> FBM m Content wrap tagname inlines = el tagname `liftM` cMapM toXml inlines -- " Create a singleton list. list :: a -> [a] list = (:[]) -- | Convert an 'Inline' to plaintext. plain :: Inline -> Text plain (Str s) = s plain (Emph ss) = mconcat $ map plain ss plain (Underline ss) = mconcat $ map plain ss plain (Span _ ss) = mconcat $ map plain ss plain (Strong ss) = mconcat $ map plain ss plain (Strikeout ss) = mconcat $ map plain ss plain (Superscript ss) = mconcat $ map plain ss plain (Subscript ss) = mconcat $ map plain ss plain (SmallCaps ss) = mconcat $ map plain ss plain (Quoted _ ss) = mconcat $ map plain ss plain (Cite _ ss) = mconcat $ map plain ss -- FIXME plain (Code _ s) = s plain Space = " " plain SoftBreak = " " plain LineBreak = "\n" plain (Math _ s) = s plain (RawInline _ _) = "" plain (Link _ text (url,_)) = mconcat (map plain text ++ [" <", url, ">"]) plain (Image _ alt _) = mconcat $ map plain alt plain (Note _) = "" -- FIXME -- | Create an XML element. el :: (Node t) => Text -- ^ unqualified element name -> t -- ^ node contents -> Content -- ^ XML content el name cs = Elem $ unode name cs -- | Put empty lines around content spaceBeforeAfter :: [Content] -> [Content] spaceBeforeAfter cs = let emptyline = el "empty-line" () in [emptyline] ++ cs ++ [emptyline] -- | Create a plain-text XML content. txt :: Text -> Content txt s = Text $ CData CDataText s Nothing -- | Create an XML attribute with an unqualified name. uattr :: Text -> Text -> X.Attr uattr name = Attr (unqual name) -- | Create an XML attribute with a qualified name from given namespace. attr :: (Text, Text) -> Text -> X.Attr attr (ns, name) = Attr (qname ns name) -- | Qualified name qname :: Text -> Text -> QName qname ns name = QName name Nothing (Just ns) -- | Abbreviation for 'concatMap'. cMap :: (a -> [b]) -> [a] -> [b] cMap = concatMap -- | Monadic equivalent of 'concatMap'. cMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b] cMapM f xs = concat `liftM` mapM f xs ================================================ FILE: src/Text/Pandoc/Writers/GridTable.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.Writers.GridTable Copyright : © 2020-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <albert+pandoc@tarleb.com> Grid representation of pandoc tables. The structures in this module allow to describe 'Text.Pandoc.Definition.Table' elements without loss of information. However, they are simpler to use when the grid layout of a table must be known. The "grid tables" handled here are conceptually similar to grid tables in reStructuredText and Markdown, but are more general. -} module Text.Pandoc.Writers.GridTable ( Table (..) , GridCell (..) , RowIndex (..) , ColIndex (..) , CellIndex , Part (..) , toTable , rowArray ) where import Control.Monad (forM_) import Control.Monad.ST import Data.Array import Data.Array.MArray import Data.Array.ST import Data.Maybe (listToMaybe) import Data.STRef import Text.Pandoc.Definition hiding (Table) import qualified Text.Pandoc.Builder as B -- | A grid cell contains either a real table cell, or is the -- continuation of a column or row-spanning cell. In the latter case, -- the index of the continued cell is provided. data GridCell = ContentCell Attr Alignment RowSpan ColSpan [Block] | ContinuationCell CellIndex | UnassignedCell deriving (Show) -- | Row index in a table part. newtype RowIndex = RowIndex Int deriving (Enum, Eq, Ix, Ord, Show) -- | Column index in a table part. newtype ColIndex = ColIndex Int deriving (Enum, Eq, Ix, Ord, Show) -- | Index to a cell in a table part. type CellIndex = (RowIndex, ColIndex) -- | Cells are placed on a grid. Row attributes are stored in a separate -- array. data Part = Part { partAttr :: Attr , partCellArray :: Array (RowIndex,ColIndex) GridCell , partRowAttrs :: Array RowIndex Attr } data Table = Table { tableAttr :: Attr , tableCaption :: Caption , tableColSpecs :: Array ColIndex ColSpec , tableRowHeads :: RowHeadColumns , tableHead :: Part , tableBodies :: [Part] , tableFoot :: Part } toTable :: B.Attr -> B.Caption -> [B.ColSpec] -> B.TableHead -> [B.TableBody] -> B.TableFoot -> Table toTable attr caption colSpecs thead' tbodies' tfoot' = Table attr caption colSpecs' rowHeads thGrid tbGrids tfGrid where -- normalize in case it's invalid shape: thead = B.normalizeTableHead numcols thead' tbodies = map (B.normalizeTableBody numcols) tbodies' tfoot = B.normalizeTableFoot numcols tfoot' numcols = length colSpecs colSpecs' = listArray (ColIndex 1, ColIndex numcols) colSpecs rowHeads = case listToMaybe tbodies of Nothing -> RowHeadColumns 0 Just (TableBody _attr rowHeadCols _headerRows _rows) -> rowHeadCols thGrid = let (TableHead headAttr rows) = thead in rowsToPart headAttr rows tbGrids = map bodyToGrid tbodies tfGrid = let (TableFoot footAttr rows) = tfoot in rowsToPart footAttr rows bodyToGrid (TableBody bodyAttr _rowHeadCols headRows rows) = rowsToPart bodyAttr (headRows ++ rows) data BuilderCell = FilledCell GridCell | FreeCell fromBuilderCell :: BuilderCell -> GridCell fromBuilderCell = \case FilledCell c -> c FreeCell -> UnassignedCell rowsToPart :: Attr -> [B.Row] -> Part rowsToPart attr = \case [] -> Part attr (listArray ((RowIndex 1, ColIndex 1), (RowIndex 0, ColIndex 0)) []) (listArray (RowIndex 1, RowIndex 0) []) rows@(Row _attr firstRow:_) -> let nrows = length rows ncols = sum $ map (\(Cell _ _ _ (ColSpan cs) _) -> cs) firstRow gbounds = ((RowIndex 1, ColIndex 1), (RowIndex nrows, ColIndex ncols)) mutableGrid :: ST s (STArray s CellIndex GridCell) mutableGrid = do grid <- newArray gbounds FreeCell ridx <- newSTRef (RowIndex 1) forM_ rows $ \(Row _attr cells) -> do cidx <- newSTRef (ColIndex 1) forM_ cells $ \(Cell cellAttr align rs cs blks) -> do ridx' <- readSTRef ridx let nextFreeInRow colindex@(ColIndex c) = do let idx = (ridx', colindex) if gbounds `inRange` idx then readArray grid idx >>= \case FreeCell -> pure (Just colindex) _ -> nextFreeInRow $ ColIndex (c + 1) else pure Nothing -- invalid table mcidx' <- readSTRef cidx >>= nextFreeInRow -- If there is a FreeCell in the current row, then fill it -- with the current cell and mark cells in this and the -- following rows as continuation cells if necessary. -- -- Just skip the current table cell if no FreeCell was -- found; this can only happen with invalid tables. case mcidx' of Nothing -> pure () -- no FreeCell left in row -- skip cell Just cidx' -> do writeArray grid (ridx', cidx') . FilledCell $ ContentCell cellAttr align rs cs blks forM_ (continuationIndices ridx' cidx' rs cs) $ \idx -> do writeArray grid idx . FilledCell $ ContinuationCell (ridx', cidx') -- go to new column writeSTRef cidx cidx' -- go to next row modifySTRef ridx (incrRowIndex 1) -- Swap BuilderCells with normal GridCells. mapArray fromBuilderCell grid in Part { partCellArray = runSTArray mutableGrid , partRowAttrs = listArray (RowIndex 1, RowIndex nrows) $ map (\(Row rowAttr _) -> rowAttr) rows , partAttr = attr } continuationIndices :: RowIndex -> ColIndex -> RowSpan -> ColSpan -> [CellIndex] continuationIndices (RowIndex ridx) (ColIndex cidx) rowspan colspan = let (RowSpan rs) = rowspan (ColSpan cs) = colspan in [ (RowIndex r, ColIndex c) | r <- [ridx..(ridx + rs - 1)] , c <- [cidx..(cidx + cs - 1)] , (r, c) /= (ridx, cidx)] rowArray :: RowIndex -> Array CellIndex GridCell -> Array ColIndex GridCell rowArray ridx grid = let ((_minRidx, minCidx), (_maxRidx, maxCidx)) = bounds grid in ixmap (minCidx, maxCidx) (ridx,) grid incrRowIndex :: RowSpan -> RowIndex -> RowIndex incrRowIndex (RowSpan n) (RowIndex r) = RowIndex $ r + n ================================================ FILE: src/Text/Pandoc/Writers/HTML.hs ================================================ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.HTML Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to HTML. -} module Text.Pandoc.Writers.HTML ( writeHtml4, writeHtml4String, writeHtml5, writeHtml5String, writeHtmlStringForEPUB, writeS5, writeSlidy, writeSlideous, writeDZSlides, writeRevealJs, tagWithAttributes ) where import Control.Monad.State.Strict ( StateT, MonadState(get), gets, modify, evalStateT ) 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, (\\)) import qualified Data.List as L import Data.List.NonEmpty (NonEmpty((:|))) import Data.Containers.ListUtils (nubOrd) import Data.Maybe (fromMaybe, isJust, isNothing) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Network.URI (URI (..), parseURIReference, escapeURIString) import Text.Pandoc.URI (urlEncode) import Numeric (showHex) import Text.DocLayout (render, literal, Doc) import Text.Blaze.Internal (MarkupM (Empty), customLeaf, customParent) import Text.DocTemplates (FromContext (lookupContext), Context (..), Val(..)) import qualified Text.DocTemplates.Internal as DT import Text.Blaze.Html hiding (contents) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatHtmlBlock, formatHtml4Block, formatHtmlInline, highlight, styleToCss, defaultStyle) import Text.Pandoc.ImageSize import Text.Pandoc.Options import Text.Pandoc.Shared import Text.Pandoc.Slides import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import qualified Text.Pandoc.Writers.AnnotatedTable as Ann import Text.Pandoc.XML (escapeStringForXML, fromEntities, toEntities, html5Attributes, html4Attributes, rdfaAttributes) import qualified Text.Blaze.XHtml5 as H5 import qualified Text.Blaze.XHtml5.Attributes as A5 import Control.Monad.Except (throwError) import System.FilePath (takeBaseName) import Text.Blaze.Html.Renderer.Text (renderHtml) import qualified Text.Blaze.XHtml1.Transitional as H import qualified Text.Blaze.XHtml1.Transitional.Attributes as A import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Translations (Term(Abstract), translateTerm) import Text.Pandoc.Class.PandocPure (runPure) import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.MIME (mediaCategory) import Text.Pandoc.Writers.Blaze (layoutMarkup) import Text.TeXMath import Text.XML.Light (elChildren, unode, unqual) import qualified Text.XML.Light as XML import Text.XML.Light.Output import Data.String (fromString) data WriterState = WriterState { stNotes :: [Html] -- ^ List of notes , stEmittedNotes :: Int -- ^ How many notes we've already pushed out to the HTML , stEmittedNoteBlocks :: Int -- ^ How many @\<div class=footnote>@ blocks we've already pushed out , stMath :: Bool -- ^ Math is used in document , stQuotes :: Bool -- ^ <q> tag is used , stHighlighting :: Bool -- ^ Syntax highlighting is used , stHtml5 :: Bool -- ^ Use HTML5 , stEPUBVersion :: Maybe EPUBVersion -- ^ EPUB version if for epub , stSlideVariant :: HTMLSlideVariant , stSlideLevel :: Int -- ^ Slide level , stInSection :: Bool -- ^ Content is in a section (revealjs) , stCodeBlockNum :: Int -- ^ Number of code block , stCsl :: Bool -- ^ Has CSL references , stCslEntrySpacing :: Maybe Int -- ^ CSL entry spacing , stBlockLevel :: Int -- ^ Current block depth, excluding section divs } defaultWriterState :: WriterState defaultWriterState = WriterState {stNotes= [], stEmittedNotes = 0, stEmittedNoteBlocks = 0, stMath = False, stQuotes = False, stHighlighting = False, stHtml5 = False, stEPUBVersion = Nothing, stSlideVariant = NoSlides, stSlideLevel = 1, stInSection = False, stCodeBlockNum = 0, stCsl = False, stCslEntrySpacing = Nothing, stBlockLevel = 0} -- Helpers to render HTML with the appropriate function. strToHtml :: Text -> Html strToHtml t | T.any isSpecial t = let !x = L.foldl' go mempty $ T.groupBy samegroup t in x | otherwise = toHtml t where samegroup c d = d == '\xFE0E' || not (isSpecial c || isSpecial d) isSpecial '\'' = True isSpecial '"' = True isSpecial c = needsVariationSelector c go h "\'" = h <> preEscapedString "\'" go h "\"" = h <> preEscapedString "\"" go h txt | T.length txt == 1 && T.all needsVariationSelector txt = h <> preEscapedString (T.unpack txt <> "\xFE0E") go h txt = h <> toHtml txt -- See #5469: this prevents iOS from substituting emojis. needsVariationSelector :: Char -> Bool needsVariationSelector '↩' = True needsVariationSelector '↔' = True needsVariationSelector _ = False -- | Hard linebreak. nl :: Html nl = preEscapedString "\n" -- | Convert Pandoc document to Html 5 string. writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHtml5String = writeHtmlString' defaultWriterState{ stHtml5 = True } -- | Convert Pandoc document to Html 5 structure. writeHtml5 :: PandocMonad m => WriterOptions -> Pandoc -> m Html writeHtml5 = writeHtml' defaultWriterState{ stHtml5 = True } -- | Convert Pandoc document to Html 4 string. writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHtml4String opts = writeHtmlString' defaultWriterState{ stHtml5 = False } opts . ensureValidXmlIdentifiers -- | Convert Pandoc document to Html 4 structure. writeHtml4 :: PandocMonad m => WriterOptions -> Pandoc -> m Html writeHtml4 opts = writeHtml' defaultWriterState{ stHtml5 = False } opts . ensureValidXmlIdentifiers -- | Convert Pandoc document to Html appropriate for an epub version. writeHtmlStringForEPUB :: PandocMonad m => EPUBVersion -> WriterOptions -> Pandoc -> m Text writeHtmlStringForEPUB version o = writeHtmlString' defaultWriterState{ stHtml5 = version == EPUB3, stEPUBVersion = Just version } o{ writerWrapText = WrapNone } -- we don't use ensureValidXmlIdentifiers here because we -- do that in the EPUB writer -- | Convert Pandoc document to Reveal JS HTML slide show. writeRevealJs :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeRevealJs = writeHtmlSlideShow' RevealJsSlides -- | Convert Pandoc document to S5 HTML slide show. writeS5 :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeS5 opts = writeHtmlSlideShow' S5Slides opts . ensureValidXmlIdentifiers -- | Convert Pandoc document to Slidy HTML slide show. writeSlidy :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeSlidy opts = writeHtmlSlideShow' SlidySlides opts . ensureValidXmlIdentifiers -- | Convert Pandoc document to Slideous HTML slide show. writeSlideous :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeSlideous opts = writeHtmlSlideShow' SlideousSlides opts . ensureValidXmlIdentifiers -- | Convert Pandoc document to DZSlides HTML slide show. writeDZSlides :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDZSlides opts = writeHtmlSlideShow' DZSlides opts writeHtmlSlideShow' :: PandocMonad m => HTMLSlideVariant -> WriterOptions -> Pandoc -> m Text writeHtmlSlideShow' variant = writeHtmlString' defaultWriterState{ stSlideVariant = variant , stHtml5 = case variant of RevealJsSlides -> True S5Slides -> False SlidySlides -> False DZSlides -> True SlideousSlides -> False NoSlides -> False } renderHtml' :: Html -> Text renderHtml' = TL.toStrict . renderHtml writeHtmlString' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Text writeHtmlString' st opts d = do (body, context) <- evalStateT (pandocToHtml opts d) st let colwidth = case writerWrapText opts of WrapAuto -> Just (writerColumns opts) _ -> Nothing (if writerPreferAscii opts then toEntities else id) <$> case writerTemplate opts of Nothing -> return $ case colwidth of Nothing -> renderHtml' body -- optimization, skip layout Just cols -> render (Just cols) $ layoutMarkup body Just tpl -> do -- warn if empty lang when (isNothing (getField "lang" context :: Maybe Text) && hasVariable "lang" tpl) $ report NoLangSpecified (context' :: Context Text) <- -- check for empty pagetitle case getField "pagetitle" context of Just (s :: Text) | not (T.null s) -> return context _ | hasVariable "pagetitle" tpl -> do let fallback = T.pack $ case lookupContext "sourcefile" (writerVariables opts) of Nothing -> "Untitled" Just [] -> "Untitled" Just (x:_) -> takeBaseName $ T.unpack x report $ NoTitleElement fallback return $ resetField "pagetitle" (literal fallback) context | otherwise -> return context return $ render colwidth $ renderTemplate tpl (defField "body" (layoutMarkup body) context') writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html writeHtml' st opts d = case writerTemplate opts of Just _ -> preEscapedText <$> writeHtmlString' st opts d Nothing | writerPreferAscii opts -> preEscapedText <$> writeHtmlString' st opts d | otherwise -> do (body, _) <- evalStateT (pandocToHtml opts d) st return body -- result is (title, authors, date, toc, body, new variables) pandocToHtml :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m (Html, Context Text) pandocToHtml opts (Pandoc meta blocks) = do lift $ setupTranslations meta let slideLevel = fromMaybe (getSlideLevel blocks) $ writerSlideLevel opts modify $ \st -> st{ stSlideLevel = slideLevel } metadata <- metaToContext opts (fmap layoutMarkup . blockListToHtml opts) (fmap layoutMarkup . inlineListToHtml opts) meta let stringifyHTML = escapeStringForXML . stringify let authsMeta = map (literal . stringifyHTML) $ docAuthors meta let dateMeta = stringifyHTML $ docDate meta let descriptionMeta = literal $ escapeStringForXML $ lookupMetaString "description" meta slideVariant <- gets stSlideVariant abstractTitle <- translateTerm Abstract let sects = makeSectionsWithOffsets (writerNumberOffset opts) (writerNumberSections opts) Nothing $ if slideVariant == NoSlides then blocks else prepSlides slideLevel blocks toc <- if writerTableOfContents opts && slideVariant /= S5Slides then fmap layoutMarkup <$> tableOfContents opts sects else return Nothing blocks' <- blockListToHtml opts sects notes <- do -- make the st private just to be safe, since we modify it right afterwards st <- get if null (stNotes st) then return mempty else do notes <- footnoteSection opts EndOfDocument (stEmittedNotes st + 1) (reverse (stNotes st)) modify (\st' -> st'{ stNotes = mempty, stEmittedNotes = stEmittedNotes st' + length (stNotes st') }) return notes st <- get let html5 = stHtml5 st let thebody = blocks' >> notes let math = layoutMarkup $ case writerHTMLMathMethod opts of MathJax url | slideVariant /= RevealJsSlides -> -- mathjax is handled via a special plugin in revealjs H.script ! A.defer mempty ! A.src (toValue $ toURI html5 url) ! A.type_ "text/javascript" $ case slideVariant of SlideousSlides -> preEscapedString "MathJax.Hub.Queue([\"Typeset\",MathJax.Hub]);" _ -> mempty KaTeX url -> do H.script ! A.defer mempty ! A.src (toValue $ toURI html5 $ url <> "katex.min.js") $ mempty nl let katexFlushLeft = case lookupContext "classoption" metadata of Just clsops | "fleqn" `elem` (clsops :: [Doc Text]) -> "true" _ -> "false" H.script $ text $ T.unlines [ "document.addEventListener(\"DOMContentLoaded\", function () {" , " var mathElements = document.getElementsByClassName(\"math\");" , " var macros = [];" , " for (var i = 0; i < mathElements.length; i++) {" , " var texText = mathElements[i].firstChild;" , " if (mathElements[i].tagName == \"SPAN\") {" , " katex.render(texText.data, mathElements[i], {" , " displayMode: mathElements[i].classList.contains('display')," , " throwOnError: false," , " macros: macros," , " fleqn: " <> katexFlushLeft , " });" , "}}});" ] nl H.link ! A.rel "stylesheet" ! A.href (toValue $ toURI html5 url <> "katex.min.css") _ -> mempty let mCss :: Maybe [Text] = lookupContext "css" metadata let context :: Context Text context = (if stHighlighting st then case writerHighlightMethod opts of Skylighting sty -> defField "highlighting-css" (literal $ T.pack $ styleToCss sty) DefaultHighlighting -> defField "highlighting-css" (literal $ T.pack $ styleToCss defaultStyle) _ -> id else id) . (if stCsl st then defField "csl-css" True . (case stCslEntrySpacing st of Nothing -> id Just n -> defField "csl-entry-spacing" (literal $ tshow n <> "em")) else id) . (if stMath st then defField "math" math else id) . defField "abstract-title" abstractTitle . (case writerHTMLMathMethod opts of MathJax u -> defField "mathjax" True . defField "mathjaxurl" (literal $ T.takeWhile (/='?') u) _ -> defField "mathjax" False) . (case writerHTMLMathMethod opts of PlainMath -> defField "displaymath-css" True WebTeX _ -> defField "displaymath-css" True _ -> id) . (if slideVariant == RevealJsSlides then -- set boolean options explicitly, since -- template can't distinguish False/undefined defField "controls" True . defField "controlsTutorial" True . defField "controlsLayout" ("bottom-right" :: Doc Text) . defField "controlsBackArrows" ("faded" :: Doc Text) . defField "progress" True . defField "slideNumber" False . defField "showSlideNumber" ("all" :: Doc Text) . defField "hashOneBasedIndex" False . defField "hash" True . defField "respondToHashChanges" True . defField "history" False . defField "keyboard" True . defField "overview" True . defField "disableLayout" False . defField "center" True . defField "touch" True . defField "loop" False . defField "rtl" False . defField "navigationMode" ("default" :: Doc Text) . defField "shuffle" False . defField "fragments" True . defField "fragmentInURL" True . defField "embedded" False . defField "help" True . defField "pause" True . defField "showNotes" False . defField "autoPlayMedia" ("null" :: Doc Text) . defField "preloadIframes" ("null" :: Doc Text) . defField "autoSlide" ("0" :: Doc Text) . defField "autoSlideStoppable" True . defField "autoSlideMethod" ("null" :: Doc Text) . defField "defaultTiming" ("null" :: Doc Text) . defField "mouseWheel" False . defField "display" ("block" :: Doc Text) . defField "hideInactiveCursor" True . defField "hideCursorTime" ("5000" :: Doc Text) . defField "previewLinks" False . defField "transition" ("slide" :: Doc Text) . defField "transitionSpeed" ("default" :: Doc Text) . defField "backgroundTransition" ("fade" :: Doc Text) . defField "viewDistance" ("3" :: Doc Text) . defField "mobileViewDistance" ("2" :: Doc Text) . (case (lookupContext "scrollProgress" metadata :: Maybe (Val Text)) of Just (BoolVal False) -> id Just (BoolVal True) -> defField "scrollProgress" True _ -> defField "scrollProgressAuto" True) . defField "scrollActivationWidth" ("0" :: Doc Text) . defField "scrollSnap" ("mandatory" :: 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) . -- for backwards compatibility we populate toc -- with the contents of the toc, rather than a -- boolean: maybe id (defField "toc") toc . maybe id (defField "table-of-contents") toc . defField "author-meta" authsMeta . maybe id (defField "date-meta" . literal) (normalizeDate dateMeta) . defField "description-meta" descriptionMeta . defField "pagetitle" (literal . stringifyHTML . docTitle $ meta) . defField "idprefix" (literal $ writerIdentifierPrefix opts) . -- these should maybe be set in pandoc.hs defField "slidy-url" ("https://www.w3.org/Talks/Tools/Slidy2" :: Doc Text) . defField "slideous-url" ("slideous" :: Doc Text) . defField "revealjs-url" ("https://unpkg.com/reveal.js@^5" :: Doc Text) $ defField "s5-url" ("s5/default" :: Doc Text) . defField "table-caption-below" (writerTableCaptionPosition opts == CaptionBelow) . defField "html5" (stHtml5 st) $ metadata return (thebody, context) -- | Like Text.XHtml's identifier, but adds the writerIdentifierPrefix prefixedId :: WriterOptions -> Text -> Attribute prefixedId opts s = case s of "" -> mempty _ -> A.id $ toValue $ writerIdentifierPrefix opts <> s toList :: PandocMonad m => (Html -> Html) -> WriterOptions -> [Html] -> StateT WriterState m Html toList listop opts items = do slideVariant <- gets stSlideVariant return $ if writerIncremental opts then if slideVariant /= RevealJsSlides then listop (mconcat items) ! A.class_ "incremental" else listop $ mconcat $ map (! A.class_ "fragment") items else listop $ mconcat items unordList :: PandocMonad m => WriterOptions -> [Html] -> StateT WriterState m Html unordList opts = toList H.ul opts . toListItems ordList :: PandocMonad m => WriterOptions -> [Html] -> StateT WriterState m Html ordList opts = toList H.ol opts . toListItems defList :: PandocMonad m => WriterOptions -> [Html] -> StateT WriterState m Html defList opts items = toList H.dl opts (items ++ [nl]) listItemToHtml :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Html listItemToHtml opts bls = case toTaskListItem bls of Just (checked, (Para is:bs)) -> taskListItem checked H.p is bs Just (checked, (Plain is:bs)) -> taskListItem checked id is bs _ -> blockListToHtml opts bls where taskListItem checked constr is bs = do let checkbox = if checked then checkbox' ! A.checked "" else checkbox' checkbox' = H.input ! A.type_ "checkbox" isContents <- inlineListToHtml opts is bsContents <- blockListToHtml opts bs return $ constr (H.label (checkbox >> isContents)) >> (if null bs then mempty else nl) >> bsContents -- | Construct table of contents from list of elements. tableOfContents :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m (Maybe Html) tableOfContents _ [] = return Nothing tableOfContents opts sects = do -- in reveal.js, we need #/apples, not #apples: slideVariant <- gets stSlideVariant let opts' = case slideVariant of RevealJsSlides -> opts{ writerIdentifierPrefix = "/" <> writerIdentifierPrefix opts } _ -> opts case toTableOfContents opts sects of bl@(BulletList (_:_)) -> Just <$> blockToHtml opts' bl _ -> return Nothing -- | Convert list of Note blocks to a footnote <div>. -- Assumes notes are sorted. footnoteSection :: PandocMonad m => WriterOptions -> ReferenceLocation -> Int -> [Html] -> StateT WriterState m Html footnoteSection opts refLocation startCounter notes = do html5 <- gets stHtml5 slideVariant <- gets stSlideVariant let hrtag = if refLocation /= EndOfBlock then (if html5 then H5.hr else H.hr) <> nl else mempty idName <- do blockCount <- gets stEmittedNoteBlocks modify $ \st -> st{ stEmittedNoteBlocks = blockCount + 1 } return $ -- Keep the first note section's id undecorated to maintain a target for -- old links which don't expect numbered sections, or for when the notes -- are rendered all together at the end of the document. if blockCount <= 0 then "footnotes" else "footnotes-" <> show (blockCount + 1) let additionalClassName = case refLocation of EndOfBlock -> "footnotes-end-of-block" EndOfDocument -> "footnotes-end-of-document" EndOfSection -> "footnotes-end-of-section" let className = "footnotes " <> additionalClassName epubVersion <- gets stEPUBVersion let container x | html5 , epubVersion == Just EPUB3 = H5.section ! A.id (fromString idName) ! A.class_ className ! customAttribute "epub:type" "footnotes" $ x | html5 , refLocation == EndOfDocument -- Note: we need a section for a new slide in slide formats. = H5.section ! prefixedId opts (fromString idName) ! A5.class_ className ! A5.role "doc-endnotes" $ x | html5 = H5.aside ! prefixedId opts (fromString idName) ! A5.class_ className ! A5.role "doc-footnote" $ x | slideVariant /= NoSlides = H.div ! A.class_ "footnotes slide" $ x | otherwise = H.div ! A.class_ className $ x return $ if null notes then mempty else do nl container $ do nl hrtag -- Keep the previous output exactly the same if we don't -- have multiple notes sections case epubVersion of Just _ -> mconcat notes Nothing | startCounter == 1 -> (H.ol (nl >> mconcat notes)) >> nl Nothing -> (H.ol ! A.start (fromString (show startCounter)) $ nl >> mconcat notes) >> nl -- | Parse a mailto link; return Just (name, domain) or Nothing. parseMailto :: Text -> Maybe (Text, Text) parseMailto s = case T.break (==':') s of (xs,T.uncons -> Just (':',addr)) | T.toLower xs == "mailto" -> do let (name', rest) = T.span (/='@') addr let domain = T.drop 1 rest return (name', domain) _ -> Prelude.fail "not a mailto: URL" -- | Obfuscate a "mailto:" link. obfuscateLink :: PandocMonad m => WriterOptions -> Attr -> Html -> Text -> StateT WriterState m Html obfuscateLink opts attr txt s | writerEmailObfuscation opts == NoObfuscation = do html5 <- gets stHtml5 addAttrs opts attr $ H.a ! A.href (toValue $ toURI html5 s) $ txt obfuscateLink opts attr (TL.toStrict . renderHtml -> txt) s = do html5 <- gets stHtml5 let meth = writerEmailObfuscation opts let s' = T.toLower (T.take 7 s) <> T.drop 7 s case parseMailto s' of (Just (name', domain)) -> let domain' = T.replace "." " dot " domain at' = obfuscateChar '@' (linkText, altText) = if txt == T.drop 7 s' -- autolink then ("e", name' <> " at " <> domain') else ("'" <> obfuscateString txt <> "'", txt <> " (" <> name' <> " at " <> domain' <> ")") (_, classNames, _) = attr classNamesStr = T.concat $ map (" "<>) classNames in case meth of ReferenceObfuscation -> -- need to use preEscapedString or &'s are escaped to & in URL return $ preEscapedText $ "<a href=\"" <> obfuscateString s' <> "\" class=\"email\">" <> obfuscateString txt <> "</a>" JavascriptObfuscation -> return $ (H.script ! A.type_ "text/javascript" $ preEscapedText ("\n<!--\nh='" <> obfuscateString domain <> "';a='" <> at' <> "';n='" <> obfuscateString name' <> "';e=n+a+h;\n" <> "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\" clas'+'s=\"em' + 'ail" <> classNamesStr <> "\">'+" <> linkText <> "+'<\\/'+'a'+'>');\n// -->\n")) >> H.noscript (preEscapedText $ obfuscateString altText) _ -> throwError $ PandocSomeError $ "Unknown obfuscation method: " <> tshow meth _ -> addAttrs opts attr $ H.a ! A.href (toValue $ toURI html5 s) $ toHtml txt -- malformed email -- | Obfuscate character as entity. obfuscateChar :: Char -> Text obfuscateChar char = let num = ord char numstr = if even num then show num else "x" <> showHex num "" in "&#" <> T.pack numstr <> ";" -- | Obfuscate string using entities. obfuscateString :: Text -> Text obfuscateString = T.concatMap obfuscateChar . fromEntities -- | Create HTML tag with attributes. tagWithAttributes :: WriterOptions -> Bool -- ^ True for HTML5 -> Bool -- ^ True if self-closing tag -> Text -- ^ Tag text -> Attr -- ^ Pandoc style tag attributes -> Text tagWithAttributes opts html5 selfClosing tagname attr = let mktag = (TL.toStrict . renderHtml <$> evalStateT (addAttrs opts attr (customLeaf (textTag tagname) selfClosing)) defaultWriterState{ stHtml5 = html5 }) in case runPure mktag of Left _ -> mempty Right t -> t addAttrs :: PandocMonad m => WriterOptions -> Attr -> Html -> StateT WriterState m Html addAttrs opts attr h = L.foldl' (!) h <$> attrsToHtml opts attr toAttrs :: PandocMonad m => [(Text, Text)] -> StateT WriterState m [Attribute] toAttrs kvs = do html5 <- gets stHtml5 mbEpubVersion <- gets stEPUBVersion reverse . snd <$> foldM (go html5 mbEpubVersion) (Set.empty, []) kvs where go html5 mbEpubVersion (keys, attrs) (k,v) = do if k `Set.member` keys then do report $ DuplicateAttribute k v return (keys, attrs) else return (Set.insert k keys, addAttr html5 mbEpubVersion k v attrs) addAttr html5 mbEpubVersion x y | T.null x = id -- see #7546 | html5 = if (x `Set.member` (html5Attributes <> rdfaAttributes) && x /= "label") -- #10048 || T.any (== ':') x -- e.g. epub: namespace || "data-" `T.isPrefixOf` x || "aria-" `T.isPrefixOf` x then (customAttribute (textTag x) (toValue y) :) else (customAttribute (textTag ("data-" <> x)) (toValue y) :) | mbEpubVersion == Just EPUB2 , not (x `Set.member` (html4Attributes <> rdfaAttributes) || "xml:" `T.isPrefixOf` x) = id | otherwise = (customAttribute (textTag x) (toValue y) :) attrsToHtml :: PandocMonad m => WriterOptions -> Attr -> StateT WriterState m [Attribute] attrsToHtml opts (id',classes',keyvals) = do attrs <- toAttrs keyvals let classes'' = nubOrd $ filter (not . T.null) classes' return $ [prefixedId opts id' | not (T.null id')] ++ [A.class_ (toValue $ T.unwords classes'') | not (null classes'')] ++ attrs imgAttrsToHtml :: PandocMonad m => WriterOptions -> Attr -> StateT WriterState m [Attribute] imgAttrsToHtml opts attr = do attrsToHtml opts (ident,cls, consolidateStyles (kvs' ++ dimensionsToAttrList attr)) where (ident,cls,kvs) = attr kvs' = filter isNotDim kvs isNotDim ("width", _) = False isNotDim ("height", _) = False isNotDim _ = True consolidateStyles :: [(Text, Text)] -> [(Text, Text)] consolidateStyles xs = case partition isStyle xs of ([], _) -> xs (ss, rest) -> ("style", T.intercalate ";" $ map snd ss) : rest isStyle ("style", _) = True isStyle _ = False dimensionsToAttrList :: Attr -> [(Text, Text)] dimensionsToAttrList attr = go Width ++ go Height where go dir = case dimension dir attr of (Just (Pixel a)) -> [(tshow dir, tshow a)] (Just x) -> [("style", tshow dir <> ":" <> tshow x)] Nothing -> [] blockToHtmlInner :: PandocMonad m => WriterOptions -> Block -> StateT WriterState m Html blockToHtmlInner opts (Plain lst) = inlineListToHtml opts lst blockToHtmlInner opts (Para lst) = do slideVariant <- gets stSlideVariant case (slideVariant, lst) of (RevealJsSlides, [Image attr@(_,classes,_) txt (src,tit)]) | "r-stretch" `elem` classes -> do -- a "stretched" image in reveal.js must be a direct child -- of the slide container inlineToHtml opts (Image attr txt (src, tit)) _ -> do contents <- inlineListToHtml opts lst case contents of Empty _ | not (isEnabled Ext_empty_paragraphs opts) -> return mempty _ -> return $ H.p contents blockToHtmlInner opts (LineBlock lns) = do htmlLines <- inlineListToHtml opts $ intercalate [LineBreak] lns return $ H.div ! A.class_ "line-block" $ htmlLines blockToHtmlInner opts (Div (ident, "section":dclasses, dkvs) (Header level hattr@(hident,hclasses,hkvs) ils : xs)) = do slideVariant <- gets stSlideVariant slideLevel <- gets stSlideLevel let slide = slideVariant /= NoSlides && level <= slideLevel {- DROPPED old fix for #5168 here -} html5 <- gets stHtml5 let titleSlide = slide && level < slideLevel let level' = if level <= slideLevel && slideVariant == SlidySlides then 1 -- see #3566 else level header' <- if ils == [Str "\0"] -- marker for hrule then return mempty else blockToHtml opts (Header level' hattr ils) let isSec (Div (_,"section":_,_) _) = True isSec (Div _ zs) = any isSec zs isSec _ = False let isPause (Para [Str ".",Space,Str ".",Space,Str "."]) = True isPause _ = False let fragmentClass = case slideVariant of RevealJsSlides -> "fragment" _ -> "incremental" let inDiv' zs = RawBlock (Format "html") ("<div class=\"" <> fragmentClass <> "\">") : (zs ++ [RawBlock (Format "html") "</div>"]) let breakOnPauses zs | slide = case splitBy isPause zs of [] -> [] y:ys -> y ++ concatMap inDiv' ys | otherwise = zs let (titleBlocks, innerSecs) = if titleSlide -- title slides have no content of their own then let (as, bs) = break isSec xs in (walk breakOnPauses as, bs) else ([], walk breakOnPauses xs) let secttag = if html5 then H5.section else H.div titleContents <- blockListToHtml opts titleBlocks inSection <- gets stInSection innerContents <- do modify $ \st -> st{ stInSection = True } res <- blockListToHtml opts innerSecs modify $ \st -> st{ stInSection = inSection } notes <- gets stNotes let emitNotes = writerReferenceLocation opts == EndOfSection && not (null notes) if emitNotes then do st <- get renderedNotes <- footnoteSection opts (writerReferenceLocation opts) (stEmittedNotes st + 1) (reverse notes) modify (\st' -> st'{ stNotes = mempty, stEmittedNotes = stEmittedNotes st' + length notes }) return (res <> renderedNotes) else return res let classes' = ["title-slide" | titleSlide] ++ ["slide" | slide] ++ ["section" | (slide || writerSectionDivs opts) && not html5 ] ++ ["level" <> tshow level | slide || writerSectionDivs opts ] <> [d | d <- dclasses, slideVariant /= RevealJsSlides || d /= "r-fit-text"] -- see #5965 let attr = (ident, classes', dkvs) if titleSlide then do t <- addAttrs opts attr $ secttag $ nl <> header' <> nl <> titleContents <> nl -- ensure 2D nesting for revealjs, but only for one level; -- revealjs doesn't like more than one level of nesting return $ if slideVariant == RevealJsSlides && not inSection && not (null innerSecs) then H5.section (nl <> t <> nl <> innerContents) else t <> nl <> if null innerSecs then mempty else innerContents <> nl else if writerSectionDivs opts || slide || (hident /= ident && not (T.null hident || T.null ident)) || (hclasses /= dclasses) || (hkvs /= dkvs) then addAttrs opts attr $ secttag $ nl <> header' <> nl <> if null innerSecs then mempty else innerContents <> nl else do let attr' = (ident, classes' \\ hclasses, dkvs \\ hkvs) t <- addAttrs opts attr' header' return $ t <> if null innerSecs then mempty else nl <> innerContents blockToHtmlInner opts (Div (ident, classes, kvs) [b]) | Just "1" <- lookup "wrapper" kvs -- unwrap "wrapper" div, putting attr on child = blockToHtmlInner opts b >>= addAttrs opts (ident, classes, [(k,v) | (k,v) <- kvs, k /= "wrapper"]) blockToHtmlInner opts (Div attr@(ident, classes, kvs') bs) = do html5 <- gets stHtml5 slideVariant <- gets stSlideVariant let isCslBibBody = ident == "refs" || "csl-bib-body" `elem` classes when isCslBibBody $ modify $ \st -> st{ stCsl = True , stCslEntrySpacing = lookup "entry-spacing" kvs' >>= safeRead } let isCslBibEntry = "csl-entry" `elem` classes let kvs = [(k,v) | (k,v) <- kvs' , k /= "width" || "column" `notElem` classes] ++ [("style", "width:" <> w <> ";") | "column" `elem` classes , ("width", w) <- kvs'] ++ [("role", "list") | isCslBibBody && html5] ++ [("role", "listitem") | isCslBibEntry && html5] let speakerNotes = "notes" `elem` classes -- we don't want incremental output inside speaker notes, see #1394 let (opts', isIncrDiv) = if | speakerNotes -> (opts{ writerIncremental = False }, False) | "incremental" `elem` classes -> (opts{ writerIncremental = True }, True) | "nonincremental" `elem` classes -> (opts{ writerIncremental = False }, True) | otherwise -> (opts, False) -- we remove "incremental" and "nonincremental" if we're in a -- slide presentation format. classes' = case slideVariant of NoSlides -> classes _ -> filter (\k -> k /= "incremental" && k /= "nonincremental") classes let paraToPlain (Para ils) = Plain ils paraToPlain x = x let bs' = if "csl-entry" `elem` classes' then walk paraToPlain bs else bs contents <- if "columns" `elem` classes' then -- we don't use blockListToHtml because it inserts -- a newline between the column divs, which throws -- off widths! see #4028 mconcat <$> mapM (blockToHtml opts) bs' else blockListToHtml opts' bs' let contents' = nl >> contents >> nl let (divtag, classes'') = if html5 && "section" `elem` classes' then (H5.section, filter (/= "section") classes') else (H.div, classes') if | isIncrDiv && (ident, classes'', kvs) == nullAttr -> -- Unwrap divs that only have (non)increment information pure contents | speakerNotes -> case slideVariant of RevealJsSlides -> addAttrs opts' attr $ H5.aside contents' DZSlides -> do t <- addAttrs opts' attr $ H5.div contents' return $ t ! A5.role "note" NoSlides -> addAttrs opts' attr $ H.div contents' _ -> return mempty | otherwise -> addAttrs opts (ident, classes'', kvs) $ divtag contents' blockToHtmlInner opts (RawBlock f str) = do ishtml <- isRawHtml f if ishtml then return $ preEscapedText str else if (f == Format "latex" || f == Format "tex") && allowsMathEnvironments (writerHTMLMathMethod opts) && isMathEnvironment str then do modify (\st -> st {stMath = True}) blockToHtml opts $ Plain [Math DisplayMath str] else do report $ BlockNotRendered (RawBlock f str) return mempty blockToHtmlInner _ HorizontalRule = do html5 <- gets stHtml5 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 } codeblocknum <- gets stCodeBlockNum return (writerIdentifierPrefix opts <> "cb" <> tshow codeblocknum) else return (writerIdentifierPrefix opts <> id') let tolhs = isEnabled Ext_literate_haskell opts && any (\c -> T.toLower c == "haskell") classes && any (\c -> T.toLower c == "literate") classes classes' = if tolhs then map (\c -> if T.toLower c == "haskell" then "literatehaskell" else c) classes else classes adjCode = if tolhs then T.unlines . map ("> " <>) . T.lines $ rawCode else rawCode 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; -- otherwise incremental slideVariant <- gets stSlideVariant if slideVariant /= NoSlides then let inc = not (writerIncremental opts) in case blocks of [BulletList lst] -> blockToHtml (opts {writerIncremental = inc}) (BulletList lst) [OrderedList attribs lst] -> blockToHtml (opts {writerIncremental = inc}) (OrderedList attribs lst) [DefinitionList lst] -> blockToHtml (opts {writerIncremental = inc}) (DefinitionList lst) _ -> do contents <- blockListToHtml opts blocks return $ H.blockquote $ nl >> contents >> nl else do contents <- blockListToHtml opts blocks return $ H.blockquote $ nl >> contents >> nl blockToHtmlInner opts (Header level (ident,classes,kvs) lst) = do contents <- inlineListToHtml opts lst let secnum = fromMaybe mempty $ lookup "number" kvs let contents' = if writerNumberSections opts && not (T.null secnum) && "unnumbered" `notElem` classes then (H.span ! A.class_ "header-section-number" $ toHtml secnum) >> toHtml ' ' >> contents else contents html5 <- gets stHtml5 let kvs' = if html5 then kvs else [ (k, v) | (k, v) <- kvs , k `elem` (["lang", "dir", "title", "style" , "align"] ++ intrinsicEventsHTML4)] let classes' = if level > 6 then "heading":classes else classes addAttrs opts (ident,classes',kvs') $ case level of 1 -> H.h1 contents' 2 -> H.h2 contents' 3 -> H.h3 contents' 4 -> H.h4 contents' 5 -> H.h5 contents' 6 -> H.h6 contents' _ -> H.p contents' blockToHtmlInner opts (BulletList lst) = do contents <- mapM (listItemToHtml opts) lst (if isJust (mapM toTaskListItem lst) then (! A.class_ "task-list") else id) <$> unordList opts contents blockToHtmlInner opts (OrderedList (startnum, numstyle, _) lst) = do contents <- mapM (listItemToHtml opts) lst html5 <- gets stHtml5 let numstyle' = case numstyle of Example -> "decimal" _ -> camelCaseToHyphenated $ tshow numstyle let attribs = [A.start $ toValue startnum | startnum /= 1] ++ [A.class_ "example" | numstyle == Example] ++ (if numstyle /= DefaultStyle then if html5 then [A.type_ $ case numstyle of Decimal -> "1" LowerAlpha -> "a" UpperAlpha -> "A" LowerRoman -> "i" UpperRoman -> "I" _ -> "1"] else [A.style $ toValue $ "list-style-type: " <> numstyle'] else []) l <- ordList opts contents return $ L.foldl' (!) l attribs blockToHtmlInner opts (DefinitionList lst) = do contents <- mapM (\(term, defs) -> do term' <- liftM H.dt $ inlineListToHtml opts term defs' <- mapM (liftM (\x -> H.dd (nl >> x >> nl)) . blockListToHtml opts) defs return $ mconcat $ nl : term' : nl : intersperse (nl) defs') lst defList opts contents blockToHtmlInner opts (Table attr caption colspecs thead tbody tfoot) = tableToHtml opts (Ann.toTable attr caption colspecs thead tbody tfoot) blockToHtmlInner opts (Figure attrs (Caption _ captBody) body) = do html5 <- gets stHtml5 figAttrs <- attrsToHtml opts attrs contents <- blockListToHtml opts body captCont <- blockListToHtml opts captBody let figCaption = mconcat $ if html5 then let fcattr = if captionIsAlt captBody body then H5.customAttribute (textTag "aria-hidden") (toValue @Text "true") else mempty in [ H5.figcaption ! fcattr $ captCont ] else [ (H.div ! A.class_ "figcaption") captCont ] let innards = mconcat $ if null captBody then [nl, contents, nl] else case writerFigureCaptionPosition opts of CaptionAbove -> [nl, figCaption, nl, contents, nl] CaptionBelow -> [nl, contents, nl, figCaption, nl] return $ if html5 then foldl (!) H5.figure figAttrs innards else foldl (!) H.div (A.class_ "float" : figAttrs) innards where captionIsAlt capt [Plain [Image (_, _, kv) desc _]] = let alt = fromMaybe (stringify desc) $ lookup "alt" kv in stringify capt == alt captionIsAlt _ _ = False -- | Convert Pandoc block element to HTML. All the legwork is done by -- 'blockToHtmlInner', this just takes care of emitting the notes after -- the block if necessary. blockToHtml :: PandocMonad m => WriterOptions -> Block -> StateT WriterState m Html blockToHtml opts block = do let isSection = case block of Div (_, classes, _) _ | "section" `elem` classes -> True _ -> False let increaseLevel = not isSection when increaseLevel $ modify (\st -> st{ stBlockLevel = stBlockLevel st + 1 }) doc <- blockToHtmlInner opts block st <- get let emitNotes = writerReferenceLocation opts == EndOfBlock && stBlockLevel st == 1 res <- if emitNotes then do notes <- if null (stNotes st) then return mempty else footnoteSection opts (writerReferenceLocation opts) (stEmittedNotes st + 1) (reverse (stNotes st)) modify (\st' -> st'{ stNotes = mempty, stEmittedNotes = stEmittedNotes st' + length (stNotes st') }) return (doc <> notes) else return doc when increaseLevel $ modify (\st' -> st'{ stBlockLevel = stBlockLevel st' - 1 }) return res tableToHtml :: PandocMonad m => WriterOptions -> Ann.Table -> StateT WriterState m Html tableToHtml opts (Ann.Table attr caption colspecs thead tbodies tfoot) = do captionDoc <- case caption of Caption _ [] -> return mempty Caption _ longCapt -> do cs <- blockListToHtml opts longCapt return $ do H.caption cs nl coltags <- colSpecListToHtml colspecs head' <- tableHeadToHtml opts thead bodies <- intersperse (nl) <$> mapM (tableBodyToHtml opts) tbodies foot' <- tableFootToHtml opts tfoot let (ident,classes,kvs) = attr -- When widths of columns are < 100%, we need to set width for the whole -- table, or some browsers give us skinny columns with lots of space -- between: let colWidth = \case ColWidth d -> d ColWidthDefault -> 0 let totalWidth = sum . map (colWidth . snd) $ colspecs let attr' = case lookup "style" kvs of Nothing | totalWidth < 1 && totalWidth > 0 -> (ident,classes, ("style","width:" <> T.pack (show (round (totalWidth * 100) :: Int)) <> "%;"):kvs) _ -> attr addAttrs opts attr' $ H.table $ do nl captionDoc coltags head' mconcat bodies foot' nl tableBodyToHtml :: PandocMonad m => WriterOptions -> Ann.TableBody -> StateT WriterState m Html tableBodyToHtml opts (Ann.TableBody attr _rowHeadCols inthead rows) = addAttrs opts attr . H.tbody =<< do intermediateHead <- if null inthead then return mempty else headerRowsToHtml opts Thead inthead bodyRows <- bodyRowsToHtml opts rows return $ intermediateHead <> bodyRows tableHeadToHtml :: PandocMonad m => WriterOptions -> Ann.TableHead -> StateT WriterState m Html tableHeadToHtml opts (Ann.TableHead attr rows) = tablePartToHtml opts Thead attr rows tableFootToHtml :: PandocMonad m => WriterOptions -> Ann.TableFoot -> StateT WriterState m Html tableFootToHtml opts (Ann.TableFoot attr rows) = tablePartToHtml opts Tfoot attr rows tablePartToHtml :: PandocMonad m => WriterOptions -> TablePart -> Attr -> [Ann.HeaderRow] -> StateT WriterState m Html tablePartToHtml opts tblpart attr rows = if null rows || all isEmptyRow rows then return mempty else do let tag' = case tblpart of Thead -> H.thead Tfoot -> H.tfoot Tbody -> H.tbody -- this would be unexpected contents <- headerRowsToHtml opts tblpart rows tablePartElement <- addAttrs opts attr $ tag' contents return $ do tablePartElement nl where isEmptyRow (Ann.HeaderRow _attr _rownum cells) = all isEmptyCell cells isEmptyCell (Ann.Cell _colspecs _colnum cell) = cell == Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) [] -- | The part of a table; header, footer, or body. data TablePart = Thead | Tfoot | Tbody deriving (Eq) data CellType = HeaderCell | BodyCell data TableRow = TableRow TablePart Attr Ann.RowNumber Ann.RowHead Ann.RowBody headerRowsToHtml :: PandocMonad m => WriterOptions -> TablePart -> [Ann.HeaderRow] -> StateT WriterState m Html headerRowsToHtml opts tablepart = rowListToHtml opts . map toTableRow where toTableRow (Ann.HeaderRow attr rownum rowbody) = TableRow tablepart attr rownum [] rowbody bodyRowsToHtml :: PandocMonad m => WriterOptions -> [Ann.BodyRow] -> StateT WriterState m Html bodyRowsToHtml opts = rowListToHtml opts . zipWith toTableRow [1..] where toTableRow rownum (Ann.BodyRow attr _rownum rowhead rowbody) = TableRow Tbody attr rownum rowhead rowbody rowListToHtml :: PandocMonad m => WriterOptions -> [TableRow] -> StateT WriterState m Html rowListToHtml opts rows = (\x -> nl *> mconcat x) <$> mapM (tableRowToHtml opts) rows colSpecListToHtml :: PandocMonad m => [ColSpec] -> StateT WriterState m Html colSpecListToHtml colspecs = do html5 <- gets stHtml5 let hasDefaultWidth (_, ColWidthDefault) = True hasDefaultWidth _ = False let percent w = show (truncate (100*w) :: Integer) <> "%" let col :: ColWidth -> Html col cw = do H.col ! case cw of ColWidthDefault -> mempty ColWidth w -> if html5 then A.style (toValue $ "width: " <> percent w) else A.width (toValue $ percent w) nl return $ if all hasDefaultWidth colspecs then mempty else do H.colgroup $ do nl mapM_ (col . snd) colspecs nl tableRowToHtml :: PandocMonad m => WriterOptions -> TableRow -> StateT WriterState m Html tableRowToHtml opts (TableRow tblpart attr _rownum rowhead rowbody) = do let celltype = case tblpart of Thead -> HeaderCell _ -> BodyCell headcells <- mapM (cellToHtml opts HeaderCell) rowhead bodycells <- mapM (cellToHtml opts celltype) rowbody rowHtml <- addAttrs opts attr $ H.tr $ do nl mconcat headcells mconcat bodycells return $ do rowHtml nl colspanAttrib :: ColSpan -> Attribute colspanAttrib = \case ColSpan 1 -> mempty ColSpan n -> A.colspan (toValue n) rowspanAttrib :: RowSpan -> Attribute rowspanAttrib = \case RowSpan 1 -> mempty RowSpan n -> A.rowspan (toValue n) cellToHtml :: PandocMonad m => WriterOptions -> CellType -> Ann.Cell -> StateT WriterState m Html cellToHtml opts celltype (Ann.Cell (colspec :| _) _colNum cell) = let align = fst colspec in tableCellToHtml opts celltype align cell tableCellToHtml :: PandocMonad m => WriterOptions -> CellType -> Alignment -> Cell -> StateT WriterState m Html tableCellToHtml opts ctype colAlign (Cell attr align rowspan colspan item) = do contents <- blockListToHtml opts item html5 <- gets stHtml5 let (ident, cls, kvs) = attr let tag' = case ctype of BodyCell -> H.td HeaderCell -> H.th let align' = case align of AlignDefault -> colAlign _ -> align let kvs' = case htmlAlignmentToString align' of Nothing -> kvs Just alignStr -> if html5 then htmlAddStyle ("text-align", alignStr) kvs else case break ((== "align") . fst) kvs of (_, []) -> ("align", alignStr) : kvs (xs, _:rest) -> xs ++ ("align", alignStr) : rest otherAttribs <- attrsToHtml opts (ident, cls, kvs') let attribs = mconcat $ colspanAttrib colspan : rowspanAttrib rowspan : otherAttribs return $ do tag' ! attribs $ contents nl toListItems :: [Html] -> [Html] toListItems items = map toListItem items ++ [nl] toListItem :: Html -> Html toListItem item = nl *> H.li item blockListToHtml :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Html blockListToHtml opts lst = mconcat . intersperse (nl) . filter nonempty <$> mapM (blockToHtml opts) lst where nonempty (Empty _) = False nonempty _ = True -- | Convert list of Pandoc inline elements to HTML. inlineListToHtml :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Html inlineListToHtml opts lst = mconcat <$> mapM (inlineToHtml opts) lst -- | Annotates a MathML expression with the tex source annotateMML :: XML.Element -> Text -> XML.Element annotateMML e tex = math (unode "semantics" [cs, unode "annotation" (annotAttrs, T.unpack tex)]) where cs = case elChildren e of [] -> unode "mrow" () [x] -> x xs -> unode "mrow" xs math childs = XML.Element q as [XML.Elem childs] l where (XML.Element q as _ l) = e annotAttrs = [XML.Attr (unqual "encoding") "application/x-tex"] -- | Convert Pandoc inline element to HTML. inlineToHtml :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m Html inlineToHtml opts inline = do html5 <- gets stHtml5 case inline of (Str str) -> return $ strToHtml str Space -> return $ toHtml ' ' SoftBreak -> return $ case writerWrapText opts of WrapNone -> toHtml ' ' WrapAuto -> toHtml ' ' WrapPreserve -> toHtml '\n' LineBreak -> return $ do if html5 then H5.br else H.br toHtml '\n' (Span ("",[cls],[]) ils) | cls == "csl-block" || cls == "csl-left-margin" || cls == "csl-right-inline" || cls == "csl-indent" -> inlineListToHtml opts ils >>= inDiv cls (Span (id',classes,kvs) ils) -> let go Nothing c | c `Set.member` htmlSpanLikeElements = Just (customParent (textTag c), []) | c == "smallcaps" = Just (H.span ! A.class_ "smallcaps", []) | c == "underline" = Just (H.u, []) | otherwise = Nothing go (Just (t,cs)) c | c `Set.member` htmlSpanLikeElements = Just (t . customParent (textTag c), cs) | c == "smallcaps" = Just (t . (H.span ! A.class_ "smallcaps"), cs) | c == "underline" = Just (t . H.u, cs) | otherwise = Just (t, c:cs) spanLikeTags = L.foldl' go Nothing in case spanLikeTags classes of Just (tag, cs) -> do h <- inlineListToHtml opts ils addAttrs opts (id',cs,kvs') $ tag h Nothing -> do h <- inlineListToHtml opts ils addAttrs opts (id',classes',kvs') (H.span h) where styles = ["font-style:normal;" | "csl-no-emph" `elem` classes] ++ ["font-weight:normal;" | "csl-no-strong" `elem` classes] ++ ["font-variant:normal;" | "csl-no-smallcaps" `elem` classes] kvs' = if null styles then kvs else ("style", T.concat styles) : kvs classes' = [ c | c <- classes , c `notElem` [ "csl-no-emph" , "csl-no-strong" , "csl-no-smallcaps" ] ] (Emph lst) -> H.em <$> inlineListToHtml opts lst (Underline lst) -> H.u <$> inlineListToHtml opts lst (Strong lst) -> H.strong <$> inlineListToHtml opts lst (Code attr@(ids,cs,kvs) str) -> case hlCode of Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg addAttrs opts (ids,cs',kvs) $ fromMaybe H.code sampOrVar $ strToHtml str Right h -> do modify $ \st -> st{ stHighlighting = True } addAttrs opts (ids,[],kvs) $ fromMaybe id sampOrVar h where hlCode = case writerHighlightMethod opts of Skylighting _ -> highlighted DefaultHighlighting -> highlighted _ -> Left "" highlighted = highlight (writerSyntaxMap opts) formatHtmlInline attr str (sampOrVar,cs') | "sample" `elem` cs = (Just H.samp,"sample" `delete` cs) | "variable" `elem` cs = (Just H.var,"variable" `delete` cs) | otherwise = (Nothing,cs) (Strikeout lst) -> H.del <$> inlineListToHtml opts lst (SmallCaps lst) -> (H.span ! A.class_ "smallcaps") <$> inlineListToHtml opts lst (Superscript lst) -> H.sup <$> inlineListToHtml opts lst (Subscript lst) -> H.sub <$> inlineListToHtml opts lst (Quoted quoteType lst) -> let (leftQuote, rightQuote) = case quoteType of SingleQuote -> (toHtml '‘', toHtml '’') DoubleQuote -> (toHtml '“', toHtml '”') in if writerHtmlQTags opts then do modify $ \st -> st{ stQuotes = True } let (maybeAttr, lst') = case lst of [Span attr@(_, _, kvs) cs] | any ((=="cite") . fst) kvs -> (Just attr, cs) cs -> (Nothing, cs) let addAttrsMb = maybe return (addAttrs opts) inlineListToHtml opts lst' >>= addAttrsMb maybeAttr . H.q else (\x -> leftQuote >> x >> rightQuote) `fmap` inlineListToHtml opts lst (Math t str) -> do modify (\st -> st {stMath = True}) let mathClass = toValue $ ("math " :: Text) <> if t == InlineMath then "inline" else "display" case writerHTMLMathMethod opts of WebTeX url -> do let imtag = if html5 then H5.img else H.img let str' = T.strip str let s = case t of InlineMath -> "\\textstyle " DisplayMath -> "\\displaystyle " return $ imtag ! A.style "vertical-align:middle" ! A.src (toValue . (url <>) . urlEncode $ s <> str') ! A.alt (toValue str') ! A.title (toValue str') ! A.class_ mathClass GladTeX -> return $ customParent (textTag "eq") ! customAttribute "env" (toValue $ if t == InlineMath then ("math" :: Text) else "displaymath") $ strToHtml str MathML -> do let conf = useShortEmptyTags (const False) defaultConfigPP res <- lift $ convertMath writeMathML t str case res of Right r -> return $ preEscapedString $ ppcElement conf (annotateMML r str) Left il -> (H.span ! A.class_ mathClass) <$> inlineToHtml opts il MathJax _ -> return $ H.span ! A.class_ mathClass $ toHtml $ case t of InlineMath -> "\\(" <> str <> "\\)" DisplayMath -> "\\[" <> str <> "\\]" KaTeX _ -> return $ H.span ! A.class_ mathClass $ toHtml $ case t of InlineMath -> str DisplayMath -> str PlainMath -> do x <- lift (texMathToInlines t str) >>= inlineListToHtml opts return $ H.span ! A.class_ mathClass $ x (RawInline f str) -> do ishtml <- isRawHtml f if ishtml then return $ preEscapedText str else do let istex = f == Format "latex" || f == Format "tex" let mm = writerHTMLMathMethod opts case istex of True | allowsMathEnvironments mm && isMathEnvironment str -> do modify (\st -> st {stMath = True}) inlineToHtml opts $ Math DisplayMath str | allowsRef mm && isRef str -> do modify (\st -> st {stMath = True}) inlineToHtml opts $ Math InlineMath str _ -> do report $ InlineNotRendered inline return mempty (Link attr txt (s,_)) | "mailto:" `T.isPrefixOf` s -> do -- We need to remove links from link text, because an -- <a> element is not allowed inside another <a> -- element. linkText <- inlineListToHtml opts (removeLinks txt) obfuscateLink opts attr linkText s (Link (ident,classes,kvs) txt (s,tit)) -> do linkText <- inlineListToHtml opts (removeLinks txt) slideVariant <- gets stSlideVariant let s' = case T.uncons s of Just ('#',xs) -> let prefix = if slideVariant == RevealJsSlides then "/" else writerIdentifierPrefix opts in "#" <> prefix <> xs _ -> s let link = H.a ! A.href (toValue $ toURI html5 s') $ linkText link' <- addAttrs opts (ident, classes, kvs) link return $ if T.null tit then link' else link' ! A.title (toValue tit) (Image attr@(_, _, attrList) txt (s, tit)) -> do epubVersion <- gets stEPUBVersion let alternate = stringify txt slideVariant <- gets stSlideVariant let isReveal = slideVariant == RevealJsSlides attrs <- imgAttrsToHtml opts attr let attributes = -- reveal.js uses data-src for lazy loading (if isReveal then customAttribute "data-src" $ toValue s else A.src $ toValue $ toURI html5 s) : [A.title $ toValue tit | not (T.null tit)] ++ attrs imageTag = (if html5 then H5.img else H.img , [A.alt $ toValue alternate | isNothing (lookup "alt" attrList) && (isJust epubVersion || not (null txt))] ) mediaTag tg fallbackTxt = let linkTxt = if null txt then fallbackTxt else alternate in (tg $ H.a ! A.href (toValue $ toURI html5 s) $ toHtml linkTxt , [A5.controls ""] ) s' = fromMaybe s $ T.stripSuffix ".gz" s category = if "data:" `T.isPrefixOf` s then Just . T.takeWhile (/= '/') . T.drop 5 $ s else case parseURIReference (T.unpack s') of Just u -> mediaCategory $ uriPath u Nothing -> mediaCategory (T.unpack s) (tag, specAttrs) = case category of Just "image" -> imageTag Just "video" -> mediaTag H5.video "Video" Just "audio" -> mediaTag H5.audio "Audio" Just _ -> (H5.embed, []) _ -> imageTag return $ L.foldl' (!) tag $ attributes ++ specAttrs -- note: null title included, as in Markdown.pl (Note contents) -> do notes <- gets stNotes emittedNotes <- gets stEmittedNotes let number = emittedNotes + length notes + 1 let ref = tshow number htmlContents <- blockListToNote opts ref contents epubVersion <- gets stEPUBVersion -- push contents onto front of notes modify $ \st -> st {stNotes = htmlContents:notes} slideVariant <- gets stSlideVariant let revealSlash = T.pack ['/' | slideVariant == RevealJsSlides] let link = H.a ! A.href (toValue $ toURI html5 $ "#" <> revealSlash <> writerIdentifierPrefix opts <> "fn" <> ref) ! A.class_ "footnote-ref" ! prefixedId opts ("fnref" <> ref) $ (if isJust epubVersion then id else H.sup) $ toHtml ref return $ case epubVersion of Just EPUB3 -> link ! customAttribute "epub:type" "noteref" ! customAttribute "role" "doc-noteref" _ | html5 -> link ! A5.role "doc-noteref" _ -> link (Cite cits il)-> do contents <- inlineListToHtml opts (if html5 then walk addBibliorefRole il else il) let citationIds = T.unwords $ map citationId cits let result = H.span ! A.class_ "citation" $ contents return $ if html5 then result ! customAttribute "data-cites" (toValue citationIds) else result addBibliorefRole :: Inline -> Inline addBibliorefRole (Link (id',classes,kvs) ils (src,tit)) | "#ref-" `T.isPrefixOf` src = Link (id',classes,("role","doc-biblioref"):kvs) ils (src,tit) addBibliorefRole x = x blockListToNote :: PandocMonad m => WriterOptions -> Text -> [Block] -> StateT WriterState m Html blockListToNote opts ref blocks = do epubVersion <- gets stEPUBVersion html5 <- gets stHtml5 case epubVersion of Nothing -> do -- web page -- If last block is Para or Plain, include the backlink at the end of -- that block. Otherwise, insert a new Plain block with the backlink. let kvs = [("role","doc-backlink") | html5] let backlink = [Link ("",["footnote-back"],kvs) [Str "↩"] ("#" <> "fnref" <> ref,"")] let blocks' = if null blocks then [] else let lastBlock = last blocks otherBlocks = init blocks in case lastBlock of Para [Image (_,cls,_) _ (_,tit)] | "fig:" `T.isPrefixOf` tit || "r-stretch" `elem` cls -> otherBlocks ++ [lastBlock, Plain backlink] Para lst -> otherBlocks ++ [Para (lst ++ backlink)] Plain lst -> otherBlocks ++ [Plain (lst ++ backlink)] _ -> otherBlocks ++ [lastBlock, Plain backlink] contents <- blockListToHtml opts blocks' let noteItem = H.li ! prefixedId opts ("fn" <> ref) $ contents return $ noteItem >> nl Just epubv -> do let kvs = [("role","doc-backlink") | html5] let backlink = Link ("",["footnote-back"],kvs) [Str ref] ("#" <> "fnref" <> ref,"") 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) $ nl >> contents >> nl return $ noteItem >> nl inDiv :: PandocMonad m=> Text -> Html -> StateT WriterState m Html inDiv cls x = do html5 <- gets stHtml5 return $ (if html5 then H5.div else H.div) x ! A.class_ (toValue cls) isRef :: Text -> Bool isRef t = "\\ref{" `T.isPrefixOf` t || "\\eqref{" `T.isPrefixOf` t isMathEnvironment :: Text -> Bool isMathEnvironment s = "\\begin{" `T.isPrefixOf` s && envName `elem` mathmlenvs where envName = T.takeWhile (/= '}') (T.drop 7 s) mathmlenvs = [ "align" , "align*" , "alignat" , "alignat*" , "aligned" , "alignedat" , "array" , "Bmatrix" , "bmatrix" , "cases" , "CD" , "eqnarray" , "eqnarray*" , "equation" , "equation*" , "gather" , "gather*" , "gathered" , "matrix" , "multline" , "multline*" , "pmatrix" , "prooftree" -- bussproofs , "smallmatrix" , "split" , "subarray" , "Vmatrix" , "vmatrix" ] allowsMathEnvironments :: HTMLMathMethod -> Bool allowsMathEnvironments (MathJax _) = True allowsMathEnvironments (KaTeX _) = True allowsMathEnvironments MathML = True allowsMathEnvironments (WebTeX _) = True allowsMathEnvironments _ = False allowsRef :: HTMLMathMethod -> Bool allowsRef (MathJax _) = True allowsRef _ = False -- | List of intrinsic event attributes allowed on all elements in HTML4. intrinsicEventsHTML4 :: [Text] intrinsicEventsHTML4 = [ "onclick", "ondblclick", "onmousedown", "onmouseup", "onmouseover" , "onmouseout", "onmouseout", "onkeypress", "onkeydown", "onkeyup"] -- | Check to see if Format is valid HTML isRawHtml :: PandocMonad m => Format -> StateT WriterState m Bool isRawHtml f = do html5 <- gets stHtml5 return $ f == Format "html" || ((html5 && f == Format "html5") || f == Format "html4") || isSlideVariant f -- | Check to see if Format matches with an HTML slide variant isSlideVariant :: Format -> Bool isSlideVariant f = f `elem` [Format "s5", Format "slidy", Format "slideous", Format "dzslides", Format "revealjs"] toURI :: Bool -> Text -> Text toURI isHtml5 t = if isHtml5 then t else escapeURI t where escapeURI = T.pack . escapeURIString (not . needsEscaping) . T.unpack needsEscaping c = isSpace c || T.any (== c) "<>|\"{}[]^`" || not (isAscii c) hasVariable :: Text -> DT.Template a -> Bool hasVariable var = checkVar where matches v' = T.intercalate "." (DT.varParts v') == var checkVar (DT.Interpolate v) = matches v checkVar (DT.Conditional v t1 t2) = matches v || checkVar t1 || checkVar t2 checkVar (DT.Iterate v t1 t2) = matches v || checkVar t1 || checkVar t2 checkVar (DT.Nested t) = checkVar t checkVar (DT.Partial _ t) = checkVar t checkVar (DT.Concat t1 t2) = checkVar t1 || checkVar t2 checkVar (DT.Literal _) = False checkVar DT.Empty = False ================================================ FILE: src/Text/Pandoc/Writers/Haddock.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.Haddock Copyright : Copyright (C) 2014-2015,2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to haddock markup. Haddock: <http://www.haskell.org/haddock/doc/html/> -} module Text.Pandoc.Writers.Haddock (writeHaddock) where import Control.Monad (zipWithM) import Control.Monad.State.Strict ( StateT, MonadState(get), modify, evalStateT ) import Data.Char (isAlphaNum) import Data.Default import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared type Notes = [[Block]] newtype WriterState = WriterState { stNotes :: Notes } instance Default WriterState where def = WriterState{ stNotes = [] } -- | Convert Pandoc to Haddock. writeHaddock :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHaddock opts document = evalStateT (pandocToHaddock opts{ writerWrapText = writerWrapText opts } document) def -- | Return haddock representation of document. pandocToHaddock :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m Text pandocToHaddock opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing body <- blockListToHaddock opts blocks st <- get notes' <- notesToHaddock opts (reverse $ stNotes st) let main = body <> (if isEmpty notes' then empty else blankline <> notes') metadata <- metaToContext opts (blockListToHaddock opts) (fmap chomp . inlineListToHaddock opts) meta let context = defField "body" main metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Return haddock representation of notes. notesToHaddock :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState m (Doc Text) notesToHaddock opts notes = if null notes then return empty else do contents <- blockToHaddock opts $ OrderedList (1,DefaultStyle,DefaultDelim) notes return $ text "#notes#" <> blankline <> contents -- | Escape special characters for Haddock. escapeString :: Text -> Text escapeString t | T.all isAlphaNum t = t | otherwise = T.concatMap escChar t where escChar '\\' = "\\\\" escChar '/' = "\\/" escChar '\'' = "\\'" escChar '`' = "\\`" escChar '"' = "\\\"" escChar '@' = "\\@" escChar '<' = "\\<" escChar c = T.singleton c -- | Convert Pandoc block element to haddock. blockToHaddock :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> StateT WriterState m (Doc Text) blockToHaddock opts (Div _ ils) = do contents <- blockListToHaddock opts ils return $ contents <> blankline blockToHaddock opts (Plain inlines) = do contents <- inlineListToHaddock opts inlines return $ contents <> cr blockToHaddock opts (Para inlines) = -- TODO: if it contains linebreaks, we need to use a @...@ block (<> blankline) `fmap` blockToHaddock opts (Plain inlines) blockToHaddock opts (LineBlock lns) = blockToHaddock opts $ linesToPara lns blockToHaddock _ b@(RawBlock f str) | f == "haddock" = return $ literal str <> text "\n" | otherwise = do report $ BlockNotRendered b return empty blockToHaddock opts HorizontalRule = return $ blankline <> text (replicate (writerColumns opts) '_') <> blankline blockToHaddock opts (Header level (ident,_,_) inlines) = do contents <- inlineListToHaddock opts inlines let attr' = if T.null ident then empty else cr <> text "#" <> literal ident <> text "#" return $ nowrap (text (replicate level '=') <> space <> contents) <> attr' <> blankline blockToHaddock _ (CodeBlock (_,_,_) str) = return $ prefixed "> " (literal str) <> blankline -- Nothing in haddock corresponds to block quotes: blockToHaddock opts (BlockQuote blocks) = blockListToHaddock opts blocks blockToHaddock opts (Table _ blkCapt specs thead tbody tfoot) = do let Caption _ caption = blkCapt caption' <- blockListToHaddock opts caption let caption'' = if null caption then empty else blankline <> caption' <> blankline tbl <- gridTable opts blockListToHaddock specs thead tbody tfoot return $ (tbl $$ blankline $$ caption'') $$ blankline blockToHaddock opts (BulletList items) = do contents <- mapM (bulletListItemToHaddock opts) items return $ (if isTightList items then vcat else vsep) contents <> blankline blockToHaddock opts (OrderedList (start,_,delim) items) = do let attribs = (start, Decimal, delim) let markers = orderedListMarkers attribs let markers' = map (\m -> if T.length m < 3 then m <> T.replicate (3 - T.length m) " " else m) markers contents <- zipWithM (orderedListItemToHaddock opts) markers' items return $ (if isTightList items then vcat else vsep) contents <> blankline blockToHaddock opts (DefinitionList items) = do contents <- mapM (definitionListItemToHaddock opts) items return $ vcat contents <> blankline blockToHaddock opts (Figure _ (Caption _ longcapt) body) = -- Haddock has no concept of figures, floats, or captions. fmap (<> blankline) (blockListToHaddock opts (body ++ longcapt)) -- | Convert bullet list item (list of blocks) to haddock bulletListItemToHaddock :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m (Doc Text) bulletListItemToHaddock opts items = do contents <- blockListToHaddock opts items let sps = replicate (writerTabStop opts - 2) ' ' let start = text ('-' : ' ' : sps) return $ hang (writerTabStop opts) start contents $$ if endsWithPlain items then cr else blankline -- | Convert ordered list item (a list of blocks) to haddock orderedListItemToHaddock :: PandocMonad m => WriterOptions -- ^ options -> Text -- ^ list item marker -> [Block] -- ^ list item (list of blocks) -> StateT WriterState m (Doc Text) orderedListItemToHaddock opts marker items = do contents <- blockListToHaddock opts items let sps = case T.length marker - writerTabStop opts of n | n > 0 -> text $ replicate n ' ' _ -> text " " let start = literal marker <> sps return $ hang (writerTabStop opts) start contents $$ if endsWithPlain items then cr else blankline -- | Convert definition list item (label, list of blocks) to haddock definitionListItemToHaddock :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> StateT WriterState m (Doc Text) definitionListItemToHaddock opts (label, defs) = do labelText <- inlineListToHaddock opts label defs' <- mapM (mapM (blockToHaddock opts)) defs let contents = (if isTightList defs then vcat else vsep) $ map (\d -> hang 4 empty $ vcat d <> cr) defs' return $ nowrap (brackets labelText) $$ contents $$ if isTightList defs then cr else blankline -- | Convert list of Pandoc block elements to haddock blockListToHaddock :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements -> StateT WriterState m (Doc Text) blockListToHaddock opts blocks = mconcat <$> mapM (blockToHaddock opts) blocks -- | Convert list of Pandoc inline elements to haddock. inlineListToHaddock :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m (Doc Text) inlineListToHaddock opts lst = mconcat <$> mapM (inlineToHaddock opts) lst -- | Convert Pandoc inline element to haddock. inlineToHaddock :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m (Doc Text) inlineToHaddock opts (Span (ident,_,_) ils) = do contents <- inlineListToHaddock opts ils if not (T.null ident) && null ils then return $ "#" <> literal ident <> "#" else return contents inlineToHaddock opts (Emph lst) = do contents <- inlineListToHaddock opts lst return $ "/" <> contents <> "/" -- Underline is not supported, treat the same as Emph inlineToHaddock opts (Underline lst) = inlineToHaddock opts (Emph lst) inlineToHaddock opts (Strong lst) = do contents <- inlineListToHaddock opts lst return $ "__" <> contents <> "__" inlineToHaddock opts (Strikeout lst) = do contents <- inlineListToHaddock opts lst -- not supported in haddock, but we fake it: return $ "~~" <> contents <> "~~" -- not supported in haddock: inlineToHaddock opts (Superscript lst) = inlineListToHaddock opts lst -- not supported in haddock: inlineToHaddock opts (Subscript lst) = inlineListToHaddock opts lst -- not supported in haddock: inlineToHaddock opts (SmallCaps lst) = inlineListToHaddock opts lst inlineToHaddock opts (Quoted SingleQuote lst) = do contents <- inlineListToHaddock opts lst return $ "‘" <> contents <> "’" inlineToHaddock opts (Quoted DoubleQuote lst) = do contents <- inlineListToHaddock opts lst return $ "“" <> contents <> "”" inlineToHaddock _ (Code _ str) = return $ "@" <> literal (escapeString str) <> "@" inlineToHaddock _ (Str str) = return $ literal $ escapeString str inlineToHaddock _ (Math mt str) = return $ case mt of DisplayMath -> cr <> "\\[" <> literal str <> "\\]" <> cr InlineMath -> "\\(" <> literal str <> "\\)" inlineToHaddock _ il@(RawInline f str) | f == "haddock" = return $ literal str | otherwise = do report $ InlineNotRendered il return empty -- no line break in haddock (see above on CodeBlock) inlineToHaddock _ LineBreak = return cr inlineToHaddock opts SoftBreak = case writerWrapText opts of WrapAuto -> return space WrapNone -> return space WrapPreserve -> return cr inlineToHaddock _ Space = return space inlineToHaddock opts (Cite _ lst) = inlineListToHaddock opts lst inlineToHaddock _ (Link _ txt (src, _)) = do let linktext = literal $ escapeString $ stringify txt let useAuto = isURI src && case txt of [Str s] | escapeURI s == src -> True _ -> False return $ nowrap $ "<" <> literal src <> (if useAuto then empty else space <> linktext) <> ">" inlineToHaddock opts (Image attr alternate (source, tit)) = do linkhaddock <- inlineToHaddock opts (Link attr alternate (source, tit)) return $ "<" <> linkhaddock <> ">" -- haddock doesn't have notes, but we can fake it: inlineToHaddock opts (Note contents) = do modify (\st -> st{ stNotes = contents : stNotes st }) st <- get let ref = literal $ writerIdentifierPrefix opts <> tshow (length $ stNotes st) return $ "<#notes [" <> ref <> "]>" ================================================ FILE: src/Text/Pandoc/Writers/ICML.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.ICML Copyright : Copyright (C) 2013-2020 github.com/mb21 License : GNU GPL, version 2 or above Stability : alpha Conversion of 'Pandoc' documents to Adobe InCopy ICML, a stand-alone XML format which is a subset of the zipped IDML format for which the documentation is available here: http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/indesign/sdk/cs6/idml/idml-specification.pdf InCopy is the companion word-processor to Adobe InDesign and ICML documents can be integrated into InDesign with File -> Place. -} module Text.Pandoc.Writers.ICML (writeICML) where import Control.Monad.Except (catchError) import Control.Monad (liftM2) import Control.Monad.State.Strict ( MonadTrans(lift), StateT(runStateT), MonadState(state, get, put) ) import Data.List (intersperse) import Data.Maybe (fromMaybe, maybeToList) import qualified Data.Set as Set import qualified Data.Text as Text import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad, fetchItem, report) import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout hiding (link) import Text.Pandoc.Shared import Text.Pandoc.URI (isURI) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Shared import Text.Pandoc.XML type Style = [Text] type Hyperlink = [(Int, Text)] data WriterState = WriterState{ blockStyles :: Set.Set Text , inlineStyles :: Set.Set Text , objectStyles :: Set.Set Text , links :: Hyperlink , listDepth :: Int , maxListDepth :: Int } type WS m = StateT WriterState m defaultWriterState :: WriterState defaultWriterState = WriterState{ blockStyles = Set.empty , inlineStyles = Set.empty , objectStyles = Set.empty , links = [] , listDepth = 1 , maxListDepth = 0 } -- inline names (appear in InDesign's character styles pane) emphName :: Text underlineName :: Text strongName :: Text strikeoutName :: Text superscriptName :: Text subscriptName :: Text smallCapsName :: Text codeName :: Text linkName :: Text emphName = "Italic" underlineName = "Underline" strongName = "Bold" strikeoutName = "Strikeout" superscriptName = "Superscript" subscriptName = "Subscript" smallCapsName = "SmallCaps" codeName = "Code" linkName = "Link" -- block element names (appear in InDesign's paragraph styles pane) paragraphName :: Text figureName :: Text imgCaptionName :: Text codeBlockName :: Text blockQuoteName :: Text orderedListName :: Text bulletListName :: Text defListTermName :: Text defListDefName :: Text headerName :: Text tableName :: Text tableHeaderName :: Text tableCaptionName :: Text alignLeftName :: Text alignRightName :: Text alignCenterName :: Text firstListItemName :: Text beginsWithName :: Text lowerRomanName :: Text upperRomanName :: Text lowerAlphaName :: Text upperAlphaName :: Text subListParName :: Text footnoteName :: Text citeName :: Text paragraphName = "Paragraph" figureName = "Figure" imgCaptionName = "Caption" codeBlockName = "CodeBlock" blockQuoteName = "Blockquote" orderedListName = "NumList" bulletListName = "BulList" defListTermName = "DefListTerm" defListDefName = "DefListDef" headerName = "Header" tableName = "TablePar" tableHeaderName = "TableHeader" tableCaptionName = "TableCaption" alignLeftName = "LeftAlign" alignRightName = "RightAlign" alignCenterName = "CenterAlign" firstListItemName = "first" beginsWithName = "beginsWith-" lowerRomanName = "lowerRoman" upperRomanName = "upperRoman" lowerAlphaName = "lowerAlpha" upperAlphaName = "upperAlpha" subListParName = "subParagraph" footnoteName = "Footnote" citeName = "Cite" -- | Convert Pandoc document to string in ICML format. writeICML :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeICML opts doc = do let Pandoc meta blocks = ensureValidXmlIdentifiers doc let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing renderBlockMeta f s = fst <$> runStateT (f opts [] s) defaultWriterState renderInlineMeta f s = fst <$> runStateT (f opts [] "" s) defaultWriterState metadata <- metaToContext opts (renderBlockMeta blocksToICML) (renderInlineMeta inlinesToICML) meta (main, st) <- runStateT (blocksToICML opts [] blocks) defaultWriterState let context = defField "body" main $ defField "charStyles" (charStylesToDoc st) $ defField "parStyles" (parStylesToDoc st) $ defField "objectStyles" (objectStylesToDoc st) $ defField "hyperlinks" (hyperlinksToDoc $ links st) metadata return $ render colwidth $ (if writerPreferAscii opts then fmap toEntities else id) $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Auxiliary functions for parStylesToDoc and charStylesToDoc. contains :: Text -> (Text, (Text, Text)) -> [(Text, Text)] -> [(Text, Text)] contains s (t, (k,v)) attrs = if t `Text.isInfixOf` s then case lookup k attrs of -- avoid duplicates, #9158 Nothing -> (k, v) : attrs Just _ -> attrs else attrs -- | The monospaced font to use as default. monospacedFont :: Doc Text monospacedFont = inTags False "AppliedFont" [("type", "string")] $ text "Courier New" -- | How much to indent blockquotes etc. defaultIndent :: Int defaultIndent = 20 -- | How much to indent numbered lists before the number. defaultListIndent :: Int defaultListIndent = 10 -- other constants lineSeparator :: Text lineSeparator = "
" -- | Convert a WriterState with its block styles to the ICML listing of Paragraph Styles. parStylesToDoc :: WriterState -> Doc Text parStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ blockStyles st where makeStyle s = let countSubStrs sub str = length $ Text.breakOnAll sub str attrs = foldr (contains s) [] [ (defListTermName, ("BulletsAndNumberingListType", "BulletList")) , (defListTermName, ("FontStyle", "Bold")) , (tableHeaderName, ("FontStyle", "Bold")) , (alignLeftName, ("Justification", "LeftAlign")) , (alignRightName, ("Justification", "RightAlign")) , (alignCenterName, ("Justification", "CenterAlign")) , (headerName<>"1", ("PointSize", "36")) , (headerName<>"2", ("PointSize", "30")) , (headerName<>"3", ("PointSize", "24")) , (headerName<>"4", ("PointSize", "18")) , (headerName<>"5", ("PointSize", "14")) ] -- what is the most nested list type, if any? (isBulletList, isOrderedList) = findList $ reverse $ splitTextBy (==' ') s where findList [] = (False, False) findList (x:xs) | x == bulletListName = (True, False) | x == orderedListName = (False, True) | otherwise = findList xs nBuls = countSubStrs bulletListName s nOrds = countSubStrs orderedListName s attrs' = numbering <> listType <> indent <> attrs where numbering | isOrderedList = [("NumberingExpression", "^#.^t"), ("NumberingLevel", tshow nOrds)] | otherwise = [] listType | isOrderedList && not (subListParName `Text.isInfixOf` s) = [("BulletsAndNumberingListType", "NumberedList")] | isBulletList && not (subListParName `Text.isInfixOf` s) = [("BulletsAndNumberingListType", "BulletList")] | otherwise = [] indent = [("LeftIndent", tshow indt)] where nBlockQuotes = countSubStrs blockQuoteName s nDefLists = countSubStrs defListDefName s indt = max 0 $ defaultListIndent*(nBuls + nOrds - 1) + defaultIndent*(nBlockQuotes + nDefLists) props = inTags True "Properties" [] (basedOn $$ tabList $$ numbForm) where font = if codeBlockName `Text.isInfixOf` s then monospacedFont else empty basedOn = inTags False "BasedOn" [("type", "object")] (text "$ID/NormalParagraphStyle") $$ font tabList = if isBulletList then inTags True "TabList" [("type","list")] $ inTags True "ListItem" [("type","record")] $ vcat [ inTags False "Alignment" [("type","enumeration")] $ text "LeftAlign" , inTags False "AlignmentCharacter" [("type","string")] $ text "." , selfClosingTag "Leader" [("type","string")] , inTags False "Position" [("type","unit")] $ text $ show $ defaultListIndent * (nBuls + nOrds) ] else empty makeNumb name = inTags False "NumberingFormat" [("type", "string")] (text name) numbForm | Text.isInfixOf lowerRomanName s = makeNumb "i, ii, iii, iv..." | Text.isInfixOf upperRomanName s = makeNumb "I, II, III, IV..." | Text.isInfixOf lowerAlphaName s = makeNumb "a, b, c, d..." | Text.isInfixOf upperAlphaName s = makeNumb "A, B, C, D..." | otherwise = empty in inTags True "ParagraphStyle" ([("Self", "ParagraphStyle/"<>s), ("Name", s)] ++ attrs') props -- | Convert a WriterState with its inline styles to the ICML listing of Character Styles. charStylesToDoc :: WriterState -> Doc Text charStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ inlineStyles st where makeStyle s = let attrs = foldr (contains s) [] [ (strikeoutName, ("StrikeThru", "true")) , (superscriptName, ("Position", "Superscript")) , (subscriptName, ("Position", "Subscript")) , (smallCapsName, ("Capitalization", "SmallCaps")) ] attrs' | Text.isInfixOf emphName s && Text.isInfixOf strongName s = ("FontStyle", "Bold Italic") : attrs | Text.isInfixOf strongName s = ("FontStyle", "Bold") : attrs | Text.isInfixOf emphName s = ("FontStyle", "Italic") : attrs | otherwise = attrs props = inTags True "Properties" [] $ inTags False "BasedOn" [("type", "object")] (text "$ID/NormalCharacterStyle") $$ font where font = if codeName `Text.isInfixOf` s then monospacedFont else empty in inTags True "CharacterStyle" ([("Self", "CharacterStyle/"<>s), ("Name", s)] ++ attrs') props -- | Convert a WriterState with its object styles to the ICML listing of Object Styles. objectStylesToDoc :: WriterState -> Doc Text objectStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ objectStyles st where makeStyle s = let attrs = [] props = inTags True "Properties" [] $ inTags False "BasedOn" [("type", "string")] (text "$ID/None") in inTags True "ObjectStyle" ([("Self", "ObjectStyle/"<>s), ("Name", s)] ++ attrs) props -- | Escape colon characters as %3a escapeColons :: Text -> Text escapeColons txt = Text.replace ":" "%3a" txt -- | figure out the link destination for a given URL -- | HyperlinkURLDestination with more than one colon crashes CS6 makeDest :: Text -> Doc Text makeDest txt = literal $ if "#" `Text.isPrefixOf` txt then "HyperlinkTextDestination/" <> escTxt else "HyperlinkURLDestination/" <> escTxt where escTxt = escapeColons $ escapeStringForXML txt -- | Convert a list of (identifier, url) pairs to the ICML listing of hyperlinks. hyperlinksToDoc :: Hyperlink -> Doc Text hyperlinksToDoc [] = empty hyperlinksToDoc (x:xs) = hyp x $$ hyperlinksToDoc xs where hyp (ident, url) = hdest $$ hlink where hdest = if "#" `Text.isPrefixOf` url then empty else selfClosingTag "HyperlinkURLDestination" [("Self", "HyperlinkURLDestination/"<>escapeColons url), ("Name","link"), ("DestinationURL",url), ("DestinationUniqueKey","1")] -- HyperlinkURLDestination with more than one colon crashes CS6 hlink = inTags True "Hyperlink" [("Self","uf-"<>tshow ident), ("Name",url), ("Source","htss-"<>tshow ident), ("Visible","false"), ("DestinationUniqueKey","1")] $ inTags True "Properties" [] $ inTags False "BorderColor" [("type","enumeration")] (text "Black") $$ inTags False "Destination" [("type","object")] (makeDest url) -- | Key for specifying user-defined styles dynamicStyleKey :: Text dynamicStyleKey = "custom-style" -- | Convert a list of Pandoc blocks to ICML. blocksToICML :: PandocMonad m => WriterOptions -> Style -> [Block] -> WS m (Doc Text) blocksToICML opts style lst = do docs <- mapM (blockToICML opts style) lst return $ intersperseBrs docs -- | Convert a Pandoc block element to ICML. blockToICML :: PandocMonad m => WriterOptions -> Style -> Block -> WS m (Doc Text) blockToICML opts style (Plain lst) = parStyle opts style "" lst blockToICML opts style (Para lst) = parStyle opts (paragraphName:style) "" lst blockToICML opts style (LineBlock lns) = blockToICML opts style $ linesToPara lns blockToICML opts style (CodeBlock _ str) = parStyle opts (codeBlockName:style) "" [Str str] blockToICML _ _ b@(RawBlock f str) | f == Format "icml" = return $ literal str | otherwise = do report $ BlockNotRendered b return empty blockToICML opts style (BlockQuote blocks) = blocksToICML opts (blockQuoteName:style) blocks blockToICML opts style (OrderedList attribs lst) = listItemsToICML opts orderedListName style (Just attribs) lst blockToICML opts style (BulletList lst) = listItemsToICML opts bulletListName style Nothing lst blockToICML opts style (DefinitionList lst) = intersperseBrs `fmap` mapM (definitionListItemToICML opts style) lst blockToICML opts style (Header lvl (ident, cls, _) lst) = let stl = (headerName <> tshow lvl <> unnumbered):style unnumbered = if "unnumbered" `elem` cls then " (unnumbered)" else "" in parStyle opts stl ident lst blockToICML _ _ HorizontalRule = return empty -- we could insert a page break instead blockToICML opts style (Table attr blkCapt specs thead tbody tfoot) = let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot style' = tableName : style noHeader = all null headers nrHeaders = if noHeader then "0" else "1" nrRows = length rows nrCols = case rows of [] -> 0 (r:_) -> length r rowsToICML [] _ = return empty rowsToICML (col:rest) rowNr = liftM2 ($$) (colsToICML col aligns rowNr (0::Int)) $ rowsToICML rest (rowNr+1) colsToICML [] _ _ _ = return empty colsToICML _ [] _ _ = return empty colsToICML (cell:rest) (alig:restAligns) rowNr colNr = do let stl = if rowNr == 0 && not noHeader then tableHeaderName:style' else style' stl' | alig == AlignLeft = alignLeftName : stl | alig == AlignRight = alignRightName : stl | alig == AlignCenter = alignCenterName : stl | otherwise = stl c <- blocksToICML opts stl' cell let cl = return $ inTags True "Cell" [("Name", tshow colNr <>":"<> tshow rowNr), ("AppliedCellStyle","CellStyle/Cell")] c liftM2 ($$) cl $ colsToICML rest restAligns rowNr (colNr+1) in do let tabl = if noHeader then rows else headers:rows cells <- rowsToICML tabl (0::Int) let colWidths w = [("SingleColumnWidth",tshow $ 500 * w) | w > 0] let tupToDoc tup = selfClosingTag "Column" $ ("Name",tshow $ fst tup) : colWidths (snd tup) let colDescs = vcat $ zipWith (curry tupToDoc) [0..nrCols-1] widths let (_,_,kvs) = attr let dynamicStyle = fromMaybe "Table" (lookup dynamicStyleKey kvs) let tableDoc = return $ inTags True "Table" [ ("AppliedTableStyle","TableStyle/" <> dynamicStyle) , ("HeaderRowCount", nrHeaders) , ("BodyRowCount", tshow nrRows) , ("ColumnCount", tshow nrCols) ] (colDescs $$ cells) liftM2 ($$) tableDoc $ parStyle opts (tableCaptionName:style) "" caption blockToICML opts style (Div (_ident, _, kvs) lst) = let dynamicStyle = maybeToList $ lookup dynamicStyleKey kvs in blocksToICML opts (dynamicStyle <> style) lst blockToICML opts style (Figure attr capt@(Caption _ longcapt) body) = case body of [Plain [img@(Image {})]] -> do figure <- parStyle opts (figureName:style) "" [img] caption <- parStyle opts (imgCaptionName:style) "" $ blocksToInlines longcapt return $ intersperseBrs [figure, caption] _ -> -- fallback to rendering the figure as a Div blockToICML opts style $ figureDiv attr capt body -- | Convert a list of lists of blocks to ICML list items. listItemsToICML :: PandocMonad m => WriterOptions -> Text -> Style -> Maybe ListAttributes -> [[Block]] -> WS m (Doc Text) listItemsToICML _ _ _ _ [] = return empty listItemsToICML opts listType style attribs (first:rest) = do st <- get put st{ listDepth = 1 + listDepth st} let stl = listType:style let f = listItemToICML opts stl True attribs first let r = map (listItemToICML opts stl False attribs) rest docs <- sequence $ f:r s <- get let maxD = max (maxListDepth s) (listDepth s) put s{ listDepth = 1, maxListDepth = maxD } return $ intersperseBrs docs -- | Convert a list of blocks to ICML list items. listItemToICML :: PandocMonad m => WriterOptions -> Style -> Bool-> Maybe ListAttributes -> [Block] -> WS m (Doc Text) listItemToICML opts style isFirst attribs item = let makeNumbStart (Just (beginsWith, numbStl, _)) = let doN DefaultStyle = [] doN LowerRoman = [lowerRomanName] doN UpperRoman = [upperRomanName] doN LowerAlpha = [lowerAlphaName] doN UpperAlpha = [upperAlphaName] doN _ = [] bw = [beginsWithName <> tshow beginsWith | beginsWith > 1] in doN numbStl ++ bw makeNumbStart Nothing = [] stl = if isFirst then firstListItemName:style else style stl' = makeNumbStart attribs ++ stl in case item of (i:rest@(_:_)) -> do let insertTab (Para lst) = blockToICML opts (subListParName:style) (Para (Str "\t":lst)) insertTab block = blockToICML opts style block f <- blockToICML opts stl' i r <- mapM insertTab rest return $ intersperseBrs (f : r) _ -> blocksToICML opts stl' item definitionListItemToICML :: PandocMonad m => WriterOptions -> Style -> ([Inline],[[Block]]) -> WS m (Doc Text) definitionListItemToICML opts style (term,defs) = do term' <- parStyle opts (defListTermName:style) "" term defs' <- mapM (blocksToICML opts (defListDefName:style)) defs return $ intersperseBrs (term' : defs') -- | Convert a list of inline elements to ICML. inlinesToICML :: PandocMonad m => WriterOptions -> Style -> Text -> [Inline] -> WS m (Doc Text) inlinesToICML opts style ident lst = vcat `fmap` mapM (inlineToICML opts style ident) (mergeStrings opts lst) -- | Convert an inline element to ICML. inlineToICML :: PandocMonad m => WriterOptions -> Style -> Text -> Inline -> WS m (Doc Text) inlineToICML _ style ident (Str str) = charStyle style ident $ literal $ escapeStringForXML str inlineToICML opts style ident (Emph lst) = inlinesToICML opts (emphName:style) ident lst inlineToICML opts style ident (Underline lst) = inlinesToICML opts (underlineName:style) ident lst inlineToICML opts style ident (Strong lst) = inlinesToICML opts (strongName:style) ident lst inlineToICML opts style ident (Strikeout lst) = inlinesToICML opts (strikeoutName:style) ident lst inlineToICML opts style ident (Superscript lst) = inlinesToICML opts (superscriptName:style) ident lst inlineToICML opts style ident (Subscript lst) = inlinesToICML opts (subscriptName:style) ident lst inlineToICML opts style ident (SmallCaps lst) = inlinesToICML opts (smallCapsName:style) ident lst inlineToICML opts style ident (Quoted SingleQuote lst) = inlinesToICML opts style ident $ mergeStrings opts $ [Str "‘"] ++ lst ++ [Str "’"] inlineToICML opts style ident (Quoted DoubleQuote lst) = inlinesToICML opts style ident $ mergeStrings opts $ [Str "“"] ++ lst ++ [Str "”"] inlineToICML opts style ident (Cite _ lst) = inlinesToICML opts (citeName:style) ident lst inlineToICML _ style ident (Code _ str) = charStyle (codeName:style) ident $ literal $ escapeStringForXML str inlineToICML _ style ident Space = charStyle style ident space inlineToICML opts style ident SoftBreak = case writerWrapText opts of WrapAuto -> charStyle style ident space WrapNone -> charStyle style ident space WrapPreserve -> charStyle style ident cr inlineToICML _ style ident LineBreak = charStyle style ident $ literal lineSeparator inlineToICML opts style ident (Math mt str) = lift (texMathToInlines mt str) >>= (fmap mconcat . mapM (inlineToICML opts style ident)) inlineToICML _ _ _ il@(RawInline f str) | f == Format "icml" = return $ literal str | otherwise = do report $ InlineNotRendered il return empty inlineToICML opts style ident (Link _ lst (url, title)) = do content <- inlinesToICML opts (linkName:style) ident lst state $ \st -> let link_id = case links st of [] -> 1 :: Int ((n,_):_) -> 1 + n newst = st{ links = (link_id, url):links st } cont = inTags True "HyperlinkTextSource" [("Self","htss-"<>tshow link_id), ("Name",title), ("Hidden","false")] content in (cont, newst) inlineToICML opts style _ident (Image attr _ target) = imageICML opts style attr target inlineToICML opts style _ (Note lst) = footnoteToICML opts style lst inlineToICML opts style _ (Span (ident, _, kvs) lst) = let dynamicStyle = maybeToList $ lookup dynamicStyleKey kvs in inlinesToICML opts (dynamicStyle <> style) ident lst -- ident will be the id of the span, that we need to use down in the hyperlink setter -- if T.null ident -- then -- else do -- | Convert a list of block elements to an ICML footnote. footnoteToICML :: PandocMonad m => WriterOptions -> Style -> [Block] -> WS m (Doc Text) footnoteToICML opts style lst = let insertTab (Para ls) = blockToICML opts (footnoteName:style) $ Para $ Str "\t":ls insertTab block = blockToICML opts (footnoteName:style) block in do contents <- mapM insertTab lst let number = inTags True "ParagraphStyleRange" [] $ inTags True "CharacterStyleRange" [] $ inTagsSimple "Content" "<?ACE 4?>" return $ inTags True "CharacterStyleRange" [("AppliedCharacterStyle","$ID/NormalCharacterStyle"), ("Position","Superscript")] $ inTags True "Footnote" [] $ number $$ intersperseBrs contents -- | Auxiliary function to merge Space elements into the adjacent Strs. mergeStrings :: WriterOptions -> [Inline] -> [Inline] mergeStrings opts = mergeStrings' . map spaceToStr where spaceToStr Space = Str " " spaceToStr SoftBreak = case writerWrapText opts of WrapPreserve -> Str "\n" _ -> Str " " spaceToStr x = x mergeStrings' (Str x : Str y : zs) = mergeStrings' (Str (x <> y) : zs) mergeStrings' (x : xs) = x : mergeStrings' xs mergeStrings' [] = [] -- | Intersperse line breaks intersperseBrs :: [Doc Text] -> Doc Text intersperseBrs = vcat . intersperse (selfClosingTag "Br" []) . filter (not . isEmpty) -- | Wrap a list of inline elements in an ICML Paragraph Style parStyle :: PandocMonad m => WriterOptions -> Style -> Text -> [Inline] -> WS m (Doc Text) parStyle opts style ident lst = let slipIn x y = if Text.null y then x else x <> " > " <> y stlStr = foldr slipIn "" $ reverse style stl = if Text.null stlStr then "" else "ParagraphStyle/" <> stlStr attrs = ("AppliedParagraphStyle", stl) attrs' = if firstListItemName `elem` style then let ats = attrs : [("NumberingContinue", "false")] begins = filter (Text.isPrefixOf beginsWithName) style in case begins of [] -> ats (b:_) -> let i = fromMaybe "" (Text.stripPrefix beginsWithName b) in ("NumberingStartAt", i) : ats else [attrs] in do content <- inlinesToICML opts [] ident lst let cont = inTags True "ParagraphStyleRange" attrs' content state $ \st -> (cont, st{ blockStyles = Set.insert stlStr $ blockStyles st }) -- | Create the destination name makeDestName :: Text -> Text makeDestName name = "#" <> Text.replace " " "-" name -- | Create a HyperlinkTextDestination for a given identifier makeLinkDest :: Text -> Doc Text -> Doc Text makeLinkDest ident cont = vcat [ selfClosingTag "HyperlinkTextDestination" [("Self", "HyperlinkTextDestination/"<>makeDestName ident), ("Name","Destination"), ("DestinationUniqueKey","1")] , inTagsSimple "Content" $ flush cont ] -- | Create the markup for the content (incl. named destinations) -- | NOTE: since we have no easy way to get actual named dests, we just create them for any short content blocks makeContent :: Text -> Doc Text -> Doc Text makeContent ident cont | isEmpty cont = empty | not (Text.null ident) = makeLinkDest ident cont | otherwise = inTagsSimple "Content" $ flush cont -- | Wrap a Doc in an ICML Character Style. charStyle :: PandocMonad m => Style -> Text -> Doc Text -> WS m (Doc Text) charStyle style ident content = let (stlStr, attrs) = styleToStrAttr style doc = inTags True "CharacterStyleRange" attrs $ makeContent ident content in state $ \st -> let styles = if Text.null stlStr then st else st{ inlineStyles = Set.insert stlStr $ inlineStyles st } in (doc, styles) -- | Transform a Style to a tuple of String (eliminating duplicates and ordered) and corresponding attribute. styleToStrAttr :: Style -> (Text, [(Text, Text)]) styleToStrAttr style = let stlStr = Text.unwords $ Set.toAscList $ Set.fromList style stl = if null style then "$ID/NormalCharacterStyle" else "CharacterStyle/" <> stlStr attrs = [("AppliedCharacterStyle", stl)] in (stlStr, attrs) -- | Key for specifying user-defined object (image) styles objectStyleKey :: Text objectStyleKey = "object-style" -- | Assemble an ICML Image. imageICML :: PandocMonad m => WriterOptions -> Style -> Attr -> Target -> WS m (Doc Text) imageICML opts style attr (src, _) = do imgS <- catchError (do (img, _) <- fetchItem src case imageSize opts img of Right size -> return size Left msg -> do report $ CouldNotDetermineImageSize src msg return def) (\e -> do report $ CouldNotFetchResource src $ tshow e return def) let (ow, oh) = sizeInPoints imgS (imgWidth, imgHeight) = desiredSizeInPoints opts attr imgS hw = showFl $ ow / 2 hh = showFl $ oh / 2 scale = showFl (imgWidth / ow) <> " 0 0 " <> showFl (imgHeight / oh) src' = if isURI src then src else "file:" <> src (stlStr, attrs) = styleToStrAttr style props = inTags True "Properties" [] $ inTags True "PathGeometry" [] $ inTags True "GeometryPathType" [("PathOpen","false")] $ inTags True "PathPointArray" [] $ vcat [ selfClosingTag "PathPointType" [("Anchor", "-"<>hw<>" -"<>hh), ("LeftDirection", "-"<>hw<>" -"<>hh), ("RightDirection", "-"<>hw<>" -"<>hh)] , selfClosingTag "PathPointType" [("Anchor", "-"<>hw<>" "<>hh), ("LeftDirection", "-"<>hw<>" "<>hh), ("RightDirection", "-"<>hw<>" "<>hh)] , selfClosingTag "PathPointType" [("Anchor", hw<>" "<>hh), ("LeftDirection", hw<>" "<>hh), ("RightDirection", hw<>" "<>hh)] , selfClosingTag "PathPointType" [("Anchor", hw<>" -"<>hh), ("LeftDirection", hw<>" -"<>hh), ("RightDirection", hw<>" -"<>hh)] ] isdata = "data:" `Text.isPrefixOf` src' && "base64," `Text.isInfixOf` src' contents = if isdata then -- see #8398 inTags True "Contents" [] $ literal ("<![CDATA[" <> Text.replace "%20" "" (Text.drop 1 (Text.dropWhile (/=',') src')) <> "]]>") else mempty link = if isdata then mempty else selfClosingTag "Link" [("Self", "ueb"), ("LinkResourceURI", src')] (_,_,kvs) = attr applyObjectStyle = lookup objectStyleKey kvs image = inTags True "Image" [("Self","ue6"), ("ItemTransform", scale <> " -" <> hw <> " -" <> hh)] $ vcat [ inTags True "Properties" [] $ vcat [ inTags True "Profile" [("type","string")] $ text "$ID/Embedded", selfClosingTag "GraphicBounds" [("Left","0"), ("Top","0"), ("Right", showFl $ ow * ow / imgWidth), ("Bottom", showFl $ oh * oh / imgHeight)], contents ], link ] doc = inTags True "CharacterStyleRange" attrs $ inTags True "Rectangle" ([("Self","uec"), ("StrokeWeight", "0"), ("ItemTransform", scale <> " " <> hw <> " -" <> hh)] ++ maybe [] (\aos -> [("AppliedObjectStyle", "ObjectStyle/" <> aos)]) applyObjectStyle ) (props $$ image) state $ \st -> (doc, st{ inlineStyles = Set.insert stlStr $ inlineStyles st, objectStyles = case applyObjectStyle of Just styleName -> Set.insert styleName $ objectStyles st Nothing -> objectStyles st }) ================================================ FILE: src/Text/Pandoc/Writers/Ipynb.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.Ipynb Copyright : Copyright (C) 2019-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Ipynb (Jupyter notebook JSON format) writer for pandoc. -} module Text.Pandoc.Writers.Ipynb ( writeIpynb ) where import Control.Monad (foldM) import Control.Monad.State ( StateT(runStateT), modify ) import qualified Data.Map as M import Data.Maybe (catMaybes, fromMaybe) import Text.Pandoc.Options import Text.Pandoc.Definition import Data.Ipynb as Ipynb import Text.Pandoc.Walk (walkM) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad import Text.Pandoc.Logging import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Aeson as Aeson import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Shared (safeRead) import Text.Pandoc.URI (isURI) import Text.Pandoc.Writers.Shared (metaToContext') import Text.Pandoc.Writers.Markdown (writePlain, writeMarkdown) import qualified Data.Text.Encoding as TE import qualified Data.ByteString.Lazy as BL import Data.Aeson.Encode.Pretty (Config(..), defConfig, encodePretty', keyOrder, Indent(Spaces)) import Text.DocLayout (literal) import Text.Pandoc.UUID (getRandomUUID) import Data.Char (isAscii, isAlphaNum) writeIpynb :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeIpynb opts d = do notebook <- pandocToNotebook opts d return $ TE.decodeUtf8 . BL.toStrict . encodePretty' defConfig{ confIndent = Spaces 1, confTrailingNewline = True, confCompare = keyOrder [ "cells", "nbformat", "nbformat_minor", "cell_type", "output_type", "execution_count", "metadata", "outputs", "source", "data", "name", "text" ] <> compare } $ notebook pandocToNotebook :: PandocMonad m => WriterOptions -> Pandoc -> m (Notebook NbV4) pandocToNotebook opts (Pandoc meta blocks) = do -- we use writePlain w/ default options because e.g. we don't want -- to add backslash escapes or convert en dashes, see #7928 let blockWriter bs = literal <$> writePlain def (Pandoc nullMeta bs) let inlineWriter ils = literal . T.stripEnd <$> writePlain def (Pandoc nullMeta [Plain ils]) let jupyterMeta = case lookupMeta "jupyter" meta of Just (MetaMap m) -> Meta m _ -> mempty let nbformat = case (lookupMeta "nbformat" jupyterMeta, lookupMeta "nbformat_minor" jupyterMeta) of (Just (MetaInlines [Str "4"]), Just (MetaInlines [Str y])) -> case safeRead y of Just z -> (4, z) Nothing -> (4, 5) _ -> (4, 5) -- write as v4.5 metadata' <- toJSON <$> metaToContext' blockWriter inlineWriter (B.deleteMeta "nbformat" . B.deleteMeta "nbformat_minor" $ jupyterMeta) -- convert from a Value (JSON object) to a M.Map Text Value: let metadata = case fromJSON metadata' of Error _ -> mempty -- TODO warning here? shouldn't happen Success x -> x cells <- extractCells nbformat opts blocks return $ Notebook{ notebookMetadata = metadata , notebookFormat = nbformat , notebookCells = cells } addAttachment :: PandocMonad m => Inline -> StateT (M.Map Text MimeBundle) m Inline addAttachment (Image attr lab (src,tit)) | not (isURI src) = do (img, mbmt) <- fetchItem src let mt = fromMaybe "application/octet-stream" mbmt modify $ M.insert src (MimeBundle (M.insert mt (BinaryData img) mempty)) return $ Image attr lab ("attachment:" <> src, tit) addAttachment x = return x extractCells :: PandocMonad m => (Int, Int) -> WriterOptions -> [Block] -> m [Ipynb.Cell a] extractCells _ _ [] = return [] extractCells nbformat opts (Div (ident,classes,kvs) xs : bs) | "cell" `elem` classes , "markdown" `elem` classes = do let meta = pairsToJSONMeta kvs (newdoc, attachments) <- runStateT (walkM addAttachment (Pandoc nullMeta xs)) mempty source <- writeMarkdown opts{ writerTemplate = Nothing } newdoc uuid <- uuidFrom nbformat ident (Ipynb.Cell{ cellType = Markdown , cellId = uuid , cellSource = Source $ breakLines $ T.stripEnd source , cellMetadata = meta , cellAttachments = if M.null attachments then Nothing else Just $ MimeAttachments attachments } :) <$> extractCells nbformat opts bs | "cell" `elem` classes , "code" `elem` classes = do let (codeContent, rest) = case xs of (CodeBlock _ t : ys) -> (t, ys) ys -> (mempty, ys) let meta = pairsToJSONMeta kvs outputs <- catMaybes <$> mapM blockToOutput rest let exeCount = lookup "execution_count" kvs >>= safeRead uuid <- uuidFrom nbformat ident (Ipynb.Cell{ cellType = Ipynb.Code { codeExecutionCount = exeCount , codeOutputs = outputs } , cellId = uuid , cellSource = Source $ breakLines codeContent , cellMetadata = meta , cellAttachments = Nothing } :) <$> extractCells nbformat opts bs | "cell" `elem` classes , "raw" `elem` classes = case consolidateAdjacentRawBlocks xs of [RawBlock (Format f) raw] -> do let format' = case T.toLower f of "html" -> "text/html" "html4" -> "text/html" "html5" -> "text/html" "s5" -> "text/html" "slidy" -> "text/html" "slideous" -> "text/html" "dzslides" -> "text/html" "revealjs" -> "text/html" "latex" -> "text/latex" "markdown" -> "text/markdown" "rst" -> "text/restructuredtext" "asciidoc" -> "text/asciidoc" _ -> f uuid <- uuidFrom nbformat ident (Ipynb.Cell{ cellType = Raw , cellId = uuid , cellSource = Source $ breakLines raw , cellMetadata = if format' == "ipynb" -- means no format given then mempty else JSONMeta $ M.insert "raw_mimetype" (Aeson.String format') mempty , cellAttachments = Nothing } :) <$> extractCells nbformat opts bs _ -> extractCells nbformat opts bs extractCells nbformat opts (CodeBlock (ident,classes,kvs) raw : bs) | "code" `elem` classes = do let meta = pairsToJSONMeta kvs let exeCount = lookup "execution_count" kvs >>= safeRead uuid <- uuidFrom nbformat ident (Ipynb.Cell{ cellType = Ipynb.Code { codeExecutionCount = exeCount , codeOutputs = [] } , cellId = uuid , cellSource = Source $ breakLines raw , cellMetadata = meta , cellAttachments = Nothing } :) <$> extractCells nbformat opts bs extractCells nbformat opts (b:bs) = do let isCodeOrDiv (CodeBlock (_,cl,_) _) = "code" `elem` cl isCodeOrDiv (Div (_,cl,_) _) = "cell" `elem` cl isCodeOrDiv _ = False let (mds, rest) = break isCodeOrDiv bs extractCells nbformat opts (Div ("",["cell","markdown"],[]) (b:mds) : rest) -- Return Nothing if nbformat < 4.5. -- Otherwise construct a UUID, using the existing identifier -- if it is a valid UUID, otherwise constructing a new one. uuidFrom :: PandocMonad m => (Int, Int) -> Text -> m (Maybe Text) uuidFrom nbformat ident = if nbformat >= (4,5) then if isValidUUID ident then return $ Just ident else Just . T.pack . drop 9 . show <$> getRandomUUID else return Nothing where isValidUUID t = not (T.null t) && T.length t <= 64 && T.all isValidUUIDChar t isValidUUIDChar c = isAscii c && (isAlphaNum c || c == '-' || c == '_') blockToOutput :: PandocMonad m => Block -> m (Maybe (Output a)) blockToOutput (Div (_,["output","stream",sname],_) (CodeBlock _ t:_)) = return $ Just $ Stream{ streamName = sname , streamText = Source (breakLines t) } blockToOutput (Div (_,["output","error"],kvs) (CodeBlock _ t:_)) = return $ Just $ Err{ errName = fromMaybe mempty (lookup "ename" kvs) , errValue = fromMaybe mempty (lookup "evalue" kvs) , errTraceback = breakLines t } blockToOutput (Div (_,["output","execute_result"],kvs) bs) = do (data', metadata') <- extractData bs return $ Just $ ExecuteResult{ executeCount = fromMaybe 0 $ lookup "execution_count" kvs >>= safeRead , executeData = data' , executeMetadata = pairsToJSONMeta kvs <> metadata'} blockToOutput (Div (_,["output","display_data"],kvs) bs) = do (data', metadata') <- extractData bs return $ Just $ DisplayData { displayData = data' , displayMetadata = pairsToJSONMeta kvs <> metadata'} blockToOutput _ = return Nothing extractData :: PandocMonad m => [Block] -> m (MimeBundle, JSONMeta) extractData bs = do (mmap, meta) <- foldM go mempty $ consolidateAdjacentRawBlocks bs return (MimeBundle mmap, meta) where go (mmap, meta) b@(Para [Image (_,_,kvs) _ (src,_)]) = do (img, mbmt) <- fetchItem src case mbmt of Just mt -> return (M.insert mt (BinaryData img) mmap, meta <> pairsToJSONMeta kvs) Nothing -> (mmap, meta) <$ report (BlockNotRendered b) go (mmap, meta) b@(CodeBlock (_,["json"],_) code) = case decode (UTF8.fromTextLazy $ TL.fromStrict code) of Just v -> return (M.insert "application/json" (JsonData v) mmap, meta) Nothing -> (mmap, meta) <$ report (BlockNotRendered b) go (mmap, meta) (CodeBlock ("",[],[]) code) = return (M.insert "text/plain" (TextualData code) mmap, meta) go (mmap, meta) (RawBlock (Format "html") raw) = return (M.insert "text/html" (TextualData raw) mmap, meta) go (mmap, meta) (RawBlock (Format "latex") raw) = return (M.insert "text/latex" (TextualData raw) mmap, meta) go (mmap, meta) (RawBlock (Format "markdown") raw) = return (M.insert "text/markdown" (TextualData raw) mmap, meta) go (mmap, meta) (Div _ bs') = foldM go (mmap, meta) bs' go (mmap, meta) b = (mmap, meta) <$ report (BlockNotRendered b) pairsToJSONMeta :: [(Text, Text)] -> JSONMeta pairsToJSONMeta kvs = JSONMeta $ M.fromList [(k, case Aeson.decode (UTF8.fromTextLazy $ TL.fromStrict v) of Just val -> val Nothing -> String v) | (k,v) <- kvs , k /= "execution_count" ] consolidateAdjacentRawBlocks :: [Block] -> [Block] consolidateAdjacentRawBlocks [] = [] consolidateAdjacentRawBlocks (RawBlock f1 x : RawBlock f2 y : xs) | f1 == f2 = consolidateAdjacentRawBlocks (RawBlock f1 (x <> "\n" <> y) : xs) consolidateAdjacentRawBlocks (x : xs) = x : consolidateAdjacentRawBlocks xs ================================================ FILE: src/Text/Pandoc/Writers/JATS/References.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.JATS.References Copyright : © 2021-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <albert+pandoc@tarleb.com> Stability : alpha Portability : portable Creation of a bibliography list using @<element-citation>@ elements in reference items. -} module Text.Pandoc.Writers.JATS.References ( referencesToJATS , referenceToJATS ) where import Citeproc.Pandoc () import Citeproc.Types ( Date (..), DateParts (..), ItemId (..), Name (..), Reference (..) , Val (..) , lookupVariable, valToText ) import Data.Text (Text) import Text.DocLayout (Doc, empty, isEmpty, literal, vcat) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Builder (Inlines) import Text.Pandoc.Options (WriterOptions) import Text.Pandoc.Shared (tshow) import Text.Pandoc.Writers.JATS.Types import Text.Pandoc.XML (escapeNCName, escapeStringForXML, inTags) import qualified Data.Text as T referencesToJATS :: PandocMonad m => WriterOptions -> [Reference Inlines] -> JATS m (Doc Text) referencesToJATS opts = fmap vcat . mapM (referenceToJATS opts) referenceToJATS :: PandocMonad m => WriterOptions -> Reference Inlines -> JATS m (Doc Text) referenceToJATS _opts ref = do let refType = referenceType ref let pubType = [("publication-type", refType) | not (T.null refType)] let ident = escapeNCName $ "ref-" <> unItemId (referenceId ref) let wrap = inTags True "ref" [("id", ident)] . inTags True "element-citation" pubType return . wrap . vcat $ [ authors , "title" `varInTag` if refType == "book" then "source" else "article-title" , if refType == "book" then empty else "container-title" `varInTag` "source" , editors , "publisher" `varInTag` "publisher-name" , "publisher-place" `varInTag` "publisher-loc" , yearTag , accessed , "volume" `varInTag` "volume" , "issue" `varInTag` "issue" , "edition" `varInTag` "edition" , "page-first" `varInTag` "fpage" , "ISBN" `varInTag` "isbn" , "ISSN" `varInTag` "issn" , "URL" `varInTag` "uri" , varInTagWith "doi" "pub-id" [("pub-id-type", "doi")] , varInTagWith "pmid" "pub-id" [("pub-id-type", "pmid")] ] ++ case lookupVariable "page" ref >>= valToText of Nothing -> [] Just val -> let isdash c = c == '-' || c == '\x2013' (fpage, lpage) = T.dropWhile isdash <$> T.break isdash val in [ inTags' "fpage" [] $ literal $ escapeStringForXML fpage, inTags' "lpage" [] $ literal $ escapeStringForXML lpage ] where varInTag var tagName = varInTagWith var tagName [] varInTagWith var tagName tagAttribs = case lookupVariable var ref >>= valToText of Nothing -> mempty Just val -> inTags' tagName tagAttribs . literal $ escapeStringForXML val authors = case lookupVariable "author" ref of Just (NamesVal names) -> inTags True "person-group" [("person-group-type", "author")] . vcat $ map toNameElements names _ -> empty editors = case lookupVariable "editor" ref of Just (NamesVal names) -> inTags True "person-group" [("person-group-type", "editor")] . vcat $ map toNameElements names _ -> empty yearTag = case lookupVariable "issued" ref of Just (DateVal date) -> toDateElements date _ -> empty accessed = case lookupVariable "accessed" ref of Just (DateVal d) -> inTags' "date-in-citation" [("content-type", "access-date")] (toDateElements d) _ -> empty toDateElements :: Date -> Doc Text toDateElements date = case dateParts date of dp@(DateParts (y:m:d:_)):_ -> yearElement y dp <> monthElement m <> dayElement d dp@(DateParts (y:m:_)):_ -> yearElement y dp <> monthElement m dp@(DateParts (y:_)):_ -> yearElement y dp _ -> empty yearElement :: Int -> DateParts -> Doc Text yearElement year dp = inTags' "year" [("iso-8601-date", iso8601 dp)] $ literal (fourDigits year) monthElement :: Int -> Doc Text monthElement month = inTags' "month" [] . literal $ twoDigits month dayElement :: Int -> Doc Text dayElement day = inTags' "day" [] . literal $ twoDigits day iso8601 :: DateParts -> Text iso8601 = T.intercalate "-" . \case DateParts (y:m:d:_) -> [fourDigits y, twoDigits m, twoDigits d] DateParts (y:m:_) -> [fourDigits y, twoDigits m] DateParts (y:_) -> [fourDigits y] _ -> [] twoDigits :: Int -> Text twoDigits n = T.takeEnd 2 $ '0' `T.cons` tshow n fourDigits :: Int -> Text fourDigits n = T.takeEnd 4 $ "000" <> tshow n toNameElements :: Name -> Doc Text toNameElements name | not (isEmpty nameTags) = inTags' "name" [] nameTags | nameLiteral name == Just "others" = "<etal/>" | otherwise = nameLiteral name `inNameTag` "string-name" where inNameTag mVal tag = case mVal of Nothing -> empty Just val -> inTags' tag [] . literal $ escapeStringForXML val surnamePrefix = maybe mempty (`T.snoc` ' ') $ nameNonDroppingParticle name givenSuffix = maybe mempty (T.cons ' ') $ nameDroppingParticle name nameTags = mconcat [ ((surnamePrefix <>) <$> nameFamily name) `inNameTag` "surname" , ((<> givenSuffix) <$> nameGiven name) `inNameTag` "given-names" , nameSuffix name `inNameTag` "suffix" ] -- | Put the supplied contents between start and end tags of tagType, -- with specified attributes. inTags' :: Text -> [(Text, Text)] -> Doc Text -> Doc Text inTags' = inTags False ================================================ FILE: src/Text/Pandoc/Writers/JATS/Table.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.Writers.JATS.Table Copyright : © 2020-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <albert+pandoc@tarleb.com> Stability : alpha Portability : portable Conversion of 'Pandoc' tables to JATS XML. -} module Text.Pandoc.Writers.JATS.Table ( tableToJATS ) where import Control.Monad.Reader (asks) import Data.List.NonEmpty (NonEmpty ((:|))) import Data.Text (Text) import Text.DocLayout (Doc, empty, vcat, ($$)) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions) import Text.Pandoc.Shared (tshow) import Text.Pandoc.Writers.JATS.Types import Text.Pandoc.XML (escapeNCName, inTags, inTagsIndented, selfClosingTag) import qualified Data.Text as T import qualified Text.Pandoc.Writers.AnnotatedTable as Ann tableToJATS :: PandocMonad m => WriterOptions -> Ann.Table -> JATS m (Doc Text) tableToJATS opts (Ann.Table attr caption colspecs thead tbodies tfoot) = do let (Caption _maybeShortCaption captionBlocks) = caption -- Only paragraphs are allowed in captions, all other blocks must be -- wrapped in @<p>@ elements. let needsWrapping = \case Plain{} -> False Para{} -> False _ -> True tbl <- captionlessTable opts attr colspecs thead tbodies tfoot captionDoc <- if null captionBlocks then return empty else do blockToJATS <- asks jatsBlockWriter inTagsIndented "caption" <$> blockToJATS needsWrapping opts captionBlocks return $ inTags True "table-wrap" [] $ captionDoc $$ tbl captionlessTable :: PandocMonad m => WriterOptions -> Attr -> [ColSpec] -> Ann.TableHead -> [Ann.TableBody] -> Ann.TableFoot -> JATS m (Doc Text) captionlessTable opts attr colspecs thead tbodies tfoot = do head' <- tableHeadToJats opts thead bodies <- mapM (tableBodyToJats opts) tbodies foot' <- tableFootToJats opts tfoot let validAttribs = [ "border", "cellpadding", "cellspacing", "content-type" , "frame", "rules", "specific-use", "style", "summary" , "width" ] let attribs = toAttribs attr validAttribs return $ inTags True "table" attribs $ vcat [ colSpecListToJATS colspecs , head' , foot' , vcat bodies ] validTablePartAttribs :: [Text] validTablePartAttribs = [ "align", "char", "charoff", "content-type", "style", "valign" ] tableBodyToJats :: PandocMonad m => WriterOptions -> Ann.TableBody -> JATS m (Doc Text) tableBodyToJats opts (Ann.TableBody attr _rowHeadCols inthead rows) = do let attribs = toAttribs attr validTablePartAttribs intermediateHead <- if null inthead then return mempty else headerRowsToJats opts Thead inthead bodyRows <- bodyRowsToJats opts rows return $ inTags True "tbody" attribs $ intermediateHead $$ bodyRows tableHeadToJats :: PandocMonad m => WriterOptions -> Ann.TableHead -> JATS m (Doc Text) tableHeadToJats opts (Ann.TableHead attr rows) = tablePartToJats opts Thead attr rows tableFootToJats :: PandocMonad m => WriterOptions -> Ann.TableFoot -> JATS m (Doc Text) tableFootToJats opts (Ann.TableFoot attr rows) = tablePartToJats opts Tfoot attr rows tablePartToJats :: PandocMonad m => WriterOptions -> TablePart -> Attr -> [Ann.HeaderRow] -> JATS m (Doc Text) tablePartToJats opts tblpart attr rows = if null rows || all isEmptyRow rows then return mempty else do let tag' = case tblpart of Thead -> "thead" Tfoot -> "tfoot" Tbody -> "tbody" -- this would be unexpected let attribs = toAttribs attr validTablePartAttribs inTags True tag' attribs <$> headerRowsToJats opts tblpart rows where isEmptyRow (Ann.HeaderRow _attr _rownum cells) = all isEmptyCell cells isEmptyCell (Ann.Cell _colspecs _colnum cell) = cell == Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) [] -- | The part of a table; header, footer, or body. data TablePart = Thead | Tfoot | Tbody deriving (Eq) data CellType = HeaderCell | BodyCell data TableRow = TableRow TablePart Attr Ann.RowNumber Ann.RowHead Ann.RowBody headerRowsToJats :: PandocMonad m => WriterOptions -> TablePart -> [Ann.HeaderRow] -> JATS m (Doc Text) headerRowsToJats opts tablepart = rowListToJats opts . map toTableRow where toTableRow (Ann.HeaderRow attr rownum rowbody) = TableRow tablepart attr rownum [] rowbody bodyRowsToJats :: PandocMonad m => WriterOptions -> [Ann.BodyRow] -> JATS m (Doc Text) bodyRowsToJats opts = rowListToJats opts . zipWith toTableRow [1..] where toTableRow rownum (Ann.BodyRow attr _rownum rowhead rowbody) = TableRow Tbody attr rownum rowhead rowbody rowListToJats :: PandocMonad m => WriterOptions -> [TableRow] -> JATS m (Doc Text) rowListToJats opts = fmap vcat . mapM (tableRowToJats opts) colSpecListToJATS :: [ColSpec] -> Doc Text colSpecListToJATS colspecs = let hasDefaultWidth (_, ColWidthDefault) = True hasDefaultWidth _ = False percent w = tshow (round (100*w) :: Integer) <> "%" col :: ColWidth -> Doc Text col = selfClosingTag "col" . \case ColWidthDefault -> mempty ColWidth w -> [("width", percent w)] in if all hasDefaultWidth colspecs then mempty else inTags True "colgroup" [] $ vcat $ map (col . snd) colspecs tableRowToJats :: PandocMonad m => WriterOptions -> TableRow -> JATS m (Doc Text) tableRowToJats opts (TableRow tblpart attr _rownum rowhead rowbody) = do let validAttribs = [ "align", "char", "charoff", "content-type" , "style", "valign" ] let attr' = toAttribs attr validAttribs let celltype = case tblpart of Thead -> HeaderCell _ -> BodyCell headcells <- mapM (cellToJats opts HeaderCell) rowhead bodycells <- mapM (cellToJats opts celltype) rowbody return $ inTags True "tr" attr' $ mconcat [ vcat headcells , vcat bodycells ] alignmentAttrib :: Alignment -> Maybe (Text, Text) alignmentAttrib = fmap ("align",) . \case AlignLeft -> Just "left" AlignRight -> Just "right" AlignCenter -> Just "center" AlignDefault -> Nothing colspanAttrib :: ColSpan -> Maybe (Text, Text) colspanAttrib = \case ColSpan 1 -> Nothing ColSpan n -> Just ("colspan", tshow n) rowspanAttrib :: RowSpan -> Maybe (Text, Text) rowspanAttrib = \case RowSpan 1 -> Nothing RowSpan n -> Just ("rowspan", tshow n) cellToJats :: PandocMonad m => WriterOptions -> CellType -> Ann.Cell -> JATS m (Doc Text) cellToJats opts celltype (Ann.Cell (colspec :| _) _colNum cell) = let align = fst colspec in tableCellToJats opts celltype align cell toAttribs :: Attr -> [Text] -> [(Text, Text)] toAttribs (ident, _classes, kvs) knownAttribs = (if T.null ident then id else (("id", escapeNCName ident) :)) $ filter ((`elem` knownAttribs) . fst) kvs tableCellToJats :: PandocMonad m => WriterOptions -> CellType -> Alignment -> Cell -> JATS m (Doc Text) tableCellToJats opts ctype colAlign (Cell attr align rowspan colspan item) = do blockToJats <- asks jatsBlockWriter inlinesToJats <- asks jatsInlinesWriter let fixBreak LineBreak = RawInline (Format "jats") "<break/>" fixBreak x = x let cellContents = \case [Plain inlines] -> inlinesToJats opts (map fixBreak inlines) -- Note: <break/> is allowed only as a direct -- child of <td>, so we don't use walk. blocks -> blockToJats needsWrapInCell opts blocks let tag' = case ctype of BodyCell -> "td" HeaderCell -> "th" let align' = case align of AlignDefault -> colAlign _ -> align let maybeCons = maybe id (:) let validAttribs = [ "abbr", "align", "axis", "char", "charoff" , "content-type", "headers", "scope", "style", "valign" ] let attribs = maybeCons (alignmentAttrib align') . maybeCons (rowspanAttrib rowspan) . maybeCons (colspanAttrib colspan) $ toAttribs attr validAttribs inTags False tag' attribs <$> cellContents item -- | Whether the JATS produced from this block should be wrapped in a -- @<p>@ element when put directly below a @<td>@ element. needsWrapInCell :: Block -> Bool needsWrapInCell = \case Plain{} -> False -- should be unwrapped anyway Para{} -> False BulletList{} -> False OrderedList{} -> False DefinitionList{} -> False HorizontalRule -> False CodeBlock{} -> False RawBlock{} -> False -- responsibility of the user _ -> True ================================================ FILE: src/Text/Pandoc/Writers/JATS/Types.hs ================================================ {- | Module : Text.Pandoc.Writers.JATS.Types Copyright : Copyright (C) 2017-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Types for pandoc's JATS writer. -} module Text.Pandoc.Writers.JATS.Types ( JATS , JATSEnv (..) , JATSState (..) , JATSTagSet (..) ) where import Citeproc.Types (Reference) import Control.Monad.Reader (ReaderT) import Control.Monad.State (StateT) import Data.Text (Text) import Text.DocLayout (Doc) import Text.Pandoc.Builder (Block, Inline, Inlines) import Text.Pandoc.Options (WriterOptions) -- | JATS tag set variant data JATSTagSet = TagSetArchiving -- ^ Archiving and Interchange Tag Set | TagSetPublishing -- ^ Journal Publishing Tag Set | TagSetArticleAuthoring -- ^ Article Authoring Tag Set deriving (Eq) -- | Internal state used by the writer. newtype JATSState = JATSState { jatsNotes :: [(Int, Doc Text)] } -- | Environment containing all information relevant for rendering. data JATSEnv m = JATSEnv { jatsTagSet :: JATSTagSet -- ^ The tag set that's being output , jatsBlockWriter :: (Block -> Bool) -> WriterOptions -> [Block] -> JATS m (Doc Text) -- ^ Converts a block list to JATS, wrapping top-level blocks into a -- @<p>@ element if the property evaluates to @True@. -- See #7227. , jatsInlinesWriter :: WriterOptions -> [Inline] -> JATS m (Doc Text) -- ^ Converts an inline list to JATS. , jatsReferences :: [Reference Inlines] -- ^ List of references } -- | JATS writer type type JATS m = StateT JATSState (ReaderT (JATSEnv m) m) ================================================ FILE: src/Text/Pandoc/Writers/JATS.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.JATS Copyright : 2017-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to JATS XML. Reference: https://jats.nlm.nih.gov/publishing/tag-library -} module Text.Pandoc.Writers.JATS ( writeJATS , writeJatsArchiving , writeJatsPublishing , writeJatsArticleAuthoring ) where import Control.Applicative ((<|>)) import Control.Monad import Control.Monad.Reader import Control.Monad.State import Data.Generics (everywhere, mkT) import qualified Data.Map as M import Data.Maybe (fromMaybe, listToMaybe, isNothing) import Data.Time (toGregorian, Day, parseTimeM, defaultTimeLocale, formatTime) import qualified Data.Text as T import Data.Text (Text) import Text.Pandoc.Citeproc (getReferences) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (languages, languagesByExtension) import Text.Pandoc.Logging import Text.Pandoc.MIME (getMimeType) import Text.Pandoc.Walk (walk) import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.DocTemplates (Context(..), Val(..), toVal) import Text.Pandoc.Writers.JATS.References (referencesToJATS) import Text.Pandoc.Writers.JATS.Table (tableToJATS) import Text.Pandoc.Writers.JATS.Types import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Pandoc.XML import Text.TeXMath import qualified Text.Pandoc.Writers.AnnotatedTable as Ann import qualified Text.XML.Light as Xml -- | Default human-readable names for roles in the Contributor Role -- Taxonomy (CRediT). This is useful for generating JATS that annotate -- contributor roles creditNames :: M.Map Text Text creditNames = M.fromList [ ("conceptualization", "Conceptualization"), ("data-curation", "Data curation"), ("formal-analysis", "Formal analysis"), ("funding-acquisition", "Funding acquisition"), ("investigation", "Investigation"), ("methodology", "Methodology"), ("project-administration", "Project administration"), ("resources", "Resources"), ("software", "Software"), ("supervision", "Supervision"), ("validation", "Validation"), ("visualization", "Visualization"), ("writing-original-draft", "Writing – original draft"), ("writing-review-editing", "Writing – review & editing")] -- | Ensure every role with a `credit` key also has a `credit-name`, -- using a default value if necessary addCreditNames :: Context Text -> Context Text addCreditNames context = case getField "author" context of -- If there is an "authors" key, then we replace the existing value -- with one we mutate by running the addCreditNamesToAuthor helper -- function on each Just (ListVal authors) -> resetField "author" (map addCreditNamesToAuthor authors) context -- If there is no "authors" key in the context, then we don't have to do -- anything, and just return the context as is _ -> context where addCreditNamesToAuthor :: Val Text -> Val Text addCreditNamesToAuthor val = fromMaybe val $ do MapVal authorCtx <- pure val ListVal roles <- getField "roles" authorCtx return $ toVal $ resetField "roles" (map addCreditNameToRole roles) authorCtx addCreditNameToRole :: Val Text -> Val Text addCreditNameToRole val = fromMaybe val $ do MapVal roleCtx <- pure val guard $ isNothing (getField "credit-name" roleCtx :: Maybe (Val Text)) creditId <- getField "credit" roleCtx creditName <- M.lookup creditId creditNames return $ toVal $ resetField "credit-name" creditName roleCtx -- | Convert a @'Pandoc'@ document to JATS (Archiving and Interchange -- Tag Set.) writeJatsArchiving :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJatsArchiving = writeJats TagSetArchiving -- | Convert a @'Pandoc'@ document to JATS (Journal Publishing Tag Set.) writeJatsPublishing :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJatsPublishing = writeJats TagSetPublishing -- | Convert a @'Pandoc'@ document to JATS (Archiving and Interchange -- Tag Set.) writeJatsArticleAuthoring :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJatsArticleAuthoring = writeJats TagSetArticleAuthoring -- | Alias for @'writeJatsArchiving'@. This function exists for backwards -- compatibility, but will be deprecated in the future. Use -- @'writeJatsArchiving'@ instead. {-# DEPRECATED writeJATS "Use writeJatsArchiving instead" #-} writeJATS :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJATS = writeJatsArchiving -- | Convert a @'Pandoc'@ document to JATS. writeJats :: PandocMonad m => JATSTagSet -> WriterOptions -> Pandoc -> m Text writeJats tagSet opts d = do refs <- if extensionEnabled Ext_element_citations $ writerExtensions opts then getReferences Nothing d else pure [] let environment = JATSEnv { jatsTagSet = tagSet , jatsInlinesWriter = inlinesToJATS , jatsBlockWriter = wrappedBlocksToJATS , jatsReferences = refs } let initialState = JATSState { jatsNotes = [] } runReaderT (evalStateT (docToJATS opts d) initialState) environment -- see #9017 for motivation ensureReferenceHeader :: [Block] -> [Block] ensureReferenceHeader [] = [] ensureReferenceHeader (h@(Header{}):refs@(Div ("refs",_,_) _) : xs) = h:refs:xs ensureReferenceHeader (refs@(Div ("refs",_,_) _) : xs) = Header 1 nullAttr mempty : refs : xs ensureReferenceHeader (x:xs) = x :ensureReferenceHeader xs -- | Convert Pandoc document to string in JATS format. docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> JATS m Text docToJATS opts (Pandoc meta blocks') = do -- The numbering here follows LaTeX's internal numbering let startLvl = case writerTopLevelDivision opts of TopLevelPart -> -1 TopLevelChapter -> 0 TopLevelSection -> 1 TopLevelDefault -> 1 let blocks = makeSections (writerNumberSections opts) (Just startLvl) $ ensureReferenceHeader blocks' let splitBackBlocks b@(Div ("refs",_,_) _) (fs, bs) = (fs, b:bs) splitBackBlocks (Div (ident,("section":_),_) ( Header lev (_,hcls,hkvs) hils : (Div rattrs@("refs",_,_) rs) : rest )) (fs, bs) = (fs ++ rest, Div rattrs (Header lev (ident,hcls,hkvs) hils : rs) : bs) splitBackBlocks b (fs, bs) = (b:fs, bs) let (bodyblocks, backblocks) = foldr splitBackBlocks ([],[]) blocks let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing metadata <- metaToContext opts (blocksToJATS opts . makeSections False (Just startLvl)) (fmap chomp . inlinesToJATS opts) meta main <- blocksToJATS opts bodyblocks notes <- gets (reverse . map snd . jatsNotes) backs <- blocksToJATS opts backblocks tagSet <- asks jatsTagSet -- In the "Article Authoring" tag set, occurrence of fn-group elements -- is restricted to table footers. Footnotes have to be placed inline. let fns = if null notes || tagSet == TagSetArticleAuthoring then mempty else inTagsIndented "fn-group" $ vcat notes let back = backs $$ fns let date = case getField "date" metadata of Nothing -> NullVal Just (SimpleVal (x :: Doc Text)) -> case parseDate (render Nothing x) of Nothing -> NullVal Just day -> let (y,m,d) = toGregorian day in MapVal . Context $ M.fromList [("year" :: Text, SimpleVal $ text $ show y) ,("month", SimpleVal $ text $ show m) ,("day", SimpleVal $ text $ show d) ,("iso-8601", SimpleVal $ text $ formatTime defaultTimeLocale "%F" day) ] Just x -> x title' <- inlinesToJATS opts $ map fixLineBreak (lookupMetaInlines "title" meta) let context = defField "body" main $ defField "back" back $ addCreditNames $ resetField "title" title' $ resetField "date" date $ defField "mathml" (case writerHTMLMathMethod opts of MathML -> True _ -> False) metadata return $ render colwidth $ (if writerPreferAscii opts then fmap toEntities else id) $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Convert a list of Pandoc blocks to JATS. blocksToJATS :: PandocMonad m => WriterOptions -> [Block] -> JATS m (Doc Text) blocksToJATS = wrappedBlocksToJATS (const False) -- | Like @'blocksToJATS'@, but wraps top-level blocks into a @<p>@ -- element if the @needsWrap@ predicate evaluates to @True@. wrappedBlocksToJATS :: PandocMonad m => (Block -> Bool) -> WriterOptions -> [Block] -> JATS m (Doc Text) wrappedBlocksToJATS needsWrap opts = fmap vcat . mapM wrappedBlockToJATS where wrappedBlockToJATS b = do inner <- blockToJATS opts b return $ if needsWrap b then inTags True "p" [("specific-use","wrapper")] inner else inner -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block plainToPara (Plain x) = Para x plainToPara x = x -- | Convert a list of pairs of terms and definitions into a list of -- JATS varlistentrys. deflistItemsToJATS :: PandocMonad m => WriterOptions -> [([Inline],[[Block]])] -> JATS m (Doc Text) deflistItemsToJATS opts items = vcat <$> mapM (uncurry (deflistItemToJATS opts)) items -- | Convert a term and a list of blocks into a JATS varlistentry. deflistItemToJATS :: PandocMonad m => WriterOptions -> [Inline] -> [[Block]] -> JATS m (Doc Text) deflistItemToJATS opts term defs = do term' <- inlinesToJATS opts term def' <- wrappedBlocksToJATS (not . isPara) opts $ concatMap (walk demoteHeaderAndRefs . map plainToPara) defs return $ inTagsIndented "def-item" $ inTagsSimple "term" term' $$ inTagsIndented "def" def' -- | Convert a list of lists of blocks to a list of JATS list items. listItemsToJATS :: PandocMonad m => WriterOptions -> Maybe [Text] -> [[Block]] -> JATS m (Doc Text) listItemsToJATS opts markers items = case markers of Nothing -> vcat <$> mapM (listItemToJATS opts Nothing) items Just ms -> vcat <$> zipWithM (listItemToJATS opts) (map Just ms) items -- | Convert a list of blocks into a JATS list item. listItemToJATS :: PandocMonad m => WriterOptions -> Maybe Text -> [Block] -> JATS m (Doc Text) listItemToJATS opts mbmarker item = do contents <- wrappedBlocksToJATS (not . isParaOrList) opts (walk demoteHeaderAndRefs item) return $ inTagsIndented "list-item" $ maybe empty (inTagsSimple "label" . text . T.unpack) mbmarker $$ contents languageFor :: WriterOptions -> [Text] -> Text languageFor opts classes = case langs of (l:_) -> escapeStringForXML l [] -> "" where syntaxMap = writerSyntaxMap opts isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap) langsFrom s = if isLang s then [s] else (languagesByExtension syntaxMap) . T.toLower $ s langs = concatMap langsFrom classes codeAttr :: WriterOptions -> Attr -> (Text, [(Text, Text)]) codeAttr opts (ident,classes,kvs) = (lang, attr) where attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("language",lang) | not (T.null lang)] ++ [(k,v) | (k,v) <- kvs, k `elem` ["code-type", "code-version", "executable", "language-version", "orientation", "platforms", "position", "specific-use"]] lang = languageFor opts classes -- <break/> is only allowed as a direct child of <td> or <title> or -- <article-title> fixLineBreak :: Inline -> Inline fixLineBreak LineBreak = RawInline (Format "jats") "<break/>" fixLineBreak x = x -- | Convert a Pandoc block element to JATS. blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m (Doc Text) blockToJATS opts (Div (id',"section":_,kvs) (Header _lvl (_,_,hkvs) ils : xs)) = do let idAttr = [ ("id", writerIdentifierPrefix opts <> escapeNCName id') | not (T.null id')] let otherAttrs = ["sec-type", "specific-use"] let attribs = idAttr ++ [(k,v) | (k,v) <- kvs, k `elem` otherAttrs] title' <- inlinesToJATS opts (map fixLineBreak ils) let label = if writerNumberSections opts then case lookup "number" hkvs of Just num -> inTagsSimple "label" (literal num) Nothing -> mempty else mempty contents <- blocksToJATS opts xs return $ inTags True "sec" attribs $ label $$ inTagsSimple "title" title' $$ contents -- Bibliography reference: blockToJATS opts (Div (ident,_,_) [Para lst]) | "ref-" `T.isPrefixOf` ident = inTags True "ref" [("id", escapeNCName ident)] . inTagsSimple "mixed-citation" <$> inlinesToJATS opts lst blockToJATS opts (Div ("refs",_,_) xs) = do refs <- asks jatsReferences contents <- if null refs then blocksToJATS opts xs else do titleElement <- case xs of (Header _ _ title:_) -> inTagsSimple "title" <$> inlinesToJATS opts title _ -> return mempty elementRefs <- referencesToJATS opts refs return $ titleElement $$ elementRefs return $ inTagsIndented "ref-list" contents blockToJATS opts (Div (ident,[cls],kvs) bs) | cls `elem` ["fig", "caption", "table-wrap"] = do contents <- blocksToJATS opts bs let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("xml:lang",l) | ("lang",l) <- kvs] ++ [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", "content-type", "orientation", "position"]] return $ inTags True cls attr contents blockToJATS opts (Div (ident,_,kvs) bs) = do contents <- blocksToJATS opts bs -- Attributes that are allowed on both @<p>@ and @<boxed-text>@ elements let generic_attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("xml:lang",l) | ("lang",l) <- kvs] ++ [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", "content-type"]] let boxed_attr = [(k,v) | (k,v) <- kvs, k `elem` ["orientation", "position"]] let attr = generic_attr <> boxed_attr return $ if null attr then contents else -- The contents must be wrapped in an appropriate element. let element = if null boxed_attr then "p" else "boxed-text" in inTags True element (generic_attr <> boxed_attr) contents blockToJATS opts (Header _ _ title) = do title' <- inlinesToJATS opts (map fixLineBreak title) return $ inTagsSimple "title" title' -- Special cases for bare images, which are rendered as graphics blockToJATS _opts (Plain [Image attr alt tgt]) = return $ graphic attr alt tgt blockToJATS _opts (Para [Image attr alt tgt]) = return $ graphic attr alt tgt -- No Plain, everything needs to be in a block-level tag blockToJATS opts (Plain lst) = blockToJATS opts (Para lst) blockToJATS opts (Para lst) = inTagsSimple "p" <$> inlinesToJATS opts lst blockToJATS opts (LineBlock lns) = blockToJATS opts $ linesToPara lns blockToJATS opts (BlockQuote blocks) = do tagSet <- asks jatsTagSet let needsWrap = if tagSet == TagSetArticleAuthoring then not . isPara else \case Header{} -> True HorizontalRule -> True _ -> False inTagsIndented "disp-quote" <$> wrappedBlocksToJATS needsWrap opts blocks blockToJATS opts (CodeBlock a str) = return $ inTags False tag attr (flush (text (T.unpack $ escapeStringForXML str))) where (lang, attr) = codeAttr opts a tag = if T.null lang then "preformat" else "code" blockToJATS _ (BulletList []) = return empty blockToJATS opts (BulletList lst) = inTags True "list" [("list-type", "bullet")] <$> listItemsToJATS opts Nothing lst blockToJATS _ (OrderedList _ []) = return empty blockToJATS opts (OrderedList (start, numstyle, delimstyle) items) = do tagSet <- asks jatsTagSet let listType = -- The Article Authoring tag set doesn't allow a more specific -- @list-type@ attribute than "order". if tagSet == TagSetArticleAuthoring then "order" else case numstyle of DefaultStyle -> "order" Decimal -> "order" Example -> "order" UpperAlpha -> "alpha-upper" LowerAlpha -> "alpha-lower" UpperRoman -> "roman-upper" LowerRoman -> "roman-lower" let simpleList = start == 1 && (delimstyle == DefaultDelim || delimstyle == Period) let markers = if simpleList then Nothing else Just $ orderedListMarkers (start, numstyle, delimstyle) inTags True "list" [("list-type", listType)] <$> listItemsToJATS opts markers items blockToJATS opts (DefinitionList lst) = inTags True "def-list" [] <$> deflistItemsToJATS opts lst blockToJATS _ b@(RawBlock f str) | f == "jats" = return $ text $ T.unpack str -- raw XML block | otherwise = do report $ BlockNotRendered b return empty blockToJATS _ HorizontalRule = return empty -- not semantic blockToJATS opts (Table attr caption colspecs thead tbody tfoot) = tableToJATS opts (Ann.toTable attr caption colspecs thead tbody tfoot) blockToJATS opts (Figure (ident, _, kvs) (Caption _short longcapt) body) = do -- Remove the alt text from images if it's the same as the caption text. let unsetAltIfDupl = \case Image attr alt tgt | stringify alt == stringify longcapt -> Image attr [] tgt inline -> inline capt <- if null longcapt then pure empty else inTagsSimple "caption" <$> blocksToJATS opts longcapt -- 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) inlinesToJATS opts lst = hcat <$> mapM (inlineToJATS opts) (fixCitations lst) where fixCitations [] = [] fixCitations (x:xs) | needsFixing x = x : Str (stringify ys) : fixCitations zs where needsFixing (RawInline (Format "jats") z) = "<pub-id pub-id-type=" `T.isPrefixOf` z needsFixing _ = False isRawInline RawInline{} = True isRawInline _ = False (ys,zs) = break isRawInline xs fixCitations (x:xs) = x : fixCitations xs -- | Convert an inline element to JATS. inlineToJATS :: PandocMonad m => WriterOptions -> Inline -> JATS m (Doc Text) inlineToJATS _ (Str str) = return $ text $ T.unpack $ escapeStringForXML str inlineToJATS opts (Emph lst) = inTagsSimple "italic" <$> inlinesToJATS opts lst inlineToJATS opts (Underline lst) = inTagsSimple "underline" <$> inlinesToJATS opts lst inlineToJATS opts (Strong lst) = inTagsSimple "bold" <$> inlinesToJATS opts lst inlineToJATS opts (Strikeout lst) = inTagsSimple "strike" <$> inlinesToJATS opts lst inlineToJATS opts (Superscript lst) = inTagsSimple "sup" <$> inlinesToJATS opts lst inlineToJATS opts (Subscript lst) = inTagsSimple "sub" <$> inlinesToJATS opts lst inlineToJATS opts (SmallCaps lst) = inTagsSimple "sc" <$> inlinesToJATS opts lst inlineToJATS opts (Quoted SingleQuote lst) = do contents <- inlinesToJATS opts lst return $ char '‘' <> contents <> char '’' inlineToJATS opts (Quoted DoubleQuote lst) = do contents <- inlinesToJATS opts lst return $ char '“' <> contents <> char '”' inlineToJATS opts (Code a str) = return $ inTags False "monospace" attr $ literal (escapeStringForXML str) where (_lang, attr) = codeAttr opts a inlineToJATS _ il@(RawInline f x) | f == "jats" = return $ literal x | otherwise = do report $ InlineNotRendered il return empty inlineToJATS _ LineBreak = return cr -- not allowed as child of p -- see https://jats.nlm.nih.gov/publishing/tag-library/1.2/element/break.html inlineToJATS _ Space = return space inlineToJATS opts SoftBreak | writerWrapText opts == WrapPreserve = return cr | otherwise = return space inlineToJATS opts (Note contents) = do tagSet <- asks jatsTagSet -- Footnotes must occur inline when using the Article Authoring tag set. if tagSet == TagSetArticleAuthoring then inTagsIndented "fn" <$> wrappedBlocksToJATS (not . isPara) opts contents else do notes <- gets jatsNotes let notenum = case notes of (n, _):_ -> n + 1 [] -> 1 thenote <- inTags True "fn" [("id", "fn" <> tshow notenum)] . (inTagsSimple "label" (literal $ tshow notenum) <>) <$> wrappedBlocksToJATS (not . isPara) opts (walk demoteHeaderAndRefs contents) modify $ \st -> st{ jatsNotes = (notenum, thenote) : notes } return $ inTags False "xref" [("ref-type", "fn"), ("rid", "fn" <> tshow notenum)] $ text (show notenum) inlineToJATS opts (Cite _ lst) = inlinesToJATS opts lst inlineToJATS opts (Span (ident,classes,kvs) ils) = do contents <- inlinesToJATS opts ils let commonAttr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("xml:lang",l) | ("lang",l) <- kvs] ++ [(k,v) | (k,v) <- kvs, k `elem` ["alt", "specific-use"]] -- A named-content element is a good fit for spans, but requires a -- content-type attribute to be present. We use either the explicit -- attribute or the first class as content type. If neither is -- available, then we fall back to using a @styled-content@ element. let (tag, specificAttr) = case lookup "content-type" kvs <|> listToMaybe classes of Just ct -> ( "named-content" , ("content-type", ct) : [(k, v) | (k, v) <- kvs , k `elem` ["rid", "vocab", "vocab-identifier", "vocab-term", "vocab-term-identifier"]]) -- Fall back to styled-content Nothing -> ("styled-content" , [(k, v) | (k,v) <- kvs , k `elem` ["style", "style-type", "style-detail", "toggle"]]) let attr = commonAttr ++ specificAttr -- unwrap if wrapping element would have no attributes return $ if null attr then contents else inTags False tag attr contents inlineToJATS _ (Math t str) = do let addPref (Xml.Attr q v) | Xml.qName q == "xmlns" = Xml.Attr q{ Xml.qName = "xmlns:mml" } v | otherwise = Xml.Attr q v let fixNS' e = e{ Xml.elName = (Xml.elName e){ Xml.qPrefix = Just "mml" } } let fixNS = everywhere (mkT fixNS') . (\e -> e{ Xml.elAttribs = map addPref (Xml.elAttribs e) }) let conf = Xml.useShortEmptyTags (const False) Xml.defaultConfigPP res <- convertMath writeMathML t str let tagtype = case t of DisplayMath -> "disp-formula" InlineMath -> "inline-formula" let rawtex = text "<![CDATA[" <> literal str <> text "]]>" let texMath = inTagsSimple "tex-math" rawtex tagSet <- asks jatsTagSet return . inTagsSimple tagtype $ case res of Right r -> let mathMl = text (Xml.ppcElement conf $ fixNS r) -- tex-math is unsupported in Article Authoring tag set in if tagSet == TagSetArticleAuthoring then mathMl else inTagsSimple "alternatives" $ cr <> texMath $$ mathMl Left _ -> if tagSet /= TagSetArticleAuthoring then texMath else rawtex inlineToJATS _ (Link _attr [Str t] (T.stripPrefix "mailto:" -> Just email, _)) | escapeURI t == email = return $ inTagsSimple "email" $ literal (escapeStringForXML email) inlineToJATS opts (Link (ident,_,kvs) txt (T.uncons -> Just ('#', src), _)) = do let attr = mconcat [ [("id", escapeNCName ident) | not (T.null ident)] , [("alt", stringify txt) | not (null txt)] , [("rid", escapeNCName src)] , [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]] , [("ref-type", "bibr") | "ref-" `T.isPrefixOf` src] ] if null txt then return $ selfClosingTag "xref" attr else do contents <- inlinesToJATS opts txt return $ inTags False "xref" attr contents inlineToJATS opts (Link (ident,_,kvs) txt (src, tit)) = do let attr = [("id", escapeNCName ident) | not (T.null ident)] ++ [("ext-link-type", "uri"), ("xlink:href", src)] ++ [("xlink:title", tit) | not (T.null tit)] ++ [(k,v) | (k,v) <- kvs, k `elem` ["assigning-authority", "specific-use", "xlink:actuate", "xlink:role", "xlink:show", "xlink:type"]] contents <- inlinesToJATS opts txt return $ inTags False "ext-link" attr contents inlineToJATS _ (Image attr alt tgt) = do let elattr = graphicAttr attr alt tgt return $ case altToJATS alt of Nothing -> selfClosingTag "inline-graphic" elattr Just altTag -> inTags True "inline-graphic" elattr altTag graphic :: Attr -> [Inline] -> Target -> (Doc Text) graphic attr alt tgt = let elattr = graphicAttr attr alt tgt in case altToJATS alt of Nothing -> selfClosingTag "graphic" elattr Just altTag -> inTags True "graphic" elattr altTag graphicAttr :: Attr -> [Inline] -> Target -> [(Text, Text)] graphicAttr (ident, _, kvs) _alt (src, tit) = let (maintype, subtype) = imageMimeType src kvs in [("id", escapeNCName ident) | not (T.null ident)] ++ [ ("mimetype", maintype) , ("mime-subtype", subtype) , ("xlink:href", src) ] ++ [("xlink:title", tit) | not (T.null tit)] ++ [(k,v) | (k,v) <- kvs , k `elem` [ "baseline-shift", "content-type", "specific-use" , "xlink:actuate", "xlink:href", "xlink:role" , "xlink:show", "xlink:type"] ] altToJATS :: [Inline] -> Maybe (Doc Text) altToJATS alt = if null alt then Nothing else Just . inTagsSimple "alt-text" . hsep . map literal . T.words $ stringify alt imageMimeType :: Text -> [(Text, Text)] -> (Text, Text) imageMimeType src kvs = let mbMT = getMimeType (T.unpack src) maintype = fromMaybe "image" $ lookup "mimetype" kvs `mplus` (T.takeWhile (/='/') <$> mbMT) subtype = fromMaybe "" $ lookup "mime-subtype" kvs `mplus` (T.drop 1 . T.dropWhile (/='/') <$> mbMT) in (maintype, subtype) isParaOrList :: Block -> Bool isParaOrList Para{} = True isParaOrList Plain{} = True isParaOrList BulletList{} = True isParaOrList OrderedList{} = True isParaOrList DefinitionList{} = True isParaOrList _ = False isPara :: Block -> Bool isPara Para{} = True isPara Plain{} = True isPara _ = False demoteHeaderAndRefs :: Block -> Block demoteHeaderAndRefs (Header _ _ ils) = Para ils demoteHeaderAndRefs (Div ("refs",cls,kvs) bs) = Div ("",cls,kvs) bs demoteHeaderAndRefs x = x parseDate :: Text -> Maybe Day parseDate s = msum (map (`parsetimeWith` T.unpack s) formats) where parsetimeWith = parseTimeM True defaultTimeLocale formats = ["%x","%m/%d/%Y", "%D","%F", "%d %b %Y", "%e %B %Y", "%b. %e, %Y", "%B %e, %Y", "%Y%m%d", "%Y%m", "%Y"] ================================================ FILE: src/Text/Pandoc/Writers/Jira.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- | Module : Text.Pandoc.Writers.Jira Copyright : © 2010-2024 Albert Krewinkel, John MacFarlane License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <albert+pandoc@tarleb.com> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to Jira markup. JIRA: <https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all> -} module Text.Pandoc.Writers.Jira ( writeJira ) where import Control.Monad.Reader (ReaderT, ask, asks, runReaderT) import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) import Data.Text (Text) import Text.Jira.Parser (plainText) import Text.Jira.Printer (prettyBlocks, prettyInlines) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions (writerTemplate, writerWrapText), WrapOption (..)) import Text.Pandoc.Shared (linesToPara, stringify) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable) import Text.DocLayout (literal, render) import qualified Data.Text as T import qualified Text.Jira.Markup as Jira -- | Convert Pandoc to Jira. writeJira :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJira opts = runDefaultConverter (writerWrapText opts) (pandocToJira opts) -- | State to keep track of footnotes. data ConverterState = ConverterState { stNotes :: [Text] -- ^ Footnotes to be appended to the end of the text , stInPanel :: Bool -- ^ whether we are in a @{panel}@ block } -- | Initial converter state. startState :: ConverterState startState = ConverterState { stNotes = [] , stInPanel = False } -- | Converter monad type JiraConverter m = ReaderT WrapOption (StateT ConverterState m) -- | Run a converter using the default state runDefaultConverter :: PandocMonad m => WrapOption -> (a -> JiraConverter m Text) -> a -> m Text runDefaultConverter wrap c x = evalStateT (runReaderT (c x) wrap) startState -- | Return Jira representation of document. pandocToJira :: PandocMonad m => WriterOptions -> Pandoc -> JiraConverter m Text pandocToJira opts (Pandoc meta blocks) = do wrap <- ask metadata <- metaToContext opts (fmap literal . runDefaultConverter wrap blockListToJira) (fmap literal . runDefaultConverter wrap inlineListToJira) meta body <- blockListToJira blocks notes <- gets $ T.intercalate "\n" . reverse . stNotes let main = body <> if T.null notes then mempty else "\n\n" <> notes let context = defField "body" main metadata return $ case writerTemplate opts of Nothing -> main Just tpl -> render Nothing $ renderTemplate tpl context blockListToJira :: PandocMonad m => [Block] -> JiraConverter m Text blockListToJira = fmap prettyBlocks . toJiraBlocks inlineListToJira :: PandocMonad m => [Inline] -> JiraConverter m Text inlineListToJira = fmap prettyInlines . toJiraInlines toJiraBlocks :: PandocMonad m => [Block] -> JiraConverter m [Jira.Block] toJiraBlocks blocks = do let convert = \case BlockQuote bs -> singleton . Jira.BlockQuote <$> toJiraBlocks bs BulletList items -> singleton . Jira.List Jira.CircleBullets <$> toJiraItems items CodeBlock attr cs -> toJiraCode attr cs DefinitionList items -> toJiraDefinitionList items Div attr bs -> toJiraPanel attr bs Header lvl attr xs -> toJiraHeader lvl attr xs HorizontalRule -> return . singleton $ Jira.HorizontalRule LineBlock xs -> toJiraBlocks [linesToPara xs] OrderedList _ items -> singleton . Jira.List Jira.Enumeration <$> toJiraItems items Para xs -> singleton . Jira.Para <$> toJiraInlines xs Plain xs -> singleton . Jira.Para <$> toJiraInlines xs RawBlock fmt cs -> rawBlockToJira fmt cs Table _ blkCapt specs thead tbody tfoot -> singleton <$> do let (_, _, _, hd, body) = toLegacyTable blkCapt specs thead tbody tfoot headerRow <- if all null hd then pure Nothing else Just <$> toRow Jira.HeaderCell hd bodyRows <- mapM (toRow Jira.BodyCell) body let rows = case headerRow of Just header -> header : bodyRows Nothing -> bodyRows return $ Jira.Table rows Figure attr _ body -> toJiraPanel attr body jiraBlocks <- mapM convert blocks return $ mconcat jiraBlocks toRow :: PandocMonad m => ([Jira.Block] -> Jira.Cell) -> [[Block]] -> JiraConverter m Jira.Row toRow mkCell cells = Jira.Row <$> mapM (fmap mkCell . toJiraBlocks) cells toJiraItems :: PandocMonad m => [[Block]] -> JiraConverter m [[Jira.Block]] toJiraItems = mapM toJiraBlocks toJiraCode :: PandocMonad m => Attr -> Text -> JiraConverter m [Jira.Block] toJiraCode (ident, classes, _attribs) code = do return . addAnchor ident . singleton $ case classes of [] -> Jira.NoFormat mempty code l:_ -> Jira.Code (Jira.Language l) mempty code -- | Prepends an anchor with the given identifier. addAnchor :: Text -> [Jira.Block] -> [Jira.Block] addAnchor ident = if T.null ident then id else \case Jira.Para xs : bs -> (Jira.Para (Jira.Anchor ident : xs) : bs) bs -> (Jira.Para (singleton (Jira.Anchor ident)) : bs) -- | Creates a Jira definition list toJiraDefinitionList :: PandocMonad m => [([Inline], [[Block]])] -> JiraConverter m [Jira.Block] toJiraDefinitionList defItems = do let convertDefItem (term, defs) = do jiraTerm <- Jira.Para <$> styled Jira.Strong term jiraDefs <- mconcat <$> mapM toJiraBlocks defs return $ jiraTerm : jiraDefs singleton . Jira.List Jira.CircleBullets <$> mapM convertDefItem defItems -- | Creates a Jira panel toJiraPanel :: PandocMonad m => Attr -> [Block] -> JiraConverter m [Jira.Block] toJiraPanel (ident, classes, attribs) blocks = do inPanel <- gets stInPanel if inPanel || ("panel" `notElem` classes && null attribs) then addAnchor ident <$> toJiraBlocks blocks else do modify $ \st -> st{ stInPanel = True } jiraBlocks <- toJiraBlocks blocks modify $ \st -> st{ stInPanel = inPanel } let params = map (uncurry Jira.Parameter) attribs return $ singleton (Jira.Panel params $ addAnchor ident jiraBlocks) -- | Creates a Jira header toJiraHeader :: PandocMonad m => Int -> Attr -> [Inline] -> JiraConverter m [Jira.Block] toJiraHeader lvl (ident, _, _) inlines = let anchor = Jira.Anchor ident in singleton . Jira.Header lvl . (anchor :) <$> toJiraInlines inlines -- | Handles raw block. Jira is included verbatim, everything else is -- discarded. rawBlockToJira :: PandocMonad m => Format -> Text -> JiraConverter m [Jira.Block] rawBlockToJira fmt cs = do rawInlines <- toJiraRaw fmt cs return $ if null rawInlines then mempty else singleton (Jira.Para rawInlines) toJiraRaw :: PandocMonad m => Format -> Text -> JiraConverter m [Jira.Inline] toJiraRaw fmt cs = case fmt of Format "jira" -> return . singleton $ Jira.Str cs _ -> return mempty -- -- Inlines -- toJiraInlines :: PandocMonad m => [Inline] -> JiraConverter m [Jira.Inline] toJiraInlines inlines = do let convert = \case Cite _ xs -> toJiraInlines xs Code _ cs -> return . singleton $ Jira.Monospaced (escapeSpecialChars cs) Emph xs -> styled Jira.Emphasis xs Underline xs -> styled Jira.Insert xs Image attr cap tgt -> uncurry (imageToJira attr cap) tgt LineBreak -> pure . singleton $ Jira.Linebreak Link attr xs tgt -> toJiraLink attr tgt xs Math mtype cs -> mathToJira mtype cs Note bs -> registerNotes bs Quoted qt xs -> quotedToJira qt xs RawInline fmt cs -> toJiraRaw fmt cs SmallCaps xs -> styled Jira.Strong xs SoftBreak -> do preserveBreak <- asks (== WrapPreserve) pure . singleton $ if preserveBreak then Jira.Linebreak else Jira.Space Space -> pure . singleton $ Jira.Space Span attr xs -> spanToJira attr xs Str s -> pure $ escapeSpecialChars s Strikeout xs -> styled Jira.Strikeout xs Strong xs -> styled Jira.Strong xs Subscript xs -> styled Jira.Subscript xs Superscript xs -> styled Jira.Superscript xs jiraInlines <- mapM convert inlines return $ mconcat jiraInlines singleton :: a -> [a] singleton = (:[]) styled :: PandocMonad m => Jira.InlineStyle -> [Inline] -> JiraConverter m [Jira.Inline] styled s = fmap (singleton . Jira.Styled s) . toJiraInlines -- | Converts a plain text value to Jira inlines, ensuring that all -- special characters will be handled appropriately. escapeSpecialChars :: Text -> [Jira.Inline] escapeSpecialChars t = case plainText t of Right xs -> xs Left _ -> singleton $ Jira.Str t imageToJira :: PandocMonad m => Attr -> [Inline] -> Text -> Text -> JiraConverter m [Jira.Inline] imageToJira (_, classes, kvs) caption src title = let imageWithParams ps = Jira.Image ps (Jira.URL src) alt = stringify caption in pure . singleton . imageWithParams $ if "thumbnail" `elem` classes then [Jira.Parameter "thumbnail" ""] else map (uncurry Jira.Parameter) . (if T.null title then id else (("title", title):)) . (if T.null alt then id else (("alt", alt):)) $ kvs -- | Creates a Jira Link element. toJiraLink :: PandocMonad m => Attr -> Target -> [Inline] -> JiraConverter m [Jira.Inline] toJiraLink (_, classes, _) (url, _) alias = do let (linkType, url') = toLinkType url description <- if url `elem` [stringify alias, "mailto:" <> stringify alias] then pure mempty else toJiraInlines alias pure . singleton $ Jira.Link linkType description (Jira.URL url') where toLinkType url' | Just email <- T.stripPrefix "mailto:" url' = (Jira.Email, email) | "user-account" `elem` classes = (Jira.User, dropTilde url) | "attachment" `elem` classes = (Jira.Attachment, url) | "smart-card" `elem` classes = (Jira.SmartCard, url) | "smart-link" `elem` classes = (Jira.SmartLink, url) | otherwise = (Jira.External, url) dropTilde txt = case T.uncons txt of Just ('~', username) -> username _ -> txt mathToJira :: PandocMonad m => MathType -> Text -> JiraConverter m [Jira.Inline] mathToJira mtype cs = do mathInlines <- toJiraInlines =<< texMathToInlines mtype cs return $ case mtype of InlineMath -> mathInlines DisplayMath -> Jira.Linebreak : mathInlines ++ [Jira.Linebreak] quotedToJira :: PandocMonad m => QuoteType -> [Inline] -> JiraConverter m [Jira.Inline] quotedToJira qtype xs = do let quoteChar = case qtype of DoubleQuote -> "\"" SingleQuote -> "'" let surroundWithQuotes = (Jira.Str quoteChar :) . (++ [Jira.Str quoteChar]) surroundWithQuotes <$> toJiraInlines xs spanToJira :: PandocMonad m => Attr -> [Inline] -> JiraConverter m [Jira.Inline] spanToJira (ident, _classes, attribs) inls = let wrap = case lookup "color" attribs of Nothing -> id Just color -> singleton . Jira.ColorInline (Jira.ColorName color) in wrap <$> case ident of "" -> toJiraInlines inls _ -> (Jira.Anchor ident :) <$> toJiraInlines inls registerNotes :: PandocMonad m => [Block] -> JiraConverter m [Jira.Inline] registerNotes contents = do curNotes <- gets stNotes let newnum = length curNotes + 1 contents' <- blockListToJira contents let thisnote = "\\[" <> T.pack (show newnum) <> "] " <> contents' <> "\n" modify $ \s -> s { stNotes = thisnote : curNotes } return . singleton . Jira.Str $ "[" <> T.pack (show newnum) <> "]" ================================================ FILE: src/Text/Pandoc/Writers/LaTeX/Caption.hs ================================================ {- | Module : Text.Pandoc.Writers.LaTeX.Caption Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Write figure or table captions as LaTeX. -} module Text.Pandoc.Writers.LaTeX.Caption ( getCaption ) where import Control.Monad.State.Strict import Data.Monoid (Any(..)) import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.DocLayout (Doc, brackets, empty) import Text.Pandoc.Shared import Text.Pandoc.Walk import Text.Pandoc.Writers.LaTeX.Notes (notesToLaTeX) import Text.Pandoc.Writers.LaTeX.Types ( LW, WriterState (stExternalNotes, stNotes, stInCaption) ) -- | Produces the components of a LaTeX 'caption' command. Returns a triple -- containing the caption text, the short caption for the list of -- figures/tables, and the footnote definitions. getCaption :: PandocMonad m => ([Inline] -> LW m (Doc Text)) -- ^ inlines converter -> Bool -- ^ whether to extract notes -> Caption -> LW m (Doc Text, Doc Text, Doc Text) getCaption inlineListToLaTeX externalNotes (Caption maybeShort long) = do modify $ \st -> st{ stInCaption = True } let long' = blocksToInlines long oldExternalNotes <- gets stExternalNotes modify $ \st -> st{ stExternalNotes = externalNotes, stNotes = [] } capt <- inlineListToLaTeX long' footnotes <- if externalNotes then notesToLaTeX <$> gets stNotes else return empty modify $ \st -> st{ stExternalNotes = oldExternalNotes, stNotes = [] } -- We can't have footnotes in the list of figures/tables, so remove them: let getNote (Note _) = Any True getNote _ = Any False let hasNotes = getAny . query getNote let toShortCapt = fmap brackets . inlineListToLaTeX . walk deNote captForLof <- case maybeShort of Nothing -> if hasNotes long' then toShortCapt long' else return empty Just short -> toShortCapt short modify $ \st -> st{ stInCaption = False } return (capt, captForLof, footnotes) ================================================ FILE: src/Text/Pandoc/Writers/LaTeX/Citation.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.LaTeX.Citation Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable -} module Text.Pandoc.Writers.LaTeX.Citation ( citationsToNatbib, citationsToBiblatex ) where import Data.Text (Text) import Data.Char (isPunctuation) import Control.Monad.State (gets) import Data.Maybe (fromMaybe) import qualified Data.Text as T import Text.Pandoc.Options import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import qualified Data.List as L import Text.DocLayout (Doc, brackets, empty, (<+>), text, isEmpty, literal, braces) import Text.Pandoc.Walk import Text.Pandoc.Writers.LaTeX.Types ( LW, WriterState(stLang, stOptions) ) import Text.Pandoc.Citeproc.Locator (parseLocator, LocatorInfo(..), toLocatorMap) import Citeproc.Types (Lang(..)) import Citeproc.Locale (getLocale) import Safe (headMay, lastMay) citationsToNatbib :: PandocMonad m => ([Inline] -> LW m (Doc Text)) -> [Citation] -> LW m (Doc Text) citationsToNatbib inlineListToLaTeX [one] = citeCommand inlineListToLaTeX c p s k where Citation { citationId = k , citationPrefix = p , citationSuffix = s , citationMode = m } = one c = case m of AuthorInText -> "citet" SuppressAuthor -> "citeyearpar" NormalCitation -> "citep" citationsToNatbib inlineListToLaTeX cits | noInnerPrefix cits && noInnerSuffix cits && ismode NormalCitation cits = citeCommand inlineListToLaTeX "citep" p s ks where noInnerPrefix [] = True noInnerPrefix (_:xs) = all (null . citationPrefix) xs noInnerSuffix [] = True noInnerSuffix [_] = True noInnerSuffix (x:xs) = null (citationSuffix x) && noInnerSuffix xs ismode m = all ((==) m . citationMode) p = maybe mempty citationPrefix $ headMay cits s = maybe mempty citationSuffix $ lastMay cits ks = T.intercalate ", " $ map citationId cits citationsToNatbib inlineListToLaTeX (c:cs) | citationMode c == AuthorInText = do author <- citeCommand inlineListToLaTeX "citeauthor" [] [] (citationId c) cits <- citationsToNatbib inlineListToLaTeX (c { citationMode = SuppressAuthor } : cs) return $ author <+> cits citationsToNatbib inlineListToLaTeX cits = do cits' <- mapM convertOne cits return $ text "\\citetext{" <> L.foldl' combineTwo empty cits' <> text "}" where citeCommand' = citeCommand inlineListToLaTeX combineTwo a b | isEmpty a = b | otherwise = a <> text "; " <> b convertOne Citation { citationId = k , citationPrefix = p , citationSuffix = s , citationMode = m } = case m of AuthorInText -> citeCommand' "citealt" p s k SuppressAuthor -> citeCommand' "citeyear" p s k NormalCitation -> citeCommand' "citealp" p s k citeCommand :: PandocMonad m => ([Inline] -> LW m (Doc Text)) -> Text -> [Inline] -> [Inline] -> Text -> LW m (Doc Text) citeCommand inlineListToLaTeX c p s k = do args <- citeArguments inlineListToLaTeX p s k return $ literal ("\\" <> c) <> args type Prefix = [Inline] type Suffix = [Inline] type CiteId = Text data CiteGroup = CiteGroup Prefix Suffix [CiteId] citeArgumentsList :: PandocMonad m => ([Inline] -> LW m (Doc Text)) -> CiteGroup -> LW m (Doc Text) citeArgumentsList _inlineListToLaTeX (CiteGroup _ _ []) = return empty citeArgumentsList inlineListToLaTeX (CiteGroup pfxs sfxs ids) = do opts <- gets stOptions mblang <- gets stLang let sfxs' = (case writerCiteMethod opts of -- In biblatex, the label p. or pp. can be omitted; -- ranges are treated as page ranges by default. See #9275. Biblatex -> removePageLabel mblang _ -> id) $ stripLocatorBraces $ case sfxs of (Str t : r) -> case T.uncons t of Just (x, xs) | T.null xs , isPunctuation x -> dropWhile (== Space) r | isPunctuation x -> Str xs : r _ -> sfxs _ -> sfxs optargs pdoc sdoc = case (isEmpty pdoc, isEmpty sdoc) of (True, True ) -> empty (True, False) -> brackets sdoc (_ , _ ) -> brackets pdoc <> brackets sdoc pdoc <- inlineListToLaTeX pfxs sdoc <- inlineListToLaTeX sfxs' return $ optargs pdoc sdoc <> braces (literal (T.intercalate "," (reverse ids))) citeArguments :: PandocMonad m => ([Inline] -> LW m (Doc Text)) -> [Inline] -> [Inline] -> Text -> LW m (Doc Text) citeArguments inlineListToLaTeX p s k = citeArgumentsList inlineListToLaTeX (CiteGroup p s [k]) -- strip off {} used to define locator in pandoc-citeproc; see #5722 stripLocatorBraces :: [Inline] -> [Inline] stripLocatorBraces = walk go where go (Str xs) = Str $ T.filter (\c -> c /= '{' && c /= '}') xs go x = x citationsToBiblatex :: PandocMonad m => ([Inline] -> LW m (Doc Text)) -> [Citation] -> LW m (Doc Text) citationsToBiblatex inlineListToLaTeX [one] = citeCommand inlineListToLaTeX cmd p s k where Citation { citationId = k , citationPrefix = p , citationSuffix = s , citationMode = m } = one cmd = case m of SuppressAuthor -> "autocite*" AuthorInText -> "textcite" NormalCitation -> "autocite" citationsToBiblatex inlineListToLaTeX (c:cs) | all (\cit -> null (citationPrefix cit) && null (citationSuffix cit)) (c:cs) = do let cmd = case citationMode c of SuppressAuthor -> "\\autocite*" AuthorInText -> "\\textcite" NormalCitation -> "\\autocite" return $ text cmd <> braces (literal (T.intercalate "," (map citationId (c:cs)))) | otherwise = do let cmd = case citationMode c of SuppressAuthor -> "\\autocites*" AuthorInText -> "\\textcites" NormalCitation -> "\\autocites" groups <- mapM (citeArgumentsList inlineListToLaTeX) (reverse (L.foldl' grouper [] (c:cs))) return $ text cmd <> mconcat groups where grouper prev cit = case prev of ((CiteGroup oPfx [] ids):rest) | null pfx && null sfx -> CiteGroup oPfx sfx (cid:ids) : rest _ -> CiteGroup pfx sfx [cid] : prev where pfx = citationPrefix cit sfx = citationSuffix cit cid = citationId cit citationsToBiblatex _ _ = return empty removePageLabel :: Maybe Lang -> [Inline] -> [Inline] removePageLabel mblang ils = case mbLocinfo of Just locinfo | locatorLabel locinfo == "page" -> Str (locatorLoc locinfo) : ils' _ -> ils where (mbLocinfo, ils') = parseLocator (toLocatorMap locale) ils lang = fromMaybe (Lang "en" Nothing (Just "US") [] [] []) mblang locale = either mempty id $ getLocale lang ================================================ FILE: src/Text/Pandoc/Writers/LaTeX/Lang.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.LaTeX.Lang Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable -} module Text.Pandoc.Writers.LaTeX.Lang ( toBabel ) where import Data.Text (Text) import Text.Collate.Lang (Lang(..)) -- Takes a list of the constituents of a BCP47 language code and -- converts it to a Babel language string. -- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf -- List of supported languages (slightly outdated): -- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf toBabel :: Lang -> Maybe Text toBabel (Lang "de" _ (Just "AT") vars _ _) | "1901" `elem` vars = Just "austrian" | otherwise = Just "naustrian" toBabel (Lang "de" _ (Just "CH") vars _ _) | "1901" `elem` vars = Just "swissgerman" | otherwise = Just "nswissgerman" toBabel (Lang "de" _ _ vars _ _) | "1901" `elem` vars = Just "german" | otherwise = Just "ngerman" toBabel (Lang "dsb" _ _ _ _ _) = Just "lowersorbian" toBabel (Lang "el" _ _ vars _ _) | "polyton" `elem` vars = Just "polytonicgreek" toBabel (Lang "en" _ (Just "AU") _ _ _) = Just "australian" toBabel (Lang "en" _ (Just "CA") _ _ _) = Just "canadian" toBabel (Lang "en" _ (Just "GB") _ _ _) = Just "british" toBabel (Lang "en" _ (Just "NZ") _ _ _) = Just "newzealand" toBabel (Lang "en" _ (Just "UK") _ _ _) = Just "british" toBabel (Lang "en" _ (Just "US") _ _ _) = Just "american" toBabel (Lang "fr" _ (Just "CA") _ _ _) = Just "canadien" toBabel (Lang "fra" _ _ vars _ _) | "aca" `elem` vars = Just "acadian" toBabel (Lang "grc" _ _ _ _ _) = Just "ancientgreek" toBabel (Lang "hsb" _ _ _ _ _) = Just "uppersorbian" toBabel (Lang "la" _ _ vars _ _) | "x-classic" `elem` vars = Just "classiclatin" toBabel (Lang "pt" _ (Just "BR") _ _ _) = Just "brazilian" toBabel (Lang "sl" _ _ _ _ _) = Just "slovene" toBabel (Lang "zh" (Just "Hant") (Just "HK") _ _ _) = Just "chinese-hant-hk" toBabel (Lang "zh" (Just "Hant") (Just "MO") _ _ _) = Just "chinese-hant-mo" toBabel (Lang "zh" (Just "Hans") (Just "HK") _ _ _) = Just "chinese-hans-hk" toBabel (Lang "zh" (Just "Hans") (Just "MO") _ _ _) = Just "chinese-hans-mo" toBabel (Lang "zh" (Just "Hans") _ _ _ _) = Just "chinese-hans" toBabel (Lang "zh" (Just "Hant") _ _ _ _) = Just "chinese-hant" toBabel (Lang "zh" _ _ _ _ _) = Just "chinese" toBabel x = commonFromBcp47 x -- Takes a list of the constituents of a BCP47 language code -- and converts it to a string shared by Babel and Polyglossia. -- https://tools.ietf.org/html/bcp47#section-2.1 commonFromBcp47 :: Lang -> Maybe Text commonFromBcp47 (Lang "sr" (Just "Cyrl") _ _ _ _) = Just "serbianc" commonFromBcp47 (Lang "zh" (Just "Latn") _ vars _ _) | "pinyin" `elem` vars = Just "pinyin" commonFromBcp47 (Lang l _ _ _ _ _) = fromIso l where fromIso "af" = Just "afrikaans" fromIso "am" = Just "amharic" fromIso "ar" = Just "arabic" fromIso "as" = Just "assamese" fromIso "ast" = Just "asturian" fromIso "bg" = Just "bulgarian" fromIso "bn" = Just "bengali" fromIso "bo" = Just "tibetan" fromIso "br" = Just "breton" fromIso "ca" = Just "catalan" fromIso "cy" = Just "welsh" fromIso "cs" = Just "czech" fromIso "cop" = Just "coptic" fromIso "da" = Just "danish" fromIso "dv" = Just "divehi" fromIso "el" = Just "greek" fromIso "en" = Just "english" fromIso "eo" = Just "esperanto" fromIso "es" = Just "spanish" fromIso "et" = Just "estonian" fromIso "eu" = Just "basque" fromIso "fa" = Just "persian" fromIso "fi" = Just "finnish" fromIso "fr" = Just "french" fromIso "fur" = Just "friulan" fromIso "ga" = Just "irish" fromIso "gd" = Just "scottish" fromIso "gez" = Just "ethiopic" fromIso "gl" = Just "galician" fromIso "gu" = Just "gujarati" fromIso "he" = Just "hebrew" fromIso "hi" = Just "hindi" fromIso "hr" = Just "croatian" fromIso "hu" = Just "magyar" fromIso "hy" = Just "armenian" fromIso "ia" = Just "interlingua" fromIso "id" = Just "indonesian" fromIso "ie" = Just "interlingua" fromIso "is" = Just "icelandic" fromIso "it" = Just "italian" fromIso "ja" = Just "japanese" fromIso "km" = Just "khmer" fromIso "kmr" = Just "kurmanji" fromIso "kn" = Just "kannada" fromIso "ko" = Just "korean" fromIso "la" = Just "latin" fromIso "lo" = Just "lao" fromIso "lt" = Just "lithuanian" fromIso "lv" = Just "latvian" fromIso "ml" = Just "malayalam" fromIso "mn" = Just "mongolian" fromIso "mr" = Just "marathi" fromIso "nb" = Just "norsk" fromIso "nl" = Just "dutch" fromIso "nn" = Just "nynorsk" fromIso "no" = Just "norsk" fromIso "nqo" = Just "nko" fromIso "oc" = Just "occitan" fromIso "or" = Just "oriya" fromIso "pa" = Just "punjabi" fromIso "pl" = Just "polish" fromIso "pms" = Just "piedmontese" fromIso "pt" = Just "portuguese" fromIso "rm" = Just "romansh" fromIso "ro" = Just "romanian" fromIso "ru" = Just "russian" fromIso "sa" = Just "sanskrit" fromIso "se" = Just "samin" fromIso "sk" = Just "slovak" fromIso "sq" = Just "albanian" fromIso "sr" = Just "serbian" fromIso "sv" = Just "swedish" fromIso "syr" = Just "syriac" fromIso "ta" = Just "tamil" fromIso "te" = Just "telugu" fromIso "th" = Just "thai" fromIso "ti" = Just "ethiopic" fromIso "tk" = Just "turkmen" fromIso "tr" = Just "turkish" fromIso "uk" = Just "ukrainian" fromIso "ur" = Just "urdu" fromIso "vi" = Just "vietnamese" fromIso _ = Nothing ================================================ FILE: src/Text/Pandoc/Writers/LaTeX/Notes.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.LaTeX.Notes Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Output tables as LaTeX. -} module Text.Pandoc.Writers.LaTeX.Notes ( notesToLaTeX ) where import Data.List (intersperse) import Text.DocLayout ( Doc, braces, empty, text, vcat, ($$)) import Data.Text (Text) notesToLaTeX :: [Doc Text] -> Doc Text notesToLaTeX = \case [] -> empty ns -> (case length ns of n | n > 1 -> "\\addtocounter" <> braces "footnote" <> braces (text $ show $ 1 - n) | otherwise -> empty) $$ vcat (intersperse ("\\addtocounter" <> braces "footnote" <> braces "1") $ map (\x -> "\\footnotetext" <> braces x) $ reverse ns) ================================================ FILE: src/Text/Pandoc/Writers/LaTeX/Table.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.LaTeX.Table Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Output LaTeX formatted tables. -} module Text.Pandoc.Writers.LaTeX.Table ( tableToLaTeX ) where import Control.Monad.State.Strict ( gets, modify ) import Control.Monad (when) import Data.List (intersperse) import qualified Data.List.NonEmpty as NonEmpty import Data.List.NonEmpty (NonEmpty ((:|))) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.DocLayout ( Doc, braces, cr, empty, hcat, hsep, isEmpty, literal, nest , text, vcat, ($$) ) import Text.Pandoc.Shared (splitBy, tshow) import Text.Pandoc.Walk (walk, query) import Data.Monoid (Any(..)) import Text.Pandoc.Writers.LaTeX.Caption (getCaption) import Text.Pandoc.Writers.LaTeX.Notes (notesToLaTeX) import Text.Pandoc.Writers.LaTeX.Types ( LW, WriterState (stBeamer, stExternalNotes, stInMinipage, stMultiRow , stNotes, stTable, stOptions) ) import Text.Pandoc.Writers.LaTeX.Util (labelFor) import Text.Printf (printf) import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.Writers.AnnotatedTable as Ann import Text.Pandoc.Options (CaptionPosition(..), WriterOptions(..)) tableToLaTeX :: PandocMonad m => ([Inline] -> LW m (Doc Text)) -> ([Block] -> LW m (Doc Text)) -> Ann.Table -> LW m (Doc Text) tableToLaTeX inlnsToLaTeX blksToLaTeX tbl = do opts <- gets stOptions let (Ann.Table (ident, _, _) caption specs thead tbodies tfoot) = tbl CaptionDocs capt captNotes <- captionToLaTeX inlnsToLaTeX caption ident let hasTopCaption = not (isEmpty capt) && writerTableCaptionPosition opts == CaptionAbove let hasBottomCaption = not (isEmpty capt) && writerTableCaptionPosition opts == CaptionBelow let isSimpleTable = all ((== ColWidthDefault) . snd) specs && all (all isSimpleCell) (mconcat [ headRows thead , concatMap bodyRows tbodies , footRows tfoot ]) let removeNote (Note _) = Span ("", [], []) [] removeNote x = x let colCount = ColumnCount $ length specs -- The first head is not repeated on the following pages. If we were to just -- use a single head, without a separate first head, then the caption would be -- repeated on all pages that contain a part of the table. We avoid this by -- making the caption part of the first head. The downside is that we must -- duplicate the header rows for this. head' <- do let mkHead = headToLaTeX blksToLaTeX isSimpleTable colCount case (hasTopCaption, isEmptyHead thead) of (False, True) -> return "\\toprule\\noalign{}" (False, False) -> mkHead thead (True, True) -> return (capt <> "\\tabularnewline" $$ "\\toprule\\noalign{}" $$ "\\endfirsthead") (True, False) -> do -- avoid duplicate notes in head and firsthead: firsthead <- mkHead thead repeated <- mkHead (walk removeNote thead) return $ capt <> "\\tabularnewline" $$ firsthead $$ "\\endfirsthead" $$ repeated rows' <- mapM (rowToLaTeX blksToLaTeX isSimpleTable colCount BodyCell) $ mconcat (map bodyRows tbodies) lastfoot <- mapM (rowToLaTeX blksToLaTeX isSimpleTable colCount BodyCell) $ footRows tfoot let foot' = (if isEmptyFoot tfoot then mempty else "\\midrule\\noalign{}" $$ vcat lastfoot) $$ "\\bottomrule\\noalign{}" $$ (if hasBottomCaption then "\\tabularnewline" $$ capt else mempty) modify $ \s -> s{ stTable = True } notes <- notesToLaTeX <$> gets stNotes beamer <- gets stBeamer let makeUnnumbered x = "{\\def\\LTcaptype{none} % do not increment counter" $$ x $$ "}" return $ (if null capt then makeUnnumbered else id) $ "\\begin{longtable}[]" <> braces ("@{}" <> colDescriptors isSimpleTable tbl <> "@{}") -- the @{} removes extra space at beginning and end $$ head' $$ "\\endhead" $$ vcat -- Longtable is not able to detect pagebreaks in Beamer; this -- causes problems with the placement of the footer, so make -- footer and bottom rule part of the body when targeting Beamer. -- See issue #8638. (if beamer then [ vcat rows' , foot' ] else [ foot' , "\\endlastfoot" , vcat rows' ]) $$ "\\end{longtable}" $$ captNotes $$ notes isSimpleCell :: Ann.Cell -> Bool isSimpleCell (Ann.Cell _ _ (Cell _attr _align _rowspan _colspan blocks)) = case blocks of [Para _] -> not (hasLineBreak blocks) [Plain _] -> not (hasLineBreak blocks) [] -> True _ -> False where hasLineBreak = getAny . query isLineBreak isLineBreak LineBreak = Any True isLineBreak _ = Any False -- | Total number of columns in a table. newtype ColumnCount = ColumnCount Int -- | Creates column descriptors for the table. colDescriptors :: Bool -> Ann.Table -> Doc Text colDescriptors isSimpleTable (Ann.Table _attr _caption specs _thead _tbodies _tfoot) = let (aligns, widths) = unzip specs defaultWidthsOnly = all (== ColWidthDefault) widths relativeWidths = if defaultWidthsOnly then replicate (length specs) (1 / fromIntegral (length specs)) else map toRelWidth widths in if null aligns then "l" -- #9350, table needs at least one column spec else if defaultWidthsOnly && isSimpleTable then hcat $ map (literal . colAlign) aligns else (cr <>) . nest 2 . vcat . map literal $ zipWith (toColDescriptor (length specs)) aligns relativeWidths where toColDescriptor :: Int -> Alignment -> Double -> Text toColDescriptor numcols align width = T.pack $ printf ">{%s\\arraybackslash}p{(\\linewidth - %d\\tabcolsep) * \\real{%0.4f}}" (T.unpack (alignCommand align)) ((numcols - 1) * 2) width toRelWidth ColWidthDefault = 0 toRelWidth (ColWidth w) = w alignCommand :: Alignment -> Text alignCommand = \case AlignLeft -> "\\raggedright" AlignRight -> "\\raggedleft" AlignCenter -> "\\centering" AlignDefault -> "\\raggedright" colAlign :: Alignment -> Text colAlign = \case AlignLeft -> "l" AlignRight -> "r" AlignCenter -> "c" AlignDefault -> "l" data CaptionDocs = CaptionDocs { captionCommand :: Doc Text , captionNotes :: Doc Text } captionToLaTeX :: PandocMonad m => ([Inline] -> LW m (Doc Text)) -> Caption -> Text -- ^ table identifier (label) -> LW m CaptionDocs captionToLaTeX inlnsToLaTeX caption ident = do (captionText, captForLot, captNotes) <- getCaption inlnsToLaTeX False caption label <- labelFor ident return $ CaptionDocs { captionNotes = captNotes , captionCommand = if isEmpty captionText && isEmpty label then empty else "\\caption" <> captForLot <> braces captionText <> label } type BlocksWriter m = [Block] -> LW m (Doc Text) headToLaTeX :: PandocMonad m => BlocksWriter m -> Bool -> ColumnCount -> Ann.TableHead -> LW m (Doc Text) headToLaTeX blocksWriter isSimpleTable colCount (Ann.TableHead _attr headerRows) = do rowsContents <- mapM (rowToLaTeX blocksWriter isSimpleTable colCount HeaderCell . headerRowCells) headerRows return ("\\toprule\\noalign{}" $$ vcat rowsContents $$ "\\midrule\\noalign{}") -- | Converts a row of table cells into a LaTeX row. rowToLaTeX :: PandocMonad m => BlocksWriter m -> Bool -> ColumnCount -> CellType -> [Ann.Cell] -> LW m (Doc Text) rowToLaTeX blocksWriter isSimpleTable colCount celltype row = do cellsDocs <- mapM (cellToLaTeX blocksWriter isSimpleTable colCount celltype) (fillRow row) return $ hsep (intersperse "&" cellsDocs) <> " \\\\" -- | Pads row with empty cells to adjust for rowspans above this row. fillRow :: [Ann.Cell] -> [Ann.Cell] fillRow = go 0 where go _ [] = [] go n (acell@(Ann.Cell _spec (Ann.ColNumber colnum) cell):cells) = let (Cell _ _ _ (ColSpan colspan) _) = cell in map mkEmptyCell [n .. colnum - 1] ++ acell : go (colnum + colspan) cells mkEmptyCell :: Int -> Ann.Cell mkEmptyCell colnum = Ann.Cell ((AlignDefault, ColWidthDefault):|[]) (Ann.ColNumber colnum) B.emptyCell isEmptyHead :: Ann.TableHead -> Bool isEmptyHead (Ann.TableHead _attr []) = True isEmptyHead (Ann.TableHead _attr rows) = all (null . headerRowCells) rows isEmptyFoot :: Ann.TableFoot -> Bool isEmptyFoot (Ann.TableFoot _attr []) = True isEmptyFoot (Ann.TableFoot _attr rows) = all (null . headerRowCells) rows -- | Gets all cells in a header row. headerRowCells :: Ann.HeaderRow -> [Ann.Cell] headerRowCells (Ann.HeaderRow _attr _rownum cells) = cells -- | Gets all cells in a body row. bodyRowCells :: Ann.BodyRow -> [Ann.Cell] bodyRowCells (Ann.BodyRow _attr _rownum rowhead cells) = rowhead <> cells -- | Gets a list of rows of the table body, where a row is a simple -- list of cells. bodyRows :: Ann.TableBody -> [[Ann.Cell]] bodyRows (Ann.TableBody _attr _rowheads headerRows rows) = map headerRowCells headerRows <> map bodyRowCells rows -- | Gets a list of rows of the table head, where a row is a simple -- list of cells. headRows :: Ann.TableHead -> [[Ann.Cell]] headRows (Ann.TableHead _attr rows) = map headerRowCells rows -- | Gets a list of rows from the foot, where a row is a simple list -- of cells. footRows :: Ann.TableFoot -> [[Ann.Cell]] footRows (Ann.TableFoot _attr rows) = map headerRowCells rows -- For simple latex tables (without minipages or parboxes), -- we need to go to some lengths to get line breaks working: -- as LineBreak bs = \vtop{\hbox{\strut as}\hbox{\strut bs}}. fixLineBreaks :: Block -> Block fixLineBreaks = walk fixLineBreaks' fixLineBreaks' :: [Inline] -> [Inline] fixLineBreaks' ils = case splitBy (== LineBreak) ils of [] -> [] [xs] -> xs chunks -> RawInline "tex" "\\vtop{" : concatMap tohbox chunks <> [RawInline "tex" "}"] where tohbox ys = RawInline "tex" "\\hbox{\\strut " : ys <> [RawInline "tex" "}"] -- We also change display math to inline math, since display -- math breaks in simple tables. displayMathToInline :: Inline -> Inline displayMathToInline (Math DisplayMath x) = Math InlineMath x displayMathToInline x = x cellToLaTeX :: PandocMonad m => BlocksWriter m -> Bool -> ColumnCount -> CellType -> Ann.Cell -> LW m (Doc Text) cellToLaTeX blockListToLaTeX isSimpleTable colCount celltype annotatedCell = do let (Ann.Cell specs colnum cell) = annotatedCell let colWidths = NonEmpty.map snd specs let hasWidths = NonEmpty.head colWidths /= ColWidthDefault let specAlign = fst (NonEmpty.head specs) let (Cell _attr align' rowspan colspan blocks) = cell let align = case align' of AlignDefault -> specAlign _ -> align' beamer <- gets stBeamer externalNotes <- gets stExternalNotes -- See #5367 -- footnotehyper/footnote don't work in beamer, -- so we need to produce the notes outside the table... modify $ \st -> st{ stExternalNotes = beamer } let isPlainOrPara = \case Para{} -> True Plain{} -> True _ -> False let hasLineBreak LineBreak = Any True hasLineBreak _ = Any False let hasLineBreaks = getAny $ query hasLineBreak blocks result <- if not hasWidths || (celltype /= HeaderCell && all isPlainOrPara blocks && not hasLineBreaks) then blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks else do cellContents <- inMinipage $ blockListToLaTeX blocks let valign = text $ case celltype of HeaderCell -> "[b]" BodyCell -> "[t]" let halign = literal $ alignCommand align return $ "\\begin{minipage}" <> valign <> braces "\\linewidth" <> halign <> cr <> cellContents <> (if hasLineBreaks then "\\strut" else mempty) <> cr <> "\\end{minipage}" modify $ \st -> st{ stExternalNotes = externalNotes } when (rowspan /= RowSpan 1) $ modify (\st -> st{ stMultiRow = True }) let inMultiColumn x = case colspan of (ColSpan 1) -> x (ColSpan n) -> let colDescr = multicolumnDescriptor isSimpleTable align colWidths colCount colnum in "\\multicolumn" <> braces (literal (tshow n)) <> braces (literal colDescr) <> braces ("%\n" <> x) -- linebreak for readability let hasColWidths = not (all (== ColWidthDefault) colWidths) let aligncmd = case specAlign of AlignCenter -> "\\centering\\arraybackslash " AlignRight -> "\\raggedright\\arraybackslash " _ -> mempty let inMultiRow x = case rowspan of (RowSpan 1) -> x (RowSpan n) -> let nrows = literal (tshow n) in "\\multirow" <> braces nrows <> braces -- width of column (if hasColWidths then "=" -- max width else "*") -- natural width <> braces (aligncmd <> x) return . inMultiColumn . inMultiRow $ result -- | Returns the width of a cell spanning @n@ columns. multicolumnDescriptor :: Bool -> Alignment -> NonEmpty ColWidth -> ColumnCount -> Ann.ColNumber -> Text multicolumnDescriptor isSimpleTable align colWidths (ColumnCount numcols) (Ann.ColNumber colnum) = let toWidth = \case ColWidthDefault -> (1 / fromIntegral numcols) ColWidth x -> x colspan = length colWidths width = sum $ NonEmpty.map toWidth colWidths -- no column separators at beginning of first and end of last column. skipColSep = "@{}" :: String in T.pack $ if isSimpleTable then printf "%s%s%s" (if colnum == 0 then skipColSep else "") (T.unpack (colAlign align)) (if colnum + colspan >= numcols then skipColSep else "") else printf "%s>{%s\\arraybackslash}p{(\\linewidth - %d\\tabcolsep) * \\real{%0.4f} + %d\\tabcolsep}%s" (if colnum == 0 then skipColSep else "") (T.unpack (alignCommand align)) (2 * (numcols - 1)) width (2 * (colspan - 1)) (if colnum + colspan >= numcols then skipColSep else "") -- | Perform a conversion, assuming that the context is a minipage. inMinipage :: Monad m => LW m a -> LW m a inMinipage action = do isInMinipage <- gets stInMinipage modify $ \st -> st{ stInMinipage = True } result <- action modify $ \st -> st{ stInMinipage = isInMinipage } return result data CellType = HeaderCell | BodyCell deriving Eq ================================================ FILE: src/Text/Pandoc/Writers/LaTeX/Types.hs ================================================ module Text.Pandoc.Writers.LaTeX.Types ( LW , WriterState (..) , startingState , PdfStandard (..) ) where import Control.Monad.State.Strict (StateT) import Data.Text (Text) import Text.DocLayout (Doc) import Text.Pandoc.Options ( WriterOptions (writerIncremental, writerTopLevelDivision) , TopLevelDivision (..) ) import Citeproc.Types (Lang) -- | LaTeX writer type. The type constructor @m@ will typically be an -- instance of PandocMonad. type LW m = StateT WriterState m data WriterState = WriterState { stInNote :: Bool -- ^ true if we're in a note , stInQuote :: Bool -- ^ true if in a blockquote , stExternalNotes :: Bool -- ^ true if in context where -- we need to store footnotes , stInMinipage :: Bool -- ^ true if in minipage , stInHeading :: Bool -- ^ true if in a section heading , stInItem :: Bool -- ^ true if in \item[..] , stInFigure :: Bool -- ^ true if in figure environment , stInCite :: Bool -- ^ true if in a Cite , stNotes :: [Doc Text] -- ^ notes in a minipage , stOLLevel :: Int -- ^ level of ordered list nesting , stOptions :: WriterOptions -- ^ writer options, so they don't have to -- be parameter , stVerbInNote :: Bool -- ^ true if document has verbatim text in note , stTable :: Bool -- ^ true if document has a table , stSubfigure :: Bool -- ^ true if document has subfigures , stMultiRow :: Bool -- ^ true if document has multirow cells , stStrikeout :: Bool -- ^ true if document has strikeout , stUrl :: Bool -- ^ true if document has visible URL link , stGraphics :: Bool -- ^ true if document contains images , stSVG :: Bool -- ^ true if document contains SVGs , stLHS :: Bool -- ^ true if document has literate haskell code , stHasChapters :: Bool -- ^ true if document has chapters , stCsquotes :: Bool -- ^ true if document uses csquotes , stHighlighting :: Bool -- ^ true if document has highlighted code , stIncremental :: Bool -- ^ true if beamer lists should be , stZwnj :: Bool -- ^ true if document has a ZWNJ character , stInternalLinks :: [Text] -- ^ list of internal link targets , stBeamer :: Bool -- ^ produce beamer , stEmptyLine :: Bool -- ^ true if no content on line , stHasCslRefs :: Bool -- ^ has a Div with class refs , stIsFirstInDefinition :: Bool -- ^ first block in a defn list , stLang :: Maybe Lang -- ^ lang specified in metadata , 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 startingState options = WriterState { stInNote = False , stInQuote = False , stExternalNotes = False , stInHeading = False , stInMinipage = False , stInItem = False , stInFigure = False , stInCite = False , stNotes = [] , stOLLevel = 1 , stOptions = options , stVerbInNote = False , stTable = False , stSubfigure = False , stMultiRow = False , stStrikeout = False , stUrl = False , stGraphics = False , stSVG = False , stLHS = False , stHasChapters = case writerTopLevelDivision options of TopLevelPart -> True TopLevelChapter -> True _ -> False , stCsquotes = False , stHighlighting = False , stIncremental = writerIncremental options , stZwnj = False , stInternalLinks = [] , stBeamer = False , stEmptyLine = True , stHasCslRefs = False , stIsFirstInDefinition = False , stLang = Nothing , stInSoulCommand = False , stCancel = False , stInCaption = False } ================================================ FILE: src/Text/Pandoc/Writers/LaTeX/Util.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.LaTeX.Util Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable -} module Text.Pandoc.Writers.LaTeX.Util ( stringToLaTeX , StringContext(..) , toLabel , inCmd , wrapDiv , hypertarget , labelFor , getListingsLanguage , mbBraced ) where import Control.Applicative ((<|>)) import Control.Monad (when) import Text.Pandoc.Class (PandocMonad, toLang) import Text.Pandoc.Options (WriterOptions(..), isEnabled) import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState(..)) import Text.Pandoc.Writers.LaTeX.Lang (toBabel) import Text.Pandoc.Highlighting (toListingsLanguage) import Text.DocLayout import Text.Pandoc.Definition import Text.Pandoc.ImageSize (showFl) import Control.Monad.State.Strict (gets, modify) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Extensions (Extension(Ext_smart)) import Data.Char (isLetter, isSpace, isDigit, isAscii, ord, isAlphaNum) import Text.Printf (printf) import Text.Pandoc.Shared (safeRead) import qualified Data.Text.Normalize as Normalize import Data.List (uncons) data StringContext = TextString | URLString | CodeString deriving (Eq) -- escape things as needed for LaTeX stringToLaTeX :: PandocMonad m => StringContext -> Text -> LW m Text stringToLaTeX context zs = do opts <- gets stOptions when (T.any (== '\x200c') zs) $ modify (\s -> s { stZwnj = True }) return $ T.pack $ foldr (go opts context) mempty $ T.unpack $ if writerPreferAscii opts then Normalize.normalize Normalize.NFD zs else zs where go :: WriterOptions -> StringContext -> Char -> String -> String go opts ctx x xs = let ligatures = isEnabled Ext_smart opts && ctx == TextString isUrl = ctx == URLString mbAccentCmd = if writerPreferAscii opts && ctx == TextString then uncons xs >>= \(c,_) -> lookupAccent c else Nothing emits s = case mbAccentCmd of Just cmd -> cmd <> "{" <> s <> "}" <> drop 1 xs -- drop combining accent Nothing -> s <> xs emitc c = case mbAccentCmd of Just cmd -> cmd <> "{" <> [c] <> "}" <> drop 1 xs -- drop combining accent Nothing -> c : xs emitcseq cs = case xs of c:_ | isLetter c , ctx == TextString -> cs <> " " <> xs | isSpace c -> cs <> "{}" <> xs | ctx == TextString -> cs <> xs _ -> cs <> "{}" <> xs emitquote cs = case xs of '`':_ -> cs <> "\\," <> xs -- add thin space '\'':_ -> cs <> "\\," <> xs -- add thin space _ -> cs <> xs in case x of _ | isUrl -> case x of '\\' -> emitc '/' -- NB / works as path sep even on Windows '#' -> emits "\\#" -- #9014 '%' -> emits "\\%" -- #9014 '{' -> emits "\\%7B" '}' -> emits "\\%7D" '|' -> emits "\\%7C" '^' -> emits "\\%5E" '[' -> emits "\\%5B" ']' -> emits "\\%5D" '`' -> emits "\\%60" _ -> emitc x '{' -> emits "\\{" '}' -> emits "\\}" '?' | ligatures -> -- avoid ?` ligature case xs of '`':_ -> emits "?{\\kern0pt}" -- se #10610 _ -> emitc x '!' | ligatures -> -- avoid !` ligature case xs of '`':_ -> emits "!{\\kern0pt}" _ -> emitc x '`' | ctx == CodeString -> emitcseq "\\textasciigrave" '$' -> emits "\\$" '%' -> emits "\\%" '&' -> emits "\\&" '_' -> emits "\\_" '#' -> emits "\\#" '-' -> case xs of -- prevent adjacent hyphens from forming ligatures ('-':_) -> emits "-\\/" _ -> emitc '-' '~' -> emitcseq "\\textasciitilde" '^' -> emits "\\^{}" '\\' -> emitcseq "\\textbackslash" '|' -> emitcseq "\\textbar" '<' -> emitcseq "\\textless" '>' -> emitcseq "\\textgreater" '[' -> emits "{[}" -- to avoid interpretation as ']' -> emits "{]}" -- optional arguments '\'' -> emitcseq "\\textquotesingle" '\160' -> emits "~" '\x00AD' -> emits "\\-" -- shy hyphen '\x200B' -> emits "\\hspace{0pt}" -- zero-width space '\x202F' -> emits "\\," '\x2026' | ligatures -> emitcseq "\\ldots" '\x2018' | ligatures -> emitquote "`" '\x2019' | ligatures -> emitquote "'" '\x201C' | ligatures -> emitquote "``" '\x201D' | ligatures -> emitquote "''" '\x2014' | ligatures -> emits "---" '\x2013' | ligatures -> emits "--" _ | writerPreferAscii opts -> case x of 'ı' -> emitcseq "\\i" 'ȷ' -> emitcseq "\\j" 'å' -> emitcseq "\\aa" 'Å' -> emitcseq "\\AA" 'ß' -> emitcseq "\\ss" 'ø' -> emitcseq "\\o" 'Ø' -> emitcseq "\\O" 'Ł' -> emitcseq "\\L" 'ł' -> emitcseq "\\l" 'æ' -> emitcseq "\\ae" 'Æ' -> emitcseq "\\AE" 'œ' -> emitcseq "\\oe" 'Œ' -> emitcseq "\\OE" '£' -> emitcseq "\\pounds" '€' -> emitcseq "\\euro" '©' -> emitcseq "\\copyright" _ -> emitc x | otherwise -> emitc x lookupAccent :: Char -> Maybe String lookupAccent '\779' = Just "\\H" lookupAccent '\768' = Just "\\`" lookupAccent '\769' = Just "\\'" lookupAccent '\770' = Just "\\^" lookupAccent '\771' = Just "\\~" lookupAccent '\776' = Just "\\\"" lookupAccent '\775' = Just "\\." lookupAccent '\772' = Just "\\=" lookupAccent '\781' = Just "\\|" lookupAccent '\817' = Just "\\b" lookupAccent '\807' = Just "\\c" lookupAccent '\783' = Just "\\G" lookupAccent '\777' = Just "\\h" lookupAccent '\803' = Just "\\d" lookupAccent '\785' = Just "\\f" lookupAccent '\778' = Just "\\r" lookupAccent '\865' = Just "\\t" lookupAccent '\782' = Just "\\U" lookupAccent '\780' = Just "\\v" lookupAccent '\774' = Just "\\u" lookupAccent '\808' = Just "\\k" lookupAccent '\8413' = Just "\\textcircled" lookupAccent _ = Nothing toLabel :: PandocMonad m => Text -> LW m Text toLabel z = go `fmap` stringToLaTeX URLString z where go = T.concatMap $ \x -> case x of _ | (isLetter x || isDigit x) && isAscii x -> T.singleton x | T.any (== x) "_-+=:;." -> T.singleton x | otherwise -> T.pack $ "ux" <> printf "%x" (ord x) -- | Puts contents into LaTeX command. inCmd :: Text -> Doc Text -> Doc Text inCmd cmd contents = char '\\' <> literal cmd <> braces contents mapAlignment :: Text -> Text mapAlignment a = case a of "top" -> "T" "top-baseline" -> "t" "bottom" -> "b" "center" -> "c" _ -> a wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text) wrapDiv (_,classes,kvs) t = do beamer <- gets stBeamer let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir lang <- toLang $ lookup "lang" kvs let wrapColumns = if beamer && "columns" `elem` classes then \contents -> let valign = maybe "T" mapAlignment (lookup "align" kvs) totalwidth = maybe [] (\x -> ["totalwidth=" <> x]) (lookup "totalwidth" kvs) onlytextwidth = filter ("onlytextwidth" ==) classes options = text $ T.unpack $ T.intercalate "," $ valign : totalwidth ++ onlytextwidth in inCmd "begin" "columns" <> brackets options $$ contents $$ inCmd "end" "columns" else id wrapColumn = if beamer && "column" `elem` classes then \contents -> let valign = maybe "" (brackets . text . T.unpack . mapAlignment) (lookup "align" kvs) w = maybe "0.48" fromPct (lookup "width" kvs) in inCmd "begin" "column" <> valign <> braces (literal w <> "\\linewidth") $$ contents $$ inCmd "end" "column" else id fromPct xs = case T.unsnoc xs of Just (ds, '%') -> case safeRead ds of Just digits -> showFl (digits / 100 :: Double) Nothing -> xs _ -> xs wrapDir = case lookup "dir" kvs of Just "rtl" -> align "RTL" Just "ltr" -> align "LTR" _ -> id wrapLang txt = case lang >>= toBabel of Just l -> inCmd "begin" "otherlanguage" <> (braces (literal l)) $$ blankline <> txt <> blankline $$ inCmd "end" "otherlanguage" Nothing -> txt return $ wrapColumns . wrapColumn . wrapDir . wrapLang $ t hypertarget :: PandocMonad m => Text -> LW m (Doc Text) hypertarget "" = return mempty hypertarget ident = do inHeading <- gets stInHeading if inHeading then do -- see #9209 (these cases should be rare) ref <- literal <$> toLabel ident return $ text "\\protect\\hypertarget" <> braces ref <> "{}" else do label <- labelFor ident return $ text "\\protect\\phantomsection" <> label labelFor :: PandocMonad m => Text -> LW m (Doc Text) labelFor "" = return empty labelFor ident = do ref <- literal `fmap` toLabel ident return $ text "\\label" <> braces ref -- Determine listings language from list of class attributes. getListingsLanguage :: [Text] -> Maybe Text getListingsLanguage xs = foldr ((<|>) . toListingsLanguage) Nothing xs mbBraced :: Text -> Text mbBraced x = if not (T.all isAlphaNum x) then "{" <> x <> "}" else x ================================================ FILE: src/Text/Pandoc/Writers/LaTeX.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.LaTeX Copyright : Copyright (C) 2006-2025 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' format into LaTeX. -} module Text.Pandoc.Writers.LaTeX ( writeLaTeX , writeBeamer ) where import Control.Monad.State.Strict ( MonadState(get, put), gets, modify, evalStateT ) import Control.Monad ( MonadPlus(mplus), liftM, when, unless ) import Crypto.Hash (hashWith, MD5(MD5)) import Data.Containers.ListUtils (nubOrd) import Data.Char (isDigit, isAscii, isLetter) 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 (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) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight, defaultStyle, styleToLaTeX) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Slides import Text.Pandoc.Walk (query, walk, walkM) import Text.Pandoc.Writers.LaTeX.Caption (getCaption) import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX) import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib, citationsToBiblatex) 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, wrapDiv, hypertarget, labelFor, getListingsLanguage, mbBraced) import Text.Pandoc.Writers.Shared import qualified Data.Attoparsec.Text as A import qualified Text.Pandoc.UTF8 as UTF8 import qualified Text.Pandoc.Writers.AnnotatedTable as Ann import Control.Applicative ((<|>)) -- Work around problems with notes inside emphasis (see #8982) isolateBigNotes :: ([Inline] -> Inline) -> [Inline] -> [Inline] isolateBigNotes constructor xs = let (before, after) = break isBigNote xs in case after of (noteInline:rest) -> constructor before : noteInline : isolateBigNotes constructor rest [] -> [constructor xs] isBigNote :: Inline -> Bool isBigNote (Note [Plain _]) = False -- A small note isBigNote (Note [Para _]) = False -- A small note isBigNote (Note _) = True -- A big note isBigNote _ = False -- Not a note raiseBigNotes :: [Inline] -> [Inline] raiseBigNotes (Emph inner : xs) = isolateBigNotes Emph (raiseBigNotes inner) ++ raiseBigNotes xs raiseBigNotes (Strong inner : xs) = isolateBigNotes Strong (raiseBigNotes inner) ++ raiseBigNotes xs raiseBigNotes (Underline inner : xs) = isolateBigNotes Underline (raiseBigNotes inner) ++ raiseBigNotes xs raiseBigNotes (Strikeout inner : xs) = isolateBigNotes Strikeout (raiseBigNotes inner) ++ raiseBigNotes xs raiseBigNotes (x : xs) = x : raiseBigNotes xs raiseBigNotes [] = [] -- | Convert Pandoc to LaTeX. writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeLaTeX options document = do let Any hasBigNotes = query (\il -> if isBigNote il then Any True else Any False) document let document' = if hasBigNotes then walk raiseBigNotes document else document evalStateT (pandocToLaTeX options document') $ startingState options -- | Convert Pandoc to LaTeX Beamer. writeBeamer :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeBeamer options document = evalStateT (pandocToLaTeX options document) $ (startingState options){ stBeamer = True } pandocToLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> LW m Text pandocToLaTeX options (Pandoc meta blocks) = do -- Strip off 'references' header if --natbib or --biblatex let method = writerCiteMethod options let isRefsDiv (Div ("refs",_,_) _) = True isRefsDiv _ = False let blocks' = if method == Biblatex || method == Natbib then filter (not . isRefsDiv) blocks else blocks -- see if there are internal links let isInternalLink (Link _ _ (s,_)) | Just ('#', xs) <- T.uncons s = [xs] isInternalLink _ = [] modify $ \s -> s{ stInternalLinks = query isInternalLink blocks' } let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing docLangs <- catMaybes <$> mapM (toLang . Just) (nubOrd (query (extract "lang") blocks)) mblang <- toLang $ case getLang options meta of Just l -> Just l Nothing | null docLangs -> Nothing | otherwise -> Just "en" modify $ \s -> s{ stLang = mblang } metadata <- metaToContext options blockListToLaTeX (fmap chomp . inlineListToLaTeX) meta let chaptersClasses = ["memoir","book","report","scrreprt","scrreport", "scrbook","extreport","extbook","tufte-book", "ctexrep","ctexbook","elegantbook"] let frontmatterClasses = ["memoir","book","scrbook","extbook","tufte-book", "ctexbook","elegantbook"] -- these have \frontmatter etc. beamer <- gets stBeamer let documentClass = case lookupContext "documentclass" (writerVariables options) `mplus` (stringify <$> lookupMeta "documentclass" meta) of Just x -> x Nothing | beamer -> "beamer" | otherwise -> case writerTopLevelDivision options of TopLevelPart -> "book" TopLevelChapter -> "book" _ -> "article" when (documentClass `elem` chaptersClasses) $ modify $ \s -> s{ stHasChapters = True } let csquotes = case lookupContext "csquotes" (writerVariables options) of Just (BoolVal v) -> v Just (SimpleVal (Text _ t)) -> t /= ("false" :: Text) _ -> case stringify <$> lookupMeta "csquotes" meta of Nothing -> False Just "false" -> False Just _ -> True when csquotes $ modify $ \s -> s{stCsquotes = True} let (blocks'', lastHeader) = if writerCiteMethod options == Citeproc then (blocks', []) else case reverse blocks' of Header 1 _ il : _ -> (init blocks', il) _ -> (blocks', []) blocks''' <- if beamer then toSlides blocks'' else return $ makeSections False Nothing blocks'' main <- blockListToLaTeX blocks''' biblioTitle <- inlineListToLaTeX lastHeader st <- get titleMeta <- escapeCommas <$> -- see #10501 stringToLaTeX TextString (stringify $ docTitle meta) subtitleMeta <- stringToLaTeX TextString (stringify $ lookupMetaInlines "subtitle" meta) authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta -- The trailer ID is as hash used to identify the PDF. Taking control of its -- value is important when aiming for reproducible PDF generation. Setting -- `SOURCE_DATE_EPOCH` is the traditional method used to control -- reproducible builds. There are no cryptographic requirements for the ID, -- so the 128bits (16 bytes) of MD5 are appropriate. reproduciblePDF <- isJust <$> lookupEnv "SOURCE_DATE_EPOCH" trailerID <- do time <- getPOSIXTime let hash = T.pack . show . hashWith MD5 $ mconcat [ UTF8.fromString $ show time , UTF8.fromText $ render Nothing main ] pure $ mconcat [ "<", hash, "> <", hash, ">" ] -- we need a default here since lang is used in template conditionals let hasStringValue x = isJust (getField x metadata :: Maybe (Doc Text)) let geometryFromMargins = mconcat $ intersperse ("," :: Doc Text) $ mapMaybe (\(x,y) -> ((x <> "=") <>) <$> getField y metadata) [("lmargin","margin-left") ,("rmargin","margin-right") ,("tmargin","margin-top") ,("bmargin","margin-bottom") ] let dirs = query (extract "dir") blocks let nociteIds = query (\case Cite cs _ -> map citationId cs _ -> []) $ lookupMetaInlines "nocite" meta -- see #7414, avoid escaped underscores let unescapeUnderscore = T.replace "\\_" "_" 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) $ defField "toc" (writerTableOfContents options) $ defField "lof" (writerListOfFigures options) $ defField "lot" (writerListOfTables options) $ defField "toc-depth" (tshow (writerTOCDepth options - if stHasChapters st then 1 else 0)) $ defField "body" main $ defField "title-meta" titleMeta $ defField "subtitle-meta" subtitleMeta $ defField "author-meta" (T.intercalate "; " authorsMeta) $ defField "documentclass" documentClass $ defField "verbatim-in-note" (stVerbInNote st) $ defField "tables" (stTable st) $ defField "multirow" (stMultiRow st) $ defField "cancel" (stCancel st) $ defField "strikeout" (stStrikeout st) $ defField "url" (stUrl st) $ defField "numbersections" (writerNumberSections options) $ defField "lhs" (stLHS st) $ defField "graphics" (stGraphics st) $ defField "subfigure" (stSubfigure st) $ defField "svg" (stSVG st) $ defField "has-chapters" (stHasChapters st) $ defField "has-frontmatter" (documentClass `elem` frontmatterClasses) $ defField "listings" (writerHighlightMethod options == IdiomaticHighlighting || stLHS st) $ defField "zero-width-non-joiner" (stZwnj st) $ defField "beamer" beamer $ (if stHighlighting st then case writerHighlightMethod options of Skylighting sty -> defField "highlighting-macros" (T.stripEnd $ styleToLaTeX sty) DefaultHighlighting -> defField "highlighting-macros" (T.stripEnd $ styleToLaTeX defaultStyle) _ -> id else id) $ (case writerCiteMethod options of Natbib -> defField "biblio-title" biblioTitle . defField "natbib" True . defField "nocite-ids" nociteIds Biblatex -> defField "biblio-title" biblioTitle . defField "biblatex" True . defField "nocite-ids" nociteIds _ -> id) $ defField "colorlinks" (any hasStringValue ["citecolor", "urlcolor", "linkcolor", "toccolor", "filecolor"]) $ (if null dirs then id else defField "dir" ("ltr" :: Text)) $ defField "section-titles" True $ defField "csl-refs" (stHasCslRefs st) $ defField "geometry" geometryFromMargins $ (case T.uncons . render Nothing <$> getField "papersize" metadata of -- uppercase a4, a5, etc. Just (Just ('A', ds)) | not (T.null ds) && T.all isDigit ds -> resetField "papersize" ("a" <> ds) _ -> id) . (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' = -- note: lang is used in some conditionals in the template, -- so we need to set it if we have any babel/polyglossia: maybe id (\l -> defField "lang" (literal $ renderLang l)) mblang $ maybe id (\l -> defField "babel-lang" (literal l)) babelLang $ (case babelLang of -- see #8283 Just l | l `notElem` ldfLanguages -> defField "babeloptions" ("provide=*" :: Text) _ -> id) $ defField "babel-otherlangs" (map literal (filter (`elem` ldfLanguages) . nubOrd . catMaybes . filter (/= babelLang) $ map toBabel docLangs)) $ defField "latex-dir-rtl" ((render Nothing <$> getField "dir" context) == Just ("rtl" :: Text)) context return $ render colwidth $ case writerTemplate options of Nothing -> main Just tpl -> renderTemplate tpl context' -- Commas in title-meta need to be put in braces; see #10501 escapeCommas :: Text -> Text escapeCommas = T.replace "," "{,}" toSlides :: PandocMonad m => [Block] -> LW m [Block] toSlides bs = do opts <- gets stOptions let slideLevel = fromMaybe (getSlideLevel bs) $ writerSlideLevel opts let bs' = prepSlides slideLevel bs walkM (elementToBeamer slideLevel) $ makeSections False Nothing bs' -- this creates section slides and marks slides with class "slide","block" elementToBeamer :: PandocMonad m => Int -> Block -> LW m Block elementToBeamer slideLevel (Div (ident,"section":dclasses,dkvs) xs@(h@(Header lvl _ _) : ys)) | lvl > slideLevel = return $ Div (ident,"block":dclasses,dkvs) xs | lvl < slideLevel = do let isSlide (Div (_,"slide":_,_) _) = True isSlide (Div (_,"section":_,_) _) = True isSlide _ = False let (titleBs, slideBs) = break isSlide ys return $ case titleBs of [] -> Div (ident,"section":dclasses,dkvs) xs [Div (_,"notes":_,_) _] -> -- see #7857, don't create frame -- just for speaker notes after section heading Div (ident,"section":dclasses,dkvs) xs _ -> Div (ident,"section":dclasses,dkvs) (h : Div ("","slide":dclasses,dkvs) (h:titleBs) : slideBs) | otherwise = return $ Div (ident,"slide":dclasses,dkvs) xs elementToBeamer _ x = return x isListBlock :: Block -> Bool isListBlock (BulletList _) = True isListBlock (OrderedList _ _) = True isListBlock (DefinitionList _) = True isListBlock _ = False -- | Convert Pandoc block element to LaTeX. blockToLaTeX :: PandocMonad m => Block -- ^ Block to convert -> LW m (Doc Text) blockToLaTeX (Div attr@(identifier,"block":dclasses,_) (Header _ _ ils : bs)) = do let blockname | "example" `elem` dclasses = "exampleblock" | "alert" `elem` dclasses = "alertblock" | otherwise = "block" anchor <- if T.null identifier then pure empty else (cr <>) <$> hypertarget identifier title' <- inlineListToLaTeX ils contents <- blockListToLaTeX bs wrapDiv attr $ ("\\begin" <> braces blockname <> braces title' <> anchor) $$ contents $$ "\\end" <> braces blockname blockToLaTeX (Div (identifier,"slide":dclasses,dkvs) (Header _ (_,hclasses,hkvs) ils : bs)) = do -- note: [fragile] is required or verbatim breaks let hasCodeBlock (CodeBlock _ _) = [True] hasCodeBlock _ = [] let hasCode (Code _ _) = [True] hasCode _ = [] let classes = nubOrd $ dclasses ++ hclasses let kvs = nubOrd $ dkvs ++ hkvs let fragile = "fragile" `elem` classes || not (null $ query hasCodeBlock bs ++ query hasCode bs) let frameoptions = ["allowdisplaybreaks", "allowframebreaks", "fragile", "b", "c", "t", "environment", "s", "squeeze", "label", "plain", "shrink", "standout", "noframenumbering", "containsverbatim"] let optionslist = ["fragile" | fragile , isNothing (lookup "fragile" kvs) , "fragile" `notElem` classes , "containsverbatim" `notElem` classes] ++ [k | k <- classes, k `elem` frameoptions] ++ [k <> "=" <> v | (k,v) <- kvs, k `elem` frameoptions] ++ [v | ("frameoptions", v) <- kvs] let options = if null optionslist then empty else brackets (literal (T.intercalate "," optionslist)) slideTitle <- if ils == [Str "\0"] -- marker for hrule then return empty else braces <$> inlineListToLaTeX ils slideAnchor <- if T.null identifier then pure empty else (cr <>) <$> hypertarget identifier contents <- blockListToLaTeX bs >>= wrapDiv (identifier,classes,kvs) return $ ("\\begin{frame}" <> options <> slideTitle <> slideAnchor) $$ contents $$ "\\end{frame}" blockToLaTeX (Div (identifier@(T.uncons -> Just (_,_)),dclasses,dkvs) (Header lvl ("",hclasses,hkvs) ils : bs)) = -- move identifier from div to header blockToLaTeX (Div ("",dclasses,dkvs) (Header lvl (identifier,hclasses,hkvs) ils : bs)) blockToLaTeX (Div (identifier,classes,kvs) bs) = do beamer <- gets stBeamer oldIncremental <- gets stIncremental if beamer && "incremental" `elem` classes then modify $ \st -> st{ stIncremental = True } else when (beamer && "nonincremental" `elem` classes) $ modify $ \st -> st { stIncremental = False } result <- if (identifier == "refs" || -- <- for backwards compatibility "csl-bib-body" `elem` classes) && (not (null bs)) then do modify $ \st -> st{ stHasCslRefs = True } inner <- blockListToLaTeX bs return $ ("\\begin{CSLReferences}" <> braces (if "hanging-indent" `elem` classes then "1" else "0") <> braces (maybe "1" literal (lookup "entry-spacing" kvs))) $$ inner $+$ "\\end{CSLReferences}" else blockListToLaTeX bs modify $ \st -> st{ stIncremental = oldIncremental } let wrap txt | beamer && "notes" `elem` classes = pure ("\\note" <> braces txt) -- speaker notes | "ref-" `T.isPrefixOf` identifier = do lab <- toLabel identifier pure $ ("\\bibitem" <> brackets "\\citeproctext" <> braces (literal lab)) $$ txt | otherwise = do linkAnchor <- hypertarget identifier pure $ linkAnchor $$ txt wrapDiv (identifier,classes,kvs) result >>= wrap blockToLaTeX (Plain lst) = inlineListToLaTeX lst -- . . . indicates pause in beamer slides blockToLaTeX (Para [Str ".",Space,Str ".",Space,Str "."]) = do beamer <- gets stBeamer if beamer then blockToLaTeX (RawBlock "latex" "\\pause") else inlineListToLaTeX [Str ".",Space,Str ".",Space,Str "."] blockToLaTeX (Para lst) = if null lst then do opts <- gets stOptions if isEnabled Ext_empty_paragraphs opts then pure "\\hfill\\par" else pure mempty else inlineListToLaTeX lst blockToLaTeX (LineBlock lns) = blockToLaTeX $ linesToPara lns blockToLaTeX (BlockQuote lst) = do beamer <- gets stBeamer csquotes <- liftM stCsquotes get case lst of [b] | beamer && isListBlock b -> do oldIncremental <- gets stIncremental modify $ \s -> s{ stIncremental = not oldIncremental } result <- blockToLaTeX b modify $ \s -> s{ stIncremental = oldIncremental } return result _ -> do oldInQuote <- gets stInQuote modify (\s -> s{stInQuote = True}) contents <- blockListToLaTeX lst modify (\s -> s{stInQuote = oldInQuote}) let envname = if csquotes then "displayquote" else "quote" return $ ("\\begin" <> braces envname) $$ contents $$ ("\\end" <> braces envname) blockToLaTeX (CodeBlock (identifier,classes,keyvalAttr) str) = do opts <- gets stOptions inNote <- stInNote <$> get linkAnchor <- if T.null identifier then pure empty else ((<> cr) . (<> "%")) <$> hypertarget identifier let lhsCodeBlock = do modify $ \s -> s{ stLHS = True } return $ flush (linkAnchor $$ "\\begin{code}" $$ literal str $$ "\\end{code}") $$ cr let rawCodeBlock = do env <- if inNote then modify (\s -> s{ stVerbInNote = True }) >> return "Verbatim" else return "verbatim" return $ flush (linkAnchor $$ literal ("\\begin{" <> env <> "}") $$ literal str $$ literal ("\\end{" <> env <> "}")) <> cr let listingsCodeBlock = do st <- get ref <- toLabel identifier kvs <- mapM (\(k,v) -> (k,) <$> stringToLaTeX TextString v) keyvalAttr let params = if writerHighlightMethod (stOptions st) == IdiomaticHighlighting then (case getListingsLanguage classes of Just l -> [ "language=" <> mbBraced l ] Nothing -> []) ++ [ "numbers=left" | "numberLines" `elem` classes || "number" `elem` classes || "number-lines" `elem` classes ] ++ [ (if key == "startFrom" then "firstnumber" else key) <> "=" <> mbBraced attr | (key,attr) <- kvs, key `notElem` ["exports", "tangle", "results"] -- see #4889 ] ++ ["label=" <> ref | not (T.null identifier)] else [] printParams | null params = empty | otherwise = brackets $ hcat (intersperse ", " (map literal params)) return $ flush ("\\begin{lstlisting}" <> printParams $$ literal str $$ "\\end{lstlisting}") $$ cr let highlightedCodeBlock = case highlight (writerSyntaxMap opts) formatLaTeXBlock ("",classes ++ ["default"],keyvalAttr) str of Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg rawCodeBlock Right h -> do when inNote $ modify (\s -> s{ stVerbInNote = True }) modify (\s -> s{ stHighlighting = True }) return (flush $ linkAnchor $$ literal h) case () of _ | isEnabled Ext_literate_haskell opts && "haskell" `elem` classes && "literate" `elem` classes -> lhsCodeBlock | writerHighlightMethod opts == IdiomaticHighlighting -> listingsCodeBlock | not (null classes), Skylighting _ <- writerHighlightMethod opts -> highlightedCodeBlock | not (null classes), DefaultHighlighting <- writerHighlightMethod opts -> highlightedCodeBlock -- we don't want to use \begin{verbatim} if our code -- contains \end{verbatim}: | inNote , "\\end{Verbatim}" `T.isInfixOf` str -> highlightedCodeBlock | not inNote , "\\end{verbatim}" `T.isInfixOf` str -> highlightedCodeBlock | otherwise -> rawCodeBlock blockToLaTeX b@(RawBlock f x) = do beamer <- gets stBeamer if f == Format "latex" || f == Format "tex" || (f == Format "beamer" && beamer) then return $ literal x else do report $ BlockNotRendered b return empty blockToLaTeX (BulletList []) = return empty -- otherwise latex error blockToLaTeX (BulletList lst) = do incremental <- gets stIncremental isFirstInDefinition <- gets stIsFirstInDefinition beamer <- gets stBeamer let inc = if beamer && incremental then "[<+->]" else "" items <- mapM (listItemToLaTeX False) lst let spacing = if isTightList lst then text "\\tightlist" else empty return $ -- force list to start on new line if in a defn list (if isFirstInDefinition then "\\hfill" else mempty) $$ text ("\\begin{itemize}" <> inc) $$ spacing $$ -- force list at beginning of definition to start on new line vcat items $$ "\\end{itemize}" blockToLaTeX (OrderedList _ []) = return empty -- otherwise latex error blockToLaTeX (OrderedList (start, numstyle, numdelim) lst) = do st <- get let inc = if stBeamer st && stIncremental st then "[<+->]" else "" let oldlevel = stOLLevel st isFirstInDefinition <- gets stIsFirstInDefinition put $ st {stOLLevel = oldlevel + 1} items <- mapM (listItemToLaTeX True) lst modify (\s -> s {stOLLevel = oldlevel}) let beamer = stBeamer st let tostyle x = case numstyle of Decimal -> "\\arabic" <> braces x UpperRoman -> "\\Roman" <> braces x LowerRoman -> "\\roman" <> braces x UpperAlpha -> "\\Alph" <> braces x LowerAlpha -> "\\alph" <> braces x Example -> "\\arabic" <> braces x DefaultStyle -> "\\arabic" <> braces x let todelim x = case numdelim of OneParen -> x <> ")" TwoParens -> parens x Period -> x <> "." _ -> x <> "." let exemplar = case numstyle of Decimal -> "1" UpperRoman -> "I" LowerRoman -> "i" UpperAlpha -> "A" LowerAlpha -> "a" Example -> "1" DefaultStyle -> "1" let enum = literal $ "enum" <> T.toLower (toRomanNumeral oldlevel) let stylecommand | numstyle == DefaultStyle && numdelim == DefaultDelim = empty | beamer && numstyle == Decimal && numdelim == Period = empty | beamer = brackets (todelim exemplar) | otherwise = "\\def" <> "\\label" <> enum <> braces (todelim $ tostyle enum) let resetcounter = if start == 1 || oldlevel > 4 then empty else "\\setcounter" <> braces enum <> braces (text $ show $ start - 1) let spacing = if isTightList lst then text "\\tightlist" else empty return $ -- force list at beginning of definition to start on new line (if isFirstInDefinition then "\\hfill" else mempty) $$ text ("\\begin{enumerate}" <> inc) $$ stylecommand $$ resetcounter $$ spacing $$ vcat items $$ "\\end{enumerate}" blockToLaTeX (DefinitionList []) = return empty blockToLaTeX (DefinitionList lst) = do incremental <- gets stIncremental beamer <- gets stBeamer let inc = if beamer && incremental then "[<+->]" else "" items <- mapM defListItemToLaTeX lst let spacing = if all (isTightList . snd) lst then text "\\tightlist" else empty return $ text ("\\begin{description}" <> inc) $$ spacing $$ vcat items $$ "\\end{description}" blockToLaTeX HorizontalRule = return "\\begin{center}\\rule{0.5\\linewidth}{0.5pt}\\end{center}" blockToLaTeX (Header level (id',classes,_) lst) = do modify $ \s -> s{stInHeading = True} hdr <- sectionHeader classes id' level lst modify $ \s -> s{stInHeading = False} return hdr blockToLaTeX (Table attr blkCapt specs thead tbodies tfoot) = tableToLaTeX inlineListToLaTeX blockListToLaTeX (Ann.toTable attr blkCapt specs thead tbodies tfoot) blockToLaTeX (Figure (ident, _, kvs) captnode body) = do opts <- gets stOptions (capt, captForLof, footnotes) <- getCaption inlineListToLaTeX True captnode lab <- labelFor ident let caption = "\\caption" <> captForLof <> braces capt <> lab placement = case lookup "latex-placement" kvs of Just p -> brackets (text (T.unpack p)) _ -> text "" isSubfigure <- gets stInFigure modify $ \st -> st{ stInFigure = True } contents <- case body of [b] -> blockToLaTeX b bs -> mconcat . intersperse (cr <> "\\hfill") <$> mapM (toSubfigure (length bs)) bs let innards = "\\centering" $$ (case writerFigureCaptionPosition opts of CaptionBelow -> contents $$ caption CaptionAbove -> caption $$ contents) <> cr modify $ \st -> st{ stInFigure = isSubfigure , stSubfigure = stSubfigure st || isSubfigure } let containsTable = getAny . query (\case Table {} -> Any True _ -> Any False) st <- get return $ (case () of _ | containsTable body -> -- placing a longtable in a figure or center environment does -- not make sense. cr <> contents _ | stInMinipage st -> -- can't have figures in notes or minipage (here, table cell) -- http://www.tex.ac.uk/FAQ-ouparmd.html cr <> "\\begin{center}" $$ contents $+$ capt $$ "\\end{center}" _ | isSubfigure -> innards _ -> cr <> "\\begin{figure}" <> placement $$ innards $$ "\\end{figure}") $$ footnotes toSubfigure :: PandocMonad m => Int -> Block -> LW m (Doc Text) toSubfigure nsubfigs blk = do contents <- blockToLaTeX blk let linewidth = tshow @Double (0.9 / fromIntegral nsubfigs) <> "\\linewidth" return $ cr <> case blk of Figure {} -> vcat [ "\\begin{subfigure}[t]" <> braces (literal linewidth) , contents , "\\end{subfigure}" ] _ -> vcat [ "\\begin{minipage}[t]" <> braces (literal linewidth) , contents , "\\end{minipage}" ] blockListToLaTeX :: PandocMonad m => [Block] -> LW m (Doc Text) blockListToLaTeX lst = vsep `fmap` mapM (\b -> setEmptyLine True >> blockToLaTeX b) lst listItemToLaTeX :: PandocMonad m => Bool -> [Block] -> LW m (Doc Text) listItemToLaTeX isOrdered lst -- we need to put some text before a header if it's the first -- element in an item. This will look ugly in LaTeX regardless, but -- this will keep the typesetter from throwing an error. | (Header{} :_) <- lst = (text "\\item ~" $$) . nest 2 <$> blockListToLaTeX lst | not isOrdered , Just (checked, bs) <- toTaskListItem lst = taskListItem checked bs | otherwise = (text "\\item" $$) . nest 2 <$> blockListToLaTeX lst where taskListItem checked bs = do let checkbox = if checked then "$\\boxtimes$" else "$\\square$" let bs' = case bs of Plain ils : xs -> Para ils : xs _ -> bs bsContents <- blockListToLaTeX bs' return $ "\\item" <> brackets checkbox $$ nest 2 bsContents defListItemToLaTeX :: PandocMonad m => ([Inline], [[Block]]) -> LW m (Doc Text) defListItemToLaTeX (term, defs) = do -- needed to turn off 'listings' because it breaks inside \item[...]: modify $ \s -> s{stInItem = True} term' <- inlineListToLaTeX term modify $ \s -> s{stInItem = False} -- put braces around term if it contains an internal link, -- since otherwise we get bad bracket interactions: \item[\hyperref[..] let isInternalLink (Link _ _ (src,_)) | Just ('#', _) <- T.uncons src = True isInternalLink _ = False let term'' = if any isInternalLink term then braces term' else term' def' <- case concat defs of [] -> return mempty (x:xs) -> do modify $ \s -> s{stIsFirstInDefinition = True } firstitem <- blockToLaTeX x modify $ \s -> s{stIsFirstInDefinition = False } rest <- blockListToLaTeX xs return $ firstitem $+$ rest return $ case defs of ((Header{} : _) : _) -> "\\item" <> brackets term'' <> " ~ " $$ def' ((CodeBlock{} : _) : _) -> -- see #4662 "\\item" <> brackets term'' <> " ~ " $$ def' _ -> "\\item" <> brackets term'' $$ def' -- | Craft the section header, inserting the section reference, if supplied. sectionHeader :: PandocMonad m => [Text] -- classes -> Text -> Int -> [Inline] -> LW m (Doc Text) sectionHeader classes ident level lst = do let unnumbered = "unnumbered" `elem` classes let unlisted = "unlisted" `elem` classes txt <- inlineListToLaTeX lst plain <- stringToLaTeX TextString $ T.concat $ map stringify lst let removeInvalidInline (Note _) = [] removeInvalidInline (Span (id', _, _) _) | not (T.null id') = [] removeInvalidInline Image{} = [] removeInvalidInline x = [x] let lstNoNotes = foldr (mappend . (\x -> walkM removeInvalidInline x)) mempty lst txtNoNotes <- inlineListToLaTeX lstNoNotes txtNoLinksNoNotes <- inlineListToLaTeX (removeLinks lstNoNotes) -- footnotes in sections don't work (except for starred variants) -- unless you specify an optional argument: -- \section[mysec]{mysec\footnote{blah}} optional <- if unnumbered || lstNoNotes == lst || null lstNoNotes then return empty else return $ brackets txtNoNotes let contents = if render Nothing txt == plain then braces txt else braces (text "\\texorpdfstring" <> braces txt <> braces (literal plain)) book <- gets stHasChapters opts <- gets stOptions let topLevelDivision = if book && writerTopLevelDivision opts == TopLevelDefault then TopLevelChapter else writerTopLevelDivision opts beamer <- gets stBeamer let level' = if beamer && topLevelDivision `elem` [TopLevelPart, TopLevelChapter] -- beamer has parts but no chapters then if level == 1 then -1 else level - 1 else case topLevelDivision of TopLevelPart -> level - 2 TopLevelChapter -> level - 1 TopLevelSection -> level TopLevelDefault -> level let sectionType = case level' of -1 -> "part" 0 -> "chapter" 1 -> "section" 2 -> "subsection" 3 -> "subsubsection" 4 -> "paragraph" 5 -> "subparagraph" _ -> "" inQuote <- gets stInQuote let prefix = if inQuote then text "\\mbox{}%" -- needed for \paragraph, \subparagraph in quote environment -- see http://tex.stackexchange.com/questions/169830/ else empty lab <- labelFor ident let star = if unnumbered then text "*" else empty let title = star <> optional <> contents return $ if level' > 5 then txt else prefix $$ text ('\\':sectionType) <> title <> lab $$ if unnumbered && not unlisted then "\\addcontentsline{toc}" <> braces (text sectionType) <> braces txtNoLinksNoNotes else empty -- | Convert list of inline elements to LaTeX. inlineListToLaTeX :: PandocMonad m => [Inline] -- ^ Inlines to convert -> LW m (Doc Text) inlineListToLaTeX lst = hcat <$> mapM inlineToLaTeX (addKerns . fixLineInitialSpaces . fixInitialLineBreaks $ lst) -- nonbreaking spaces (~) in LaTeX don't work after line breaks, -- so we insert a strut: this is mostly used in verse. where fixLineInitialSpaces [] = [] fixLineInitialSpaces (LineBreak : Str s : xs) | Just ('\160', _) <- T.uncons s = LineBreak : RawInline "latex" "\\strut " : Str s : fixLineInitialSpaces xs fixLineInitialSpaces (x:xs) = x : fixLineInitialSpaces xs -- We need \hfill\break for a line break at the start -- of a paragraph. See #5591. fixInitialLineBreaks (LineBreak:xs) = RawInline (Format "latex") "\\hfill\\break\n" : fixInitialLineBreaks xs fixInitialLineBreaks xs = xs addKerns [] = [] addKerns (Str s : q@Quoted{} : rest) | isQuote (T.takeEnd 1 s) = Str s : RawInline (Format "latex") "\\," : addKerns (q:rest) addKerns (q@Quoted{} : Str s : rest) | isQuote (T.take 1 s) = q : RawInline (Format "latex") "\\," : addKerns (Str s : rest) addKerns (x:xs) = x : addKerns xs isQuote "\"" = True isQuote "'" = True isQuote "\x2018" = True isQuote "\x2019" = True isQuote "\x201C" = True isQuote "\x201D" = True isQuote _ = False -- | Convert inline element to LaTeX inlineToLaTeX :: PandocMonad m => Inline -- ^ Inline to convert -> LW m (Doc Text) inlineToLaTeX (Span ("",["mark"],[]) lst) = do modify $ \st -> st{ stStrikeout = True } -- this gives us the soul package inCmd "hl" <$> inSoulCommand (inlineListToLaTeX lst) inlineToLaTeX (Span (id',classes,kvs) ils) = do linkAnchor <- hypertarget id' lang <- toLang $ lookup "lang" kvs let classToCmd "csl-no-emph" = Just "textup" classToCmd "csl-no-strong" = Just "textnormal" classToCmd "csl-no-smallcaps" = Just "textnormal" classToCmd "csl-block" = Just "CSLBlock" classToCmd "csl-left-margin" = Just "CSLLeftMargin" classToCmd "csl-right-inline" = Just "CSLRightInline" classToCmd "csl-indent" = Just "CSLIndent" classToCmd _ = Nothing kvToCmd ("dir","rtl") = Just "RL" kvToCmd ("dir","ltr") = Just "LR" kvToCmd _ = Nothing langCmds = case lang >>= toBabel of Just l -> ["foreignlanguage{" <> l <> "}"] Nothing -> [] let cmds = mapMaybe classToCmd classes ++ mapMaybe kvToCmd kvs ++ langCmds contents <- inlineListToLaTeX ils return $ (if "csl-right-inline" `elem` classes then ("%" <>) -- see #7932 else id) $ (if any (`elem` classes) ["csl-block","csl-left-margin","csl-right-inline","csl-indent"] then (cr <>) else id) $ (if T.null id' then empty else linkAnchor) <> (if null cmds then braces contents else foldr inCmd contents cmds) inlineToLaTeX (Emph lst) = inCmd "emph" <$> inlineListToLaTeX lst inlineToLaTeX (Underline lst) = do modify $ \st -> st{ stStrikeout = True } -- this gives us the soul package inCmd "ul" <$> inSoulCommand (inlineListToLaTeX lst) inlineToLaTeX (Strong lst) = inCmd "textbf" <$> inlineListToLaTeX lst inlineToLaTeX (Strikeout lst) = do modify $ \s -> s{ stStrikeout = True } inCmd "st" <$> inSoulCommand (inlineListToLaTeX lst) inlineToLaTeX (Superscript lst) = inCmd "textsuperscript" <$> inlineListToLaTeX lst inlineToLaTeX (Subscript lst) = inCmd "textsubscript" <$> inlineListToLaTeX lst inlineToLaTeX (SmallCaps lst) = inCmd "textsc"<$> inlineListToLaTeX lst inlineToLaTeX (Cite cits lst) = do opts <- gets stOptions modify $ \st -> st{ stInCite = True } res <- case writerCiteMethod opts of Natbib -> citationsToNatbib inlineListToLaTeX cits Biblatex -> citationsToBiblatex inlineListToLaTeX cits _ -> inlineListToLaTeX lst modify $ \st -> st{ stInCite = False } pure res inlineToLaTeX (Code (_,classes,kvs) str) = do opts <- gets stOptions inHeading <- gets stInHeading inItem <- gets stInItem inSoul <- gets stInSoulCommand inCaption <- gets stInCaption let listingsCode = do let listingsopts = (case getListingsLanguage classes of Just l -> (("language", mbBraced l):) Nothing -> id) [(k,v) | (k,v) <- kvs , k `notElem` ["exports","tangle","results"]] let listingsopt = if null listingsopts then "" else "[" <> T.intercalate ", " (map (\(k,v) -> k <> "=" <> v) listingsopts) <> "]" inNote <- gets stInNote when inNote $ modify $ \s -> s{ stVerbInNote = True } let chr = case "!\"'()*,-./:;?@" \\ T.unpack str of (c:_) -> c [] -> '!' let isEscapable '\\' = True isEscapable '{' = True isEscapable '}' = True isEscapable '%' = True isEscapable '~' = True isEscapable '_' = True isEscapable '&' = True isEscapable '#' = True isEscapable '^' = True isEscapable _ = False let escChar c | isEscapable c = T.pack ['\\',c] | otherwise = T.singleton c let str' = T.concatMap escChar str -- we always put lstinline in a dummy 'passthrough' command -- (defined in the default template) so that we don't have -- to change the way we escape characters depending on whether -- the lstinline is inside another command. See #1629: return $ literal $ "\\passthrough{\\lstinline" <> listingsopt <> T.singleton chr <> str' <> T.singleton chr <> "}" let rawCode = liftM (literal . (\s -> "\\texttt{" <> escapeSpaces s <> "}")) $ stringToLaTeX CodeString str where escapeSpaces = T.concatMap (\c -> if c == ' ' then "\\ " else T.singleton c) let highlightCode = case highlight (writerSyntaxMap opts) formatLaTeXInline ("",classes,[]) str of Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg rawCode Right h -> modify (\st -> st{ stHighlighting = True }) >> return (text (T.unpack h)) -- for soul commands we need to protect VERB in an mbox or we get an error -- (see #1294). with regular texttt we don't get an error, but we get -- incorrect results if there is a space (see #5529). let inMbox x = "\\mbox" <> braces x -- for captions we need to protect VERB with \protect (see #6821) let protect x = "\\protect" <> x let optionalProtect = case () of _ | inSoul -> inMbox | inCaption -> protect | otherwise -> id optionalProtect <$> case writerHighlightMethod opts of _ | inHeading || inItem -> rawCode -- see #5574 IdiomaticHighlighting -> listingsCode Skylighting _ | not (null classes) -> highlightCode DefaultHighlighting | not (null classes) -> highlightCode _noHighlighting -> rawCode inlineToLaTeX (Quoted qt lst) = do contents <- inlineListToLaTeX lst csquotes <- liftM stCsquotes get opts <- gets stOptions if csquotes then return $ case qt of DoubleQuote -> "\\enquote" <> braces contents SingleQuote -> "\\enquote*" <> braces contents else do let endsWithQuote xs = case reverse xs of Quoted{}:_ -> True Span _ ys : _ -> endsWithQuote ys Str s:_ -> T.takeEnd 1 s == "'" _ -> False let beginsWithQuote xs = case xs of Quoted{}:_ -> True Span _ ys : _ -> beginsWithQuote ys Str s:_ -> T.take 1 s == "`" _ -> False let inner = (if beginsWithQuote lst then "\\," else mempty) <> contents <> (if endsWithQuote lst then "\\," else mempty) return $ case qt of DoubleQuote -> if isEnabled Ext_smart opts then text "``" <> inner <> text "''" else char '\x201C' <> inner <> char '\x201D' SingleQuote -> if isEnabled Ext_smart opts then char '`' <> inner <> char '\'' else char '\x2018' <> inner <> char '\x2019' inlineToLaTeX (Str str) = do setEmptyLine False liftM literal $ stringToLaTeX TextString str inlineToLaTeX (Math _ str) | isMathEnv str -- see #9711 = do setEmptyLine False when (needsCancel str) $ modify $ \st -> st{ stCancel = True } pure $ literal str inlineToLaTeX (Math InlineMath str) = do setEmptyLine False inSoul <- gets stInSoulCommand let contents = literal (handleMathComment str) when (needsCancel str) $ modify $ \st -> st{ stCancel = True } return $ if inSoul -- #9597 then "$" <> contents <> "$" else "\\(" <> contents <> "\\)" inlineToLaTeX (Math DisplayMath str) = do setEmptyLine False inSoul <- gets stInSoulCommand let contents = literal (handleMathComment str) when (needsCancel str) $ modify $ \st -> st{ stCancel = True } return $ if inSoul -- # 9597 then "$$" <> contents <> "$$" else "\\[" <> contents <> "\\]" inlineToLaTeX il@(RawInline f str) = do beamer <- gets stBeamer if f == Format "latex" || f == Format "tex" || (f == Format "beamer" && beamer) then do setEmptyLine False return $ literal str else do report $ InlineNotRendered il return empty inlineToLaTeX LineBreak = do emptyLine <- gets stEmptyLine setEmptyLine True return $ (if emptyLine then "\\strut " else "") <> "\\\\" <> cr inlineToLaTeX SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of WrapAuto -> return space WrapNone -> return space WrapPreserve -> return cr inlineToLaTeX Space = return space inlineToLaTeX (Link (id',_,_) txt (src,_)) = (case T.uncons src of Just ('#', ident) -> do contents <- inlineListToLaTeX txt lab <- toLabel ident inCite <- gets stInCite beamer <- gets stBeamer return $ if inCite && "#ref-" `T.isPrefixOf` src then "\\citeproc" <> braces (literal lab) <> braces contents else if beamer then "\\hyperlink" <> braces (literal lab) <> braces contents else "\\hyperref" <> brackets (literal lab) <> braces contents _ -> case txt of -- For soul sommands we need to protect \url and \href in an mbox or -- we get an error (see #9366) [Str x] | T.all isAscii x -- see #8802 , unEscapeString (T.unpack x) == unEscapeString (T.unpack src) -> -- autolink do modify $ \s -> s{ stUrl = True } src' <- stringToLaTeX URLString (escapeURI src) protectInMboxIfInSoul $ literal $ "\\url{" <> src' <> "}" [Str x] | Just rest <- T.stripPrefix "mailto:" src, unEscapeString (T.unpack x) == unEscapeString (T.unpack rest) -> -- email autolink do modify $ \s -> s{ stUrl = True } src' <- stringToLaTeX URLString (escapeURI src) contents <- inlineListToLaTeX txt protectInMboxIfInSoul $ "\\href" <> braces (literal src') <> braces ("\\nolinkurl" <> braces contents) _ -> do contents <- inlineListToLaTeX txt src' <- stringToLaTeX URLString (escapeURI src) protectInMboxIfInSoul $ literal ("\\href{" <> src' <> "}{") <> contents <> char '}') >>= (if T.null id' then return else \x -> do linkAnchor <- hypertarget id' return (linkAnchor <> x)) inlineToLaTeX il@(Image _ _ (src, _)) | Just _ <- T.stripPrefix "data:" src = do report $ InlineNotRendered il return empty inlineToLaTeX (Image attr@(_,_,kvs) description (source, _)) = do setEmptyLine False let isSVG = ".svg" `T.isSuffixOf` source || ".SVG" `T.isSuffixOf` source modify $ \s -> s{ stGraphics = True , stSVG = stSVG s || isSVG } opts <- gets stOptions mbalt <- if isSVG then pure Nothing else case lookup "alt" kvs of Just x -> Just <$> stringToLaTeX TextString x Nothing | null description -> pure Nothing | otherwise -> Just <$> stringToLaTeX TextString (stringify description) let showDim dir = let d = text (show dir) <> "=" in case dimension dir attr of Just (Pixel a) -> [d <> literal (showInInch opts (Pixel a)) <> "in"] Just (Percent a) -> [d <> literal (showFl (a / 100)) <> case dir of Width -> "\\linewidth" Height -> "\\textheight" ] Just dim -> [d <> text (show dim)] Nothing -> case dir of Width | isJust (dimension Height attr) -> [d <> "\\linewidth"] Height | isJust (dimension Width attr) -> [d <> "\\textheight"] _ -> [] optList = showDim Width <> showDim Height <> (case (dimension Height attr, dimension Width attr) of (Just _, Just _) -> [] _ -> ["keepaspectratio"]) <> maybe [] (\x -> ["page=" <> literal x]) (lookup "page" kvs) <> maybe [] (\x -> ["trim=" <> literal x]) (lookup "trim" kvs) <> maybe [] (\x -> ["alt=" <> braces (literal x)]) mbalt <> maybe [] (const ["clip"]) (lookup "clip" kvs) options = if null optList then empty else brackets $ mconcat (intersperse "," optList) source' = if isURI source then source else T.pack $ unEscapeString $ T.unpack source source'' <- stringToLaTeX URLString source' inHeading <- gets stInHeading return $ (if inHeading then "\\protect" else "") <> (case dimension Width attr `mplus` dimension Height attr of Just _ -> id Nothing -> ("\\pandocbounded" <>) . braces) ((if isSVG then "\\includesvg" else "\\includegraphics") <> options <> braces (literal source'')) inlineToLaTeX (Note contents) = do setEmptyLine False externalNotes <- gets stExternalNotes modify (\s -> s{stInNote = True, stExternalNotes = True}) contents' <- blockListToLaTeX contents modify (\s -> s {stInNote = False, stExternalNotes = externalNotes}) let optnl = case reverse contents of (CodeBlock _ _ : _) -> cr _ -> empty let noteContents = nest 2 contents' <> optnl beamer <- gets stBeamer -- in beamer slides, display footnote from current overlay forward -- and ensure that the note is on the frame, not e.g. the column (#5769, #5954) incremental <- gets stIncremental let beamerMark = if beamer then if incremental then text "<.->[frame]" else text "<\\value{beamerpauses}->[frame]" else empty if externalNotes then do modify $ \st -> st{ stNotes = noteContents : stNotes st } return "\\footnotemark{}" -- note: a \n before } needed when note ends with a Verbatim environment else return $ "\\footnote" <> beamerMark <> braces noteContents -- A comment at the end of math needs to be followed by a newline, -- or the closing delimiter gets swallowed. handleMathComment :: Text -> Text handleMathComment s = let (_, ys) = T.break (\c -> c == '\n' || c == '%') $ T.reverse s -- no T.breakEnd in case T.uncons ys of Just ('%', ys') -> case T.uncons ys' of Just ('\\', _) -> s _ -> s <> "\n" _ -> s setEmptyLine :: PandocMonad m => Bool -> LW m () setEmptyLine b = modify $ \st -> st{ stEmptyLine = b } -- Extract a key from divs and spans extract :: Text -> Block -> [Text] extract key (Div attr _) = lookKey key attr extract key (Plain ils) = query (extractInline key) ils extract key (Para ils) = query (extractInline key) ils extract key (Header _ _ ils) = query (extractInline key) ils extract _ _ = [] -- Extract a key from spans extractInline :: Text -> Inline -> [Text] extractInline key (Span attr _) = lookKey key attr extractInline _ _ = [] -- Look up a key in an attribute and give a list of its values lookKey :: Text -> Attr -> [Text] lookKey key (_,_,kvs) = maybe [] T.words $ lookup key kvs -- soul doesn't like \(..\) delimiters, so we change these to $ (#9597) -- when processing their contents. inSoulCommand :: PandocMonad m => LW m a -> LW m a inSoulCommand pa = do oldInSoulCommand <- gets stInSoulCommand modify $ \st -> st{ stInSoulCommand = True } result <- pa modify $ \st -> st{ stInSoulCommand = oldInSoulCommand } pure result -- Inside soul commands some commands need to be protected in an mbox -- or we get an error (e.g. see #1294) protectInMboxIfInSoul :: (PandocMonad m, HasChars a) => Doc a -> LW m (Doc a) protectInMboxIfInSoul command = do inSoul <- gets stInSoulCommand return $ if inSoul then "\\mbox" <> braces command else command -- Babel languages with a .ldf that works well with all engines (see #8283). -- We follow the guidance from the Babel documentation: -- "In general, you should do this for European languages written in Latin -- and Cyrillic scripts, as well as for Vietnamese." ldfLanguages :: [Text] ldfLanguages = [ "magyar" , "croatian" , "ngerman" , "germanb" , "german" , "austrian" , "ngermanb" , "naustrian" , "nswissgerman" , "swissgerman" , "italian" , "greek" , "azerbaijani" , "american" , "newzealand" , "UKenglish" , "USenglish" , "australian" , "british" , "canadian" , "english" , "bahasa" , "slovak" , "finnish" , "occitan" , "swedish" , "brazil" , "portuguese" , "portuges" , "brazilian" , "spanish" , "norwegian" , "norsk" , "nynorsk" , "bulgarian" , "breton" , "belarusian" , "piedmontese" , "esperanto" , "lithuanian" , "ukraineb" , "scottishgaelic" , "scottish" , "dutch" , "afrikaans" , "czech" , "serbian" , "latvian" , "catalan" , "basque" , "albanian" , "irish" , "serbianc" , "interlingua" , "bosnian" , "friulan" , "romanian" , "icelandic" , "classiclatin" , "ecclesiasticlatin" , "medievallatin" , "latin" , "georgian" , "macedonian" , "welsh" , "vietnamese" , "romansh" , "danish" , "lsorbian" , "usorbian" , "polish-compat" , "polish" , "estonian" , "french" , "frenchb" , "canadien" , "acadian" , "francais" , "turkish" , "hindi" , "northernsami" , "samin" , "russianb" , "galician" , "slovene" ] isMathEnv :: Text -> Bool isMathEnv t = case T.stripPrefix "\\begin{" (T.dropWhile isSpaceChar t) of Nothing -> False Just t' -> T.takeWhile (/= '}') t' `elem` [ "align", "align*" , "flalign", "flalign*" , "alignat", "alignat*" , "dmath", "dmath*" , "dgroup", "dgroup*" , "darray", "darray*" , "gather", "gather*" , "multline", "multline*" , "subequations" , "equation", "equation*" , "eqnarray" , "displaymath" ] where isSpaceChar '\n' = True isSpaceChar '\r' = True isSpaceChar '\t' = True isSpaceChar ' ' = True isSpaceChar _ = False -- True if the math needs the cancel package needsCancel :: Text -> Bool needsCancel t = case A.parseOnly pCancel t of Right True -> True _ -> False where pCancel = (False <$ A.endOfInput) <|> do c <- A.anyChar case c of '\\' -> do x <- A.takeWhile isLetter if x == "cancel" || x == "xcancel" || x == "bcancel" 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 } ================================================ FILE: src/Text/Pandoc/Writers/Man.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.Man Copyright : Copyright (C) 2007-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to roff man page format. -} module Text.Pandoc.Writers.Man ( writeMan ) where import Control.Monad ( liftM, zipWithM, forM, unless ) import Control.Monad.State.Strict ( StateT, gets, modify, evalStateT ) import Control.Monad.Trans (MonadTrans(lift)) import Data.List (intersperse) import Data.List.NonEmpty (nonEmpty) import Data.Maybe (fromMaybe) import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Builder (deleteMeta) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Pandoc.Writers.Roff import Text.Pandoc.Highlighting import Text.Printf (printf) import Skylighting (TokenType(..), SourceLine, FormatOptions, defaultFormatOpts, defStyle, TokenStyle(..), Style(..)) -- | Convert Pandoc to Man. writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMan opts document = evalStateT (pandocToMan opts document) defaultWriterState -- | Return roff man representation of document. pandocToMan :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m Text pandocToMan opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing titleText <- inlineListToMan opts $ docTitle meta let title' = render Nothing titleText let setFieldsFromTitle = case T.break (== ' ') title' of (cmdName, rest) -> case T.break (=='(') cmdName of (xs, ys) | "(" `T.isPrefixOf` ys && ")" `T.isSuffixOf` ys -> defField "title" xs . defField "section" (T.init $ T.drop 1 ys) . case T.splitOn "|" rest of (ft:hds) -> defField "footer" (T.strip ft) . defField "header" (T.strip $ mconcat hds) [] -> id _ -> defField "title" title' metadata <- metaToContext opts (blockListToMan opts) (fmap chomp . inlineListToMan opts) $ deleteMeta "title" meta body <- blockListToMan opts blocks notes <- gets stNotes notes' <- notesToMan opts (reverse notes) let main = body $$ notes' $$ text "" hasTables <- gets stHasTables let context = defField "body" main $ setFieldsFromTitle $ defField "has-tables" hasTables metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context escString :: WriterOptions -> Text -> Text escString opts = escapeString True (if writerPreferAscii opts then AsciiOnly else AllowUTF8) -- | Return man representation of notes. notesToMan :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState m (Doc Text) notesToMan opts notes = if null notes then return empty else (text ".SH NOTES" $$) . vcat <$> zipWithM (noteToMan opts) [1..] notes -- | Return man representation of a note. noteToMan :: PandocMonad m => WriterOptions -> Int -> [Block] -> StateT WriterState m (Doc Text) noteToMan opts num note = do contents <- blockListToMan opts note let marker = cr <> text ".SS " <> brackets (text (show num)) return $ marker $$ contents -- We split inline lists into sentences, and print one sentence per -- line. roff treats the line-ending period differently. -- See http://code.google.com/p/pandoc/issues/detail?id=148. -- | Convert Pandoc block element to man. blockToMan :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> StateT WriterState m (Doc Text) blockToMan opts (Div _ bs) = blockListToMan opts bs blockToMan opts (Plain inlines) = splitSentences <$> inlineListToMan opts inlines blockToMan opts (Para inlines) = do contents <- inlineListToMan opts inlines return $ text ".PP" $$ splitSentences contents blockToMan opts (LineBlock lns) = blockToMan opts $ linesToPara lns blockToMan _ b@(RawBlock f str) | f == Format "man" = return $ literal str | otherwise = do report $ BlockNotRendered b return empty blockToMan _ HorizontalRule = return $ literal ".PP" $$ literal " * * * * *" blockToMan opts (Header level _ inlines) = do contents <- inlineListToMan opts inlines let heading = case level of 1 -> ".SH " _ -> ".SS " return $ nowrap $ literal heading <> contents blockToMan opts (CodeBlock attr str) = do hlCode <- case highlight (writerSyntaxMap opts) (formatSource opts) attr str of Right d -> pure d Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg pure $ formatSource opts defaultFormatOpts (map (\t -> [(NormalTok,t)]) $ T.lines str) pure $ literal ".IP" $$ literal ".EX" $$ hlCode $$ literal ".EE" blockToMan opts (BlockQuote blocks) = do contents <- blockListToMan opts blocks return $ literal ".RS" $$ contents $$ literal ".RE" blockToMan opts (Table _ blkCapt specs thead tbody tfoot) = let (caption, alignments, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot aligncode AlignLeft = "l" aligncode AlignRight = "r" aligncode AlignCenter = "c" aligncode AlignDefault = "l" in do caption' <- inlineListToMan opts caption modify $ \st -> st{ stHasTables = True } let iwidths = if all (== 0) widths then repeat "" else map (T.pack . printf "w(%0.1fn)" . (70 *)) widths -- 78n default width - 8n indent = 70n let coldescriptions = literal $ T.unwords (zipWith (\align width -> aligncode align <> width) alignments iwidths) <> "." colheadings <- mapM (blockListToMan opts) headers let makeRow cols = literal "T{" $$ vcat (intersperse (literal "T}@T{") cols) $$ literal "T}" let colheadings' = if all null headers then empty else makeRow colheadings $$ char '_' body <- mapM (\row -> do cols <- mapM (blockListToMan opts) row return $ makeRow cols) rows return $ literal ".PP" $$ caption' $$ literal ".TS" $$ literal "tab(@);" $$ coldescriptions $$ colheadings' $$ vcat body $$ literal ".TE" blockToMan opts (BulletList items) = do contents <- mapM (bulletListItemToMan opts) items return (vcat contents) blockToMan opts (OrderedList attribs items) = do let markers = take (length items) $ orderedListMarkers attribs let indent = 1 + maybe 0 maximum (nonEmpty (map T.length markers)) contents <- mapM (\(num, item) -> orderedListItemToMan opts num indent item) $ zip markers items return (vcat contents) blockToMan opts (DefinitionList items) = do contents <- mapM (definitionListItemToMan opts) items return (vcat contents) blockToMan opts (Figure attr capt body) = do blockToMan opts (figureDiv attr capt body) -- | Convert bullet list item (list of blocks) to man. bulletListItemToMan :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m (Doc Text) bulletListItemToMan _ [] = return empty bulletListItemToMan opts (Para first:rest) = bulletListItemToMan opts (Plain first:rest) bulletListItemToMan opts (Plain first:rest) = do first' <- blockToMan opts (Plain first) rest' <- blockListToMan opts rest let first'' = literal ".IP \\(bu 2" $$ first' let rest'' = if null rest then empty else literal ".RS 2" $$ rest' $$ literal ".RE" return (first'' $$ rest'') bulletListItemToMan opts (first:rest) = do first' <- blockToMan opts first rest' <- blockListToMan opts rest return $ literal "\\(bu .RS 2" $$ first' $$ rest' $$ literal ".RE" -- | Convert ordered list item (a list of blocks) to man. orderedListItemToMan :: PandocMonad m => WriterOptions -- ^ options -> Text -- ^ order marker for list item -> Int -- ^ number of spaces to indent -> [Block] -- ^ list item (list of blocks) -> StateT WriterState m (Doc Text) orderedListItemToMan _ _ _ [] = return empty orderedListItemToMan opts num indent (Para first:rest) = orderedListItemToMan opts num indent (Plain first:rest) orderedListItemToMan opts num indent (first:rest) = do first' <- blockToMan opts first rest' <- blockListToMan opts rest let num' = printf ("%" ++ show (indent - 1) ++ "s") num let first'' = literal (".IP \"" <> T.pack num' <> "\" " <> tshow indent) $$ first' let rest'' = if null rest then empty else literal ".RS 4" $$ rest' $$ literal ".RE" return $ first'' $$ rest'' -- | Convert definition list item (label, list of blocks) to man. definitionListItemToMan :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> StateT WriterState m (Doc Text) definitionListItemToMan opts (label, defs) = do -- in most man pages, option and other code in option lists is boldface, -- but not other things, so we try to reproduce this style: labelText <- inlineListToMan opts label contents <- if null defs then return empty else liftM vcat $ forM defs $ \case (x:xs) -> do first' <- blockToMan opts $ case x of Para y -> Plain y _ -> x rest' <- liftM vcat $ mapM (\item -> blockToMan opts item) xs return $ first' $$ if null xs then empty else literal ".RS" $$ rest' $$ literal ".RE" [] -> return empty return $ literal ".TP" $$ nowrap labelText $$ contents -- | Convert list of Pandoc block elements to man. blockListToMan :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements -> StateT WriterState m (Doc Text) blockListToMan opts blocks = vcat <$> mapM (blockToMan opts) (go blocks) where -- Avoid .PP right after .SH; this is a no-op in groff man and mandoc -- but may cause unwanted extra space in some renderers (see #9020) go [] = [] go (h@Header{} : Para ils : rest) = h : Plain ils : go rest go (x : xs) = x : go xs -- | Convert list of Pandoc inline elements to man. inlineListToMan :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m (Doc Text) inlineListToMan opts lst = hcat <$> mapM (inlineToMan opts) lst -- | Convert Pandoc inline element to man. inlineToMan :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m (Doc Text) inlineToMan opts (Span _ ils) = inlineListToMan opts ils inlineToMan opts (Emph lst) = withFontFeature 'I' (inlineListToMan opts lst) -- Underline is not supported, so treat the same as Emph inlineToMan opts (Underline lst) = withFontFeature 'I' (inlineListToMan opts lst) inlineToMan opts (Strong lst) = withFontFeature 'B' (inlineListToMan opts lst) inlineToMan opts (Strikeout lst) = do contents <- inlineListToMan opts lst return $ literal "[STRIKEOUT:" <> contents <> char ']' inlineToMan opts (Superscript lst) = do contents <- inlineListToMan opts lst return $ char '^' <> contents <> char '^' inlineToMan opts (Subscript lst) = do contents <- inlineListToMan opts lst return $ char '~' <> contents <> char '~' inlineToMan opts (SmallCaps lst) = inlineListToMan opts lst -- not supported inlineToMan opts (Quoted SingleQuote lst) = do contents <- inlineListToMan opts lst return $ char '`' <> contents <> char '\'' inlineToMan opts (Quoted DoubleQuote lst) = do contents <- inlineListToMan opts lst return $ literal "\\(lq" <> contents <> literal "\\(rq" inlineToMan opts (Cite _ lst) = inlineListToMan opts lst inlineToMan opts (Code _ str) = withFontFeature 'C' (return (literal $ escString opts str)) inlineToMan opts (Str str@(T.uncons -> Just ('.',_))) = return $ afterBreak "\\&" <> literal (escString opts str) inlineToMan opts (Str str) = return $ literal $ escString opts str inlineToMan opts (Math InlineMath str) = lift (texMathToInlines InlineMath str) >>= inlineListToMan opts inlineToMan opts (Math DisplayMath str) = do contents <- lift (texMathToInlines DisplayMath str) >>= inlineListToMan opts return $ cr <> literal ".RS" $$ contents $$ literal ".RE" inlineToMan _ il@(RawInline f str) | f == Format "man" = return $ literal str | otherwise = do report $ InlineNotRendered il return empty inlineToMan _ LineBreak = return $ cr <> literal ".PD 0" $$ literal ".P" $$ literal ".PD" <> cr inlineToMan _ SoftBreak = return $ afterBreak "\\" <> space inlineToMan _ Space = return $ afterBreak "\\" <> space inlineToMan opts (Link _ txt (src, _)) | not (isURI src) = inlineListToMan opts txt -- skip relative links | otherwise = do let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src) linktext <- case txt of [Str s] | escapeURI s == srcSuffix -> pure mempty _ -> inlineListToMan opts txt let (start, end) = if "mailto:" `T.isPrefixOf` src then (".MT", ".ME") else (".UR", ".UE") return $ "\\c" <> cr -- \c avoids extra space $$ nowrap (start <+> literal srcSuffix) $$ linktext $$ (end <+> "\\c" <> cr) -- \c avoids space after inlineToMan opts (Image attr alternate (source, tit)) = do let txt = if null alternate || (alternate == [Str ""]) || (alternate == [Str source]) -- to prevent autolinks then [Str "image"] else alternate linkPart <- inlineToMan opts (Link attr txt (source, tit)) return $ char '[' <> literal "IMAGE: " <> linkPart <> char ']' inlineToMan _ (Note contents) = do -- add to notes in state modify $ \st -> st{ stNotes = contents : stNotes st } notes <- gets stNotes let ref = tshow (length notes) return $ char '[' <> literal ref <> char ']' formatSource :: WriterOptions -> FormatOptions -> [SourceLine] -> Doc Text formatSource wopts fopts = vcat . map (formatSourceLine wopts fopts) formatSourceLine :: WriterOptions -> FormatOptions -> SourceLine -> Doc Text formatSourceLine _wopts _fopts [] = blankline formatSourceLine wopts fopts ts@((_,firstTxt):_) = (case T.uncons firstTxt of Just ('.',_) -> literal "\\&" _ -> mempty) <> mconcat (map (formatTok wopts fopts) ts) <> literal "\n" formatTok :: WriterOptions -> FormatOptions -> (TokenType, Text) -> Doc Text formatTok wopts _fopts (toktype, t) = let txt = literal (escString wopts t) styleMap = case writerHighlightMethod wopts of Skylighting style -> Just $ tokenStyles style DefaultHighlighting -> Just $ tokenStyles defaultStyle _ -> Nothing tokStyle = fromMaybe defStyle $ styleMap >>= M.lookup toktype in if toktype == NormalTok then txt else let fonts = ['B' | tokenBold tokStyle] ++ ['I' | tokenItalic tokStyle || tokenUnderline tokStyle] in if null fonts then txt else literal ("\\f[" <> T.pack fonts <> "]") <> txt <> literal "\\f[R]" ================================================ FILE: src/Text/Pandoc/Writers/Markdown/Inline.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.Markdown.Inline Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable -} module Text.Pandoc.Writers.Markdown.Inline ( inlineListToMarkdown, linkAttributes, attrsToMarkdown, attrsToMarkua ) where import Control.Monad (when, liftM2) import Control.Monad.Reader ( asks, MonadReader(local) ) import Control.Monad.State.Strict ( MonadState(get), gets, modify ) import Data.Char (isAlphaNum, isDigit) import Data.List (find, intersperse) import Data.List.NonEmpty (nonEmpty) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, blanklines, char, space) import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI (urlEncode, escapeURI, isURI) import Text.Pandoc.Writers.Shared import Text.Pandoc.Walk import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.XML (toHtml5Entities) import Data.Coerce (coerce) import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..), WriterState(..), WriterEnv(..), MD) -- | Escape special characters for Markdown. escapeText :: WriterOptions -> Text -> Text escapeText opts = T.pack . go' . T.unpack where startsWithSpace (' ':_) = True startsWithSpace ('\t':_) = True startsWithSpace [] = True startsWithSpace _ = False go' ('#':cs) | isEnabled Ext_space_in_atx_header opts = if startsWithSpace (dropWhile (=='#') cs) then '\\':'#':go cs else '#':go cs | otherwise = '\\':'#':go cs go' ('@':cs) | isEnabled Ext_citations opts = case cs of (d:_) | isAlphaNum d || d == '_' || d == '{' -> '\\':'@':go cs _ -> '@':go cs go' cs = go cs go [] = [] go ['\\'] = ['\\','\\'] go ('-':'-':cs) | isEnabled Ext_smart opts = '\\':'-':go('-':cs) go ('.':'.':'.':cs) | isEnabled Ext_smart opts = '\\':'.':'.':'.':go cs go (c:'_':d:cs) | isAlphaNum c , isAlphaNum d = if isEnabled Ext_intraword_underscores opts then c:'_':go (d:cs) else c:'\\':'_':go (d:cs) go ('\\':c:cs) | isEnabled Ext_raw_tex opts = '\\':'\\':go (c:cs) | isAlphaNum c = '\\' : go (c:cs) | otherwise = '\\':'\\': go cs go ('!':'[':cs) = '\\':'!':'[': go cs go ('=':'=':cs) | isEnabled Ext_mark opts = '\\':'=':go ('=':cs) go ('~':'~':cs) | isEnabled Ext_strikeout opts = '\\':'~':go ('~':cs) go ('&':cs) | Right _ <- parse characterReference "" ('&':cs) = '\\':'&': go cs go (c:cs) = case c of '[' -> '\\':c:go cs ']' -> '\\':c:go cs '`' -> '\\':c:go cs '*' -> '\\':c:go cs '_' -> '\\':c:go cs '>' | isEnabled Ext_all_symbols_escapable opts -> '\\':'>':go cs | otherwise -> ">" ++ go cs '<' | isEnabled Ext_all_symbols_escapable opts -> '\\':'<':go cs | otherwise -> "<" ++ go cs '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':go cs '^' | isEnabled Ext_superscript opts -> '\\':'^':go cs '~' | isEnabled Ext_subscript opts -> '\\':'~':go cs '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':go cs '\'' | isEnabled Ext_smart opts -> '\\':'\'':go cs '"' | isEnabled Ext_smart opts -> '\\':'"':go cs _ -> c : go cs -- Escape the escape character, as well as formatting pairs escapeMarkuaString :: Text -> Text escapeMarkuaString s = foldr (uncurry T.replace) s [("--","~-~-"), ("**","~*~*"),("//","~/~/"),("^^","~^~^"),(",,","~,~,")] attrsToMarkdown :: WriterOptions -> Attr -> Doc Text attrsToMarkdown opts attribs = braces $ hsep [attribId, attribClasses, attribKeys] where attribId = case attribs of ("",_,_) -> empty (i,_,_) -> "#" <> escAttr (writerIdentifierPrefix opts <> i) attribClasses = case attribs of (_,[],_) -> empty (_,cs,_) -> hsep $ map (escAttr . ("."<>)) $ filter (not . T.null) cs attribKeys = case attribs of (_,_,[]) -> empty (_,_,ks) -> hsep $ map (\(k,v) -> escAttr k <> "=\"" <> escAttr v <> "\"") ks escAttr = mconcat . map escAttrChar . T.unpack escAttrChar '"' = literal "\\\"" escAttrChar '\\' = literal "\\\\" escAttrChar c = literal $ T.singleton c attrsToMarkua:: WriterOptions -> Attr -> Doc Text attrsToMarkua opts attributes | null list = empty | otherwise = braces $ intercalateDocText list where attrId = case attributes of ("",_,_) -> [] (i,_,_) -> [literal $ "id: " <> writerIdentifierPrefix opts <> i] -- all non explicit (key,value) attributes besides id are getting -- a default class key to be Markua conform attrClasses = case attributes of (_,[],_) -> [] (_,classes,_) -> map (escAttr . ("class: " <>)) classes attrKeyValues = case attributes of (_,_,[]) -> [] (_,_,keyvalues) -> map ((\(k,v) -> escAttr k <> ": " <> escAttr v) . preprocessKeyValues) keyvalues escAttr = mconcat . map escAttrChar . T.unpack escAttrChar '"' = literal "\"" escAttrChar c = literal $ T.singleton c list = concat [attrId, attrClasses, attrKeyValues] -- if attribute key is alt, caption, title then content -- gets wrapped inside quotes -- attribute gets removed preprocessKeyValues :: (Text, Text) -> (Text, Text) preprocessKeyValues (key,value) | key == "alt" || key == "caption" || key == "title" = (key, inquotes value) | otherwise = (key,value) intercalateDocText :: [Doc Text] -> Doc Text intercalateDocText [] = empty intercalateDocText [x] = x intercalateDocText (x:xs) = x <> ", " <> (intercalateDocText xs) -- | Add a (key, value) pair to Pandoc attr type addKeyValueToAttr :: Attr -> (Text,Text) -> Attr addKeyValueToAttr (ident,classes,kvs) (key,value) | not (T.null key) && not (T.null value) = (ident, classes, (key,value): kvs) | otherwise = (ident,classes,kvs) linkAttributes :: WriterOptions -> Attr -> Doc Text linkAttributes opts attr = if (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) && attr /= nullAttr then attrsToMarkdown opts attr else empty getKey :: Doc Text -> Key getKey = toKey . render Nothing findUsableIndex :: [Text] -> Int -> Int findUsableIndex lbls i = if tshow i `elem` lbls then findUsableIndex lbls (i + 1) else i getNextIndex :: PandocMonad m => MD m Int getNextIndex = do prevRefs <- gets stPrevRefs refs <- gets stRefs i <- (+ 1) <$> gets stLastIdx modify $ \s -> s{ stLastIdx = i } let refLbls = map (\(r,_,_) -> r) $ prevRefs ++ refs return $ findUsableIndex refLbls i -- | Get reference for target; if none exists, create unique one and return. -- Prefer label if possible; otherwise, generate a unique key. getReference :: PandocMonad m => Attr -> Doc Text -> Target -> MD m Text getReference attr label target = do refs <- gets stRefs case find (\(_,t,a) -> t == target && a == attr) refs of Just (ref, _, _) -> return ref Nothing -> do keys <- gets stKeys let key = getKey label let rawkey = coerce key case M.lookup key keys of Nothing -> do -- no other refs with this label (lab', idx) <- if T.null rawkey || T.length rawkey > 999 || T.any (\c -> c == '[' || c == ']') rawkey then do i <- getNextIndex return (tshow i, i) else return (render Nothing label, 0) modify (\s -> s{ stRefs = (lab', target, attr) : refs, stKeys = M.insert (getKey label) (M.insert (target, attr) idx mempty) (stKeys s) }) return lab' Just km -> -- we have refs with this label case M.lookup (target, attr) km of Just i -> do let lab' = render Nothing $ label <> if i == 0 then mempty else literal (tshow i) -- make sure it's in stRefs; it may be -- a duplicate that was printed in a previous -- block: when ((lab', target, attr) `notElem` refs) $ modify (\s -> s{ stRefs = (lab', target, attr) : refs }) return lab' Nothing -> do -- but this one is to a new target i <- getNextIndex let lab' = tshow i modify (\s -> s{ stRefs = (lab', target, attr) : refs, stKeys = M.insert key (M.insert (target, attr) i km) (stKeys s) }) return lab' -- | Convert list of Pandoc inline elements to markdown. inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m (Doc Text) inlineListToMarkdown opts ils = do inlist <- asks envInList avoidBadWraps inlist <$> go ils where go [] = return empty go (x@Math{}:y@(Str t):zs) | T.all isDigit (T.take 1 t) -- starts with digit -- see #7058 = liftM2 (<>) (inlineToMarkdown opts x) (go (RawInline (Format "html") "<!-- -->" : y : zs)) go (Str t : i : is) | isLinkOrSpan i , T.takeEnd 1 t == "!" = do x <- inlineToMarkdown opts (Str (T.dropEnd 1 t)) ((x <> "\\!") <>) <$> go (i:is) go (i:is) = case i of Link {} -> case is of -- If a link is followed by another link, or '[', '(' or ':' -- then we don't shortcut Link {}:_ -> unshortcutable Space:Link {}:_ -> unshortcutable Space:(Str(thead -> Just '[')):_ -> unshortcutable Space:(RawInline _ (thead -> Just '[')):_ -> unshortcutable Space:(Cite _ _):_ -> unshortcutable SoftBreak:Link {}:_ -> unshortcutable SoftBreak:(Str(thead -> Just '[')):_ -> unshortcutable SoftBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable SoftBreak:(Cite _ _):_ -> unshortcutable LineBreak:Link {}:_ -> unshortcutable LineBreak:(Str(thead -> Just '[')):_ -> unshortcutable LineBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable LineBreak:(Cite _ _):_ -> unshortcutable (Cite _ _):_ -> unshortcutable Str (thead -> Just '['):_ -> unshortcutable Str (thead -> Just '('):_ -> unshortcutable Str (thead -> Just ':'):_ -> unshortcutable (RawInline _ (thead -> Just '[')):_ -> unshortcutable (RawInline _ (thead -> Just '(')):_ -> unshortcutable (RawInline _ (thead -> Just ':')):_ -> unshortcutable (RawInline _ (T.stripPrefix " [" -> Just _ )):_ -> unshortcutable _ -> shortcutable _ -> shortcutable where shortcutable = liftM2 (<>) (inlineToMarkdown opts i) (go is) unshortcutable = do iMark <- local (\env -> env { envRefShortcutable = False }) (inlineToMarkdown opts i) fmap (iMark <>) (go is) thead = fmap fst . T.uncons isLinkOrSpan Link{} = True isLinkOrSpan Span{} = True isLinkOrSpan _ = False -- Remove breaking spaces that might cause bad wraps. avoidBadWraps :: Bool -> Doc Text -> Doc Text avoidBadWraps inListItem = go . toList where go [] = mempty go (BreakingSpace : Text len t : BreakingSpace : xs) = case T.uncons t of Just (c,t') | c == '>' || ((c == '-' || c == '*' || c == '+') && T.null t') || (inListItem && isOrderedListMarker t) || (t == "1." || t == "1)") -> Text (len + 1) (" " <> t) <> go (BreakingSpace : xs) _ -> BreakingSpace <> Text len t <> go (BreakingSpace : xs) go (x:xs) = x <> go xs toList (Concat (Concat a b) c) = toList (Concat a (Concat b c)) toList (Concat a b) = a : toList b toList x = [x] -- | Convert Pandoc inline element to markdown. inlineToMarkdown :: PandocMonad m => WriterOptions -> Inline -> MD m (Doc Text) inlineToMarkdown opts (Span ("",["emoji"],kvs) [Str s]) = case lookup "data-emoji" kvs of Just emojiname | isEnabled Ext_emoji opts -> return $ ":" <> literal emojiname <> ":" _ -> inlineToMarkdown opts (Str s) inlineToMarkdown opts (Span ("",["mark"],[]) ils) | isEnabled Ext_mark opts = do contents <- inlineListToMarkdown opts ils return $ "==" <> contents <> "==" inlineToMarkdown opts (Span attrs ils) = do variant <- asks envVariant contents <- inlineListToMarkdown opts ils return $ case attrs of (_,["csl-block"],_) -> (cr <>) (_,["csl-left-margin"],_) -> (cr <>) (_,["csl-indent"],_) -> (cr <>) _ -> id $ case variant of PlainText -> contents Markua -> "`" <> contents <> "`" <> attrsToMarkua opts attrs _ | attrs == nullAttr -> contents | isEnabled Ext_bracketed_spans opts -> let attrs' = if attrs /= nullAttr then attrsToMarkdown opts attrs else empty in "[" <> contents <> "]" <> attrs' | isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts -> tagWithAttrs "span" attrs <> contents <> literal "</span>" | otherwise -> contents inlineToMarkdown _ (Emph []) = return empty inlineToMarkdown opts (Emph [Emph ils]) = -- #10642 inlineListToMarkdown opts ils inlineToMarkdown opts (Emph lst) = do variant <- asks envVariant contents <- inlineListToMarkdown opts lst return $ case variant of PlainText | isEnabled Ext_gutenberg opts -> delimited "_" "_" contents | otherwise -> contents _ -> delimited "*" "*" contents inlineToMarkdown _ (Underline []) = return empty inlineToMarkdown opts (Underline lst) = do variant <- asks envVariant contents <- inlineListToMarkdown opts lst case variant of PlainText -> return contents _ | isEnabled Ext_bracketed_spans opts -> return $ delimited "[" "]{.underline}" contents | isEnabled Ext_native_spans opts -> return $ tagWithAttrs "span" ("", ["underline"], []) <> contents <> literal "</span>" | isEnabled Ext_raw_html opts -> return $ "<u>" <> contents <> "</u>" | otherwise -> inlineToMarkdown opts (Emph lst) inlineToMarkdown _ (Strong []) = return empty inlineToMarkdown opts (Strong lst) = do variant <- asks envVariant case variant of PlainText -> inlineListToMarkdown opts $ if isEnabled Ext_gutenberg opts then capitalize lst else lst _ -> do contents <- inlineListToMarkdown opts lst return $ delimited "**" "**" contents inlineToMarkdown _ (Strikeout []) = return empty inlineToMarkdown opts (Strikeout lst) = do contents <- inlineListToMarkdown opts lst return $ if isEnabled Ext_strikeout opts then delimited "~~" "~~" contents else if isEnabled Ext_raw_html opts then "<s>" <> contents <> "</s>" else contents inlineToMarkdown _ (Superscript []) = return empty inlineToMarkdown opts (Superscript lst) = local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do contents <- inlineListToMarkdown opts lst if isEnabled Ext_superscript opts then return $ delimited "^" "^" contents else if isEnabled Ext_raw_html opts then return $ "<sup>" <> contents <> "</sup>" else case traverse toSuperscriptInline lst of Just xs' | not (writerPreferAscii opts) -> inlineListToMarkdown opts xs' _ -> do let rendered = render Nothing contents return $ case mapM toSuperscript (T.unpack rendered) of Just r -> literal $ T.pack r Nothing -> literal $ "^(" <> rendered <> ")" inlineToMarkdown _ (Subscript []) = return empty inlineToMarkdown opts (Subscript lst) = local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do contents <- inlineListToMarkdown opts lst if isEnabled Ext_subscript opts then return $ delimited "~" "~" contents else if isEnabled Ext_raw_html opts then return $ "<sub>" <> contents <> "</sub>" else case traverse toSubscriptInline lst of Just xs' | not (writerPreferAscii opts) -> inlineListToMarkdown opts xs' _ -> do let rendered = render Nothing contents return $ case mapM toSuperscript (T.unpack rendered) of Just r -> literal $ T.pack r Nothing -> literal $ "_(" <> rendered <> ")" inlineToMarkdown opts (SmallCaps lst) = do variant <- asks envVariant if variant /= PlainText && (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts) then inlineToMarkdown opts (Span ("",["smallcaps"],[]) lst) else inlineListToMarkdown opts $ capitalize lst inlineToMarkdown opts (Quoted SingleQuote lst) = do contents <- inlineListToMarkdown opts lst return $ if isEnabled Ext_smart opts then "'" <> contents <> "'" else if writerPreferAscii opts then "‘" <> contents <> "’" else "‘" <> contents <> "’" inlineToMarkdown opts (Quoted DoubleQuote lst) = do contents <- inlineListToMarkdown opts lst return $ if isEnabled Ext_smart opts then "\"" <> contents <> "\"" else if writerPreferAscii opts then "“" <> contents <> "”" else "“" <> contents <> "”" inlineToMarkdown opts (Code attr str) = do variant <- asks envVariant let tickGroups = filter (T.any (== '`')) $ T.group str let longest = maybe 0 maximum $ nonEmpty $ map T.length tickGroups let marker = T.replicate (longest + 1) "`" let spacer = if longest == 0 then "" else " " let attrsEnabled = isEnabled Ext_inline_code_attributes opts || isEnabled Ext_attributes opts let attrs = case variant of Markua -> attrsToMarkua opts attr _ -> if attrsEnabled && attr /= nullAttr then attrsToMarkdown opts attr else empty case variant of PlainText -> return $ literal str _ -> return $ literal (marker <> spacer <> str <> spacer <> marker) <> attrs inlineToMarkdown opts (Str str) = do variant <- asks envVariant let str' = case variant of Markua -> escapeMarkuaString str _ -> (if writerPreferAscii opts then toHtml5Entities else id) . (if isEnabled Ext_smart opts then unsmartify opts else id) . (if variant == PlainText then id 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 -> inlineToMarkdown opts (Image nullAttr [Str str'] (url <> urlEncode str', str')) _ | isEnabled Ext_tex_math_gfm opts -> return $ "$`" <> literal str' <> "`$" | isEnabled Ext_tex_math_dollars opts -> return $ "$" <> literal str' <> "$" | isEnabled Ext_tex_math_single_backslash opts -> return $ "\\(" <> literal str' <> "\\)" | isEnabled Ext_tex_math_double_backslash opts -> return $ "\\\\(" <> literal str' <> "\\\\)" | otherwise -> texMathToInlines InlineMath str' >>= inlineListToMarkdown opts . (if variant == PlainText then makeMathPlainer else id) inlineToMarkdown opts (Math DisplayMath str) = do variant <- asks envVariant case () of _ | variant == Markua -> do let attributes = attrsToMarkua opts (addKeyValueToAttr ("",[],[]) ("format", "latex")) return $ blankline <> attributes <> cr <> literal "```" <> cr <> literal str <> cr <> literal "```" <> blankline | otherwise -> case writerHTMLMathMethod opts of WebTeX url -> let str' = T.strip str in (\x -> blankline <> x <> blankline) `fmap` inlineToMarkdown opts (Image nullAttr [Str str'] (url <> urlEncode str', str')) _ | isEnabled Ext_tex_math_gfm opts -> return $ cr <> (literal "``` math" $$ literal (T.dropAround (=='\n') str) $$ literal "```") <> cr | isEnabled Ext_tex_math_dollars opts -> return $ "$$" <> literal str <> "$$" | isEnabled Ext_tex_math_single_backslash opts -> return $ "\\[" <> literal str <> "\\]" | isEnabled Ext_tex_math_double_backslash opts -> return $ "\\\\[" <> literal str <> "\\\\]" | otherwise -> (\x -> cr <> x <> cr) `fmap` (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts) inlineToMarkdown opts il@(RawInline f str) = do let tickGroups = filter (T.any (== '`')) $ T.group str let numticks = 1 + maybe 0 maximum (nonEmpty (map T.length tickGroups)) variant <- asks envVariant let Format fmt = f let rawAttribInline = return $ literal (T.replicate numticks "`") <> literal str <> literal (T.replicate numticks "`") <> literal "{=" <> literal fmt <> literal "}" let renderEmpty = mempty <$ report (InlineNotRendered il) case variant of PlainText | f == "plain" -> return $ literal str Commonmark | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"] -> return $ literal str Markdown | f `elem` ["markdown", "markdown_github", "markdown_phpextra", "markdown_mmd", "markdown_strict"] -> return $ literal str Markua -> renderEmpty _ | isEnabled Ext_raw_attribute opts -> rawAttribInline | f `elem` ["html", "html5", "html4"] , isEnabled Ext_raw_html opts -> return $ literal str | f `elem` ["latex", "tex"] , isEnabled Ext_raw_tex opts -> return $ literal str _ -> renderEmpty inlineToMarkdown opts LineBreak = do variant <- asks envVariant if variant == PlainText || isEnabled Ext_hard_line_breaks opts then return cr else return $ if variant == Commonmark || isEnabled Ext_escaped_line_breaks opts then "\\" <> cr else " " <> cr inlineToMarkdown _ Space = do escapeSpaces <- asks envEscapeSpaces return $ if escapeSpaces then "\\ " else space inlineToMarkdown opts SoftBreak = do escapeSpaces <- asks envEscapeSpaces let space' = if escapeSpaces then "\\ " else space return $ case writerWrapText opts of WrapNone -> space' WrapAuto -> space' WrapPreserve -> cr inlineToMarkdown opts (Cite [] lst) = inlineListToMarkdown opts lst inlineToMarkdown opts (Cite (c:cs) lst) | not (isEnabled Ext_citations opts) = inlineListToMarkdown opts lst | otherwise = if citationMode c == AuthorInText then do suffs <- inlineListToMarkdown opts $ citationSuffix c rest <- mapM convertOne cs let inbr = suffs <> (if not (null (citationSuffix c)) && not (null rest) then text ";" else mempty) <+> joincits rest br = if isEmpty inbr then empty else char '[' <> inbr <> char ']' return $ literal ("@" <> maybeInBraces (citationId c)) <+> br else do cits <- mapM convertOne (c:cs) return $ literal "[" <> joincits cits <> literal "]" where maybeInBraces key = case readWith (citeKey False >> spaces >> eof) defaultParserState ("@" <> key) of Left _ -> "{" <> key <> "}" Right _ -> key joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty) convertOne Citation { citationId = k , citationPrefix = pinlines , citationSuffix = sinlines , citationMode = m } = do pdoc <- inlineListToMarkdown opts pinlines sdoc <- inlineListToMarkdown opts sinlines let k' = literal (modekey m <> "@" <> maybeInBraces k) r = case sinlines of Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc Space:_ -> k' <> sdoc _ -> k' <+> sdoc return $ pdoc <+> r modekey SuppressAuthor = "-" modekey _ = "" inlineToMarkdown opts lnk@(Link attr@(ident,classes,kvs) txt (src, tit)) = do variant <- asks envVariant linktext <- inlineListToMarkdown opts txt let linktitle = if T.null tit then empty else literal $ " \"" <> tit <> "\"" let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src) let useAuto = isURI src && T.null ident && null kvs && (null classes || classes == ["uri"] || classes == ["email"]) && case txt of [Str s] | escapeURI s == srcSuffix -> True _ -> False let useWikilink = "wikilink" `elem` classes && (isEnabled Ext_wikilinks_title_after_pipe opts || isEnabled Ext_wikilinks_title_before_pipe opts) let useRefLinks = writerReferenceLinks opts && not useAuto shortcutable <- asks envRefShortcutable let useShortcutRefLinks = shortcutable && (variant == Commonmark || isEnabled Ext_shortcut_reference_links opts) reftext <- if useRefLinks then literal <$> getReference attr linktext (src, tit) else return mempty case variant of PlainText | useAuto -> return $ literal srcSuffix | otherwise -> return linktext Markua | T.null tit -> return $ result <> attrsToMarkua opts attr | otherwise -> return $ result <> attrsToMarkua opts attributes where result = "[" <> linktext <> "](" <> (literal src) <> ")" attributes = addKeyValueToAttr attr ("title", tit) -- Use wikilinks where possible _ | src == stringify txt && useWikilink -> return $ "[[" <> literal (stringify txt) <> "]]" | useAuto -> return $ "<" <> literal srcSuffix <> ">" | useWikilink && isEnabled Ext_wikilinks_title_after_pipe opts -> return $ "[[" <> literal src <> "|" <> literal (stringify txt) <> "]]" | useWikilink && isEnabled Ext_wikilinks_title_before_pipe opts -> return $ "[[" <> literal (stringify txt) <> "|" <> literal src <> "]]" | useRefLinks -> let first = "[" <> linktext <> "]" second = if getKey linktext == getKey reftext then if useShortcutRefLinks then "" else "[]" else "[" <> reftext <> "]" in return $ first <> second | isEnabled Ext_raw_html opts , not (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) , attr /= nullAttr -> -- use raw HTML to render attributes literal . T.strip <$> writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain [lnk]]) | otherwise -> return $ "[" <> linktext <> "](" <> literal src <> linktitle <> ")" <> linkAttributes opts attr inlineToMarkdown opts img@(Image attr alternate (source, tit)) | isEnabled Ext_raw_html opts && not (isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts) && attr /= nullAttr = -- use raw HTML literal . T.strip <$> writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain [img]]) | otherwise = do variant <- asks envVariant let txt = if null alternate || alternate == [Str source] -- to prevent autolinks then [Str ""] else alternate linkPart <- inlineToMarkdown opts (Link attr txt (source, tit)) alt <- inlineListToMarkdown opts alternate let attributes | variant == Markua = attrsToMarkua opts $ addKeyValueToAttr (addKeyValueToAttr attr ("title", tit)) ("alt", render (Just (writerColumns opts)) alt) | otherwise = empty return $ case variant of PlainText -> "[" <> linkPart <> "]" Markua -> cr <> attributes <> cr <> literal "![](" <> literal source <> ")" <> cr _ -> "!" <> linkPart inlineToMarkdown opts (Note contents) = do modify (\st -> st{ stNotes = contents : stNotes st }) st <- get let ref = literal $ writerIdentifierPrefix opts <> tshow (stNoteNum st + length (stNotes st) - 1) if isEnabled Ext_footnotes opts then return $ "[^" <> ref <> "]" else return $ "[" <> ref <> "]" makeMathPlainer :: [Inline] -> [Inline] makeMathPlainer = walk go where go (Emph xs) = Span nullAttr xs go x = x ================================================ FILE: src/Text/Pandoc/Writers/Markdown/Table.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.Markdown Copyright : © 2006-2024 John MacFarlane License : GPL-2.0-or-later Maintainer : John MacFarlane <jgm@berkeley.edu> Create Markdown pipe-tables and pandoc-style tables. -} module Text.Pandoc.Writers.Markdown.Table ( pipeTable , pandocTable ) where import Control.Monad.Reader (asks) import Data.List (intersperse, transpose) import Data.List.NonEmpty (nonEmpty) import Data.Text (Text) import qualified Data.Text as T import Text.DocLayout import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition (Alignment (..)) import Text.Pandoc.Options (WriterOptions (writerColumns, writerWrapText), WrapOption(WrapAuto)) import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(Markdown), WriterEnv(..), MD) -- | Creates a Markdown pipe table. pipeTable :: PandocMonad m => WriterOptions -> Bool -- ^ headless? -> [Alignment] -- ^ column alignments -> [Double] -- ^ column widhts -> [Doc Text] -- ^ table header cells -> [[Doc Text]] -- ^ table body rows -> MD m (Doc Text) pipeTable opts headless aligns widths rawHeaders rawRows = do let sp = literal " " let contentWidths = map (max 3 . maybe 3 maximum . nonEmpty . map offset) $ transpose (rawHeaders : rawRows) let colwidth = writerColumns opts let numcols = length contentWidths let maxwidth = sum contentWidths -- if cell contents are > COLUMNS, adding padding looks bad let pad = maxwidth <= writerColumns opts let blockFor _ _ y | not pad = sp <> y <> sp <> lblock 0 empty blockFor AlignLeft x y = lblock (x + 2) (sp <> y) <> lblock 0 empty blockFor AlignCenter x y = cblock (x + 2) (sp <> y <> sp) <> lblock 0 empty blockFor AlignRight x y = rblock (x + 2) (y <> sp) <> lblock 0 empty blockFor AlignDefault x y = lblock (x + 2) (sp <> y) <> lblock 0 empty variant <- asks envVariant let pipeWidths = if variant == Markdown && not (all (== 0) widths) && maxwidth + (numcols + 1) > colwidth then map (max 0 . floor . (* fromIntegral (colwidth - (numcols +1)))) widths else if pad then contentWidths else map (const 2) widths let torow cs = nowrap $ literal "|" <> hcat (intersperse (literal "|") $ zipWith3 blockFor aligns contentWidths (map chomp cs)) <> literal "|" let toborder a w = literal $ case a of AlignLeft -> ":" <> T.replicate (w + 1) "-" AlignCenter -> ":" <> T.replicate w "-" <> ":" AlignRight -> T.replicate (w + 1) "-" <> ":" AlignDefault -> T.replicate (w + 2) "-" -- note: pipe tables can't completely lack a -- header; for a headerless table, we need a header of empty cells. -- see jgm/pandoc#1996. let header = if headless then torow (replicate (length aligns) empty) else torow rawHeaders let border = nowrap $ literal "|" <> hcat (intersperse (literal "|") $ zipWith toborder aligns pipeWidths) <> literal "|" let body = vcat $ map torow rawRows return $ header $$ border $$ body -- | Write a pandoc-style Markdown table. pandocTable :: PandocMonad m => WriterOptions -> Bool -- ^ whether this is a multiline table -> Bool -- ^ whether the table has a header -> [Alignment] -- ^ column alignments -> [Double] -- ^ column widths -> [Doc Text] -- ^ table header cells -> [[Doc Text]] -- ^ table body rows -> MD m (Doc Text) pandocTable opts multiline headless aligns widths rawHeaders rawRows = do let isSimple = all (==0) widths let alignHeader alignment = case alignment of AlignLeft -> lblock AlignCenter -> cblock AlignRight -> rblock AlignDefault -> lblock -- Number of characters per column necessary to output every cell -- without requiring a line break. -- The @+2@ is needed for specifying the alignment. let numChars = (+ 2) . maybe 0 maximum . nonEmpty . map offset -- Number of characters per column necessary to output every cell -- without requiring a line break *inside a word*. -- The @+2@ is needed for specifying the alignment. let minNumChars = (+ 2) . maybe 0 maximum . nonEmpty . map minOffset let columns = transpose (rawHeaders : rawRows) -- minimal column width without wrapping a single word let relWidth w col = max (floor $ fromIntegral (writerColumns opts - 1) * w) (if writerWrapText opts == WrapAuto then minNumChars col else numChars col) let widthsInChars | isSimple = map numChars columns | otherwise = zipWith relWidth widths columns let makeRow = hcat . intersperse (lblock 1 (literal " ")) . zipWith3 alignHeader aligns widthsInChars let rows' = map makeRow rawRows let head' = makeRow rawHeaders let underline = mconcat $ intersperse (literal " ") $ map (\width -> literal (T.replicate width "-")) widthsInChars let border | multiline = literal (T.replicate (sum widthsInChars + length widthsInChars - 1) "-") | headless = underline | otherwise = empty let head'' = if headless then empty else border <> cr <> head' let body = if multiline then vsep rows' $$ if length rows' < 2 then blankline -- #4578 else empty else vcat rows' let bottom = if headless then underline else border return $ head'' $$ underline $$ body $$ bottom ================================================ FILE: src/Text/Pandoc/Writers/Markdown/Types.hs ================================================ {- | Module : Text.Pandoc.Writers.Markdown.Types Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable -} module Text.Pandoc.Writers.Markdown.Types ( MarkdownVariant(..), WriterState(..), WriterEnv(..), Notes, Ref, Refs, MD, evalMD ) where import Control.Monad.Reader import Control.Monad.State.Strict import Data.Default import qualified Data.Map as M import qualified Data.Set as Set import Data.Text (Text) import Text.Pandoc.Parsing (Key) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition type Notes = [[Block]] type Ref = (Text, Target, Attr) type Refs = [Ref] type MD m = ReaderT WriterEnv (StateT WriterState m) evalMD :: PandocMonad m => MD m a -> WriterEnv -> WriterState -> m a evalMD md env st = evalStateT (runReaderT md env) st data WriterEnv = WriterEnv { envInList :: Bool , envVariant :: MarkdownVariant , envRefShortcutable :: Bool , envBlockLevel :: Int , envEscapeSpaces :: Bool } data MarkdownVariant = Markua | PlainText | Commonmark | Markdown deriving (Show, Eq) instance Default WriterEnv where def = WriterEnv { envInList = False , envVariant = Markdown , envRefShortcutable = True , envBlockLevel = 0 , envEscapeSpaces = False } data WriterState = WriterState { stNotes :: Notes , stPrevRefs :: Refs , stRefs :: Refs , stKeys :: M.Map Key (M.Map (Target, Attr) Int) , stLastIdx :: Int , stIds :: Set.Set Text , stNoteNum :: Int } instance Default WriterState where def = WriterState{ stNotes = [] , stPrevRefs = [] , stRefs = [] , stKeys = M.empty , stLastIdx = 0 , stIds = Set.empty , stNoteNum = 1 } ================================================ FILE: src/Text/Pandoc/Writers/Markdown.hs ================================================ {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE BangPatterns #-} {- | Module : Text.Pandoc.Writers.Markdown Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to markdown-formatted plain text. Markdown: <https://daringfireball.net/projects/markdown/> -} module Text.Pandoc.Writers.Markdown ( writeMarkdown, writeCommonMark, writeMarkua, writePlain) where import Control.Monad (foldM, zipWithM, MonadPlus(..), when, liftM) import Control.Monad.Reader ( asks, MonadReader(local) ) import Control.Monad.State.Strict ( gets, modify ) import Data.Default import Data.List (intersperse, sortOn, union, find) import Data.List.NonEmpty (nonEmpty, NonEmpty(..)) import qualified Data.Map as M import Data.Maybe (fromMaybe, mapMaybe, isNothing) import qualified Data.Set as Set import Data.Text (Text) import Data.Char (isSpace) import qualified Data.Text as T import Text.HTML.TagSoup (Tag (..), isTagText, parseTags) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, blanklines, char, space) import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.Writers.Shared import Text.Pandoc.Templates (renderTemplate) import Text.DocTemplates (Val(..), Context(..), FromContext(..)) import Text.Pandoc.Walk import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Markdown.Inline (inlineListToMarkdown, linkAttributes, attrsToMarkdown, attrsToMarkua) import Text.Pandoc.Writers.Markdown.Table (pipeTable, pandocTable) import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..), WriterState(..), WriterEnv(..), Ref, Refs, MD, evalMD) -- | Convert Pandoc to Markdown. writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMarkdown opts document = evalMD (pandocToMarkdown opts{ writerWrapText = if isEnabled Ext_hard_line_breaks opts then WrapNone else writerWrapText opts } document) def def -- | Convert Pandoc to plain text (like markdown, but without links, -- pictures, or inline formatting). writePlain :: PandocMonad m => WriterOptions -> Pandoc -> m Text writePlain opts document = evalMD (pandocToMarkdown opts document) def{ envVariant = PlainText } def -- | Convert Pandoc to Commonmark. writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeCommonMark opts document = evalMD (pandocToMarkdown opts' document) def{ envVariant = Commonmark } def where opts' = opts{ writerExtensions = -- These extensions can't be enabled or disabled -- for commonmark because they're part of the core; -- we set them here so that escapeText will behave -- properly. enableExtension Ext_all_symbols_escapable $ enableExtension Ext_intraword_underscores $ writerExtensions opts , writerWrapText = if isEnabled Ext_hard_line_breaks opts then WrapNone else writerWrapText opts } -- | Convert Pandoc to Markua. writeMarkua :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMarkua opts document = evalMD (pandocToMarkdown opts' document) def{ envVariant = Markua } def where opts' = opts{ writerExtensions = enableExtension Ext_hard_line_breaks $ enableExtension Ext_pipe_tables $ -- required for fancy list enumerators enableExtension Ext_fancy_lists $ enableExtension Ext_startnum $ enableExtension Ext_strikeout $ enableExtension Ext_subscript $ enableExtension Ext_superscript $ enableExtension Ext_definition_lists $ enableExtension Ext_smart $ enableExtension Ext_footnotes mempty , writerWrapText = if isEnabled Ext_hard_line_breaks opts then WrapNone else writerWrapText opts } pandocTitleBlock :: Doc Text -> [Doc Text] -> Doc Text -> Doc Text pandocTitleBlock tit auths dat = hang 2 (text "% ") tit <> cr <> hang 2 (text "% ") (vcat $ map nowrap auths) <> cr <> hang 2 (text "% ") dat <> cr mmdTitleBlock :: Context Text -> Doc Text mmdTitleBlock (Context hashmap) = vcat $ map go $ sortOn (T.toCaseFold . fst) $ M.toList hashmap where go (k,v) = case (text (T.unpack k), v) of (k', ListVal xs) | null xs -> empty | otherwise -> k' <> ":" <> space <> hcat (intersperse "; " $ mapMaybe fromVal xs) (k', SimpleVal x) | isEmpty x -> empty | otherwise -> k' <> ":" <> space <> nest 2 (removeBlankLines (chomp x)) _ -> empty removeBlankLines BlankLines{} = cr <> text "." <> cr removeBlankLines (Concat x y) = removeBlankLines x <> removeBlankLines y removeBlankLines x = x plainTitleBlock :: Doc Text -> [Doc Text] -> Doc Text -> Doc Text plainTitleBlock tit auths dat = tit <> cr <> hcat (intersperse (text "; ") auths) <> cr <> dat <> cr yamlMetadataBlock :: Context Text -> Doc Text yamlMetadataBlock v = "---" $$ contextToYaml v $$ "---" contextToYaml :: Context Text -> Doc Text contextToYaml (Context o) = vcat $ map keyvalToYaml $ sortOn (T.toCaseFold . fst) $ M.toList o where keyvalToYaml (k,v) = case (text (T.unpack k), v) of (k', ListVal vs) | null vs -> empty | otherwise -> (k' <> ":") $$ valToYaml v (k', MapVal (Context m)) | M.null m -> k' <> ": {}" | otherwise -> (k' <> ":") $$ nest 2 (valToYaml v) (_, SimpleVal x) | isEmpty x -> empty (_, NullVal) -> empty (k', _) -> k' <> ":" <+> hang 2 "" (valToYaml v) valToYaml :: Val Text -> Doc Text valToYaml (ListVal xs) = vcat $ map (\v -> hang 2 "- " (valToYaml v)) xs valToYaml (MapVal c) = contextToYaml c valToYaml (BoolVal True) = "true" valToYaml (BoolVal False) = "false" valToYaml (SimpleVal x) | isEmpty x = empty | otherwise = if hasNewlines x then hang 0 ("|" <> cr) x else case x of Text _ t | isSpecialString t -> "\"" <> fmap escapeInDoubleQuotes x <> "\"" _ | isNothing (foldM needsDoubleQuotes True x) -> "\"" <> fmap escapeInDoubleQuotes x <> "\"" | otherwise -> x where isSpecialString t = Set.member t specialStrings specialStrings = Set.fromList ["y", "Y", "yes", "Yes", "YES", "n", "N", "no", "No", "NO", "true", "True", "TRUE", "false", "False", "FALSE", "on", "On", "ON", "off", "Off", "OFF", "null", "Null", "NULL", "~", "*"] needsDoubleQuotes isFirst t = if T.any isBadAnywhere t || (isFirst && T.any isYamlPunct (T.take 1 t)) then Nothing else Just False isBadAnywhere '#' = True isBadAnywhere ':' = True isBadAnywhere _ = False hasNewlines NewLine = True hasNewlines BlankLines{} = True hasNewlines CarriageReturn = True hasNewlines (Concat w z) = hasNewlines w || hasNewlines z hasNewlines _ = False isYamlPunct = (`elem` ['-','?',':',',','[',']','{','}', '#','&','*','!','|','>','\'','"', '%','@','`']) escapeInDoubleQuotes = T.replace "\"" "\\\"" . T.replace "\\" "\\\\" valToYaml _ = empty -- | Return markdown representation of document. pandocToMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> MD m Text pandocToMarkdown opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing variant <- asks envVariant metadata <- metaToContext' (blockListToMarkdown opts) (inlineListToMarkdown opts) meta let title' = fromMaybe empty $ getField "title" metadata let authors' = fromMaybe [] $ getField "author" metadata let date' = fromMaybe empty $ getField "date" metadata let titleblock = case writerTemplate opts of Just _ | variant == PlainText -> plainTitleBlock title' authors' date' | isEnabled Ext_yaml_metadata_block opts -> yamlMetadataBlock metadata | isEnabled Ext_pandoc_title_block opts -> pandocTitleBlock title' authors' date' | isEnabled Ext_mmd_title_block opts -> mmdTitleBlock metadata | otherwise -> empty Nothing -> empty let modifyTOC = if isEnabled Ext_link_attributes opts || isEnabled Ext_attributes opts then id else walk $ \inln -> case inln of Link _attr contents tgt -> Link nullAttr contents tgt _ -> inln toc <- if writerTableOfContents opts then blockToMarkdown opts . modifyTOC $ toTableOfContents opts blocks else return mempty -- Strip off final 'references' header if markdown citations enabled let blocks' = if isEnabled Ext_citations opts then case reverse blocks of (Div ("refs",_,_) _):xs -> reverse xs _ -> blocks else blocks body <- blockListToMarkdown opts blocks' notesAndRefs' <- notesAndRefs opts let main = body <> notesAndRefs' let context = -- for backwards compatibility we populate toc -- with the contents of the toc, rather than a -- boolean: defField "toc" toc $ defField "table-of-contents" toc $ defField "body" main $ (if isNullMeta meta then id else defField "titleblock" titleblock) $ addVariablesToContext opts metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Return markdown representation of reference key table. refsToMarkdown :: PandocMonad m => WriterOptions -> Refs -> MD m (Doc Text) refsToMarkdown opts refs = vcat <$> mapM (keyToMarkdown opts) refs -- | Return markdown representation of a reference key. keyToMarkdown :: PandocMonad m => WriterOptions -> Ref -> MD m (Doc Text) keyToMarkdown opts (label', (src, tit), attr) = do let tit' = if T.null tit then empty else space <> "\"" <> literal tit <> "\"" return $ nest 2 $ hang 2 ("[" <> literal label' <> "]:" <> space) (literal src <> tit') <+> linkAttributes opts attr -- | Return markdown representation of notes. notesToMarkdown :: PandocMonad m => WriterOptions -> [[Block]] -> MD m (Doc Text) notesToMarkdown opts notes = do n <- gets stNoteNum notes' <- zipWithM (noteToMarkdown opts) [n..] notes modify $ \st -> st { stNoteNum = stNoteNum st + length notes } return $ vsep notes' -- | Return markdown representation of a note. noteToMarkdown :: PandocMonad m => WriterOptions -> Int -> [Block] -> MD m (Doc Text) noteToMarkdown opts num blocks = do contents <- blockListToMarkdown opts blocks let num' = literal $ writerIdentifierPrefix opts <> tshow num let marker = if isEnabled Ext_footnotes opts then literal "[^" <> num' <> literal "]:" else literal "[" <> num' <> literal "]" let markerSize = 4 + offset num' let hspacer = case writerTabStop opts - markerSize of n | n > 0 -> literal $ T.replicate n " " _ -> literal " " let spacer = case blocks of Para{}:_ -> hspacer Plain{}:_ -> hspacer _ -> cr return $ if isEnabled Ext_footnotes opts then hang (writerTabStop opts) (marker <> spacer) contents else marker <> spacer <> contents -- | (Code) blocks with a single class and no attributes can just use it -- standalone, no need to bother with curly braces. classOrAttrsToMarkdown :: WriterOptions -> Attr -> Doc Text classOrAttrsToMarkdown _ ("",[cls],[]) = literal cls classOrAttrsToMarkdown opts attrs = attrsToMarkdown opts attrs -- | Ordered list start parser for use in Para below. olMarker :: Parsec Text ParserState () olMarker = do (start, style', delim) <- anyOrderedListMarker if delim == Period && (style' == UpperAlpha || (style' == UpperRoman && start `elem` [1, 5, 10, 50, 100, 500, 1000])) then mzero -- it needs 2 spaces anyway else eof -- | True if string begins with an ordered list marker beginsWithOrderedListMarker :: Text -> Bool beginsWithOrderedListMarker str = case runParser olMarker defaultParserState "para start" (T.take 10 str) of Left _ -> False Right _ -> True notesAndRefs :: PandocMonad m => WriterOptions -> MD m (Doc Text) notesAndRefs opts = do notes' <- gets stNotes >>= notesToMarkdown opts . reverse modify $ \s -> s { stNotes = [] } refs' <- gets stRefs >>= refsToMarkdown opts . reverse modify $ \s -> s { stPrevRefs = stPrevRefs s ++ stRefs s , stRefs = []} let endSpacing = if | writerReferenceLocation opts == EndOfDocument -> empty | isEmpty notes' && isEmpty refs' -> empty | otherwise -> blankline return $ (if isEmpty notes' then empty else blankline <> notes') <> (if isEmpty refs' then empty else blankline <> refs') <> endSpacing -- | Convert Pandoc block element to markdown. blockToMarkdown :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> MD m (Doc Text) blockToMarkdown opts blk = local (\env -> env {envBlockLevel = envBlockLevel env + 1}) $ do doc <- blockToMarkdown' opts blk blkLevel <- asks envBlockLevel if writerReferenceLocation opts == EndOfBlock && blkLevel == 1 then notesAndRefs opts >>= (\d -> return $ doc <> d) else return doc blockToMarkdown' :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> MD m (Doc Text) blockToMarkdown' opts (Div attrs@(_,classes,_) bs) | ("sourceCode":_) <- classes , [CodeBlock (_,"sourceCode":_,_) _] <- bs -- skip pandoc-generated Div wrappers around code blocks = blockListToMarkdown opts bs | isEnabled Ext_alerts opts , (cls:_) <- classes , cls `elem` ["note", "tip", "warning", "caution", "important"] , (Div ("", ["title"], []) _ : bs') <- bs = do contents <- blockListToMarkdown opts bs' let alertLabel = literal $ "[!" <> T.toUpper cls <> "]" pure $ text "> " <> alertLabel $$ prefixed "> " contents $$ blankline | otherwise = do contents <- blockListToMarkdown opts bs variant <- asks envVariant return $ case () of _ | variant == Markua -> case () of () | "blurb" `elem` classes' -> prefixed "B> " contents <> blankline | "aside" `elem` classes' -> prefixed "A> " contents <> blankline -- necessary to enable option to create a bibliography | (take 3 (T.unpack id')) == "ref" -> contents <> blankline | otherwise -> contents <> blankline | isEnabled Ext_fenced_divs opts -> let attrsToMd = if variant == Commonmark then attrsToMarkdown opts else classOrAttrsToMarkdown opts divNesting = computeDivNestingLevel bs numcolons = 3 + divNesting colons = literal $ T.replicate numcolons ":" in nowrap (colons <+> attrsToMd attrs) $$ chomp contents $$ colons <> blankline | isEnabled Ext_native_divs opts || (isEnabled Ext_raw_html opts && (variant == Commonmark || isEnabled Ext_markdown_in_html_blocks opts)) -> tagWithAttrs "div" attrs <> blankline <> contents <> blankline <> "</div>" <> blankline | isEnabled Ext_raw_html opts && isEnabled Ext_markdown_attribute opts -> tagWithAttrs "div" attrs' <> blankline <> contents <> blankline <> "</div>" <> blankline | otherwise -> contents <> blankline where (id',classes',kvs') = attrs attrs' = (id',classes',("markdown","1"):kvs') blockToMarkdown' opts (Plain inlines) = do -- escape if para starts with ordered list marker variant <- asks envVariant let escapeMarker = T.concatMap $ \x -> if T.any (== x) ".()" then T.pack ['\\', x] else T.singleton x let startsWithSpace (Space:_) = True startsWithSpace (SoftBreak:_) = True startsWithSpace _ = False let inlines' = if variant == PlainText then inlines else case inlines of (Str t:ys) | null ys || startsWithSpace ys , beginsWithOrderedListMarker t -> RawInline (Format "markdown") (escapeMarker t):ys (Str t:_) | t == "+" || t == "-" || (t == "%" && isEnabled Ext_pandoc_title_block opts && isEnabled Ext_all_symbols_escapable opts) -> RawInline (Format "markdown") "\\" : inlines _ -> inlines contents <- inlineListToMarkdown opts inlines' return $ contents <> cr blockToMarkdown' opts (Para inlines) = (<> blankline) `fmap` blockToMarkdown opts (Plain inlines) blockToMarkdown' opts (LineBlock lns) = do variant <- asks envVariant case variant of PlainText -> do let emptyToBlank l = if isEmpty l then blankline else l mdLines <- mapM (liftM emptyToBlank . inlineListToMarkdown opts) lns return $ vcat mdLines <> blankline _ -> if isEnabled Ext_line_blocks opts then do mdLines <- mapM (inlineListToMarkdown opts) lns return $ vcat (map (hang 2 (literal "| ")) mdLines) <> blankline else blockToMarkdown opts $ linesToPara lns blockToMarkdown' opts b@(RawBlock f str) = do variant <- asks envVariant let Format fmt = f let rawAttribBlock = return $ (literal "```{=" <> literal fmt <> "}") $$ literal str $$ (literal "```" <> literal "\n") let renderEmpty = mempty <$ report (BlockNotRendered b) case variant of PlainText | f == "plain" -> return $ nest 0 (literal str) <> literal "\n" Commonmark | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"] -> return $ nest 0 (literal str) $$ blankline | f `elem` ["html", "html5", "html4"] -> return $ literal (removeBlankLinesInHTML str) $$ blankline Markdown | f `elem` ["markdown", "markdown_github", "markdown_phpextra", "markdown_mmd", "markdown_strict"] -- the 'nest 0' ensures that leading and trailing newlines -- don't get collapsed. See #10477 for context; -> return $ nest 0 (literal str) <> literal "\n" Markua -> renderEmpty _ | f `elem` ["html", "html5", "html4"] , isEnabled Ext_markdown_attribute opts -> return $ literal (addMarkdownAttribute str) <> literal "\n" | f `elem` ["html", "html5", "html4"] , isEnabled Ext_raw_html opts -> return $ literal str <> literal "\n" | f `elem` ["latex", "tex"] , isEnabled Ext_raw_tex opts -> return $ literal str <> literal "\n" | isEnabled Ext_raw_attribute opts -> rawAttribBlock _ -> renderEmpty blockToMarkdown' opts HorizontalRule = do variant <- asks envVariant let indicator = case variant of Markua -> "* * *" _ -> T.replicate (writerColumns opts) "-" return $ blankline <> literal indicator <> blankline blockToMarkdown' opts (Header level attr inlines) = do -- first, if we're putting references at the end of a section, we -- put them here. blkLevel <- asks envBlockLevel refs <- if writerReferenceLocation opts == EndOfSection && blkLevel == 1 then notesAndRefs opts else return empty variant <- asks envVariant -- we calculate the id that would be used by auto_identifiers -- or gfm_auto_identifiers -- so we know whether to print an explicit identifier ids <- gets stIds let autoId = uniqueIdent (writerExtensions opts) inlines ids modify $ \st -> st{ stIds = Set.insert autoId ids } let attr' = case attr of ("",[],[]) -> empty (id',[],[]) | (isEnabled Ext_auto_identifiers opts || isEnabled Ext_gfm_auto_identifiers opts) && id' == autoId -> empty (id',_,_) | isEnabled Ext_mmd_header_identifiers opts -> space <> brackets (literal id') _ | variant == Markua -> attrsToMarkua opts attr | isEnabled Ext_header_attributes opts || isEnabled Ext_attributes opts -> space <> attrsToMarkdown opts attr | otherwise -> empty let setext = level <= 2 && writerSetextHeaders opts || (variant == Commonmark && hasLineBreaks inlines) -- #11341 contents <- inlineListToMarkdown opts $ (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 when (not setext && isEnabled Ext_literate_haskell opts) $ report $ ATXHeadingInLHS level (render Nothing contents) let hdr = nowrap $ case level of 1 | variant == PlainText -> if isEnabled Ext_gutenberg opts then blanklines 3 <> contents <> blanklines 2 else contents <> blankline | setext -> contents <> attr' <> cr <> literal (T.replicate (offset contents) "=") <> blankline 2 | variant == PlainText -> if isEnabled Ext_gutenberg opts then blanklines 2 <> contents <> blankline else contents <> blankline | setext -> contents <> attr' <> cr <> literal (T.replicate (offset contents) "-") <> blankline -- ghc interprets '#' characters in column 1 as linenum specifiers. _ | variant == PlainText || isEnabled Ext_literate_haskell opts -> contents <> blankline _ | variant == Markua -> attr' <> cr <> literal (T.replicate level "#") <> space <> contents <> blankline _ -> literal (T.replicate level "#") <> space <> contents <> attr' <> blankline return $ refs <> hdr blockToMarkdown' opts (CodeBlock (_,classes,_) str) | "haskell" `elem` classes && "literate" `elem` classes && isEnabled Ext_literate_haskell opts = return $ prefixed "> " (literal str) <> blankline blockToMarkdown' opts (CodeBlock attribs str) = do variant <- asks envVariant return $ case attribs == nullAttr of False | variant == Commonmark || isEnabled Ext_backtick_code_blocks opts -> backticks <> attrs <> cr <> literal str <> cr <> backticks <> blankline | isEnabled Ext_fenced_code_blocks opts -> tildes <> attrs <> cr <> literal str <> cr <> tildes <> blankline _ | variant == Markua -> blankline <> attrsToMarkua opts attribs <> cr <> backticks <> cr <> literal str <> cr <> backticks <> cr <> blankline | otherwise -> nest (writerTabStop opts) (literal str) <> blankline where endlineLen c = maybe 3 ((+1) . maximum) $ nonEmpty [T.length ln | ln <- map trim (T.lines str) , T.pack [c,c,c] `T.isPrefixOf` ln , T.all (== c) ln] endline c = literal $ T.replicate (endlineLen c) $ T.singleton c backticks = endline '`' tildes = endline '~' attrs = if isEnabled Ext_fenced_code_attributes opts || isEnabled Ext_attributes opts then nowrap $ " " <> classOrAttrsToMarkdown opts attribs else let (_,cls,_) = attribs in case getLangFromClasses cls of Just l -> " " <> literal l Nothing -> empty blockToMarkdown' opts (BlockQuote blocks) = do variant <- asks envVariant -- if we're writing literate haskell, put a space before the bird tracks -- so they won't be interpreted as lhs... let leader | isEnabled Ext_literate_haskell opts = " > " | variant == PlainText = " " | otherwise = "> " contents <- blockListToMarkdown opts blocks return $ text leader <> prefixed leader contents <> blankline blockToMarkdown' opts t@(Table attr blkCapt specs thead tbody tfoot) = do 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 let tfootHasColRowSpans (TableFoot _ rs) = any rowHasColRowSpans rs let hasColRowSpans = theadHasColRowSpans thead || any tbodyHasColRowSpans tbody || tfootHasColRowSpans tfoot let numcols = maximum (length aligns :| length widths : map length (headers:rows)) caption' <- inlineListToMarkdown opts caption let caption'' = if attr == nullAttr then caption' else caption' <+> attrsToMarkdown opts attr let caption''' | null caption = blankline | isEnabled Ext_table_captions opts = blankline $$ (": " <> caption'') $$ blankline | otherwise = blankline $$ caption'' $$ blankline let hasSimpleCells = onlySimpleTableCells $ headers : rows 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) let padRow r = case numcols - length r of x | x > 0 -> r ++ replicate x empty | otherwise -> r let aligns' = case numcols - length aligns of x | x > 0 -> aligns ++ replicate x AlignDefault | otherwise -> aligns let widths' = case numcols - length widths of x | x > 0 -> widths ++ replicate x 0.0 | otherwise -> widths let mkTable f = do rawHeaders <- padRow <$> mapM (blockListToMarkdown opts) headers rawRows <- mapM (fmap padRow . mapM (blockListToMarkdown opts)) rows f (all null headers) aligns' widths' rawHeaders rawRows case True of _ | isSimple && isEnabled Ext_simple_tables opts -> do tbl <- mkTable (pandocTable opts False) return $ nest 2 (tbl $$ caption''') $$ blankline | isSimple && isEnabled Ext_pipe_tables opts -> do tbl <- mkTable (pipeTable opts) return $ (tbl $$ caption''') $$ blankline | 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 || hasFooter) -> do tbl <- gridTable opts blockListToMarkdown specs thead tbody tfoot return $ (tbl $$ caption''') $$ blankline | hasSimpleCells, not hasColRowSpans, isEnabled Ext_pipe_tables opts -> do tbl <- mkTable (pipeTable opts) return $ (tbl $$ caption''') $$ blankline | isEnabled Ext_raw_html opts -> do -- HTML fallback tbl <- literal . removeBlankLinesInHTML <$> writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [t]) return $ tbl $$ blankline -- caption is in the HTML table | hasSimpleCells, hasColRowSpans, isEnabled Ext_pipe_tables opts -> do -- In this case an approximate pipe table will be rendered, -- without col/row spans. This is better than nothing, since -- we have no other way to render the table correctly (#11128). tbl <- mkTable (pipeTable opts) return $ (tbl $$ caption''') $$ blankline | otherwise -> do report (BlockNotRendered t) return $ (literal "[TABLE]" $$ caption''') $$ blankline blockToMarkdown' opts (BulletList items) = do contents <- inList $ mapM (bulletListItemToMarkdown opts) items return $ (if isTightList items then vcat else vsep) contents <> blankline blockToMarkdown' opts (OrderedList (start,sty,delim) items) = do variant <- asks envVariant let start' = if variant == Commonmark || isEnabled Ext_startnum opts then start else 1 let sty' = if isEnabled Ext_fancy_lists opts then sty else DefaultStyle let delim' | isEnabled Ext_fancy_lists opts = case variant of -- Markua supports 'fancy' enumerators, but no TwoParens Markua -> if delim == TwoParens then OneParen else delim _ -> delim | variant == Commonmark && --commonmark only supports one paren (delim == OneParen || delim == TwoParens) = OneParen | otherwise = DefaultDelim let attribs = (start', sty', delim') let markers = orderedListMarkers attribs let markers' = case variant of Markua -> markers _ -> map (\m -> if T.length m < 3 then m <> T.replicate (3 - T.length m) " " else m) markers contents <- inList $ zipWithM (orderedListItemToMarkdown opts) markers' items return $ (if isTightList items then vcat else vsep) contents <> blankline blockToMarkdown' opts (DefinitionList items) = do contents <- inList $ mapM (definitionListItemToMarkdown opts) items return $ mconcat contents <> blankline blockToMarkdown' opts (Figure figattr capt body) = do let combinedAttr imgattr = case imgattr of ("", cls, kvs) | (figid, [], []) <- figattr -> Just (figid, cls, [(k,v) | (k,v) <- kvs , k /= "alt" || v /= "" && v /= trim (stringify capt)]) _ -> Nothing case body of [Plain [Image imgAttr alt (src, ttl)]] | isEnabled Ext_implicit_figures opts , Just imgAttr' <- combinedAttr imgAttr , isEnabled Ext_link_attributes opts || imgAttr' == nullAttr -> do -- use implicit figures if possible let tgt' = (src, fromMaybe ttl $ T.stripPrefix "fig:" ttl) let descr = case capt of Caption _ bs -> blocksToInlines bs -- add alt attribute if image description different from caption, -- so this won't be lost: let imgAttr'' = case imgAttr' of (i,c,kv) | not (null alt) , Nothing <- lookup "alt" kv , stringify descr /= stringify alt -> (i, c, ("alt", stringify alt) : kv) _ -> imgAttr' contents <- inlineListToMarkdown opts [Image imgAttr'' descr tgt'] return $ contents <> blankline _ -> -- fallback to raw html if possible or div otherwise if isEnabled Ext_raw_html opts then figureToMarkdown opts figattr capt body else if (isEnabled Ext_fenced_divs opts || isEnabled Ext_native_divs opts) || not (isEnabled Ext_implicit_figures opts) then blockToMarkdown' opts $ figureDiv figattr capt body else blockListToMarkdown opts body inList :: Monad m => MD m a -> MD m a inList p = local (\env -> env {envInList = True}) p addMarkdownAttribute :: Text -> Text addMarkdownAttribute s = case span isTagText $ reverse $ parseTags s of (xs, TagOpen t attrs:rest) -> renderTags' $ reverse rest ++ (TagOpen t attrs' : reverse xs) where attrs' = ("markdown","1"):[(x,y) | (x,y) <- attrs, x /= "markdown"] _ -> s -- | Converts a figure to Markdown by wrapping it in a div named `figure`. figureToMarkdown :: PandocMonad m => WriterOptions -> Attr -> Caption -> [Block] -> MD m (Doc Text) figureToMarkdown opts attr@(ident, classes, kvs) capt body | isEnabled Ext_raw_html opts = (<> blankline) . literal . T.strip <$> writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Figure attr capt body]) | otherwise = do let attr' = (ident, ["figure"] `union` classes, kvs) let Caption _mbshort caption = capt let captionBs = [Div ("",["caption"],[]) caption | not (null caption)] blockToMarkdown' opts (Div attr' (body <> captionBs)) itemEndsWithTightList :: [Block] -> Bool itemEndsWithTightList bs = case bs of [Plain _, BulletList xs] -> isTightList xs [Plain _, OrderedList _ xs] -> isTightList xs _ -> False -- | Convert bullet list item (list of blocks) to markdown. bulletListItemToMarkdown :: PandocMonad m => WriterOptions -> [Block] -> MD m (Doc Text) bulletListItemToMarkdown opts bs = do variant <- asks envVariant let exts = writerExtensions opts contents <- blockListToMarkdown opts $ taskListItemToAscii exts bs let start = case variant of Markua -> "* " Commonmark -> "- " Markdown | isEnabled Ext_four_space_rule opts -> "- " <> T.replicate (writerTabStop opts - 2) " " PlainText | isEnabled Ext_four_space_rule opts -> "- " <> T.replicate (writerTabStop opts - 2) " " _ -> "- " -- remove trailing blank line if item ends with a tight list let contents' = if itemEndsWithTightList bs then chomp contents <> cr else contents return $ hang (T.length start) (literal start) contents' -- | Convert ordered list item (a list of blocks) to markdown. orderedListItemToMarkdown :: PandocMonad m => WriterOptions -- ^ options -> Text -- ^ list item marker -> [Block] -- ^ list item (list of blocks) -> MD m (Doc Text) orderedListItemToMarkdown opts marker bs = do let exts = writerExtensions opts contents <- blockListToMarkdown opts $ taskListItemToAscii exts bs variant <- asks envVariant let sps = case writerTabStop opts - T.length marker of n | n > 0 -> literal $ T.replicate n " " _ -> literal " " let ind = if isEnabled Ext_four_space_rule opts then writerTabStop opts else max (writerTabStop opts) (T.length marker + 1) let start = case variant of Markua -> literal marker <> " " _ -> literal marker <> sps -- remove trailing blank line if item ends with a tight list let contents' = if itemEndsWithTightList bs then chomp contents <> cr else contents return $ hang ind start contents' -- | Convert definition list item (label, list of blocks) to markdown. definitionListItemToMarkdown :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> MD m (Doc Text) definitionListItemToMarkdown opts (label, defs) = do labelText <- blockToMarkdown opts (Plain label) defs' <- mapM (mapM (blockToMarkdown opts)) defs if isEnabled Ext_definition_lists opts then do let tabStop = writerTabStop opts variant <- asks envVariant let leader = case variant of PlainText -> " " _ -> ":" let leadingChars = case tabStop of -- Always use two leading characters for Markua n | n >= 2 && variant /= Markua -> n _ -> 2 let sps = literal $ T.replicate (leadingChars - 1) " " let isTight = case defs of ((Plain _ : _): _) -> True _ -> False let contents = (if isTight then vcat else vsep) $ map (\d -> hang leadingChars (leader <> sps) $ vcat d) defs' return $ blankline <> nowrap labelText $$ (if isTight then empty else blankline) <> contents <> blankline else return $ nowrap (chomp labelText <> literal " " <> cr) <> vsep (map vsep defs') <> blankline -- | Convert list of Pandoc block elements to markdown. blockListToMarkdown :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements -> MD m (Doc Text) blockListToMarkdown opts blocks = do inlist <- asks envInList variant <- asks envVariant -- a) insert comment between list and indented code block, or the -- code block will be treated as a list continuation paragraph -- b) change Plain to Para unless it's followed by a RawBlock -- or has a list as its parent (#3487) let fixBlocks (b : CodeBlock attr x : rest) | (not (variant == Commonmark || isEnabled Ext_backtick_code_blocks opts || isEnabled Ext_fenced_code_blocks opts) || attr == nullAttr) && isListBlock b = b : commentSep : CodeBlock attr x : fixBlocks rest fixBlocks (b1@(BulletList _) : b2@(BulletList _) : bs) = b1 : commentSep : fixBlocks (b2:bs) fixBlocks (b1@(OrderedList _ _) : b2@(OrderedList _ _) : bs) = b1 : commentSep : fixBlocks (b2:bs) fixBlocks (b1@(DefinitionList _) : b2@(DefinitionList _) : bs) = b1 : commentSep : fixBlocks (b2:bs) fixBlocks (Plain ils : bs@(RawBlock{}:_)) = Plain ils : fixBlocks bs fixBlocks (Plain ils : bs@(Div{}:_)) | isEnabled Ext_fenced_divs opts = Para ils : fixBlocks bs fixBlocks (Plain ils : bs) | inlist = Plain ils : fixBlocks bs fixBlocks (Plain ils : bs) = Para ils : fixBlocks bs fixBlocks (r@(RawBlock f raw) : b : bs) | not (T.null raw) , T.last raw /= '\n' = case b of Plain{} -> r : fixBlocks (b:bs) RawBlock{} -> r : fixBlocks (b:bs) _ -> RawBlock f (raw <> "\n") : fixBlocks (b:bs) -- #4629 fixBlocks (x : xs) = x : fixBlocks xs fixBlocks [] = [] isListBlock (BulletList _) = True isListBlock (OrderedList _ _) = True isListBlock (DefinitionList _) = True isListBlock _ = False commentSep | variant == PlainText = Plain [] | variant == Markua = Plain [] | isEnabled Ext_raw_html opts = RawBlock "html" "<!-- -->\n" | otherwise = RawBlock "markdown" " \n" mconcat <$> mapM (blockToMarkdown opts) (fixBlocks blocks) lineBreakToSpace :: Inline -> Inline lineBreakToSpace LineBreak = Space lineBreakToSpace SoftBreak = Space lineBreakToSpace x = x removeBlankLinesInHTML :: Text -> Text removeBlankLinesInHTML = T.pack . go False . T.unpack where go _ [] = [] go True ('\n':cs) = " " <> go False cs go False ('\n':cs) = '\n' : go True cs go !afternewline (!c:cs) | isSpace c = c : go afternewline cs | otherwise = c : go False cs computeDivNestingLevel :: [Block] -> Int computeDivNestingLevel = foldr go 0 where go (Div _ bls') n = max (n + 1) (foldr go (n + 1) bls') go _ n = n -- Identify the class in a list of classes that corresponds to -- the language syntax. language-X turns to X. getLangFromClasses :: [Text] -> Maybe Text getLangFromClasses cs = case find ("language-" `T.isPrefixOf`) cs of Just x -> Just (T.drop 9 x) Nothing -> case filter (/= "sourceCode") cs of (x:_) -> Just x [] -> Nothing ================================================ FILE: src/Text/Pandoc/Writers/Math.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Text.Pandoc.Writers.Math ( texMathToInlines , convertMath , defaultMathJaxURL , defaultKaTeXURL ) where import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.TeXMath (DisplayType (..), Exp, readTeX, writePandoc) import Text.Pandoc.Options (defaultMathJaxURL, defaultKaTeXURL) -- | Converts a raw TeX math formula to a list of 'Pandoc' inlines. -- Defaults to raw formula between @$@ or @$$@ characters if entire formula -- can't be converted. texMathToInlines :: PandocMonad m => MathType -> T.Text -- ^ String to parse (assumes @'\n'@ line endings) -> m [Inline] texMathToInlines mt inp = do res <- convertMath writePandoc mt inp case res of Right (Just ils) -> return ils Right Nothing -> do report $ CouldNotConvertTeXMath inp "" return [mkFallback mt inp] Left il -> return [il] mkFallback :: MathType -> T.Text -> Inline mkFallback mt str = Str (delim <> str <> delim) where delim = case mt of DisplayMath -> "$$" InlineMath -> "$" -- | Converts a raw TeX math formula using a writer function, -- issuing a warning and producing a fallback (a raw string) -- on failure. convertMath :: PandocMonad m => (DisplayType -> [Exp] -> a) -> MathType -> T.Text -> m (Either Inline a) convertMath writer mt str = case writer dt <$> readTeX str of Right r -> return (Right r) Left e -> do report $ CouldNotConvertTeXMath str e return (Left $ mkFallback mt str) where dt = case mt of DisplayMath -> DisplayBlock InlineMath -> DisplayInline ================================================ FILE: src/Text/Pandoc/Writers/MediaWiki.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.MediaWiki Copyright : Copyright (C) 2008-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to MediaWiki markup. MediaWiki: <http://www.mediawiki.org/wiki/MediaWiki> -} module Text.Pandoc.Writers.MediaWiki ( writeMediaWiki, highlightingLangs ) where import Control.Monad.Reader import Control.Monad.State.Strict import Data.Maybe (fromMaybe) import qualified Data.List as DL import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import Data.List.NonEmpty (NonEmpty((:|))) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import qualified Text.Pandoc.Writers.AnnotatedTable as Ann import Text.Pandoc.Writers.Shared import Text.Pandoc.XML (escapeStringForXML) data WriterState = WriterState { stNotes :: Bool -- True if there are notes , stOptions :: WriterOptions -- writer options , stInDefLabel :: Bool -- True if in definition list label } data WriterReader = WriterReader { options :: WriterOptions -- Writer options , listLevel :: [Char] -- String at beginning of list items, e.g. "**" , useTags :: Bool -- True if we should use HTML tags because we're in a complex list } type MediaWikiWriter m = ReaderT WriterReader (StateT WriterState m) -- | Convert Pandoc to MediaWiki. writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMediaWiki opts document = let initialState = WriterState { stNotes = False, stOptions = opts, stInDefLabel = False } env = WriterReader { options = opts, listLevel = [], useTags = False } in evalStateT (runReaderT (pandocToMediaWiki document) env) initialState -- | Return MediaWiki representation of document. pandocToMediaWiki :: PandocMonad m => Pandoc -> MediaWikiWriter m Text pandocToMediaWiki (Pandoc meta blocks) = do opts <- asks options metadata <- metaToContext opts (fmap chomp . blockListToMediaWiki) (fmap chomp . inlineListToMediaWiki) meta body <- blockListToMediaWiki blocks notesExist <- gets stNotes let notes = if notesExist then cr <> literal "<references />" else mempty let main = body <> notes let context = defField "body" main $ defField "toc" (writerTableOfContents opts) metadata return $ render Nothing $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Escape special characters for MediaWiki. escapeText :: Text -> Text escapeText = escapeStringForXML -- | Convert Pandoc block element to MediaWiki. blockToMediaWiki :: PandocMonad m => Block -- ^ Block element -> MediaWikiWriter m (Doc Text) blockToMediaWiki (Div attrs bs) = do contents <- blockListToMediaWiki bs return $ tagWithAttrs "div" attrs $$ contents $$ literal "</div>" $$ blankline blockToMediaWiki (Plain inlines) = inlineListToMediaWiki inlines 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' then "" else "alt=" <> capt' else "alt=" <> tit let separator = "<nowiki></nowiki>" -- External images (URIs) cannot use [[File:...]] syntax; -- they must be rendered as bare URLs. MediaWiki will auto-embed -- them when $wgAllowExternalImages is enabled. return $ if isURI src then literal (separator <> src <> separator) <> cr else literal ("[[" <> T.intercalate "|" (filter (not . T.null) ["File:" <> src , "thumb" , "none" , img , opt , capt' ]) <> "]]") <> cr blockToMediaWiki (Para inlines) = do tags <- asks useTags lev <- asks listLevel contents <- inlineListToMediaWiki inlines let contents' = render Nothing contents let initEsc = if startsWithListMarker contents' then literal "\\" else mempty return $ if tags 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 $ literal str | f == Format "html" = return $ literal str | otherwise = mempty <$ report (BlockNotRendered b) blockToMediaWiki HorizontalRule = return $ blankline <> literal "-----" <> blankline blockToMediaWiki (Header level (ident,_,_) inlines) = do let autoId = T.replace " " "_" $ stringify inlines contents <- inlineListToMediaWiki inlines let eqs = literal $ T.replicate level "=" return $ (if T.null ident || autoId == ident 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 $ literal $ case Set.toList at of [] -> "<pre" <> (if null classes then ">" else " class=\"" <> T.unwords classes <> "\">") <> escapeText str <> "</pre>" (l:_) -> "<syntaxhighlight lang=\"" <> l <> "\"" <> (if numberLines then " line" else "") <> maybe "" (\x -> " start=\"" <> x <> "\"") start <> ">" <> str <> "</syntaxhighlight>" -- note: no escape! even for <! blockToMediaWiki (BlockQuote blocks) = do contents <- blockListToMediaWiki blocks 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) blockToMediaWiki x@(BulletList items) = do tags <- (|| not (isSimpleList x)) <$> asks useTags if tags then do contents <- local (\ s -> s { useTags = True }) $ mapM listItemToMediaWiki items 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 blankline else mempty blockToMediaWiki x@(OrderedList attribs items) = do tags <- (|| not (isSimpleList x)) <$> asks useTags if tags then do contents <- local (\s -> s { useTags = True }) $ mapM listItemToMediaWiki items 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 blankline else mempty blockToMediaWiki x@(DefinitionList items) = do tags <- (|| not (isSimpleList x)) <$> asks useTags if tags then do contents <- local (\s -> s { useTags = True }) $ mapM definitionListItemToMediaWiki items 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 blankline else mempty blockToMediaWiki (Figure (ident, classes, kvs) _ body) = blockToMediaWiki (Div (ident, ["figure"] `DL.union` classes, kvs) body) -- Auxiliary functions for lists: -- | Convert ordered list attributes to HTML attribute string listAttribsToText :: ListAttributes -> Text listAttribsToText (startnum, numstyle, _) = let numstyle' = camelCaseToHyphenated $ tshow numstyle in (if startnum /= 1 then " start=\"" <> tshow startnum <> "\"" else "") <> (if numstyle /= DefaultStyle then " style=\"list-style-type: " <> numstyle' <> ";\"" else "") -- | Convert bullet or ordered list item (list of blocks) to MediaWiki. listItemToMediaWiki :: PandocMonad m => [Block] -> MediaWikiWriter m (Doc Text) listItemToMediaWiki items = do contents <- blockListToMediaWiki items tags <- asks useTags if tags then return $ literal "<li>" <> chomp contents <> literal "</li>" else do marker <- asks listLevel 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 (Doc Text) definitionListItemToMediaWiki (label, items) = do modify $ \st -> st{ stInDefLabel = True } labelText <- inlineListToMediaWiki label modify $ \st -> st{ stInDefLabel = False } contents <- mapM blockListToMediaWiki items tags <- asks useTags if tags 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 $ 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 isSimpleList x = case x of BulletList items -> all isSimpleListItem items OrderedList (num, sty, _) items -> all isSimpleListItem items && num == 1 && sty `elem` [DefaultStyle, Decimal] DefinitionList items -> all isSimpleListItem $ concatMap snd items _ -> False -- | True if list item can be handled with the simple wiki syntax. False if -- HTML tags will be needed. isSimpleListItem :: [Block] -> Bool isSimpleListItem [] = True isSimpleListItem [x] = case x of Plain _ -> True Para _ -> True BulletList _ -> isSimpleList x OrderedList _ _ -> isSimpleList x DefinitionList _ -> isSimpleList x _ -> False isSimpleListItem [x, y] | isPlainOrPara x = case y of BulletList _ -> isSimpleList y OrderedList _ _ -> isSimpleList y DefinitionList _ -> isSimpleList y _ -> False isSimpleListItem _ = False isPlainOrPara :: Block -> Bool isPlainOrPara (Plain _) = True isPlainOrPara (Para _) = True isPlainOrPara _ = False -- Auxiliary functions for tables: 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 $ literal "|+ " <> chomp c <> cr head' <- tableHeadToMW thead bodies' <- mconcat <$> mapM tableBodyToMW tbodies foot' <- tableFootToMW tfoot return $ literal "{|" <> htmlAttrs (ident, "wikitable":classes, kvs) <> cr <> caption <> head' <> bodies' <> foot' <> literal "|}" <> blankline tableHeadToMW :: PandocMonad m => Ann.TableHead -> MediaWikiWriter m (Doc Text) tableHeadToMW (Ann.TableHead _ rows) = headerRowsToMW rows tableFootToMW :: PandocMonad m => Ann.TableFoot -> MediaWikiWriter m (Doc Text) tableFootToMW (Ann.TableFoot _ rows) = headerRowsToMW rows 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 (Doc Text) headerRowsToMW rows = mconcat <$> mapM headerRowToMW rows headerRowToMW :: PandocMonad m => Ann.HeaderRow -> MediaWikiWriter m (Doc Text) headerRowToMW (Ann.HeaderRow attr _ cells) = do cells' <- mconcat <$> mapM (cellToMW "!") cells return $ literal "|-" <> htmlAttrs attr <> cr <> cells' bodyRowsToMW :: PandocMonad m => [Ann.BodyRow] -> MediaWikiWriter m (Doc Text) bodyRowsToMW rows = mconcat <$> mapM bodyRowToMW rows bodyRowToMW :: PandocMonad m => Ann.BodyRow -> MediaWikiWriter m (Doc Text) bodyRowToMW (Ann.BodyRow attr _ headCells bodyCells) = do 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 (Doc Text) cellToMW marker (Ann.Cell (colSpec :| _) _ (Cell attr align rowspan colspan content)) = do content' <- blockListToMediaWiki content let (ident,classes,keyVals) = attr let align' = case align of AlignDefault -> fst colSpec _ -> align let keyVals' = case (htmlAlignmentToString align') of Nothing -> keyVals Just alignStr -> htmlAddStyle ("text-align", alignStr) keyVals let rowspan' = case rowspan of RowSpan 1 -> mempty RowSpan n -> [("rowspan", T.pack(show n))] let colspan' = case colspan of ColSpan 1 -> mempty ColSpan n -> [("colspan", T.pack(show n))] 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 opts <- gets stOptions let (_, cls, _) = attr toPx = fmap (showInPixel opts) . checkPct checkPct (Just (Percent _)) = Nothing checkPct maybeDim = maybeDim go (Just w) Nothing = w <> "px" go (Just w) (Just h) = w <> "x" <> h <> "px" go Nothing (Just h) = "x" <> h <> "px" go Nothing Nothing = "" dims = go (toPx $ dimension Width attr) (toPx $ dimension Height attr) classes = if null cls then "" else "class=" <> T.unwords cls return $ T.intercalate "|" $ filter (not . T.null) [dims, classes] -- | Convert list of Pandoc block elements to MediaWiki. blockListToMediaWiki :: PandocMonad m => [Block] -- ^ List of block elements -> MediaWikiWriter m (Doc Text) blockListToMediaWiki blocks = vcat <$> mapM blockToMediaWiki blocks -- | Convert list of Pandoc inline elements to MediaWiki. inlineListToMediaWiki :: PandocMonad m => [Inline] -> MediaWikiWriter m (Doc Text) inlineListToMediaWiki lst = hcat <$> mapM inlineToMediaWiki (fixup lst) where fixup [] = [] fixup (Str t : x : xs) | not (T.null t) && T.last t == '[' , isLinkOrImage x = Str t : RawInline (Format "mediawiki") "<nowiki/>" : x : fixup xs fixup (x:xs) = x : fixup xs isLinkOrImage Link{} = True isLinkOrImage Image{} = True isLinkOrImage _ = False -- | Convert Pandoc inline element to MediaWiki. inlineToMediaWiki :: PandocMonad m => Inline -> MediaWikiWriter m (Doc Text) inlineToMediaWiki (Span attrs ils) = do contents <- inlineListToMediaWiki ils return $ tagWithAttrs "span" attrs <> contents <> literal "</span>" inlineToMediaWiki (Emph lst) = do contents <- inlineListToMediaWiki lst return $ literal "''" <> contents <> literal "''" inlineToMediaWiki (Underline lst) = do contents <- inlineListToMediaWiki lst return $ literal "<u>" <> contents <> literal "</u>" inlineToMediaWiki (Strong lst) = do contents <- inlineListToMediaWiki lst return $ literal "'''" <> contents <> literal "'''" inlineToMediaWiki (Strikeout lst) = do contents <- inlineListToMediaWiki lst return $ literal "<s>" <> contents <> literal "</s>" inlineToMediaWiki (Superscript lst) = do contents <- inlineListToMediaWiki lst return $ literal "<sup>" <> contents <> literal "</sup>" inlineToMediaWiki (Subscript lst) = do contents <- inlineListToMediaWiki lst return $ literal "<sub>" <> contents <> literal "</sub>" inlineToMediaWiki (SmallCaps lst) = inlineListToMediaWiki lst inlineToMediaWiki (Quoted SingleQuote lst) = do contents <- inlineListToMediaWiki lst return $ literal "\8216" <> contents <> literal "\8217" inlineToMediaWiki (Quoted DoubleQuote lst) = do contents <- inlineListToMediaWiki lst return $ literal "\8220" <> contents <> literal "\8221" inlineToMediaWiki (Cite _ lst) = inlineListToMediaWiki lst inlineToMediaWiki (Code _ str) = return $ literal $ "<code>" <> escapeText str <> "</code>" inlineToMediaWiki (Str str) = do inDefLabel <- gets stInDefLabel return $ literal $ if inDefLabel then T.intercalate "<nowiki>:</nowiki>" $ map escapeText $ T.splitOn ":" str else escapeText str 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 $ literal str | f == Format "html" = return $ literal str | otherwise = mempty <$ report (InlineNotRendered il) inlineToMediaWiki LineBreak = return $ literal "<br />" <> cr inlineToMediaWiki SoftBreak = do wrapText <- gets (writerWrapText . stOptions) listlevel <- asks listLevel case wrapText of WrapAuto -> return space WrapNone -> return space WrapPreserve -> if null listlevel then return cr else return space 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 $ literal src _ -> return $ literal $ if isURI src then "[" <> src <> " " <> label' <> "]" else if src == label' then "[[" <> src' <> "]]" 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 let separator = "<nowiki></nowiki>" -- External images (URIs) cannot use [[File:...]] syntax; -- they must be rendered as bare URLs. MediaWiki will auto-embed -- them when $wgAllowExternalImages is enabled. if isURI source then return $ literal $ separator <> source <> separator else do img <- imageToMediaWiki attr alt' <- inlineListToMediaWiki alt let altText = render Nothing alt' let txt = if T.null altText then if T.null tit then "" else tit else altText return $ literal $ "[[" <> T.intercalate "|" (filter (not . T.null) [ "File:" <> source , img , txt ]) <> "]]" inlineToMediaWiki (Note contents) = do contents' <- blockListToMediaWiki contents modify (\s -> s { stNotes = True }) let rendered = render Nothing contents' return $ literal $ "<ref>" <> stripTrailingNewlines rendered <> "</ref>" -- note - does not work for notes with multiple blocks highlightingLangs :: Set.Set Text highlightingLangs = Set.fromList [ "abap", "abl", "abnf", "aconf", "actionscript", "actionscript3", "ada", "ada2005", "ada95", "adl", "agda", "ahk", "alloy", "ambienttalk", "ambienttalk/2", "antlr", "antlr-actionscript", "antlr-as", "antlr-c#", "antlr-cpp", "antlr-csharp", "antlr-java", "antlr-objc", "antlr-perl", "antlr-python", "antlr-rb", "antlr-ruby", "apache", "apacheconf", "apl", "applescript", "arduino", "arexx", "as", "as3", "asm", "aspectj", "aspx-cs", "aspx-vb", "asy", "asymptote", "at", "autohotkey", "autoit", "awk", "b3d", "basemake", "bash", "basic", "bat", "batch", "bbcode", "because", "befunge", "bf", "blitzbasic", "blitzmax", "bmax", "bnf", "boo", "boogie", "bplus", "brainfuck", "bro", "bsdmake", "bugs", "c", "c#", "c++", "c++-objdumb", "c-objdump", "ca65", "cadl", "camkes", "cbmbas", "ceylon", "cf3", "cfc", "cfengine3", "cfg", "cfm", "cfs", "chai", "chaiscript", "chapel", "cheetah", "chpl", "cirru", "cl", "clay", "clipper", "clj", "cljs", "clojure", "clojurescript", "cmake", "cobol", "cobolfree", "coffee", "coffee-script", "coffeescript", "common-lisp", "componentpascal", "console", "control", "coq", "cp", "cpp", "cpp-objdump", "cpsa", "crmsh", "croc", "cry", "cryptol", "csh", "csharp", "csound", "csound-csd", "csound-document", "csound-orc", "csound-sco", "csound-score", "css", "css+django", "css+erb", "css+genshi", "css+genshitext", "css+jinja", "css+lasso", "css+mako", "css+mozpreproc", "css+myghty", "css+php", "css+ruby", "css+smarty", "cu", "cucumber", "cuda", "cxx-objdump", "cypher", "cython", "d", "d-objdump", "dart", "debcontrol", "debsources", "delphi", "dg", "diff", "django", "docker", "dockerfile", "dosbatch", "doscon", "dosini", "dpatch", "dtd", "duby", "duel", "dylan", "dylan-console", "dylan-lid", "dylan-repl", "earl-grey", "earlgrey", "easytrieve", "ebnf", "ec", "ecl", "eg", "eiffel", "elisp", "elixir", "elm", "emacs", "erb", "erl", "erlang", "evoque", "ex", "exs", "ezhil", "f#", "factor", "fan", "fancy", "felix", "fish", "fishshell", "flx", "fortran", "fortranfixed", "foxpro", "fsharp", "fy", "gap", "gas", "gawk", "genshi", "genshitext", "gherkin", "glsl", "gnuplot", "go", "golo", "gooddata-cl", "gosu", "groff", "groovy", "gst", "haml", "handlebars", "haskell", "haxe", "haxeml", "hexdump", "hs", "html", "html+cheetah", "html+django", "html+erb", "html+evoque", "html+genshi", "html+handlebars", "html+jinja", "html+kid", "html+lasso", "html+mako", "html+myghty", "html+php", "html+ruby", "html+smarty", "html+spitfire", "html+twig", "html+velocity", "htmlcheetah", "htmldjango", "http", "hx", "hxml", "hxsl", "hy", "hybris", "hylang", "i6", "i6t", "i7", "idl", "idl4", "idr", "idris", "iex", "igor", "igorpro", "ik", "inform6", "inform7", "ini", "io", "ioke", "irb", "irc", "isabelle", "j", "jade", "jags", "jasmin", "jasminxt", "java", "javascript", "javascript+cheetah", "javascript+django", "javascript+erb", "javascript+genshi", "javascript+genshitext", "javascript+jinja", "javascript+lasso", "javascript+mako", "javascript+mozpreproc", "javascript+myghty", "javascript+php", "javascript+ruby", "javascript+smarty", "javascript+spitfire", "jbst", "jcl", "jinja", "jl", "jlcon", "jproperties", "js", "js+cheetah", "js+django", "js+erb", "js+genshi", "js+genshitext", "js+jinja", "js+lasso", "js+mako", "js+myghty", "js+php", "js+ruby", "js+smarty", "js+spitfire", "json", "json-ld", "jsonld", "jsonml+bst", "jsp", "julia", "kal", "kconfig", "kernel-config", "kid", "koka", "kotlin", "ksh", "lagda", "lasso", "lassoscript", "latex", "lcry", "lcryptol", "lean", "less", "lhaskell", "lhs", "lid", "lidr", "lidris", "lighttpd", "lighty", "limbo", "linux-config", "liquid", "lisp", "literate-agda", "literate-cryptol", "literate-haskell", "literate-idris", "live-script", "livescript", "llvm", "logos", "logtalk", "lsl", "lua", "m2", "make", "makefile", "mako", "man", "maql", "mask", "mason", "mathematica", "matlab", "matlabsession", "mawk", "menuconfig", "mf", "minid", "mma", "modelica", "modula2", "moin", "monkey", "moo", "moocode", "moon", "moonscript", "mozhashpreproc", "mozpercentpreproc", "mq4", "mq5", "mql", "mql4", "mql5", "msc", "mscgen", "mupad", "mxml", "myghty", "mysql", "nasm", "nawk", "nb", "nemerle", "nesc", "newlisp", "newspeak", "nginx", "nim", "nimrod", "nit", "nix", "nixos", "nroff", "nsh", "nsi", "nsis", "numpy", "obj-c", "obj-c++", "obj-j", "objc", "objc++", "objdump", "objdump-nasm", "objective-c", "objective-c++", "objective-j", "objectivec", "objectivec++", "objectivej", "objectpascal", "objj", "ocaml", "octave", "odin", "ooc", "opa", "openbugs", "openedge", "pacmanconf", "pan", "parasail", "pas", "pascal", "pawn", "pcmk", "perl", "perl6", "php", "php3", "php4", "php5", "pig", "pike", "pkgconfig", "pl", "pl6", "plpgsql", "po", "posh", "postgres", "postgres-console", "postgresql", "postgresql-console", "postscr", "postscript", "pot", "pov", "powershell", "praat", "progress", "prolog", "properties", "proto", "protobuf", "ps1", "ps1con", "psm1", "psql", "puppet", "py", "py3", "py3tb", "pycon", "pypy", "pypylog", "pyrex", "pytb", "python", "python3", "pyx", "qbasic", "qbs", "qml", "qvt", "qvto", "r", "racket", "ragel", "ragel-c", "ragel-cpp", "ragel-d", "ragel-em", "ragel-java", "ragel-objc", "ragel-rb", "ragel-ruby", "raw", "rb", "rbcon", "rconsole", "rd", "rebol", "red", "red/system", "redcode", "registry", "resource", "resourcebundle", "rest", "restructuredtext", "rexx", "rhtml", "rkt", "roboconf-graph", "roboconf-instances", "robotframework", "rout", "rql", "rsl", "rst", "rts", "ruby", "rust", "s", "sage", "salt", "sass", "sc", "scala", "scaml", "scheme", "scilab", "scm", "scss", "sh", "shell", "shell-session", "shen", "slim", "sls", "smali", "smalltalk", "smarty", "sml", "snobol", "sources.list", "sourceslist", "sp", "sparql", "spec", "spitfire", "splus", "sql", "sqlite3", "squeak", "squid", "squid.conf", "squidconf", "ssp", "st", "stan", "supercollider", "sv", "swift", "swig", "systemverilog", "tads3", "tap", "tcl", "tcsh", "tcshcon", "tea", "termcap", "terminfo", "terraform", "tex", "text", "tf", "thrift", "todotxt", "trac-wiki", "trafficscript", "treetop", "ts", "turtle", "twig", "typescript", "udiff", "urbiscript", "v", "vala", "vapi", "vb.net", "vbnet", "vctreestatus", "velocity", "verilog", "vfp", "vgl", "vhdl", "vim", "winbatch", "winbugs", "x10", "xbase", "xml", "xml+cheetah", "xml+django", "xml+erb", "xml+evoque", "xml+genshi", "xml+jinja", "xml+kid", "xml+lasso", "xml+mako", "xml+myghty", "xml+php", "xml+ruby", "xml+smarty", "xml+spitfire", "xml+velocity", "xq", "xql", "xqm", "xquery", "xqy", "xslt", "xten", "xtend", "xul+mozpreproc", "yaml", "yaml+jinja", "zephir" ] startsWithListMarker :: Text -> Bool startsWithListMarker t = case T.uncons t of Nothing -> False Just (c,_) -> c == '#' || c == ':' || c == ';' || c == '*' ================================================ FILE: src/Text/Pandoc/Writers/Ms.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.Ms Copyright : Copyright (C) 2007-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to roff ms format. TODO: [ ] use base URL to construct absolute URLs from relative ones for external links [ ] is there a better way to do strikeout? [ ] tight/loose list distinction -} module Text.Pandoc.Writers.Ms ( writeMs ) where import Control.Monad.State.Strict ( gets, modify, evalStateT ) import Control.Monad ( MonadPlus(mplus), liftM, unless, forM ) import Data.Containers.ListUtils (nubOrd) import Data.Char (isAscii, isLower, isUpper, ord) import Data.List (intercalate, intersperse) import Data.List.NonEmpty (nonEmpty) import qualified Data.Map as Map import Data.Maybe (catMaybes, isNothing) import Data.Text (Text) import qualified Data.Text as T import Network.URI (escapeURIString, isAllowedInURI) import Skylighting import System.FilePath (takeExtension) import Text.Pandoc.Asciify (toAsciiChar) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout hiding (Color) import Text.DocTemplates (lookupContext) import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Pandoc.Writers.Roff import Text.Pandoc.Writers.Markdown (writePlain) import Text.Printf (printf) import Text.TeXMath (writeEqn) import qualified Data.Text.Encoding as TE import qualified Data.ByteString as B -- | Convert Pandoc to Ms. writeMs :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMs opts document = evalStateT (pandocToMs opts document) defaultWriterState -- | Return roff ms representation of document. pandocToMs :: PandocMonad m => WriterOptions -> Pandoc -> MS m Text pandocToMs opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing title <- chomp <$> inlineListToMs' opts (lookupMetaInlines "title" meta) metadata <- metaToContext opts (blockListToMs opts) (fmap chomp . inlineListToMs' opts) meta main <- blockListToMs opts blocks hasInlineMath <- gets stHasInlineMath let titleMeta = (escapeStr opts . stringify) $ docTitle meta let authorsMeta = map (escapeStr opts . stringify) $ docAuthors meta hasHighlighting <- gets stHighlighting let highlightingMacros = if hasHighlighting then case writerHighlightMethod opts of Skylighting sty -> styleToMs sty _ -> mempty else mempty let context = defField "body" main $ defField "has-inline-math" hasInlineMath $ defField "hyphenate" True $ defField "toc" (writerTableOfContents opts) $ defField "title-meta" titleMeta $ defField "author-meta" (T.intercalate "; " authorsMeta) $ defField "highlighting-macros" highlightingMacros $ resetField "title" title metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context escapeStr :: WriterOptions -> Text -> Text escapeStr opts = escapeString False (if writerPreferAscii opts then AsciiOnly else AllowUTF8) -- In PDFs we need to escape parentheses and backslash. -- In PDF we need to encode as UTF-16 BE. escapePDFString :: Text -> Text escapePDFString t | T.all (\c -> isAscii c && c /= '(' && c /= ')' && c /= '\\' && c /= '"') t = t | otherwise = ("\\376\\377" <>) . -- add bom mconcat . map encodeChar . T.unpack $ t where encodeChar c = if isAscii c && c /= '\\' && c /= '(' && c /= ')' then "\\000" <> T.singleton c else mconcat . map toOctal . B.unpack . TE.encodeUtf16BE $ T.singleton c toOctal n = "\\" <> T.pack (printf "%03o" n) escapeUri :: Text -> Text escapeUri = T.pack . escapeURIString (\c -> c /= '@' && isAllowedInURI c) . T.unpack toSmallCaps :: WriterOptions -> Text -> Text toSmallCaps opts s = case T.uncons s of Nothing -> "" Just (c, cs) | isLower c -> let (lowers,rest) = T.span isLower s in "\\s-2" <> escapeStr opts (T.toUpper lowers) <> "\\s0" <> toSmallCaps opts rest | isUpper c -> let (uppers,rest) = T.span isUpper s in escapeStr opts uppers <> toSmallCaps opts rest | otherwise -> escapeStr opts (T.singleton c) <> toSmallCaps opts cs -- We split inline lists into sentences, and print one sentence per -- line. roff treats the line-ending period differently. -- See http://code.google.com/p/pandoc/issues/detail?id=148. getPdfEngine :: WriterOptions -> Maybe Text getPdfEngine opts = lookupContext "pdf-engine" (writerVariables opts) blockToMs :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> MS m (Doc Text) blockToMs opts (Div (ident,cls,kvs) bs) = do let anchor = if isNothing (getPdfEngine opts) || T.null ident then empty else nowrap $ literal ".pdfhref M " <> doubleQuotes (literal (toAscii ident)) case cls of _ | "csl-entry" `elem` cls -> (".CSLENTRY" $$) . vcat <$> mapM (cslEntryToMs True opts) bs | "csl-bib-body" `elem` cls -> do res <- blockListToMs opts bs return $ anchor $$ -- so that XP paragraphs are indented: ".nr PI 3n" $$ -- space between entries ".de CSLENTRY" $$ (case lookup "entry-spacing" kvs >>= safeRead of Just n | n > (0 :: Int) -> ".sp" _ -> mempty) $$ ".." $$ ".de CSLP" $$ (if "hanging-indent" `elem` cls then ".XP" else ".LP") $$ ".." $$ res _ -> do setFirstPara res <- blockListToMs opts bs setFirstPara return $ anchor $$ res blockToMs opts (Plain inlines) = splitSentences <$> inlineListToMs' opts inlines blockToMs opts (Para inlines) = do firstPara <- gets stFirstPara resetFirstPara contents <- inlineListToMs' opts inlines return $ literal (if firstPara then ".LP" else ".PP") $$ splitSentences contents blockToMs _ b@(RawBlock f str) | f == Format "ms" = return $ literal str | otherwise = do report $ BlockNotRendered b return empty blockToMs _ HorizontalRule = do resetFirstPara return $ literal ".HLINE" blockToMs opts (Header level (ident,classes,_) inlines) = do setFirstPara modify $ \st -> st{ stInHeader = True } let inlines' = map breakToSpace inlines contents <- inlineListToMs' opts inlines' plainContents <- inlinesToPlain inlines' modify $ \st -> st{ stInHeader = False } let (heading, secnum) = if writerNumberSections opts && "unnumbered" `notElem` classes then (".NH", "\\*[SN]") else (".SH", "") let mbPdfEngine = getPdfEngine opts let anchor = if T.null ident then empty else nowrap $ literal ".pdfhref M " <> doubleQuotes (literal (toAscii ident)) let bookmark = literal ".pdfhref O " <> literal (tshow level <> " ") <> nowrap (doubleQuotes (literal $ secnum <> (if T.null secnum then "" else " ") <> (case mbPdfEngine of Just "groff" -> id _ -> escapePDFString) plainContents)) let backlink = nowrap (literal ".pdfhref L -D " <> doubleQuotes (literal (toAscii ident)) <> space <> literal "\\") <> cr <> literal " -- " let tocEntry = if writerTableOfContents opts && level <= writerTOCDepth opts then literal ".XS" $$ backlink <> doubleQuotes ( nowrap (literal (T.replicate level "\t") <> (if T.null secnum then empty else literal secnum <> literal "\\~\\~") <> contents)) $$ literal ".XE" else empty modify $ \st -> st{ stFirstPara = True } return $ (literal heading <> space <> literal (tshow level)) $$ contents $$ case mbPdfEngine of Nothing -> mempty Just _ -> bookmark $$ anchor $$ tocEntry blockToMs opts (CodeBlock attr str) = do hlCode <- highlightCode opts attr str setFirstPara return $ literal ".IP" $$ literal ".nf" $$ literal "\\f[C]" $$ ((case T.uncons str of Just ('.',_) -> literal "\\&" _ -> mempty) <> hlCode) $$ literal "\\f[]" $$ literal ".fi" blockToMs opts (LineBlock ls) = do setFirstPara -- use .LP, see #5588 blockToMs opts $ Para $ intercalate [LineBreak] ls blockToMs opts (BlockQuote blocks) = do setFirstPara contents <- blockListToMs opts blocks setFirstPara return $ literal ".QS" $$ contents $$ literal ".QE" blockToMs opts (Table _ blkCapt specs thead tbody tfoot) = let (caption, alignments, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot aligncode AlignLeft = "l" aligncode AlignRight = "r" aligncode AlignCenter = "c" aligncode AlignDefault = "l" in do caption' <- inlineListToMs' opts caption let isSimple = all (== 0) widths let totalWidth = 70 -- 78n default width - 8n indent = 70n let coldescriptions = literal $ T.unwords (zipWith (\align width -> aligncode align <> if width == 0 then "" else T.pack $ printf "w(%0.1fn)" (totalWidth * width)) alignments widths) <> "." colheadings <- mapM (blockListToMs opts) headers let makeRow cols = literal "T{" $$ vcat (intersperse (literal "T}\tT{") cols) $$ literal "T}" let colheadings' = if all null headers then empty else makeRow colheadings $$ char '_' body <- mapM (\row -> do cols <- mapM (\(cell, w) -> (if isSimple then id else (literal (".nr LL " <> T.pack (printf "%0.1fn" (w * totalWidth))) $$)) <$> blockListToMs opts cell) (zip row widths) return $ makeRow cols) rows setFirstPara return $ literal ".PP" $$ caption' $$ literal ".na" $$ -- we don't want justification in table cells (if isSimple then "" else ".nr LLold \\n[LL]") $$ literal ".TS" $$ literal "delim(@@) tab(\t);" $$ coldescriptions $$ colheadings' $$ vcat body $$ literal ".TE" $$ (if isSimple then "" else ".nr LL \\n[LLold]") $$ literal ".ad" blockToMs opts (BulletList items) = do contents <- mapM (bulletListItemToMs opts) items setFirstPara return (vcat contents) blockToMs opts (OrderedList attribs items) = do let markers = take (length items) $ orderedListMarkers attribs let indent = 2 + maybe 0 maximum (nonEmpty (map T.length markers)) contents <- mapM (\(num, item) -> orderedListItemToMs opts num indent item) $ zip markers items setFirstPara return (vcat contents) blockToMs opts (DefinitionList items) = do contents <- mapM (definitionListItemToMs opts) items setFirstPara return (vcat contents) blockToMs opts (Figure figattr (Caption _ caption) body) = case body of [Plain [ Image attr _alt (src, _tit) ]] -> do let ext = takeExtension (T.unpack src) let sizeAttrs = getSizeAttrs opts attr capt <- blockToMs opts (Div figattr caption) let captlines = height capt let cmd = case ext of ".ps" -> ".PSPIC" ".eps" -> ".PSPIC" ".pdf" -> ".PDFPIC" _ -> "\\\" .IMAGE" return $ nowrap (literal cmd <+> doubleQuotes (literal src) <> sizeAttrs) $$ literal (".ce " <> tshow captlines) $$ capt $$ literal ".sp 1" _ -> blockToMs opts $ Div figattr body -- | Convert bullet list item (list of blocks) to ms. bulletListItemToMs :: PandocMonad m => WriterOptions -> [Block] -> MS m (Doc Text) bulletListItemToMs _ [] = return empty bulletListItemToMs opts (Para first:rest) = bulletListItemToMs opts (Plain first:rest) bulletListItemToMs opts (Plain first:rest) = do first' <- blockToMs opts (Plain first) rest' <- blockListToMs opts rest let first'' = literal ".IP \\(bu 3" $$ first' let rest'' = if null rest then empty else literal ".RS 3" $$ rest' $$ literal ".RE" return (first'' $$ rest'') bulletListItemToMs opts (first:rest) = do first' <- blockToMs opts first rest' <- blockListToMs opts rest return $ literal "\\(bu .RS 3" $$ first' $$ rest' $$ literal ".RE" -- | Convert ordered list item (a list of blocks) to ms. orderedListItemToMs :: PandocMonad m => WriterOptions -- ^ options -> Text -- ^ order marker for list item -> Int -- ^ number of spaces to indent -> [Block] -- ^ list item (list of blocks) -> MS m (Doc Text) orderedListItemToMs _ _ _ [] = return empty orderedListItemToMs opts num indent (Para first:rest) = orderedListItemToMs opts num indent (Plain first:rest) orderedListItemToMs opts num indent (first:rest) = do first' <- blockToMs opts first rest' <- blockListToMs opts rest let num' = T.pack $ printf ("%" <> show (indent - 1) <> "s") num let first'' = literal (".IP \"" <> num' <> "\" " <> tshow indent) $$ first' let rest'' = if null rest then empty else literal ".RS " <> literal (tshow indent) $$ rest' $$ literal ".RE" return $ first'' $$ rest'' -- | Convert definition list item (label, list of blocks) to ms. definitionListItemToMs :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> MS m (Doc Text) definitionListItemToMs opts (label, defs) = do labelText <- withFontFeature 'B' $ inlineListToMs' opts $ map breakToSpace label contents <- if null defs then return empty else liftM vcat $ forM defs $ \blocks -> do let (first, rest) = case blocks of (Para x:y) -> (Plain x,y) (x:y) -> (x,y) [] -> (Plain [], []) -- should not happen rest' <- liftM vcat $ mapM (\item -> blockToMs opts item) rest first' <- blockToMs opts first return $ first' $$ literal ".RS 3" $$ rest' $$ literal ".RE" return $ nowrap (literal ".IP " <> doubleQuotes labelText <> " 3") $$ contents -- | Convert list of Pandoc block elements to ms. blockListToMs :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements -> MS m (Doc Text) blockListToMs opts blocks = vcat <$> mapM (blockToMs opts) blocks -- | Convert list of Pandoc inline elements to ms. inlineListToMs :: PandocMonad m => WriterOptions -> [Inline] -> MS m (Doc Text) -- if list starts with ., insert a zero-width character \& so it -- won't be interpreted as markup if it falls at the beginning of a line. inlineListToMs opts lst = hcat <$> mapM (inlineToMs opts) lst -- This version to be used when there is no further inline content; -- forces a note at the end. inlineListToMs' :: PandocMonad m => WriterOptions -> [Inline] -> MS m (Doc Text) inlineListToMs' opts lst = do x <- hcat <$> mapM (inlineToMs opts) lst y <- handleNotes opts empty return $ x <> y -- | Convert Pandoc inline element to ms. inlineToMs :: PandocMonad m => WriterOptions -> Inline -> MS m (Doc Text) inlineToMs opts (Span _ ils) = inlineListToMs opts ils inlineToMs opts (Emph lst) = withFontFeature 'I' (inlineListToMs opts lst) inlineToMs opts (Underline lst) = inlineToMs opts (Emph lst) inlineToMs opts (Strong lst) = withFontFeature 'B' (inlineListToMs opts lst) inlineToMs opts (Strikeout lst) = do contents <- inlineListToMs opts lst -- we use grey color instead of strikeout, which seems quite -- hard to do in roff for arbitrary bits of text return $ literal "\\m[strikecolor]" <> contents <> literal "\\m[]" inlineToMs opts (Superscript lst) = do contents <- inlineListToMs opts lst return $ literal "\\*{" <> contents <> literal "\\*}" inlineToMs opts (Subscript lst) = do contents <- inlineListToMs opts lst return $ literal "\\*<" <> contents <> literal "\\*>" inlineToMs opts (SmallCaps lst) = do -- see https://lists.gnu.org/archive/html/groff/2015-01/msg00016.html modify $ \st -> st{ stSmallCaps = not (stSmallCaps st) } res <- inlineListToMs opts lst modify $ \st -> st{ stSmallCaps = not (stSmallCaps st) } return res inlineToMs opts (Quoted SingleQuote lst) = do contents <- inlineListToMs opts lst return $ char '`' <> contents <> char '\'' inlineToMs opts (Quoted DoubleQuote lst) = do contents <- inlineListToMs opts lst return $ literal "\\(lq" <> contents <> literal "\\(rq" inlineToMs opts (Cite _ lst) = inlineListToMs opts lst inlineToMs opts (Code attr str) = do hlCode <- highlightCode opts attr str withFontFeature 'C' (return hlCode) inlineToMs opts (Str str) = do let shim = case T.uncons str of Just ('.',_) -> afterBreak "\\&" _ -> empty smallcaps <- gets stSmallCaps if smallcaps then return $ shim <> literal (toSmallCaps opts str) else return $ shim <> literal (escapeStr opts str) inlineToMs opts (Math InlineMath str) = do modify $ \st -> st{ stHasInlineMath = True } res <- convertMath writeEqn InlineMath str case res of Left il -> inlineToMs opts il Right r -> return $ literal "@" <> literal r <> literal "@" inlineToMs opts (Math DisplayMath str) = do res <- convertMath writeEqn DisplayMath str case res of Left il -> do contents <- inlineToMs opts il return $ cr <> literal ".RS 3" $$ contents $$ literal ".RE" Right r -> return $ cr <> literal ".EQ" $$ literal r $$ literal ".EN" <> cr inlineToMs _ il@(RawInline f str) | f == Format "ms" = return $ literal str | otherwise = do report $ InlineNotRendered il return empty inlineToMs _ LineBreak = return $ cr <> literal ".br" <> cr inlineToMs opts SoftBreak = handleNotes opts $ case writerWrapText opts of WrapAuto -> space WrapNone -> space WrapPreserve -> cr inlineToMs opts Space = handleNotes opts space inlineToMs opts (Link _ txt (T.uncons -> Just ('#',ident), _)) = do -- internal link contents <- inlineListToMs' opts $ map breakToSpace txt return $ case getPdfEngine opts of Just _ -> literal "\\c" <> cr <> nowrap (literal ".pdfhref L -D " <> doubleQuotes (literal (toAscii ident)) <> literal " -A " <> doubleQuotes (literal "\\c") <> space <> literal "\\") <> cr <> literal " -- " <> doubleQuotes (nowrap contents) <> cr <> literal "\\&" Nothing -> contents inlineToMs opts (Link _ txt (src, _)) = do -- external link contents <- inlineListToMs' opts $ map breakToSpace txt return $ case getPdfEngine opts of Just _ -> literal "\\c" <> cr <> nowrap (literal ".pdfhref W -D " <> doubleQuotes (literal (escapeUri src)) <> literal " -A " <> doubleQuotes (literal "\\c") <> space <> literal "\\") <> cr <> literal " -- " <> doubleQuotes (nowrap contents) <> cr <> literal "\\&" Nothing -> contents inlineToMs opts (Image attr alternate (src, _)) = do let desc = literal "[IMAGE: " <> literal (escapeStr opts (stringify alternate)) <> char ']' let sizeAttrs = getSizeAttrs opts attr let ext = takeExtension (T.unpack src) let cmd = case ext of ".ps" -> ".PSPIC" ".eps" -> ".PSPIC" ".pdf" -> ".PDFPIC" _ -> "" return $ cr <> nowrap (if T.null cmd then desc <> " \\\" " <> doubleQuotes (literal src) <> sizeAttrs else literal cmd <+> doubleQuotes (literal src) <> sizeAttrs) <> cr inlineToMs _ (Note contents) = do modify $ \st -> st{ stNotes = contents : stNotes st } return $ literal "\\**" cslEntryToMs :: PandocMonad m => Bool -> WriterOptions -> Block -> MS m (Doc Text) cslEntryToMs atStart opts (Para xs) = case xs of (Span ("",["csl-left-margin"],[]) lils : rest@(Span ("",["csl-right-inline"],[]) _ : _)) -> do lils' <- inlineListToMs' opts lils ((cr <> literal ".IP " <> doubleQuotes (nowrap lils') <> literal " 5") $$) <$> cslEntryToMs False opts (Para rest) (Span ("",["csl-block"],[]) ils : rest) -> ((cr <> literal ".LP") $$) <$> cslEntryToMs False opts (Para (ils ++ rest)) (Span ("",["csl-left-margin"],[]) ils : rest) -> ((cr <> literal ".LP") $$) <$> cslEntryToMs False opts (Para (ils ++ rest)) (Span ("",["csl-indented"],[]) ils : rest) -> ((cr <> literal ".LP") $$) <$> cslEntryToMs False opts (Para (ils ++ rest)) _ | atStart -> (".CSLP" $$) <$> cslEntryToMs False opts (Para xs) | otherwise -> case xs of [] -> return mempty (x:rest) -> (<>) <$> inlineToMs opts x <*> cslEntryToMs False opts (Para rest) cslEntryToMs _ opts x = blockToMs opts x handleNotes :: PandocMonad m => WriterOptions -> Doc Text -> MS m (Doc Text) handleNotes opts fallback = do notes <- gets stNotes if null notes then return fallback else do modify $ \st -> st{ stNotes = [] } vcat <$> mapM (handleNote opts) notes handleNote :: PandocMonad m => WriterOptions -> Note -> MS m (Doc Text) handleNote opts bs = do -- don't start with Paragraph or we'll get a spurious blank -- line after the note ref: let bs' = case bs of (Para ils : rest) -> Plain ils : rest _ -> bs contents <- blockListToMs opts bs' return $ cr <> literal ".FS" $$ contents $$ literal ".FE" <> cr setFirstPara :: PandocMonad m => MS m () setFirstPara = modify $ \st -> st{ stFirstPara = True } resetFirstPara :: PandocMonad m => MS m () resetFirstPara = modify $ \st -> st{ stFirstPara = False } breakToSpace :: Inline -> Inline breakToSpace SoftBreak = Space breakToSpace LineBreak = Space breakToSpace x = x -- Highlighting styleToMs :: Style -> Doc Text styleToMs sty = vcat $ colordefs <> map (toMacro sty) alltoktypes where alltoktypes = enumFromTo KeywordTok NormalTok colordefs = map toColorDef allcolors toColorDef c = literal (".defcolor " <> hexColor c <> " rgb #" <> hexColor c) allcolors = catMaybes $ nubOrd $ [defaultColor sty, backgroundColor sty, lineNumberColor sty, lineNumberBackgroundColor sty] <> concatMap (colorsForToken. snd) (Map.toList (tokenStyles sty)) colorsForToken ts = [tokenColor ts, tokenBackground ts] hexColor :: Color -> Text hexColor (RGB r g b) = T.pack $ printf "%02x%02x%02x" r g b toMacro :: Style -> TokenType -> Doc Text toMacro sty toktype = nowrap (literal ".ds " <> literal (tshow toktype) <> literal " \\&" <> setbg <> setcolor <> setfont <> literal "\\\\$1" <> resetfont <> resetcolor <> resetbg) where setcolor = maybe empty fgcol tokCol resetcolor = maybe empty (const $ literal "\\\\m[]") tokCol setbg = empty -- maybe empty bgcol tokBg resetbg = empty -- maybe empty (const $ text "\\\\M[]") tokBg fgcol c = literal $ "\\\\m[" <> hexColor c <> "]" -- bgcol c = literal $ "\\\\M[" <> hexColor c <> "]" setfont = if tokBold || tokItalic then literal $ T.pack $ "\\\\f[C" <> ['B' | tokBold] <> ['I' | tokItalic] <> "]" else empty resetfont = if tokBold || tokItalic then literal "\\\\f[C]" else empty tokSty = Map.lookup toktype (tokenStyles sty) tokCol = (tokSty >>= tokenColor) `mplus` defaultColor sty -- tokBg = (tokSty >>= tokenBackground) `mplus` backgroundColor sty tokBold = maybe False tokenBold tokSty tokItalic = maybe False tokenItalic tokSty -- tokUnderline = fromMaybe False (tokSty >>= tokUnderline) -- lnColor = lineNumberColor sty -- lnBkgColor = lineNumberBackgroundColor sty msFormatter :: WriterOptions -> FormatOptions -> [SourceLine] -> Doc Text msFormatter opts _fmtopts = literal . T.intercalate "\n" . map fmtLine where fmtLine = mconcat . map fmtToken fmtToken (toktype, tok) = "\\*[" <> tshow toktype <> " \"" <> escapeStr opts tok <> "\"]" highlightCode :: PandocMonad m => WriterOptions -> Attr -> Text -> MS m (Doc Text) highlightCode opts attr str = case highlight (writerSyntaxMap opts) (msFormatter opts) attr str of Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg return $ literal (escapeStr opts str) Right h -> do modify (\st -> st{ stHighlighting = True }) return h -- This is used for PDF anchors. toAscii :: Text -> Text toAscii = T.concatMap (\c -> case toAsciiChar c of Nothing -> "_u" <> tshow (ord c) <> "_" Just '/' -> "_u" <> tshow (ord c) <> "_" -- see #4515 Just c' -> T.singleton c') getSizeAttrs :: WriterOptions -> Attr -> Doc Text getSizeAttrs opts attr = case (mbW, mbH) of (Just wp, Nothing) -> space <> doubleQuotes (literal (tshow (floor wp :: Int) <> "p")) (Just wp, Just hp) -> space <> doubleQuotes (literal (tshow (floor wp :: Int) <> "p")) <> space <> doubleQuotes (literal (tshow (floor hp :: Int) <> "p")) _ -> empty where mbW = inPoints opts <$> dimension Width attr mbH = inPoints opts <$> dimension Height attr inlinesToPlain :: PandocMonad m => [Inline] -> m Text inlinesToPlain ils = T.strip <$> writePlain def{ writerWrapText = WrapNone } (Pandoc nullMeta [Plain ils]) ================================================ FILE: src/Text/Pandoc/Writers/Muse.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.Muse Copyright : Copyright (C) 2017-2020 Alexander Krotov License : GNU GPL, version 2 or above Maintainer : Alexander Krotov <ilabdsf@gmail.com> Stability : stable Portability : portable Conversion of 'Pandoc' documents to Muse. This module is mostly intended for <https://amusewiki.org/ Amusewiki> markup support, as described by <https://amusewiki.org/library/manual Text::Amuse markup manual>. Original <https://www.gnu.org/software/emacs-muse/ Emacs Muse> markup support is a secondary goal. Where Text::Amuse markup <https://metacpan.org/pod/Text::Amuse#DIFFERENCES-WITH-THE-ORIGINAL-EMACS-MUSE-MARKUP differs> from <https://www.gnu.org/software/emacs-muse/manual/ Emacs Muse markup>, Text::Amuse markup is supported. For example, native tables are always used instead of Org Mode tables. However, @\<literal style="html">@ tag is used for HTML raw blocks even though it is supported only in Emacs Muse. -} module Text.Pandoc.Writers.Muse (writeMuse) where import Control.Monad (zipWithM) import Control.Monad.Except (throwError) import Control.Monad.Reader ( asks, MonadReader(local), ReaderT(runReaderT) ) import Control.Monad.State.Strict ( StateT, gets, modify, evalStateT ) import Data.Char (isAlphaNum, isAsciiLower, isAsciiUpper, isDigit, isSpace) import Data.Default import Data.List (intersperse, transpose) import Data.List.NonEmpty (nonEmpty, NonEmpty(..)) import qualified Data.Set as Set import qualified Data.Text as T import Data.Text (Text) import System.FilePath (takeExtension) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.ImageSize import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared type Notes = [[Block]] type Muse m = ReaderT WriterEnv (StateT WriterState m) data WriterEnv = WriterEnv { envOptions :: WriterOptions , envTopLevel :: Bool , envInsideBlock :: Bool , envInlineStart :: Bool -- ^ True if there is only whitespace since last newline , envInsideLinkDescription :: Bool -- ^ Escape ] if True , envAfterSpace :: Bool -- ^ There is whitespace (not just newline) before , envOneLine :: Bool -- ^ True if newlines are not allowed , envInsideAsterisks :: Bool -- ^ True if outer element is emphasis with asterisks , envNearAsterisks :: Bool -- ^ Rendering inline near asterisks } data WriterState = WriterState { stNotes :: Notes , stNoteNum :: Int , stIds :: Set.Set Text , stUseTags :: Bool -- ^ Use tags for emphasis, for example because previous character is a letter } instance Default WriterState where def = WriterState { stNotes = [] , stNoteNum = 1 , stIds = Set.empty , stUseTags = False } evalMuse :: PandocMonad m => Muse m a -> WriterEnv -> WriterState -> m a evalMuse document env = evalStateT $ runReaderT document env -- | Convert Pandoc to Muse. writeMuse :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMuse opts document = evalMuse (pandocToMuse document) env def where env = WriterEnv { envOptions = opts , envTopLevel = True , envInsideBlock = False , envInlineStart = True , envInsideLinkDescription = False , envAfterSpace = False , envOneLine = False , envInsideAsterisks = False , envNearAsterisks = False } -- | Return Muse representation of document. pandocToMuse :: PandocMonad m => Pandoc -> Muse m Text pandocToMuse (Pandoc meta blocks) = do opts <- asks envOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing metadata <- metaToContext opts blockListToMuse (fmap chomp . inlineListToMuse) meta body <- blockListToMuse blocks notes <- currentNotesToMuse let main = body $+$ notes let context = defField "body" main metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Helper function for flatBlockListToMuse -- | Render all blocks and insert blank lines between the first two catWithBlankLines :: PandocMonad m => [Block] -- ^ List of block elements -> Int -- ^ Number of blank lines -> Muse m (Doc Text) catWithBlankLines (b : bs) n = do b' <- blockToMuseWithNotes b bs' <- flatBlockListToMuse bs return $ b' <> blanklines n <> bs' catWithBlankLines _ _ = error "Expected at least one block" -- | Convert list of Pandoc block elements to Muse -- | without setting envTopLevel. flatBlockListToMuse :: PandocMonad m => [Block] -- ^ List of block elements -> Muse m (Doc Text) flatBlockListToMuse bs@(BulletList _ : BulletList _ : _) = catWithBlankLines bs 2 flatBlockListToMuse bs@(OrderedList (_, style1, _) _ : OrderedList (_, style2, _) _ : _) = catWithBlankLines bs (if style1' == style2' then 2 else 0) where style1' = normalizeStyle style1 style2' = normalizeStyle style2 normalizeStyle DefaultStyle = Decimal normalizeStyle s = s flatBlockListToMuse bs@(DefinitionList _ : DefinitionList _ : _) = catWithBlankLines bs 2 flatBlockListToMuse bs@(_ : _) = catWithBlankLines bs 0 flatBlockListToMuse [] = return mempty simpleTable :: PandocMonad m => [Inline] -> [[Block]] -> [[[Block]]] -> Muse m (Doc Text) simpleTable caption headers rows = do topLevel <- asks envTopLevel caption' <- inlineListToMuse caption headers' <- mapM blockListToMuse headers rows' <- mapM (mapM blockListToMuse) rows let widthsInChars = maybe 0 maximum . nonEmpty . map offset <$> transpose (headers' : rows') let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks where sep' = lblock (T.length sep) $ literal sep let makeRow sep = hpipeBlocks sep . zipWith lblock widthsInChars let head' = makeRow " || " headers' rows'' <- mapM (\row -> makeRow rowSeparator <$> mapM blockListToMuse row) rows let body = vcat rows'' return $ (if topLevel then nest 1 else id) ((if noHeaders then empty else head') $$ body $$ (if null caption then empty else "|+ " <> caption' <> " +|")) $$ blankline where noHeaders = all null headers rowSeparator = if noHeaders then " | " else " | " -- | Convert list of Pandoc block elements to Muse. blockListToMuse :: PandocMonad m => [Block] -- ^ List of block elements -> Muse m (Doc Text) blockListToMuse = local (\env -> env { envTopLevel = not (envInsideBlock env) , envInsideBlock = True }) . flatBlockListToMuse -- | Convert Pandoc block element to Muse. blockToMuse :: PandocMonad m => Block -- ^ Block element -> Muse m (Doc Text) blockToMuse (Plain inlines) = inlineListToMuse' inlines blockToMuse (Para inlines) = do contents <- inlineListToMuse' inlines return $ contents <> blankline blockToMuse (LineBlock lns) = do lns' <- local (\env -> env { envOneLine = True }) $ mapM inlineListToMuse lns return $ nowrap $ vcat (map (literal "> " <>) lns') <> blankline blockToMuse (CodeBlock (_,_,_) str) = return $ "<example>" $$ literal str $$ "</example>" $$ blankline blockToMuse (RawBlock (Format format) str) = return $ blankline $$ "<literal style=\"" <> literal format <> "\">" $$ literal str $$ "</literal>" $$ blankline blockToMuse (BlockQuote blocks) = do contents <- flatBlockListToMuse blocks return $ blankline <> "<quote>" $$ nest 0 contents -- nest 0 to remove trailing blank lines $$ "</quote>" <> blankline blockToMuse (OrderedList (start, style, _) items) = do let markers = take (length items) $ orderedListMarkers (start, style, Period) contents <- zipWithM orderedListItemToMuse markers items topLevel <- asks envTopLevel return $ (if topLevel then nest 1 else id) (vcat contents) $$ blankline where orderedListItemToMuse :: PandocMonad m => Text -- marker for list item -> [Block] -- list item (list of blocks) -> Muse m (Doc Text) orderedListItemToMuse marker item = hang (T.length marker + 1) (literal marker <> space) <$> blockListToMuse item blockToMuse (BulletList items) = do contents <- mapM bulletListItemToMuse items topLevel <- asks envTopLevel return $ (if topLevel then nest 1 else id) (vcat contents) $$ blankline where bulletListItemToMuse :: PandocMonad m => [Block] -> Muse m (Doc Text) bulletListItemToMuse item = do modify $ \st -> st { stUseTags = False } hang 2 "- " <$> blockListToMuse item blockToMuse (DefinitionList items) = do contents <- mapM definitionListItemToMuse items topLevel <- asks envTopLevel return $ (if topLevel then nest 1 else id) (vcat contents) $$ blankline where definitionListItemToMuse :: PandocMonad m => ([Inline], [[Block]]) -> Muse m (Doc Text) definitionListItemToMuse (label, defs) = do modify $ \st -> st { stUseTags = False } label' <- local (\env -> env { envOneLine = True, envAfterSpace = True }) $ inlineListToMuse' label let ind = offset' label' -- using Text.DocLayout.offset results in round trip failures hang ind (nowrap label') . vcat <$> mapM descriptionToMuse defs where offset' d = maximum (0 :| map T.length (T.lines $ render Nothing d)) descriptionToMuse :: PandocMonad m => [Block] -> Muse m (Doc Text) descriptionToMuse desc = hang 4 " :: " <$> blockListToMuse desc blockToMuse (Header level (ident,_,_) inlines) = do opts <- asks envOptions topLevel <- asks envTopLevel contents <- local (\env -> env { envOneLine = True }) $ inlineListToMuse' inlines ids <- gets stIds let autoId = uniqueIdent (writerExtensions opts) inlines ids modify $ \st -> st{ stIds = Set.insert autoId ids } let attr' = if T.null ident || (isEnabled Ext_auto_identifiers opts && ident == autoId) then empty else "#" <> literal ident <> cr let header' = if topLevel then literal (T.replicate level "*") <> space else mempty return $ blankline <> attr' $$ nowrap (header' <> contents) <> blankline -- https://www.gnu.org/software/emacs-muse/manual/muse.html#Horizontal-Rules-and-Anchors blockToMuse HorizontalRule = return $ blankline $$ "----" $$ blankline blockToMuse (Table _ blkCapt specs thead@(TableHead hattr hrows) tbody tfoot) = if isSimple && numcols > 1 then simpleTable caption headers rows else do opts <- asks envOptions let tbody' = case hrows of [] -> tbody _ -> TableBody nullAttr 0 [] hrows : tbody gridTable opts blocksToDoc specs (TableHead hattr []) tbody' tfoot where (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot blocksToDoc opts blocks = local (\env -> env { envOptions = opts }) $ blockListToMuse blocks numcols = maximum (length aligns :| length widths : map length (headers:rows)) isSimple = onlySimpleTableCells (headers : rows) && all (== 0) widths blockToMuse (Div _ bs) = flatBlockListToMuse bs blockToMuse (Figure attr capt body) = do blockToMuse (figureDiv attr capt body) -- | Return Muse representation of notes collected so far. currentNotesToMuse :: PandocMonad m => Muse m (Doc Text) currentNotesToMuse = do notes <- reverse <$> gets stNotes modify $ \st -> st { stNotes = mempty } notesToMuse notes -- | Return Muse representation of notes. notesToMuse :: PandocMonad m => Notes -> Muse m (Doc Text) notesToMuse notes = do n <- gets stNoteNum modify $ \st -> st { stNoteNum = stNoteNum st + length notes } vsep <$> zipWithM noteToMuse [n ..] notes -- | Return Muse representation of a note. noteToMuse :: PandocMonad m => Int -> [Block] -> Muse m (Doc Text) noteToMuse num note = do res <- hang (T.length marker) (literal marker) <$> local (\env -> env { envInsideBlock = True , envInlineStart = True , envAfterSpace = True }) (blockListToMuse note) return $ res <> blankline where marker = "[" <> tshow num <> "] " -- | Return Muse representation of block and accumulated notes. blockToMuseWithNotes :: PandocMonad m => Block -> Muse m (Doc Text) blockToMuseWithNotes blk = do topLevel <- asks envTopLevel opts <- asks envOptions let hdrToMuse hdr@Header{} = do b <- blockToMuse hdr if topLevel && writerReferenceLocation opts == EndOfSection then do notes <- currentNotesToMuse return $ notes $+$ b else return b hdrToMuse b = blockToMuse b b <- hdrToMuse blk if topLevel && writerReferenceLocation opts == EndOfBlock then do notes <- currentNotesToMuse return $ b $+$ notes <> blankline else return b -- | Escape special characters for Muse. escapeText :: Text -> Text escapeText s = "<verbatim>" <> T.replace "</verbatim>" "<</verbatim><verbatim>/verbatim>" s <> "</verbatim>" -- | Replace newlines with spaces replaceNewlines :: Text -> Text replaceNewlines = T.map $ \c -> if c == '\n' then ' ' else c startsWithMarker :: (Char -> Bool) -> Text -> Bool startsWithMarker f t = case T.uncons $ T.dropWhile f' t of Just ('.', xs) -> T.null xs || isSpace (T.head xs) _ -> False where f' c = c == ' ' || f c containsNotes :: Char -> Char -> Text -> Bool containsNotes left right = p . T.unpack -- This ought to be a parser where p (left':xs) | left' == left = q xs || p xs | otherwise = p xs p "" = False q (x:xs) | x `elem` ("123456789"::String) = r xs || p xs | otherwise = p xs q [] = False r ('0':xs) = r xs || p xs r xs = s xs || q xs || p xs s (right':xs) | right' == right = True | otherwise = p xs s [] = False -- | Return True if string should be escaped with <verbatim> tags shouldEscapeText :: PandocMonad m => Text -> Muse m Bool shouldEscapeText s = do insideLink <- asks envInsideLinkDescription return $ T.null s || T.any (`elem` ("#*<=|" :: String)) s || "::" `T.isInfixOf` s || "~~" `T.isInfixOf` s || "[[" `T.isInfixOf` s || ">>>" `T.isInfixOf` s || ("]" `T.isInfixOf` s && insideLink) || containsNotes '[' ']' s || containsNotes '{' '}' s -- | Escape special characters for Muse if needed. conditionalEscapeText :: PandocMonad m => Text -> Muse m Text conditionalEscapeText s = do shouldEscape <- shouldEscapeText s return $ if shouldEscape then escapeText s else s -- Expand Math and Cite before normalizing inline list preprocessInlineList :: PandocMonad m => [Inline] -> m [Inline] preprocessInlineList (Math t str:xs) = (++) <$> texMathToInlines t str <*> preprocessInlineList xs -- Amusewiki does not support <cite> tag, -- and Emacs Muse citation support is limited -- (https://www.gnu.org/software/emacs-muse/manual/html_node/Citations.html#Citation) -- so just fallback to expanding inlines. preprocessInlineList (Cite _ lst:xs) = (lst ++) <$> preprocessInlineList xs preprocessInlineList (x:xs) = (x:) <$> preprocessInlineList xs preprocessInlineList [] = return [] replaceSmallCaps :: Inline -> Inline replaceSmallCaps (SmallCaps lst) = Emph lst replaceSmallCaps x = x removeKeyValues :: Inline -> Inline removeKeyValues (Code (i, cls, _) xs) = Code (i, cls, []) xs -- Do not remove attributes from Link -- Do not remove attributes, such as "width", from Image -- Do not remove attributes, such as "dir", from Span removeKeyValues x = x normalizeInlineList :: [Inline] -> [Inline] normalizeInlineList (Str "" : xs) = normalizeInlineList xs normalizeInlineList (x : Str "" : xs) = normalizeInlineList (x:xs) normalizeInlineList (Str x1 : Str x2 : xs) = normalizeInlineList $ Str (x1 <> x2) : xs normalizeInlineList (Emph x1 : Emph x2 : ils) = normalizeInlineList $ Emph (x1 <> x2) : ils normalizeInlineList (Strong x1 : Strong x2 : ils) = normalizeInlineList $ Strong (x1 <> x2) : ils normalizeInlineList (Strikeout x1 : Strikeout x2 : ils) = normalizeInlineList $ Strikeout (x1 <> x2) : ils normalizeInlineList (Superscript x1 : Superscript x2 : ils) = normalizeInlineList $ Superscript (x1 <> x2) : ils normalizeInlineList (Subscript x1 : Subscript x2 : ils) = normalizeInlineList $ Subscript (x1 <> x2) : ils normalizeInlineList (SmallCaps x1 : SmallCaps x2 : ils) = normalizeInlineList $ SmallCaps (x1 <> x2) : ils normalizeInlineList (Code _ x1 : Code _ x2 : ils) = normalizeInlineList $ Code nullAttr (x1 <> x2) : ils normalizeInlineList (RawInline f1 x1 : RawInline f2 x2 : ils) | f1 == f2 = normalizeInlineList $ RawInline f1 (x1 <> x2) : ils -- Do not join Span's during normalization normalizeInlineList (x:xs) = x : normalizeInlineList xs normalizeInlineList [] = [] fixNotes :: [Inline] -> [Inline] fixNotes [] = [] fixNotes (Space : n@Note{} : rest) = Str " " : n : fixNotes rest fixNotes (SoftBreak : n@Note{} : rest) = Str " " : n : fixNotes rest fixNotes (x:xs) = x : fixNotes xs startsWithSpace :: [Inline] -> Bool startsWithSpace (Space:_) = True startsWithSpace (SoftBreak:_) = True startsWithSpace (Str s:_) = stringStartsWithSpace s startsWithSpace _ = False endsWithSpace :: [Inline] -> Bool endsWithSpace [Space] = True endsWithSpace [SoftBreak] = True endsWithSpace [Str s] = stringEndsWithSpace s endsWithSpace (_:xs) = endsWithSpace xs endsWithSpace [] = False urlEscapeBrackets :: Text -> Text urlEscapeBrackets = T.concatMap $ \c -> case c of ']' -> "%5D" _ -> T.singleton c isHorizontalRule :: Text -> Bool isHorizontalRule s = T.length s >= 4 && T.all (== '-') s stringStartsWithSpace :: Text -> Bool stringStartsWithSpace = maybe False (isSpace . fst) . T.uncons stringEndsWithSpace :: Text -> Bool stringEndsWithSpace = maybe False (isSpace . snd) . T.unsnoc fixOrEscape :: Bool -> Inline -> Bool fixOrEscape b (Str s) = fixOrEscapeStr b s where fixOrEscapeStr sp t = case T.uncons t of Just ('-', xs) | T.null xs -> sp | otherwise -> (sp && isSpace (T.head xs)) || isHorizontalRule t Just (';', xs) | T.null xs -> not sp | otherwise -> not sp && isSpace (T.head xs) Just ('>', xs) | T.null xs -> True | otherwise -> isSpace (T.head xs) _ -> (sp && (startsWithMarker isDigit s || startsWithMarker isAsciiLower s || startsWithMarker isAsciiUpper s)) || stringStartsWithSpace s fixOrEscape _ Space = True fixOrEscape _ SoftBreak = True fixOrEscape _ _ = False inlineListStartsWithAlnum :: PandocMonad m => [Inline] -> Muse m Bool inlineListStartsWithAlnum (Str s:_) = do esc <- shouldEscapeText s return $ esc || isAlphaNum (T.head s) inlineListStartsWithAlnum _ = return False -- | Convert list of Pandoc inline elements to Muse renderInlineList :: PandocMonad m => [Inline] -> Muse m (Doc Text) renderInlineList [] = pure "" renderInlineList (x:xs) = do start <- asks envInlineStart afterSpace <- asks envAfterSpace topLevel <- asks envTopLevel insideAsterisks <- asks envInsideAsterisks nearAsterisks <- asks envNearAsterisks useTags <- gets stUseTags alnumNext <- inlineListStartsWithAlnum xs let newUseTags = useTags || alnumNext modify $ \st -> st { stUseTags = newUseTags } r <- local (\env -> env { envInlineStart = False , envInsideAsterisks = False , envNearAsterisks = nearAsterisks || (null xs && insideAsterisks) }) $ inlineToMuse x opts <- asks envOptions let isNewline = (x == SoftBreak && writerWrapText opts == WrapPreserve) || x == LineBreak lst' <- local (\env -> env { envInlineStart = isNewline , envAfterSpace = x == Space || (not topLevel && isNewline) , envNearAsterisks = False }) $ renderInlineList xs if start && fixOrEscape afterSpace x then pure (literal "<verbatim></verbatim>" <> r <> lst') else pure (r <> lst') -- | Normalize and convert list of Pandoc inline elements to Muse. inlineListToMuse :: PandocMonad m => [Inline] -> Muse m (Doc Text) inlineListToMuse lst = do lst' <- normalizeInlineList . fixNotes <$> preprocessInlineList (map (removeKeyValues . replaceSmallCaps) lst) insideAsterisks <- asks envInsideAsterisks start <- asks envInlineStart modify $ \st -> st { stUseTags = False } -- Previous character is likely a '>' or some other markup if start && null lst' then pure "<verbatim></verbatim>" else local (\env -> env { envNearAsterisks = insideAsterisks }) $ renderInlineList lst' inlineListToMuse' :: PandocMonad m => [Inline] -> Muse m (Doc Text) inlineListToMuse' lst = do topLevel <- asks envTopLevel afterSpace <- asks envAfterSpace local (\env -> env { envInlineStart = True , envAfterSpace = afterSpace || not topLevel }) $ inlineListToMuse lst emphasis :: PandocMonad m => Text -> Text -> [Inline] -> Muse m (Doc Text) emphasis b e lst = do contents <- local (\env -> env { envInsideAsterisks = inAsterisks }) $ inlineListToMuse lst modify $ \st -> st { stUseTags = useTags } return $ literal b <> contents <> literal e where inAsterisks = T.last b == '*' || T.head e == '*' useTags = T.last e /= '>' -- | Convert Pandoc inline element to Muse. inlineToMuse :: PandocMonad m => Inline -> Muse m (Doc Text) inlineToMuse (Str str) = do escapedStr <- conditionalEscapeText $ replaceNewlines str let useTags = isAlphaNum $ T.last escapedStr -- escapedStr is never empty because empty strings are escaped modify $ \st -> st { stUseTags = useTags } return $ literal escapedStr inlineToMuse (Emph [Strong lst]) = do useTags <- gets stUseTags let lst' = normalizeInlineList lst if useTags then emphasis "<em>**" "**</em>" lst' else if null lst' || startsWithSpace lst' || endsWithSpace lst' then emphasis "*<strong>" "</strong>*" lst' else emphasis "***" "***" lst' inlineToMuse (Emph lst) = do useTags <- gets stUseTags let lst' = normalizeInlineList lst if useTags || null lst' || startsWithSpace lst' || endsWithSpace lst' then emphasis "<em>" "</em>" lst' else emphasis "*" "*" lst' inlineToMuse (Strong [Emph lst]) = do useTags <- gets stUseTags let lst' = normalizeInlineList lst if useTags then emphasis "<strong>*" "*</strong>" lst' else if null lst' || startsWithSpace lst' || endsWithSpace lst' then emphasis "**<em>" "</em>**" lst' else emphasis "***" "***" lst' -- Underline is only supported in Emacs Muse mode. inlineToMuse (Underline lst) = do opts <- asks envOptions contents <- inlineListToMuse lst if isEnabled Ext_amuse opts then return $ "_" <> contents <> "_" else inlineToMuse (Emph lst) inlineToMuse (Strong lst) = do useTags <- gets stUseTags let lst' = normalizeInlineList lst if useTags || null lst' || startsWithSpace lst' || endsWithSpace lst' then emphasis "<strong>" "</strong>" lst' else emphasis "**" "**" lst' inlineToMuse (Strikeout lst) = do contents <- inlineListToMuse lst modify $ \st -> st { stUseTags = False } return $ "<del>" <> contents <> "</del>" inlineToMuse (Superscript lst) = do contents <- inlineListToMuse lst modify $ \st -> st { stUseTags = False } return $ "<sup>" <> contents <> "</sup>" inlineToMuse (Subscript lst) = do contents <- inlineListToMuse lst modify $ \st -> st { stUseTags = False } return $ "<sub>" <> contents <> "</sub>" inlineToMuse SmallCaps {} = throwError $ PandocShouldNeverHappenError "SmallCaps should be expanded before normalization" inlineToMuse (Quoted SingleQuote lst) = do contents <- inlineListToMuse lst modify $ \st -> st { stUseTags = False } return $ "‘" <> contents <> "’" inlineToMuse (Quoted DoubleQuote lst) = do contents <- inlineListToMuse lst modify $ \st -> st { stUseTags = False } return $ "“" <> contents <> "”" inlineToMuse Cite {} = throwError $ PandocShouldNeverHappenError "Citations should be expanded before normalization" inlineToMuse (Code _ str) = do useTags <- gets stUseTags modify $ \st -> st { stUseTags = False } return $ if useTags || T.null str || T.any (== '=') str || isSpace (T.head str) || isSpace (T.last str) then "<code>" <> literal (T.replace "</code>" "<</code><code>/code>" str) <> "</code>" else "=" <> literal str <> "=" inlineToMuse Math{} = throwError $ PandocShouldNeverHappenError "Math should be expanded before normalization" inlineToMuse (RawInline (Format f) str) = do modify $ \st -> st { stUseTags = False } return $ "<literal style=\"" <> literal f <> "\">" <> literal str <> "</literal>" inlineToMuse LineBreak = do oneline <- asks envOneLine modify $ \st -> st { stUseTags = False } return $ if oneline then "<br>" else "<br>" <> cr inlineToMuse Space = do modify $ \st -> st { stUseTags = False } return space inlineToMuse SoftBreak = do oneline <- asks envOneLine wrapText <- asks $ writerWrapText . envOptions modify $ \st -> st { stUseTags = False } return $ if not oneline && wrapText == WrapPreserve then cr else space inlineToMuse (Link _ txt (src, _)) = case txt of [Str x] | escapeURI x == src -> do modify $ \st -> st { stUseTags = False } return $ "[[" <> literal (escapeLink x) <> "]]" _ -> do contents <- local (\env -> env { envInsideLinkDescription = True }) $ inlineListToMuse txt modify $ \st -> st { stUseTags = False } return $ "[[" <> literal (escapeLink src) <> "][" <> contents <> "]]" where escapeLink lnk = if isImageUrl lnk then "URL:" <> urlEscapeBrackets lnk else urlEscapeBrackets lnk -- Taken from muse-image-regexp defined in Emacs Muse file lisp/muse-regexps.el imageExtensions = [".eps", ".gif", ".jpg", ".jpeg", ".pbm", ".png", ".tiff", ".xbm", ".xpm"] isImageUrl = (`elem` imageExtensions) . takeExtension . T.unpack inlineToMuse (Image attr alt (source,T.stripPrefix "fig:" -> Just title)) = inlineToMuse (Image attr alt (source,title)) inlineToMuse (Image attr@(_, classes, _) inlines (source, title)) = do opts <- asks envOptions alt <- local (\env -> env { envInsideLinkDescription = True }) $ inlineListToMuse inlines title' <- if T.null title then if null inlines then return "" else return $ "[" <> alt <> "]" else do s <- local (\env -> env { envInsideLinkDescription = True }) $ conditionalEscapeText title return $ "[" <> literal s <> "]" let width = case dimension Width attr of Just (Percent x) | isEnabled Ext_amuse opts -> " " <> tshow (round x :: Integer) _ -> "" let leftalign = if "align-left" `elem` classes then " l" else "" let rightalign = if "align-right" `elem` classes then " r" else "" modify $ \st -> st { stUseTags = False } return $ "[[" <> literal (urlEscapeBrackets source <> width <> leftalign <> rightalign) <> "]" <> title' <> "]" inlineToMuse (Note contents) = do -- add to notes in state notes <- gets stNotes modify $ \st -> st { stNotes = contents:notes , stUseTags = False } n <- gets stNoteNum let ref = tshow $ n + length notes return $ "[" <> literal ref <> "]" inlineToMuse (Span (anchor,names,kvs) inlines) = do contents <- inlineListToMuse inlines let (contents', hasDir) = case lookup "dir" kvs of Just "rtl" -> ("<<<" <> contents <> ">>>", True) Just "ltr" -> (">>>" <> contents <> "<<<", True) _ -> (contents, False) let anchorDoc = if T.null anchor then mempty else literal ("#" <> anchor) <> space modify $ \st -> st { stUseTags = False } return $ anchorDoc <> if null inlines && not (T.null anchor) then mempty else case names of [] | hasDir -> contents' | otherwise -> "<class>" <> contents' <> "</class>" (n:_) -> "<class name=\"" <> literal n <> "\">" <> contents' <> "</class>" ================================================ FILE: src/Text/Pandoc/Writers/Native.hs ================================================ {- | Module : Text.Pandoc.Writers.Native Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of a 'Pandoc' document to a string representation. -} module Text.Pandoc.Writers.Native ( writeNative ) where import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions (..)) import Text.Show.Pretty (ppDoc) import Text.PrettyPrint (renderStyle, Style(..), style, char) -- | Prettyprint Pandoc document. writeNative :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeNative opts (Pandoc meta blocks) = do let style' = style{ lineLength = writerColumns opts, ribbonsPerLine = 1.2 } return $ T.pack $ renderStyle style' $ case writerTemplate opts of Just _ -> ppDoc (Pandoc meta blocks) <> char '\n' Nothing -> ppDoc blocks ================================================ FILE: src/Text/Pandoc/Writers/ODT.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.ODT Copyright : Copyright (C) 2008-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to ODT. -} module Text.Pandoc.Writers.ODT ( writeODT ) where import Codec.Archive.Zip import Control.Monad.Except (catchError, throwError) import Control.Monad.State.Strict (StateT, evalStateT, gets, modify, lift) import Control.Monad (MonadPlus(mplus)) import qualified Data.ByteString.Lazy as B import Data.Maybe (fromMaybe) import Data.Generics (everywhere', mkT) import Data.List (isPrefixOf) import qualified Data.Map as Map import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Time import System.FilePath (takeDirectory, takeExtension, (<.>), (</>), isAbsolute) import Text.Collate.Lang (Lang (..), renderLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report, toLang) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Data (readDataFile) import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) import Text.Pandoc.Options (WrapOption (..), WriterOptions (..), HighlightMethod(Skylighting)) import Text.DocLayout import Text.Pandoc.Shared (stringify, tshow) import Text.Pandoc.Version (pandocVersionText) import Text.Pandoc.Writers.Shared (lookupMetaString, lookupMetaBlocks, fixDisplayMath, getLang, ensureValidXmlIdentifiers) import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toTextLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) import Text.Pandoc.XML import Text.Pandoc.XML.Light import Text.TeXMath import qualified Text.XML.Light as XL import Network.URI (parseRelativeReference, URI(uriPath), isURI) import Skylighting newtype ODTState = ODTState { stEntries :: [Entry] } type O m = StateT ODTState m -- | Produce an ODT file from a Pandoc document. writeODT :: PandocMonad m => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> m B.ByteString writeODT opts doc = let initState = ODTState{ stEntries = [] } doc' = fixInternalLinks . ensureValidXmlIdentifiers $ doc in evalStateT (pandocToODT opts doc') initState -- | ODT internal links are evaluated relative to an imaginary folder -- structure that mirrors the zip structure. The result is that relative -- links in the document need to start with `..`. See #3524. fixInternalLinks :: Pandoc -> Pandoc fixInternalLinks = walk go where go (Link attr ils (src,tit)) = Link attr ils (fixRel src,tit) go x = x fixRel uri = case parseRelativeReference (T.unpack uri) of Just u | not (null (uriPath u)) -> tshow $ u{ uriPath = "../" <> uriPath u } _ -> uri -- | Produce an ODT file from a Pandoc document. pandocToODT :: PandocMonad m => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> O m B.ByteString pandocToODT opts doc@(Pandoc meta _) = do let title = docTitle meta let authors = docAuthors meta utctime <- P.getTimestamp lang <- toLang (getLang opts meta) refArchive <- case writerReferenceDoc opts of Just f -> lift $ toArchive . B.fromStrict . fst <$> (P.fetchItem (T.pack f)) Nothing -> lift $ toArchive . B.fromStrict <$> readDataFile "reference.odt" -- handle formulas and pictures -- picEntriesRef <- P.newIORef ([] :: [Entry]) doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc newContents <- lift $ writeOpenDocument opts{writerWrapText = WrapNone} doc' epochtime <- floor `fmap` lift P.getPOSIXTime let contentEntry = toEntry "content.xml" epochtime $ fromTextLazy $ TL.fromStrict newContents picEntries <- gets stEntries let archive = foldr addEntryToArchive refArchive $ contentEntry : picEntries -- construct META-INF/manifest.xml based on archive let toFileEntry fp = case getMimeType fp of Nothing -> empty Just m -> selfClosingTag "manifest:file-entry" [("manifest:media-type", m) ,("manifest:full-path", T.pack fp) ] let files = [ ent | ent <- filesInArchive archive, not ("META-INF" `isPrefixOf` ent) ] let formulas = [ takeDirectory ent ++ "/" | ent <- filesInArchive archive, "Formula-" `isPrefixOf` ent, takeExtension ent == ".xml" ] let manifestEntry = toEntry "META-INF/manifest.xml" epochtime $ fromStringLazy $ render Nothing $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>" $$ inTags True "manifest:manifest" [("xmlns:manifest","urn:oasis:names:tc:opendocument:xmlns:manifest:1.0") ,("manifest:version","1.3")] ( selfClosingTag "manifest:file-entry" [("manifest:media-type","application/vnd.oasis.opendocument.text") ,("manifest:full-path","/") ,("manifest:version", "1.3")] $$ vcat ( map toFileEntry files ) $$ vcat ( map toFileEntry formulas ) ) let archive' = addEntryToArchive manifestEntry archive -- create meta.xml let userDefinedMetaFields = [k | k <- Map.keys (unMeta meta) , k `notElem` ["title", "lang", "author" , "description", "subject", "keywords"]] let escapedText = text . T.unpack . escapeStringForXML let keywords = case lookupMeta "keywords" meta of Just (MetaList xs) -> map stringify xs _ -> [] let userDefinedMeta = map (\k -> inTags False "meta:user-defined" [ ("meta:name", escapeStringForXML k) ,("meta:value-type", "string") ] (escapedText $ lookupMetaString k meta)) userDefinedMetaFields let metaTag metafield = inTagsSimple metafield . escapedText let metaEntry = toEntry "meta.xml" epochtime $ fromStringLazy $ render Nothing $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>" $$ inTags True "office:document-meta" [("xmlns:office","urn:oasis:names:tc:opendocument:xmlns:office:1.0") ,("xmlns:xlink","http://www.w3.org/1999/xlink") ,("xmlns:dc","http://purl.org/dc/elements/1.1/") ,("xmlns:meta","urn:oasis:names:tc:opendocument:xmlns:meta:1.0") ,("xmlns:ooo","http://openoffice.org/2004/office") ,("xmlns:grddl","http://www.w3.org/2003/g/data-view#") ,("office:version","1.3")] ( inTags True "office:meta" [] ( metaTag "meta:generator" ("Pandoc/" <> pandocVersionText) $$ metaTag "dc:title" (stringify title) $$ metaTag "dc:description" (T.intercalate "\n" (map stringify $ lookupMetaBlocks "description" meta)) $$ metaTag "dc:subject" (lookupMetaString "subject" meta) $$ metaTag "meta:keyword" (T.intercalate ", " keywords) $$ case lang of Just l -> metaTag "dc:language" (renderLang l) Nothing -> empty $$ (\d a -> metaTag "meta:initial-creator" a $$ metaTag "dc:creator" a $$ metaTag "meta:creation-date" d $$ metaTag "dc:date" d ) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime) (T.intercalate "; " (map stringify authors)) $$ vcat userDefinedMeta ) ) -- make sure mimetype is first let mimetypeEntry = toEntry "mimetype" epochtime $ fromStringLazy "application/vnd.oasis.opendocument.text" archive'' <- updateStyle opts lang $ addEntryToArchive mimetypeEntry $ addEntryToArchive metaEntry archive' return $ fromArchive archive'' updateStyle :: forall m . PandocMonad m => WriterOptions -> Maybe Lang -> Archive -> O m Archive updateStyle opts mbLang arch = do epochtime <- floor `fmap` lift P.getPOSIXTime let goEntry :: Entry -> O m Entry goEntry e | eRelativePath e == "styles.xml" = case parseXMLElement (toTextLazy (fromEntry e)) of Left msg -> throwError $ PandocXMLError "styles.xml" msg Right d -> return $ toEntry "styles.xml" epochtime ( fromTextLazy . TL.fromStrict . showTopElement . maybe id addLang mbLang . transformElement (\qn -> qName qn == "styles" && qPrefix qn == Just "office" ) (case writerHighlightMethod opts of Skylighting style -> addHlStyles style _ -> id) $ d ) | otherwise = pure e entries <- mapM goEntry (zEntries arch) return arch{ zEntries = entries } addHlStyles :: Style -> Element -> Element addHlStyles sty el = el{ elContent = filter (not . isHlStyle) (elContent el) ++ styleToOpenDocument sty } where isHlStyle (Elem e) = "Tok" `T.isSuffixOf` (qName (elName e)) isHlStyle _ = False -- top-down search transformElement :: (QName -> Bool) -> (Element -> Element) -> Element -> Element transformElement g f el | g (elName el) = f el | otherwise = el{ elContent = map go (elContent el) } where go (Elem e) = Elem (transformElement g f e) go x = x -- TODO FIXME avoid this generic traversal! addLang :: Lang -> Element -> Element addLang lang = everywhere' (mkT updateLangAttr) where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _) = Attr n (langLanguage lang) updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _) = Attr n (fromMaybe "" $ langRegion lang) updateLangAttr x = x -- | transform both Image and Math elements transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError (do (img, mbMimeType) <- P.fetchItem src (ptX, ptY) <- case imageSize opts img of Right s -> return $ sizeInPoints s Left msg -> do report $ CouldNotDetermineImageSize src msg return (100, 100) let dims = case (getDim Width, getDim Height) of (Just w, Just h) -> [("width", tshow w), ("height", tshow h)] (Just w@(Percent _), Nothing) -> [("rel-width", tshow w),("rel-height", "scale"),("width", tshow ptX <> "pt"),("height", tshow ptY <> "pt")] (Nothing, Just h@(Percent _)) -> [("rel-width", "scale"),("rel-height", tshow h),("width", tshow ptX <> "pt"),("height", tshow ptY <> "pt")] (Just w@(Inch i), Nothing) -> [("width", tshow w), ("height", tshow (i / ratio) <> "in")] (Nothing, Just h@(Inch i)) -> [("width", tshow (i * ratio) <> "in"), ("height", tshow h)] _ -> [("width", tshow ptX <> "pt"), ("height", tshow ptY <> "pt")] where ratio = ptX / ptY getDim dir = case dimension dir attr of Just (Percent i) -> Just $ Percent i Just dim -> Just $ Inch $ inInch opts dim Nothing -> Nothing let newattr = (id', cls, dims) src' <- if writerLinkImages opts then case T.unpack src of s | isURI s -> return src | isAbsolute s -> return src | otherwise -> return $ T.pack $ ".." </> s else do entries <- gets stEntries let extension = maybe (takeExtension $ takeWhile (/='?') $ T.unpack src) T.unpack (mbMimeType >>= extensionFromMimeType) let newsrc = "Pictures/" ++ show (length entries) <.> extension let toLazy = B.fromChunks . (:[]) epochtime <- floor `fmap` lift P.getPOSIXTime let entry = toEntry newsrc epochtime $ toLazy img modify $ \st -> st{ stEntries = entry : entries } return $ T.pack newsrc return $ Image newattr lab (src', t)) (\e -> do report $ CouldNotFetchResource src $ T.pack (show e) return $ Emph lab) transformPicMath _ (Math t math) = do entries <- gets stEntries let dt = if t == InlineMath then DisplayInline else DisplayBlock case writeMathML dt <$> readTeX math of Left _ -> return $ Math t math Right r -> do let conf = XL.useShortEmptyTags (const False) XL.defaultConfigPP let mathml = XL.ppcTopElement conf r epochtime <- floor `fmap` lift P.getPOSIXTime let dirname = "Formula-" ++ show (length entries) ++ "/" let fname = dirname ++ "content.xml" let entry = toEntry fname epochtime (fromStringLazy mathml) let fname' = dirname ++ "settings.xml" let entry' = toEntry fname' epochtime $ documentSettings (t == InlineMath) modify $ \st -> st{ stEntries = entry' : (entry : entries) } return $ RawInline (Format "opendocument") $ render Nothing $ inTags False "draw:frame" (if t == DisplayMath then [("draw:style-name","fr2") -- `draw:frame` does not support either -- `style:vertical-pos` or `style:vertical-rel`, -- therefore those attributes must go into the -- `style:style` element ,("text:anchor-type","paragraph")] else [("draw:style-name","fr1") ,("text:anchor-type","as-char")]) $ selfClosingTag "draw:object" [("xlink:href", T.pack dirname) , ("xlink:type", "simple") , ("xlink:show", "embed") , ("xlink:actuate", "onLoad")] transformPicMath _ x = return x documentSettings :: Bool -> B.ByteString documentSettings isTextMode = fromStringLazy $ render Nothing $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>" $$ inTags True "office:document-settings" [("xmlns:office","urn:oasis:names:tc:opendocument:xmlns:office:1.0") ,("xmlns:xlink","http://www.w3.org/1999/xlink") ,("xmlns:config","urn:oasis:names:tc:opendocument:xmlns:config:1.0") ,("xmlns:ooo","http://openoffice.org/2004/office") ,("office:version","1.3")] ( inTagsSimple "office:settings" $ inTags False "config:config-item-set" [("config:name", "ooo:configuration-settings")] $ inTags False "config:config-item" [("config:name", "IsTextMode") ,("config:type", "boolean")] $ text $ if isTextMode then "true" else "false") styleToOpenDocument :: Style -> [Content] styleToOpenDocument style = map (Elem . toStyle) alltoktypes where alltoktypes = enumFromTo KeywordTok NormalTok styleName x = case T.break (== ':') x of (b, a) | T.null a -> QName x Nothing (Just "style") | otherwise -> QName (T.drop 1 a) Nothing (Just b) styleAttr (x, y) = Attr (styleName x) y styleAttrs = map styleAttr styleElement x attrs cs = Element (styleName x) (styleAttrs attrs) cs Nothing toStyle toktype = styleElement "style" [("name", tshow toktype), ("family", "text")] [Elem (styleElement "text-properties" (tokColor toktype ++ tokBgColor toktype ++ [("fo:font-style", "italic") | tokFeature tokenItalic toktype ] ++ [("fo:font-weight", "bold") | tokFeature tokenBold toktype ] ++ [("style:text-underline-style", "solid") | tokFeature tokenUnderline toktype ]) [])] tokStyles = tokenStyles style tokFeature f toktype = maybe False f $ Map.lookup toktype tokStyles tokColor toktype = maybe [] (\c -> [("fo:color", T.pack (fromColor c))]) ((tokenColor =<< Map.lookup toktype tokStyles) `mplus` defaultColor style) tokBgColor toktype = maybe [] (\c -> [("fo:background-color", T.pack (fromColor c))]) (tokenBackground =<< Map.lookup toktype tokStyles) ================================================ FILE: src/Text/Pandoc/Writers/OOXML.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.OOXML Copyright : Copyright (C) 2012-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Functions common to OOXML writers (Docx and Powerpoint) -} module Text.Pandoc.Writers.OOXML ( mknode , mktnode , nodename , toLazy , renderXml , parseXml , elemToNameSpaces , elemName , isElem , NameSpaces , fitToPage ) where import Codec.Archive.Zip import Control.Monad (mplus) import Control.Monad.Except (throwError) import Text.Pandoc.Error import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import Data.Maybe (mapMaybe) import qualified Data.Text as T import Data.Text (Text) import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML.Light mknode :: Node t => Text -> [(Text,Text)] -> t -> Element mknode s attrs = add_attrs (map (\(k,v) -> Attr (nodename k) v) attrs) . node (nodename s) mktnode :: Text -> [(Text,Text)] -> T.Text -> Element mktnode s attrs = mknode s attrs nodename :: Text -> QName nodename s = QName{ qName = name, qURI = Nothing, qPrefix = prefix } where (name, prefix) = case T.break (==':') s of (xs,ys) -> case T.uncons ys of Nothing -> (xs, Nothing) Just (_,zs) -> (zs, Just xs) toLazy :: B.ByteString -> BL.ByteString toLazy = BL.fromChunks . (:[]) renderXml :: Element -> BL.ByteString renderXml elt = BL.fromStrict (UTF8.fromText (showTopElement elt)) parseXml :: PandocMonad m => Archive -> Archive -> String -> m Element parseXml refArchive distArchive relpath = case findEntryByPath relpath refArchive `mplus` findEntryByPath relpath distArchive of Nothing -> throwError $ PandocSomeError $ T.pack relpath <> " missing in reference file" Just e -> case parseXMLElement . UTF8.toTextLazy . fromEntry $ e of Left msg -> throwError $ PandocXMLError (T.pack relpath) msg Right d -> return d -- Copied from Util attrToNSPair :: Attr -> Maybe (Text, Text) attrToNSPair (Attr (QName s _ (Just "xmlns")) val) = Just (s, val) attrToNSPair _ = Nothing elemToNameSpaces :: Element -> NameSpaces elemToNameSpaces = mapMaybe attrToNSPair . elAttribs elemName :: NameSpaces -> Text -> Text -> QName elemName ns prefix name = QName name (lookup prefix ns) (if T.null prefix then Nothing else Just prefix) isElem :: NameSpaces -> Text -> Text -> Element -> Bool isElem ns prefix name element = let ns' = ns ++ elemToNameSpaces element in qName (elName element) == name && qURI (elName element) == lookup prefix ns' type NameSpaces = [(Text, Text)] -- | Scales the image to fit the page -- sizes are passed in emu fitToPage :: (Double, Double) -> Integer -> (Integer, Integer) fitToPage (x, y) pageWidth -- Fixes width to the page width and scales the height | x > fromIntegral pageWidth = (pageWidth, floor $ (fromIntegral pageWidth / x) * y) | otherwise = (floor x, floor y) ================================================ FILE: src/Text/Pandoc/Writers/OPML.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-} {- | Module : Text.Pandoc.Writers.OPML Copyright : Copyright (C) 2013-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to OPML XML. -} module Text.Pandoc.Writers.OPML ( writeOPML) where import Data.Text (Text) import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad) import Data.Time import Text.Pandoc.Definition import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Markdown (writeMarkdown) import Text.Pandoc.Writers.Shared import Text.Pandoc.XML -- | Convert Pandoc document to string in OPML format. writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOPML opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing meta' = B.setMeta "date" (B.str $ convertDate $ docDate meta) meta metadata <- metaToContext opts (fmap literal . writeMarkdown' opts . Pandoc nullMeta) (\ils -> literal . T.stripEnd <$> writeMarkdown' opts (Pandoc nullMeta [Plain ils])) meta' let blocks' = makeSections False (Just 1) blocks main <- render colwidth . vcat <$> mapM (blockToOPML opts) blocks' let context = defField "body" main metadata return $ (if writerPreferAscii opts then toEntities else id) $ case writerTemplate opts of Nothing -> main Just tpl -> render colwidth $ renderTemplate tpl context writeMarkdown' :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMarkdown' opts = writeMarkdown def{ writerWrapText = writerWrapText opts , writerColumns = writerColumns opts , writerExtensions = pandocExtensions } writeHtmlInlines :: PandocMonad m => [Inline] -> m Text writeHtmlInlines ils = T.strip <$> writeHtml5String def (Pandoc nullMeta [Plain ils]) -- date format: RFC 822: Thu, 14 Jul 2005 23:41:05 GMT showDateTimeRFC822 :: UTCTime -> Text showDateTimeRFC822 = T.pack . formatTime defaultTimeLocale "%a, %d %b %Y %X %Z" convertDate :: [Inline] -> Text convertDate ils = maybe "" showDateTimeRFC822 $ parseTimeM True defaultTimeLocale "%F" . T.unpack =<< normalizeDate (stringify ils) -- | Convert a Block to OPML. blockToOPML :: PandocMonad m => WriterOptions -> Block -> m (Doc Text) blockToOPML opts (Div (_,"section":_,_) (Header _ _ title : xs)) = do let isSect (Div (_,"section":_,_) (Header{}:_)) = True isSect _ = False let (blocks, rest) = break isSect xs htmlIls <- writeHtmlInlines title md <- if null blocks then return mempty else writeMarkdown' opts $ Pandoc nullMeta blocks let attrs = ("text", htmlIls) : [("_note", T.stripEnd md) | not (null blocks)] rest' <- vcat <$> mapM (blockToOPML opts) rest return $ inTags True "outline" attrs rest' blockToOPML _ _ = return empty ================================================ FILE: src/Text/Pandoc/Writers/OpenDocument.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- | Module : Text.Pandoc.Writers.OpenDocument Copyright : Copyright (C) 2008-2020 Andrea Rossato and John MacFarlane License : GNU GPL, version 2 or above Maintainer : Andrea Rossato <andrea.rossato@ing.unitn.it> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to OpenDocument XML. -} module Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) where import Control.Arrow ((***), (>>>)) import Control.Monad (unless, liftM) import Control.Monad.State.Strict ( StateT(..), modify, gets, lift ) import Data.Char (chr) import Data.Foldable (find) 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 import Data.Text (Text) import qualified Data.Text as T import Text.Collate.Lang (Lang (..), parseLang) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Translations (translateTerm) import Text.Pandoc.Definition import qualified Text.Pandoc.Builder as B import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout hiding (link) import Text.Pandoc.Shared (linesToPara, tshow, blocksToInlines) import Text.Pandoc.Templates (renderTemplate) import qualified Text.Pandoc.Translations as Term (Term(Figure, Table)) import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import qualified Text.Pandoc.Writers.AnnotatedTable as Ann import Text.Pandoc.XML import Text.Printf (printf) import Text.Pandoc.Highlighting (highlight) import Skylighting (FormatOptions(..), SourceLine, Token) -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block plainToPara (Plain x) = Para x plainToPara x = x -- -- OpenDocument writer -- type OD m = StateT WriterState m data ReferenceType = HeaderRef | TableRef | FigureRef data WriterState = WriterState { stNotes :: [Doc Text] , stTableStyles :: [Doc Text] , stParaStyles :: [Doc Text] , stListStyles :: [(Int, [Doc Text])] , stTextStyles :: Map.Map (Set.Set TextStyle) (Text, Doc Text) , stTextStyleAttr :: Set.Set TextStyle , stIndentPara :: Int , stInDefinition :: Bool , stTight :: Bool , stFirstPara :: Bool , stImageId :: Int , stTableCaptionId :: Int , stImageCaptionId :: Int , stIdentTypes :: [(Text,ReferenceType)] } defaultWriterState :: WriterState defaultWriterState = WriterState { stNotes = [] , stTableStyles = [] , stParaStyles = [] , stListStyles = [] , stTextStyles = Map.empty , stTextStyleAttr = Set.empty , stIndentPara = 0 , stInDefinition = False , stTight = False , stFirstPara = False , stImageId = 1 , stTableCaptionId = 1 , stImageCaptionId = 1 , stIdentTypes = [] } when :: Bool -> Doc Text -> Doc Text when p a = if p then a else empty addTableStyle :: PandocMonad m => Doc Text -> OD m () addTableStyle i = modify $ \s -> s { stTableStyles = i : stTableStyles s } addNote :: PandocMonad m => Doc Text -> OD m () addNote i = modify $ \s -> s { stNotes = i : stNotes s } addParaStyle :: PandocMonad m => Doc Text -> OD m () addParaStyle i = modify $ \s -> s { stParaStyles = i : stParaStyles s } addTextStyle :: PandocMonad m => Set.Set TextStyle -> (Text, Doc Text) -> OD m () addTextStyle attrs i = modify $ \s -> s { stTextStyles = Map.insert attrs i (stTextStyles s) } increaseIndent :: PandocMonad m => OD m () increaseIndent = modify $ \s -> s { stIndentPara = 1 + stIndentPara s } resetIndent :: PandocMonad m => OD m () resetIndent = modify $ \s -> s { stIndentPara = stIndentPara s - 1 } inTightList :: PandocMonad m => OD m a -> OD m a inTightList f = modify (\s -> s { stTight = True }) >> f >>= \r -> modify (\s -> s { stTight = False }) >> return r setInDefinitionList :: PandocMonad m => Bool -> OD m () setInDefinitionList b = modify $ \s -> s { stInDefinition = b } setFirstPara :: PandocMonad m => OD m () setFirstPara = modify $ \s -> s { stFirstPara = True } inParagraphTags :: PandocMonad m => Doc Text -> OD m (Doc Text) inParagraphTags d = do b <- gets stFirstPara a <- if b then do modify $ \st -> st { stFirstPara = False } return [("text:style-name", "First_20_paragraph")] else return [("text:style-name", "Text_20_body")] return $ inTags False "text:p" a d inParagraphTagsWithStyle :: Text -> Doc Text -> Doc Text inParagraphTagsWithStyle sty = inTags False "text:p" [("text:style-name", sty)] inSpanTags :: Text -> Doc Text -> Doc Text inSpanTags s = inTags False "text:span" [("text:style-name",s)] withAlteredTextStyles :: PandocMonad m => (Set.Set TextStyle -> Set.Set TextStyle) -> OD m a -> OD m a withAlteredTextStyles f action = do oldTextStyleAttr <- gets stTextStyleAttr modify $ \st -> st{ stTextStyleAttr = f oldTextStyleAttr } res <- action modify $ \st -> st{ stTextStyleAttr = oldTextStyleAttr } return res withTextStyle :: PandocMonad m => TextStyle -> OD m a -> OD m a withTextStyle s = withAlteredTextStyles (Set.insert s) inTextStyle :: PandocMonad m => Doc Text -> OD m (Doc Text) inTextStyle d = do at <- gets stTextStyleAttr if Set.null at then return d else do styles <- gets stTextStyles case Map.lookup at styles of Just (styleName, _) -> return $ inTags False "text:span" [("text:style-name",styleName)] d Nothing -> do let styleName = "T" <> tshow (Map.size styles + 1) addTextStyle at (styleName, inTags False "style:style" [("style:name", styleName) ,("style:family", "text")] $ selfClosingTag "style:text-properties" (sortOn fst . Map.toList $ L.foldl' textStyleAttr mempty (Set.toList at))) return $ inTags False "text:span" [("text:style-name",styleName)] d formulaStyles :: [Doc Text] formulaStyles = [formulaStyle InlineMath, formulaStyle DisplayMath] formulaStyle :: MathType -> Doc Text formulaStyle mt = inTags False "style:style" [("style:name", if mt == InlineMath then "fr1" else "fr2") ,("style:family", "graphic") ,("style:parent-style-name", "Formula")] $ selfClosingTag "style:graphic-properties" $ if mt == InlineMath then [("style:vertical-pos", "middle") ,("style:vertical-rel", "text")] else [("style:vertical-pos", "middle") ,("style:vertical-rel", "text") ,("style:horizontal-pos", "center") ,("style:horizontal-rel", "paragraph-content") ,("style:wrap", "none")] inBookmarkTags :: Text -> Doc Text -> Doc Text inBookmarkTags ident d = selfClosingTag "text:bookmark-start" [ ("text:name", ident) ] <> d <> selfClosingTag "text:bookmark-end" [ ("text:name", ident) ] selfClosingBookmark :: Text -> Doc Text selfClosingBookmark ident = selfClosingTag "text:bookmark" [("text:name", ident)] inHeaderTags :: PandocMonad m => Int -> Text -> Doc Text -> OD m (Doc Text) inHeaderTags i ident d = return $ inTags False "text:h" [ ("text:style-name", "Heading_20_" <> tshow i) , ("text:outline-level", tshow i)] $ if T.null ident then d else inBookmarkTags ident d inQuotes :: QuoteType -> Doc Text -> Doc Text inQuotes SingleQuote s = char '\8216' <> s <> char '\8217' inQuotes DoubleQuote s = char '\8220' <> s <> char '\8221' handleSpaces :: Text -> Doc Text handleSpaces s = case T.uncons s of Just (' ', _) -> genTag s Just ('\t',x) -> selfClosingTag "text:tab" [] <> rm x _ -> rm s where genTag = T.span (==' ') >>> tag . T.length *** rm >>> uncurry (<>) tag n = when (n /= 0) $ selfClosingTag "text:s" [("text:c", tshow n)] rm t = case T.uncons t of Just ( ' ',xs) -> char ' ' <> genTag xs Just ('\t',xs) -> selfClosingTag "text:tab" [] <> genTag xs Just ( x,xs) -> char x <> rm xs Nothing -> empty -- | Convert Pandoc document to string in OpenDocument format. writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOpenDocument opts (Pandoc meta blocks) = do setupTranslations meta let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing let meta' = case lookupMetaBlocks "abstract" meta of [] -> meta xs -> B.setMeta "abstract" (B.divWith ("",[],[("custom-style","Abstract")]) (B.fromList xs)) meta ((body, metadata),s) <- flip runStateT defaultWriterState $ do let collectBlockIdent (Header _ (ident,_,_) _) = [(ident,HeaderRef)] collectBlockIdent (Figure (ident,_,_) _ _ ) = [(ident,FigureRef)] collectBlockIdent (Table (ident,_,_) _ _ _ _ _) = [(ident,TableRef)] collectBlockIdent _ = [] modify $ \s -> s{ stIdentTypes = query collectBlockIdent blocks } m <- metaToContext opts (inlinesToOpenDocument opts . blocksToInlines) (fmap chomp . inlinesToOpenDocument opts) meta' b <- blocksToOpenDocument opts blocks return (b, m) let styles = stTableStyles s ++ stParaStyles s ++ formulaStyles ++ map snd (sortBy (comparing (Down . fst)) ( Map.elems (stTextStyles s))) listStyle (n,l) = inTags True "text:list-style" [("style:name", "L" <> tshow n)] (vcat l) let listStyles = map listStyle (stListStyles s) let automaticStyles = vcat $ reverse $ styles ++ listStyles let context = defField "body" body . defField "toc" (writerTableOfContents opts) . defField "toc-depth" (tshow $ writerTOCDepth opts) . defField "automatic-styles" automaticStyles $ metadata return $ render colwidth $ case writerTemplate opts of Nothing -> body Just tpl -> renderTemplate tpl context withParagraphStyle :: PandocMonad m => WriterOptions -> Text -> [Block] -> OD m (Doc Text) withParagraphStyle o s (b:bs) | Para l <- b = go =<< inParagraphTagsWithStyle s <$> inlinesToOpenDocument o l | otherwise = go =<< blockToOpenDocument o b where go i = (<>) i <$> withParagraphStyle o s bs withParagraphStyle _ _ [] = return empty inPreformattedTags :: PandocMonad m => [Doc Text] -> OD m (Doc Text) inPreformattedTags s = do n <- paraStyle [("style:parent-style-name","Preformatted_20_Text")] return $ inParagraphTagsWithStyle ("P" <> tshow n) $ hcat s orderedListToOpenDocument :: PandocMonad m => WriterOptions -> Int -> [[Block]] -> OD m (Doc Text) orderedListToOpenDocument o pn bs = vcat . map (inTagsIndented "text:list-item") <$> mapM (orderedItemToOpenDocument o pn . map plainToPara) bs orderedItemToOpenDocument :: PandocMonad m => WriterOptions -> Int -> [Block] -> OD m (Doc Text) orderedItemToOpenDocument o n bs = vcat <$> mapM go bs where go (OrderedList a l) = newLevel a l go (Para l) = inParagraphTagsWithStyle ("P" <> tshow n) <$> inlinesToOpenDocument o l go b = blockToOpenDocument o b newLevel a l = do nn <- length <$> gets stParaStyles liststyles <- gets stListStyles let ls = case liststyles of [] -> (1,[]) -- should never happen (s:_) -> s modify $ \s -> s { stListStyles = orderedListLevelStyle a ls : drop 1 (stListStyles s) } inTagsIndented "text:list" <$> orderedListToOpenDocument o nn l isTightList :: [[Block]] -> Bool isTightList [] = False isTightList (b:_) | Plain {} : _ <- b = True | otherwise = False newOrderedListStyle :: PandocMonad m => Bool -> ListAttributes -> OD m (Int,Int) newOrderedListStyle b a = do ln <- (+) 1 . length <$> gets stListStyles let nbs = orderedListLevelStyle a (ln, []) pn <- if b then inTightList (paraListStyle ln) else paraListStyle ln modify $ \s -> s { stListStyles = nbs : stListStyles s } return (ln,pn) bulletListToOpenDocument :: PandocMonad m => WriterOptions -> [[Block]] -> OD m (Doc Text) bulletListToOpenDocument o b = do ln <- (+) 1 . length <$> gets stListStyles (pn,ns) <- if isTightList b then inTightList (bulletListStyle ln) else bulletListStyle ln modify $ \s -> s { stListStyles = ns : stListStyles s } is <- listItemsToOpenDocument ("P" <> tshow pn) o b return $ inTags True "text:list" [("text:style-name", "L" <> tshow ln)] is listItemsToOpenDocument :: PandocMonad m => Text -> WriterOptions -> [[Block]] -> OD m (Doc Text) listItemsToOpenDocument s o is = vcat . map (inTagsIndented "text:list-item") <$> mapM (withParagraphStyle o s . map plainToPara) is deflistItemToOpenDocument :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> OD m (Doc Text) deflistItemToOpenDocument o (t,d) = do let ts = if isTightList d then "Definition_20_Term_20_Tight" else "Definition_20_Term" ds = if isTightList d then "Definition_20_Definition_20_Tight" else "Definition_20_Definition" t' <- withParagraphStyle o ts [Para t] d' <- liftM vcat $ mapM (withParagraphStyle o ds . map plainToPara) d return $ t' $$ d' inBlockQuote :: PandocMonad m => WriterOptions -> Int -> [Block] -> OD m (Doc Text) inBlockQuote o i (b:bs) | BlockQuote l <- b = do increaseIndent ni <- paraStyle [("style:parent-style-name","Quotations")] go =<< inBlockQuote o ni (map plainToPara l) | Para l <- b = go =<< inParagraphTagsWithStyle ("P" <> tshow i) <$> inlinesToOpenDocument o l | otherwise = go =<< blockToOpenDocument o b where go block = ($$) block <$> inBlockQuote o i bs inBlockQuote _ _ [] = resetIndent >> return empty -- | Convert a list of Pandoc blocks to OpenDocument. blocksToOpenDocument :: PandocMonad m => WriterOptions -> [Block] -> OD m (Doc Text) blocksToOpenDocument o b = vcat <$> mapM (blockToOpenDocument o) b -- | Convert a Pandoc block element to OpenDocument. blockToOpenDocument :: PandocMonad m => WriterOptions -> Block -> OD m (Doc Text) blockToOpenDocument o = \case Plain b -> if null b then return empty else inParagraphTags =<< inlinesToOpenDocument o b Para b -> if null b && not (isEnabled Ext_empty_paragraphs o) then return empty else inParagraphTags =<< inlinesToOpenDocument o b LineBlock b -> blockToOpenDocument o $ linesToPara b Div attr xs -> mkDiv attr xs Header i (ident,_,_) b -> do setFirstPara inHeaderTags i ident =<< inlinesToOpenDocument o b BlockQuote b -> setFirstPara >> mkBlockQuote b DefinitionList b -> setFirstPara >> defList b BulletList b -> setFirstPara >> bulletListToOpenDocument o b OrderedList a b -> setFirstPara >> orderedList a b CodeBlock attrs s -> do setFirstPara case writerHighlightMethod o of Skylighting {} -> case highlight (writerSyntaxMap o) formatOpenDocument attrs s of Right h -> return $ flush . vcat $ map (inTags True "text:p" [("text:style-name", "Preformatted_20_Text")] . hcat) h Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg unhighlighted s _ -> unhighlighted s Table a bc s th tb tf -> setFirstPara >> table o (Ann.toTable a bc s th tb tf) HorizontalRule -> setFirstPara >> return (selfClosingTag "text:p" [ ("text:style-name", "Horizontal_20_Line") ]) b@(RawBlock f s) -> if f == Format "opendocument" then return $ text $ T.unpack s else empty <$ report (BlockNotRendered b) Figure a capt b -> figure o a capt b where defList b = do setInDefinitionList True r <- vcat <$> mapM (deflistItemToOpenDocument o) b setInDefinitionList False return r unhighlighted s = flush . vcat <$> (mapM ((inPreformattedTags . (:[])) . preformatted) (T.lines s)) mkDiv attr s = do let (ident,_,kvs) = attr i = withLangFromAttr attr $ case lookup "custom-style" kvs of Just sty -> withParagraphStyle o sty s _ -> blocksToOpenDocument o s mkBookmarkedDiv = inTags False "text:section" [("text:name", ident)] if T.null ident then i else fmap mkBookmarkedDiv i mkBlockQuote b = do increaseIndent i <- paraStyle [("style:parent-style-name","Quotations")] inBlockQuote o i (map plainToPara b) orderedList a b = do (ln,pn) <- newOrderedListStyle (isTightList b) a inTags True "text:list" [ ("text:style-name", "L" <> tshow ln)] <$> orderedListToOpenDocument o pn b table :: PandocMonad m => WriterOptions -> Ann.Table -> OD m (Doc Text) table opts (Ann.Table (ident, _, _) (Caption _ c) colspecs thead tbodies tfoot) = do tn <- length <$> gets stTableStyles pn <- length <$> gets stParaStyles let genIds = map chr [65..] name = "Table" <> tshow (tn + 1) (aligns, mwidths) = unzip colspecs fromWidth (ColWidth w) | w > 0 = w fromWidth _ = 0 widths = map fromWidth mwidths textWidth = sum widths columnIds = zip genIds widths mkColumn n = selfClosingTag "table:table-column" [("table:style-name", name <> "." <> T.singleton (fst n))] columns = map mkColumn columnIds paraHStyles = paraTableStyles "Heading" pn aligns paraStyles = paraTableStyles "Contents" (pn + length (newPara paraHStyles)) aligns newPara = map snd . filter (not . isEmpty . snd) addTableStyle $ tableStyle tn textWidth columnIds mapM_ addParaStyle . newPara $ paraHStyles ++ paraStyles captionDoc <- if null c then return empty else inlinesToOpenDocument o (blocksToInlines c) >>= if isEnabled Ext_native_numbering o then numberedTableCaption ident else unNumberedCaption "TableCaption" th <- colHeadsToOpenDocument o (map fst paraHStyles) thead tr <- mapM (tableBodyToOpenDocument o (map fst paraHStyles) (map fst paraStyles)) tbodies tf <- tableFootToOpenDocument o (map fst paraStyles) tfoot let tableDoc = inTags True "table:table" [ ("table:name" , name) , ("table:style-name", name) ] (vcat columns $$ th $$ vcat tr $$ tf) return $ case writerTableCaptionPosition opts of CaptionAbove -> captionDoc $$ tableDoc CaptionBelow -> tableDoc $$ captionDoc figure opts (ident, _, _) (Caption _ longcapt) body = case blocksToInlines longcapt of [] -> withParagraphStyle o "Figure" body caption -> do imageDoc <- withParagraphStyle o "FigureWithCaption" $ map (\case {Plain i -> Para i; b -> b}) body captionDoc <- inlinesToOpenDocument o caption >>= if isEnabled Ext_native_numbering o then numberedFigureCaption ident else unNumberedCaption "FigureCaption" return $ case writerFigureCaptionPosition opts of CaptionAbove -> captionDoc $$ imageDoc CaptionBelow -> imageDoc $$ captionDoc numberedTableCaption :: PandocMonad m => Text -> Doc Text -> OD m (Doc Text) numberedTableCaption ident caption = do id' <- gets stTableCaptionId modify (\st -> st{ stTableCaptionId = id' + 1 }) capterm <- translateTerm Term.Table return $ numberedCaption "TableCaption" capterm "Table" id' ident caption numberedFigureCaption :: PandocMonad m => Text -> Doc Text -> OD m (Doc Text) numberedFigureCaption ident caption = do id' <- gets stImageCaptionId modify (\st -> st{ stImageCaptionId = id' + 1 }) capterm <- translateTerm Term.Figure return $ numberedCaption "FigureCaption" capterm "Illustration" id' ident caption numberedCaption :: Text -> Text -> Text -> Int -> Text -> Doc Text -> Doc Text numberedCaption style term name num ident caption = let t = text $ T.unpack term r = num - 1 ident' = case ident of "" -> "ref" <> name <> tshow r _ -> ident s = inTags False "text:sequence" [ ("text:ref-name", ident'), ("text:name", name), ("text:formula", "ooow:" <> name <> "+1"), ("style:num-format", "1") ] $ text $ show num c = text ": " in inParagraphTagsWithStyle style $ hcat [ t, text " ", s, c, caption ] unNumberedCaption :: Monad m => Text -> Doc Text -> OD m (Doc Text) unNumberedCaption style caption = return $ inParagraphTagsWithStyle style caption colHeadsToOpenDocument :: PandocMonad m => WriterOptions -> [Text] -> Ann.TableHead -> OD m (Doc Text) colHeadsToOpenDocument o ns (Ann.TableHead _ hs) = case hs of [] -> return empty xs -> inTagsIndented "table:table-header-rows" <$> tableHeaderRowsToOpenDocument o ns "TableHeaderRowCell" xs tableHeaderRowsToOpenDocument :: PandocMonad m => WriterOptions -> [Text] -> Text -> [Ann.HeaderRow] -> OD m (Doc Text) tableHeaderRowsToOpenDocument o ns s headerRows = vcat <$> mapM headerRowToOpenDocument headerRows where headerRowToOpenDocument (Ann.HeaderRow _ _ c) = inTagsIndented "table:table-row" . vcat <$> mapM (tableItemToOpenDocument o s) (zip ns c) tableBodyToOpenDocument:: PandocMonad m => WriterOptions -> [Text] -> [Text] -> Ann.TableBody -> OD m (Doc Text) tableBodyToOpenDocument o headns bodyns tb = do let (Ann.TableBody _ _ hs r) = tb tableRowHeaders <- tableHeaderRowsToOpenDocument o headns "TableRowCell" hs tableRows <- mapM (tableRowToOpenDocument o headns bodyns) r return $ tableRowHeaders $$ vcat tableRows tableFootToOpenDocument :: PandocMonad m => WriterOptions -> [Text] -> Ann.TableFoot -> OD m (Doc Text) tableFootToOpenDocument o ns (Ann.TableFoot _ r) = tableHeaderRowsToOpenDocument o ns "TableRowCell" r tableRowToOpenDocument :: PandocMonad m => WriterOptions -> [Text] -> [Text] -> Ann.BodyRow -> OD m (Doc Text) tableRowToOpenDocument o headns bodyns r = let (Ann.BodyRow _ _ rowheaders cs) = r in inTagsIndented "table:table-row" . vcat <$> mapM (tableItemToOpenDocument o "TableRowCell") ((zip headns rowheaders) ++ (zip (drop (length rowheaders) bodyns) cs)) colspanAttrib :: ColSpan -> [(Text, Text)] colspanAttrib cs = case cs of ColSpan 1 -> mempty ColSpan n -> [("table:number-columns-spanned", tshow n)] rowspanAttrib :: RowSpan -> [(Text, Text)] rowspanAttrib rs = case rs of RowSpan 1 -> mempty RowSpan n -> [("table:number-rows-spanned", tshow n)] alignAttrib :: Alignment -> [(Text,Text)] alignAttrib a = case a of AlignRight -> ("fo:text-align","end") : style AlignCenter -> ("fo:text-align","center") : style _ -> [] where style = [("style:justify-single-word","false")] tableItemToOpenDocument :: PandocMonad m => WriterOptions -> Text -> (Text,Ann.Cell) -> OD m (Doc Text) tableItemToOpenDocument o s (n,c) = do let (Ann.Cell _colspecs _colnum (Cell _ align rs cs i) ) = c csa = colspanAttrib cs rsa = rowspanAttrib rs aa = alignAttrib align a = [ ("table:style-name" , s ) , ("office:value-type", "string" ) ] ++ csa ++ rsa itemParaStyle <- case aa of [] -> return 0 _ -> paraStyleFromParent n aa let itemParaStyle' = case itemParaStyle of 0 -> n x -> "P" <> tshow x inTags True "table:table-cell" a <$> withParagraphStyle o itemParaStyle' (map plainToPara i) -- | Convert a list of inline elements to OpenDocument. inlinesToOpenDocument :: PandocMonad m => WriterOptions -> [Inline] -> OD m (Doc Text) inlinesToOpenDocument o l = hcat <$> toChunks o l toChunks :: PandocMonad m => WriterOptions -> [Inline] -> OD m [Doc Text] toChunks _ [] = return [] toChunks o (x : xs) | isChunkable x = do contents <- (inTextStyle . hcat) =<< mapM (inlineToOpenDocument o) (x:ys) rest <- toChunks o zs return (contents : rest) | otherwise = do contents <- inlineToOpenDocument o x rest <- toChunks o xs return (contents : rest) where (ys, zs) = span isChunkable xs isChunkable :: Inline -> Bool isChunkable (Str _) = True isChunkable Space = True isChunkable SoftBreak = True isChunkable _ = False -- | Convert an inline element to OpenDocument. inlineToOpenDocument :: PandocMonad m => WriterOptions -> Inline -> OD m (Doc Text) inlineToOpenDocument o ils = case ils of Space -> return space SoftBreak | writerWrapText o == WrapPreserve -> return $ preformatted "\n" | otherwise -> return space Span ("", ["mark"], []) xs -> inTags False "text:span" [("text:style-name","Highlighted")] <$> inlinesToOpenDocument o xs Span attr xs -> mkSpan attr xs LineBreak -> return $ selfClosingTag "text:line-break" [] Str s -> return $ handleSpaces $ escapeStringForXML s Emph l -> withTextStyle Italic $ inlinesToOpenDocument o l Underline l -> withTextStyle Under $ inlinesToOpenDocument o l Strong l -> withTextStyle Bold $ inlinesToOpenDocument o l Strikeout l -> withTextStyle Strike $ inlinesToOpenDocument o l Superscript l -> withTextStyle Sup $ inlinesToOpenDocument o l Subscript l -> withTextStyle Sub $ inlinesToOpenDocument o l SmallCaps l -> withTextStyle SmallC $ inlinesToOpenDocument o l Quoted t l -> inQuotes t <$> inlinesToOpenDocument o l Code attrs s -> case writerHighlightMethod o of Skylighting {} -> case highlight (writerSyntaxMap o) formatOpenDocument attrs s of Right h -> inlinedCode $ mconcat $ mconcat h Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg unhighlighted s _ -> unhighlighted s Math t s -> lift (texMathToInlines t s) >>= inlinesToOpenDocument o Cite _ l -> inlinesToOpenDocument o l RawInline f s -> if f == Format "opendocument" then return $ text $ T.unpack s else do report $ InlineNotRendered ils return empty Link _ l (s,t) -> do identTypes <- gets stIdentTypes mkLink o identTypes s t <$> inlinesToOpenDocument o l Image attr _ (s,t) -> mkImg attr s t Note l -> mkNote l where unhighlighted s = inlinedCode $ preformatted s inlinedCode s = return $ inTags False "text:span" [("text:style-name", "Source_20_Text")] s mkImg (_, _, kvs) s _ = do id' <- gets stImageId modify (\st -> st{ stImageId = id' + 1 }) let getDims [] = [] getDims (("width", w) :xs) = ("svg:width", w) : getDims xs getDims (("rel-width", w):xs) = ("style:rel-width", w) : getDims xs getDims (("height", h):xs) = ("svg:height", h) : getDims xs getDims (("rel-height", w):xs) = ("style:rel-height", w) : getDims xs getDims (_:xs) = getDims xs return $ inTags False "draw:frame" (("draw:name", "img" <> tshow id') : getDims kvs) $ selfClosingTag "draw:image" [ ("xlink:href" , s ) , ("xlink:type" , "simple") , ("xlink:show" , "embed" ) , ("xlink:actuate", "onLoad")] mkSpan attr xs = do let (ident,_,kvs) = attr i = maybe id (\sty -> fmap (inTags False "text:span" [ ("text:style-name", sty) ])) (lookup "custom-style" kvs) . withLangFromAttr attr $ inlinesToOpenDocument o xs mkBookmarkedSpan b = if isEmpty b then selfClosingBookmark ident else inBookmarkTags ident b if T.null ident then i else fmap mkBookmarkedSpan i mkNote l = do n <- length <$> gets stNotes let footNote t = inTags False "text:note" [ ("text:id" , "ftn" <> tshow n) , ("text:note-class", "footnote" )] $ inTagsSimple "text:note-citation" (text . show $ n + 1) <> inTagsSimple "text:note-body" t nn <- footNote <$> withAlteredTextStyles (const mempty) (withParagraphStyle o "Footnote" l) addNote nn return nn formatOpenDocument :: FormatOptions -> [SourceLine] -> [[Doc Text]] formatOpenDocument _fmtOpts = map (map toHlTok) toHlTok :: Token -> Doc Text toHlTok (toktype,tok) = inTags False "text:span" [("text:style-name", T.pack $ show toktype)] $ preformatted tok preformatted :: Text -> Doc Text preformatted s = handleSpaces $ escapeStringForXML s mkLink :: WriterOptions -> [(Text,ReferenceType)] -> Text -> Text -> Doc Text -> Doc Text mkLink o identTypes s t d = let maybeIdentAndType = case T.uncons s of Just ('#', ident) -> find ((ident ==) . fst) identTypes _ -> Nothing d' = inSpanTags "Definition" d ref refType format ident = inTags False refType [ ("text:reference-format", format ), ("text:ref-name", ident) ] inlineSpace = selfClosingTag "text:s" [] bookmarkRef = ref "text:bookmark-ref" bookmarkRefNumber ident = bookmarkRef "number" ident mempty bookmarkRefName ident = bookmarkRef "text" ident d bookmarkRefNameNumber ident = bookmarkRefNumber ident <> inlineSpace <> bookmarkRefName ident bookmarkRef' | isEnabled Ext_xrefs_number o && isEnabled Ext_xrefs_name o = bookmarkRefNameNumber | isEnabled Ext_xrefs_name o = bookmarkRefName | otherwise = bookmarkRefNumber sequenceRef = ref "text:sequence-ref" sequenceRefNumber ident = sequenceRef "value" ident mempty sequenceRefName ident = sequenceRef "caption" ident d sequenceRefNameNumber ident = sequenceRefNumber ident <> inlineSpace <> sequenceRefName ident sequenceRef' | isEnabled Ext_xrefs_number o && isEnabled Ext_xrefs_name o = sequenceRefNameNumber | isEnabled Ext_xrefs_name o = sequenceRefName | otherwise = sequenceRefNumber link = inTags False "text:a" [ ("xlink:type" , "simple") , ("xlink:href" , s ) , ("office:name", t ) ] d' linkOrReference = case maybeIdentAndType of Just (ident, HeaderRef) -> bookmarkRef' ident Just (ident, TableRef) -> sequenceRef' ident Just (ident, FigureRef) -> sequenceRef' ident _ -> link in if isEnabled Ext_xrefs_name o || isEnabled Ext_xrefs_number o then linkOrReference else link bulletListStyle :: PandocMonad m => Int -> OD m (Int,(Int,[Doc Text])) bulletListStyle l = do let doStyles i = inTags True "text:list-level-style-bullet" [ ("text:level" , tshow (i + 1)) , ("text:style-name" , "Bullet_20_Symbols" ) , ("style:num-suffix", "." ) , ("text:bullet-char", T.singleton (bulletList !! i)) ] (listLevelStyle (1 + i)) bulletList = map chr $ cycle [8226,9702,9642] listElStyle = map doStyles [0..9] pn <- paraListStyle l return (pn, (l, listElStyle)) orderedListLevelStyle :: ListAttributes -> (Int, [Doc Text]) -> (Int,[Doc Text]) orderedListLevelStyle (s,n, d) (l,ls) = let suffix = case d of OneParen -> [("style:num-suffix", ")")] TwoParens -> [("style:num-prefix", "(") ,("style:num-suffix", ")")] _ -> [("style:num-suffix", ".")] format = case n of UpperAlpha -> "A" LowerAlpha -> "a" UpperRoman -> "I" LowerRoman -> "i" _ -> "1" listStyle = inTags True "text:list-level-style-number" ([ ("text:level" , tshow $ 1 + length ls ) , ("text:style-name" , "Numbering_20_Symbols") , ("style:num-format", format ) , ("text:start-value", tshow s ) ] ++ suffix) (listLevelStyle (1 + length ls)) in (l, ls ++ [listStyle]) listLevelStyle :: Int -> Doc Text listLevelStyle i = let indent = tshow (0.25 + (0.25 * fromIntegral i :: Double)) in inTags True "style:list-level-properties" [ ("text:list-level-position-and-space-mode", "label-alignment") , ("fo:text-align", "right") ] $ selfClosingTag "style:list-level-label-alignment" [ ("text:label-followed-by", "listtab") , ("text:list-tab-stop-position", indent <> "in") , ("fo:text-indent", "-0.25in") , ("fo:margin-left", indent <> "in") ] tableStyle :: Int -> Double -> [(Char,Double)] -> Doc Text tableStyle num textWidth wcs = let tableId = "Table" <> tshow (num + 1) tableWidthAttr :: [(Text,Text)] tableWidthAttr | textWidth <= 1 && textWidth > 0 = [("style:rel-width", T.pack (show (round (textWidth * 100) :: Int) <> "%"))] | otherwise = [] table = inTags True "style:style" [("style:name", tableId) ,("style:family", "table")] $ selfClosingTag "style:table-properties" (("table:align", "center") : tableWidthAttr) colStyle (c,0) = selfClosingTag "style:style" [ ("style:name" , tableId <> "." <> T.singleton c) , ("style:family", "table-column" )] colStyle (c,w) = inTags True "style:style" [ ("style:name" , tableId <> "." <> T.singleton c) , ("style:family", "table-column" )] $ selfClosingTag "style:table-column-properties" [("style:rel-column-width", T.pack $ printf "%d*" (floor $ w * 65535 :: Integer))] headerRowCellStyle = inTags True "style:style" [ ("style:name" , "TableHeaderRowCell") , ("style:family", "table-cell" )] $ selfClosingTag "style:table-cell-properties" [ ("fo:border", "none")] rowCellStyle = inTags True "style:style" [ ("style:name" , "TableRowCell") , ("style:family", "table-cell" )] $ selfClosingTag "style:table-cell-properties" [ ("fo:border", "none")] cellStyles = if num == 0 then headerRowCellStyle $$ rowCellStyle else empty columnStyles = map colStyle wcs in cellStyles $$ table $$ vcat columnStyles paraStyle :: PandocMonad m => [(Text,Text)] -> OD m Int paraStyle attrs = do pn <- (+) 1 . length <$> gets stParaStyles i <- (*) (0.5 :: Double) . fromIntegral <$> gets stIndentPara b <- gets stInDefinition t <- gets stTight let styleAttr = [ ("style:name" , "P" <> tshow pn) , ("style:family" , "paragraph" )] indentVal = flip (<>) "in" . tshow $ if b then max 0.5 i else i tight = if t then [ ("fo:margin-top" , "0in" ) , ("fo:margin-bottom" , "0in" )] else [] indent = if i /= 0 || b then [ ("fo:margin-left" , indentVal) , ("fo:margin-right" , "0in" ) , ("fo:text-indent" , "0in" ) , ("style:auto-text-indent" , "false" )] else [] attributes = indent <> tight paraProps = if null attributes then mempty else selfClosingTag "style:paragraph-properties" attributes addParaStyle $ inTags True "style:style" (styleAttr <> attrs) paraProps return pn paraStyleFromParent :: PandocMonad m => Text -> [(Text,Text)] -> OD m Int paraStyleFromParent parent attrs = do pn <- (+) 1 . length <$> gets stParaStyles let styleAttr = [ ("style:name" , "P" <> tshow pn) , ("style:family" , "paragraph") , ("style:parent-style-name", parent)] paraProps = if null attrs then mempty else selfClosingTag "style:paragraph-properties" attrs addParaStyle $ inTags True "style:style" styleAttr paraProps return pn paraListStyle :: PandocMonad m => Int -> OD m Int paraListStyle l = paraStyle [("style:parent-style-name","Text_20_body") ,("style:list-style-name", "L" <> tshow l)] paraTableStyles :: Text -> Int -> [Alignment] -> [(Text, Doc Text)] paraTableStyles _ _ [] = [] paraTableStyles t s (a:xs) | AlignRight <- a = ( pName s, res s "end" ) : paraTableStyles t (s + 1) xs | AlignCenter <- a = ( pName s, res s "center") : paraTableStyles t (s + 1) xs | otherwise = ("Table_20_" <> t, empty ) : paraTableStyles t s xs where pName sn = "P" <> tshow (sn + 1) res sn x = inTags True "style:style" [ ("style:name" , pName sn ) , ("style:family" , "paragraph" ) , ("style:parent-style-name", "Table_20_" <> t)] $ selfClosingTag "style:paragraph-properties" [ ("fo:text-align", x) , ("style:justify-single-word", "false")] data TextStyle = Italic | Bold | Under | Strike | Sub | Sup | SmallC | Pre | Language Lang deriving ( Eq,Ord ) textStyleAttr :: Map.Map Text Text -> TextStyle -> Map.Map Text Text textStyleAttr m = \case Italic -> Map.insert "fo:font-style" "italic" . Map.insert "style:font-style-asian" "italic" . Map.insert "style:font-style-complex" "italic" $ m Bold -> Map.insert "fo:font-weight" "bold" . Map.insert "style:font-weight-asian" "bold" . Map.insert "style:font-weight-complex" "bold" $ m Under -> Map.insert "style:text-underline-style" "solid" . Map.insert "style:text-underline-width" "auto" . Map.insert "style:text-underline-color" "font-color" $ m Strike -> Map.insert "style:text-line-through-style" "solid" m Sub -> Map.insert "style:text-position" "sub 58%" m Sup -> Map.insert "style:text-position" "super 58%" m SmallC -> Map.insert "fo:font-variant" "small-caps" m Pre -> Map.insert "style:font-name" "Courier New" . Map.insert "style:font-name-asian" "Courier New" . Map.insert "style:font-name-complex" "Courier New" $ m Language lang -> Map.insert "fo:language" (langLanguage lang) . maybe id (Map.insert "fo:country") (langRegion lang) $ m withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action Just l -> case parseLang l of Right lang -> withTextStyle (Language lang) action Left _ -> do report $ InvalidLang l action ================================================ FILE: src/Text/Pandoc/Writers/Org.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- | Module : Text.Pandoc.Writers.Org Copyright : © 2010-2015 Puneeth Chaganti <punchagan@gmail.com> 2010-2025 John MacFarlane <jgm@berkeley.edu> 2016-2025 Albert Krewinkel <albert+pandoc@tarleb.com> License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <albert+pandoc@tarleb.com> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to Emacs Org-Mode. Org-Mode: <http://orgmode.org> -} module Text.Pandoc.Writers.Org (writeOrg) where import Control.Monad (zipWithM) import Control.Monad.State.Strict ( StateT, gets, modify, evalStateT ) import Data.Char (isAlphaNum, isDigit) import Data.List (intersperse, partition, dropWhileEnd, transpose) import Data.List.NonEmpty (nonEmpty) import Data.Maybe (isJust) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Map as M import Text.DocLayout import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Citeproc.Locator (parseLocator, LocatorMap(..), LocatorInfo(..)) import Text.Pandoc.Walk (query) import Text.Pandoc.Writers.Shared data WriterState = WriterState { stNotes :: [[Block]] , stHasMath :: Bool , stOptions :: WriterOptions } type Org = StateT WriterState -- | Convert Pandoc to Org. writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOrg opts document = do let st = WriterState { stNotes = [], stHasMath = False, stOptions = opts } evalStateT (pandocToOrg document) st -- | Return Org representation of document. pandocToOrg :: PandocMonad m => Pandoc -> Org m Text pandocToOrg (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing metadata <- metaToContext opts blockListToOrg (fmap chomp . inlineListToOrg) meta body <- blockListToOrg blocks notes <- gets (reverse . stNotes) >>= notesToOrg hasMath <- gets stHasMath let main = body $+$ notes let context = defField "body" main . defField "math" hasMath . defField "options" (M.fromList ((if isEnabled Ext_smart_quotes opts then (("'", "t"):) else id) . (if not (isEnabled Ext_special_strings opts) then (("-", "nil"):) else id) $ ([] :: [(Text, Text)]))) . defField "option-special-strings" (isEnabled Ext_special_strings opts) $ metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Return Org representation of notes. notesToOrg :: PandocMonad m => [[Block]] -> Org m (Doc Text) notesToOrg notes = vsep <$> zipWithM noteToOrg [1..] notes -- | Return Org representation of a note. noteToOrg :: PandocMonad m => Int -> [Block] -> Org m (Doc Text) noteToOrg num note = do contents <- blockListToOrg note let marker = "[fn:" ++ show num ++ "] " return $ hang (length marker) (text marker) contents -- | Replace Unicode characters with their ASCII representation replaceSpecialStrings :: Text -> Text replaceSpecialStrings = let expand c = case c of '\x00ad' -> "\\-" '\x2013' -> "--" '\x2014' -> "---" '\x2019' -> "'" '\x2026' -> "..." _ -> T.singleton c in T.concatMap expand -- | Escape special characters for Org. escapeString :: Text -> Doc Text escapeString t | T.all isAlphaNum t = literal t | otherwise = mconcat $ map escChar (T.unpack t) where -- escape special chars with ZERO WIDTH SPACE as org manual suggests escChar c = if c == '*' || c == '#' || c == '|' then afterBreak "\x200B" <> char c else char c isRawFormat :: Format -> Bool isRawFormat f = f == Format "latex" || f == Format "tex" || f == Format "org" -- | Convert Pandoc block element to Org. blockToOrg :: PandocMonad m => Block -- ^ Block element -> Org m (Doc Text) blockToOrg (Div (_, ["cell", "code"], _) (CodeBlock attr t : bs)) = do -- ipynb code cell let (ident, classes, kvs) = attr blockListToOrg (CodeBlock (ident, classes ++ ["code"], kvs) t : bs) blockToOrg (Div (_, ["output", "execute_result"], _) [CodeBlock _attr t]) = do -- ipynb code result return $ "#+RESULTS:" $$ (prefixed ": " . vcat . map literal $ T.split (== '\n') t) blockToOrg (Div attr@(ident,_,_) bs) = do opts <- gets stOptions -- Strip off bibliography if citations enabled if ident == "refs" && isEnabled Ext_citations opts then return mempty else divToOrg attr bs blockToOrg (Plain inlines) = inlineListToOrg inlines blockToOrg (Para inlines) = do contents <- inlineListToOrg inlines return $ contents <> blankline blockToOrg (LineBlock lns) = do let splitStanza [] = [] splitStanza xs = case break (== mempty) xs of (l, []) -> [l] (l, _:r) -> l : splitStanza r let joinWithLinefeeds = nowrap . mconcat . intersperse cr let joinWithBlankLines = mconcat . intersperse blankline let prettifyStanza ls = joinWithLinefeeds <$> mapM inlineListToOrg ls contents <- joinWithBlankLines <$> mapM prettifyStanza (splitStanza lns) return $ blankline $$ "#+begin_verse" $$ nest 2 contents $$ "#+end_verse" <> blankline blockToOrg (RawBlock "html" str) = return $ blankline $$ "#+begin_html" $$ nest 2 (literal str) $$ "#+end_html" $$ blankline blockToOrg b@(RawBlock f str) | isRawFormat f = return $ literal str | otherwise = do report $ BlockNotRendered b return empty blockToOrg HorizontalRule = return $ blankline $$ "--------------" $$ blankline blockToOrg (Header level attr inlines) = do let tagName inline = case inline of Span (_, _, kv) _ -> (:[]) <$> lookup "tag-name" kv _ -> Nothing let (htext, tagsInlines) = break (isJust . tagName) inlines contents <- inlineListToOrg $ dropWhileEnd (== Space) htext columns <- writerColumns <$> gets stOptions let headerDoc = mconcat [ text $ if level > 999 then " " else replicate level '*' , literal " " , contents ] let tags = case query tagName tagsInlines of Nothing -> "" Just ts -> T.cons ':' (T.intercalate ":" ts) `T.snoc` ':' let tagsDoc = if T.null tags then empty else (<> literal tags) . text . (`replicate` ' ') . max 1 $ columns - offset headerDoc - realLength tags let drawerStr = if attr == nullAttr then empty else cr <> propertiesDrawer attr return $ nowrap (headerDoc <> tagsDoc) <> drawerStr <> cr blockToOrg (CodeBlock (ident,classes,kvs) str) = do let name = if T.null ident then empty else literal $ "#+name: " <> ident let startnum = maybe "" (\x -> " " <> trimr x) $ lookup "startFrom" kvs let numberlines = if "numberLines" `elem` classes then if "continuedSourceBlock" `elem` classes then " +n" <> startnum else " -n" <> startnum else "" let lang = case filter (`notElem` ["example","code"]) classes of [] -> Nothing l:_ -> if "code" `elem` classes -- check for ipynb code cell then Just ("jupyter-" <> pandocLangToOrg l) else Just (pandocLangToOrg l) let args = mconcat $ [ " :" <> k <> " " <> v | (k, v) <- kvs, k `notElem` ["startFrom", "org-language"]] let (beg, end) = case lang of Nothing -> ("#+begin_example" <> numberlines, "#+end_example") Just x -> ("#+begin_src " <> x <> numberlines <> args, "#+end_src") -- escape special lines let escape_line line = let (spaces, code) = T.span (\c -> c == ' ' || c == '\t') line in spaces <> (if T.isPrefixOf "#+" code || T.isPrefixOf "*" code then T.cons ',' code else code) let escaped = T.unlines . map escape_line . T.lines $ str return $ name $$ literal beg $$ literal escaped $$ literal end $$ blankline blockToOrg (BlockQuote blocks) = do contents <- blockListToOrg blocks return $ blankline $$ "#+begin_quote" $$ chomp contents $$ "#+end_quote" $$ blankline blockToOrg (Table _ blkCapt specs thead tbody tfoot) = do let (caption', _, _, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot caption'' <- inlineListToOrg caption' let caption = if null caption' then empty else "#+caption: " <> caption'' headers' <- mapM blockListToOrg headers rawRows <- mapM (mapM blockListToOrg) rows let numChars = maybe 0 maximum . nonEmpty . map offset -- FIXME: width is not being used. let widthsInChars = map numChars $ transpose (headers' : rawRows) -- FIXME: Org doesn't allow blocks with height more than 1. let hpipeBlocks blocks = hcat [beg, middle, end] where sep' = vfill " | " beg = vfill "| " end = vfill " |" middle = hcat $ intersperse sep' blocks let makeRow = hpipeBlocks . zipWith lblock widthsInChars let head' = makeRow headers' rows' <- mapM (\row -> do cols <- mapM blockListToOrg row return $ makeRow cols) rows let border ch = char '|' <> char ch <> (hcat . intersperse (char ch <> char '+' <> char ch) $ map (\l -> text $ replicate l ch) widthsInChars) <> char ch <> char '|' let body = vcat rows' let head'' = if all null headers then empty else head' $$ border '-' return $ head'' $$ body $$ caption $$ blankline blockToOrg (BulletList items) = do contents <- mapM bulletListItemToOrg items return $ (if isTightList items then vcat else vsep) contents $$ blankline blockToOrg (OrderedList (start, _, delim) items) = do let delim' = case delim of TwoParens -> OneParen x -> x let markers = take (length items) $ orderedListMarkers (start, Decimal, delim') counters = (case start of 1 -> Nothing; n -> Just n) : repeat Nothing contents <- zipWithM (\x f -> f x) items $ zipWith orderedListItemToOrg markers counters return $ (if isTightList items then vcat else vsep) contents $$ blankline blockToOrg (DefinitionList items) = do contents <- mapM definitionListItemToOrg items return $ vcat contents $$ blankline blockToOrg (Figure (ident, _, _) caption body) = do -- Represent the figure as content that can be internally linked from other -- parts of the document. capt <- case caption of Caption _ [] -> pure empty Caption _ cpt -> ("#+caption: " <>) <$> inlineListToOrg (blocksToInlines cpt) contents <- blockListToOrg body let anchor = if T.null ident then empty else "<<" <> literal ident <> ">>" return (capt $$ anchor $$ contents $$ blankline) -- | Convert bullet list item (list of blocks) to Org. bulletListItemToOrg :: PandocMonad m => [Block] -> Org m (Doc Text) bulletListItemToOrg items = do exts <- gets $ writerExtensions . stOptions contents <- blockListToOrg (taskListItemToOrg exts items) -- if list item starts with non-paragraph, it must go on -- the next line: let contents' = (case items of Plain{}:_ -> mempty Para{}:_ -> mempty _ -> cr) <> chomp contents return $ hang 2 "- " contents' $$ if null items || endsWithPlain items then cr else blankline -- | Convert ordered list item (a list of blocks) to Org. orderedListItemToOrg :: PandocMonad m => Text -- ^ marker for list item -> Maybe Int -- ^ maybe number for a counter cookie -> [Block] -- ^ list item (list of blocks) -> Org m (Doc Text) orderedListItemToOrg marker counter items = do exts <- gets $ writerExtensions . stOptions contents <- blockListToOrg (taskListItemToOrg exts items) -- if list item starts with non-paragraph, it must go on -- the next line: let contents' = (case items of Plain{}:_ -> mempty Para{}:_ -> mempty _ -> cr) <> chomp contents let cookie = maybe empty (\n -> space <> literal "[@" <> literal (tshow n) <> literal "]") counter return $ hang (T.length marker + 1) (literal marker <> cookie <> space) contents' $$ if null items || endsWithPlain items then cr else blankline -- | Convert a list item containing text starting with @U+2610 BALLOT BOX@ -- or @U+2612 BALLOT BOX WITH X@ to org checkbox syntax (e.g. @[X]@). taskListItemToOrg :: Extensions -> [Block] -> [Block] taskListItemToOrg = handleTaskListItem toOrg where toOrg (Str "☐" : Space : is) = Str "[ ]" : Space : is toOrg (Str "☒" : Space : is) = Str "[X]" : Space : is toOrg is = is -- | Convert definition list item (label, list of blocks) to Org. definitionListItemToOrg :: PandocMonad m => ([Inline], [[Block]]) -> Org m (Doc Text) definitionListItemToOrg (label, defs) = do label' <- inlineListToOrg label contents <- vcat <$> mapM blockListToOrg defs return $ hang 2 "- " (label' <> " :: " <> contents) $$ if isTightList defs then cr else blankline -- | Convert list of key/value pairs to Org :PROPERTIES: drawer. propertiesDrawer :: Attr -> Doc Text propertiesDrawer (ident, classes, kv) = let drawerStart = text ":PROPERTIES:" drawerEnd = text ":END:" kv' = if classes == mempty then kv else ("CLASS", T.unwords classes):kv kv'' = if ident == mempty then kv' else ("CUSTOM_ID", ident):kv' properties = vcat $ map kvToOrgProperty kv'' in drawerStart <> cr <> properties <> cr <> drawerEnd where kvToOrgProperty :: (Text, Text) -> Doc Text kvToOrgProperty (key, value) = text ":" <> literal key <> text ": " <> literal value <> cr -- | The different methods to represent a Div block. data DivBlockType = GreaterBlock Text Attr -- ^ Greater block like @center@ or @quote@. | Drawer Text Attr -- ^ Org drawer with of given name; keeps -- key-value pairs. | UnwrappedWithAnchor Text -- ^ Not mapped to other type, only -- identifier is retained (if any). deriving (Show) -- | Gives the most suitable method to render a list of blocks -- with attributes. divBlockType :: Attr-> DivBlockType divBlockType (ident, classes, kvs) -- if any class is named "drawer", then output as org :drawer: | ([_], drawerName:classes') <- partition (== "drawer") classes = Drawer drawerName (ident, classes', kvs) -- if any class is either @center@ or @quote@, then use a org block. | (blockName:classes'', classes') <- partition isGreaterBlockClass classes = GreaterBlock blockName (ident, classes' <> classes'', kvs) -- if no better method is found, unwrap div and set anchor | otherwise = UnwrappedWithAnchor ident where isGreaterBlockClass :: Text -> Bool isGreaterBlockClass t = case T.toLower t of "center" -> True "quote" -> True x -> isAdmonition x isAdmonition :: Text -> Bool isAdmonition "warning" = True isAdmonition "important" = True isAdmonition "tip" = True isAdmonition "note" = True isAdmonition "caution" = True isAdmonition _ = False -- | Converts a Div to an org-mode element. divToOrg :: PandocMonad m => Attr -> [Block] -> Org m (Doc Text) divToOrg attr bs = do case divBlockType attr of GreaterBlock blockName attr' -> do -- Write as greater block. The ID, if present, is added via -- the #+name keyword; other classes and key-value pairs -- are kept as #+attr_html attributes. contents <- case bs of (Div ("",["title"],[]) _ : bs') | isAdmonition blockName -> blockListToOrg bs' _ -> blockListToOrg bs return $ blankline $$ attrHtml attr' $$ "#+begin_" <> literal blockName $$ chomp contents $$ "#+end_" <> literal blockName $$ blankline Drawer drawerName (_,_,kvs) -> do contents <- blockListToOrg bs -- Write as drawer. Only key-value pairs are retained. let keys = vcat $ map (\(k,v) -> ":" <> literal k <> ":" <> space <> literal v) kvs return $ ":" <> literal drawerName <> ":" $$ cr $$ keys $$ blankline $$ contents $$ blankline $$ text ":END:" $$ blankline UnwrappedWithAnchor ident -> do contents <- blockListToOrg bs -- Unwrap the div. All attributes are discarded, except for -- the identifier, which is added as an anchor before the -- div contents. let contents' = if T.null ident then contents else "<<" <> literal ident <> ">>" $$ contents return (blankline $$ contents' $$ blankline) attrHtml :: Attr -> Doc Text attrHtml ("" , [] , []) = mempty attrHtml (ident, classes, kvs) = let name = if T.null ident then mempty else "#+name: " <> literal ident <> cr keyword = "#+attr_html" addClassKv = if null classes then id else (("class", T.unwords classes):) kvStrings = map (\(k,v) -> ":" <> k <> " " <> v) (addClassKv kvs) in name <> if null kvStrings then mempty else keyword <> ": " <> literal (T.unwords kvStrings) <> cr -- | Convert list of Pandoc block elements to Org. blockListToOrg :: PandocMonad m => [Block] -- ^ List of block elements -> Org m (Doc Text) blockListToOrg blocks = vcat <$> mapM blockToOrg blocks -- | Convert list of Pandoc inline elements to Org. inlineListToOrg :: PandocMonad m => [Inline] -> Org m (Doc Text) inlineListToOrg lst = hcat <$> mapM inlineToOrg (fixMarkers lst) where -- Prevent note refs and list markers from wrapping, see #4171 -- and #7132. fixMarkers [] = [] fixMarkers (Space : x : rest) | shouldFix x = Str " " : x : fixMarkers rest fixMarkers (SoftBreak : x : rest) | shouldFix x = Str " " : x : fixMarkers rest fixMarkers (x : rest) = x : fixMarkers rest shouldFix Note{} = True -- Prevent footnotes shouldFix (Str "-") = True -- Prevent bullet list items shouldFix (Str x) -- Prevent ordered list items | Just (cs, c) <- T.unsnoc x = T.all isDigit cs && (c == '.' || c == ')') shouldFix _ = False -- | Convert Pandoc inline element to Org. inlineToOrg :: PandocMonad m => Inline -> Org m (Doc Text) inlineToOrg (Span (uid, [], []) []) = return $ "<<" <> literal uid <> ">>" inlineToOrg (Span _ lst) = inlineListToOrg lst inlineToOrg (Emph lst) = do contents <- inlineListToOrg lst return $ "/" <> contents <> "/" inlineToOrg (Underline lst) = do contents <- inlineListToOrg lst return $ "_" <> contents <> "_" inlineToOrg (Strong lst) = do contents <- inlineListToOrg lst return $ "*" <> contents <> "*" inlineToOrg (Strikeout lst) = do contents <- inlineListToOrg lst return $ "+" <> contents <> "+" inlineToOrg (Superscript lst) = do contents <- inlineListToOrg lst return $ "^{" <> contents <> "}" inlineToOrg (Subscript lst) = do contents <- inlineListToOrg lst return $ "_{" <> contents <> "}" inlineToOrg (SmallCaps lst) = inlineListToOrg lst inlineToOrg (Quoted SingleQuote lst) = do contents <- inlineListToOrg lst opts <- gets stOptions return $ if isEnabled Ext_smart opts || isEnabled Ext_smart_quotes opts then "'" <> contents <> "'" else "‘" <> contents <> "’" inlineToOrg (Quoted DoubleQuote lst) = do contents <- inlineListToOrg lst opts <- gets stOptions return $ if isEnabled Ext_smart opts || isEnabled Ext_smart_quotes opts then "\"" <> contents <> "\"" else "“" <> contents <> "”" inlineToOrg (Cite cs lst) = do opts <- gets stOptions if isEnabled Ext_citations opts then do let renderCiteItem c = do citePref <- inlineListToOrg (citationPrefix c) let (locinfo, suffix) = parseLocator locmap (citationSuffix c) citeSuff <- inlineListToOrg suffix let locator = case locinfo of Just info -> literal $ T.replace "\160" " " $ T.replace "{" "" $ T.replace "}" "" $ locatorRaw info Nothing -> mempty return $ hsep [ citePref , ("@" <> literal (citationId c)) , locator , citeSuff ] citeItems <- mconcat . intersperse "; " <$> mapM renderCiteItem cs let sty = case cs of (d:_) | citationMode d == AuthorInText -> literal "/t" [d] | citationMode d == SuppressAuthor -> literal "/na" _ -> mempty return $ "[cite" <> sty <> ":" <> citeItems <> "]" else inlineListToOrg lst inlineToOrg (Code _ str) = return $ "=" <> literal str <> "=" inlineToOrg (Str str) = do opts <- gets stOptions let str' = if isEnabled Ext_smart opts || isEnabled Ext_special_strings opts then replaceSpecialStrings str else str return $ escapeString str' inlineToOrg (Math t str) = do modify $ \st -> st{ stHasMath = True } return $ if t == InlineMath then "\\(" <> literal str <> "\\)" else "\\[" <> literal str <> "\\]" inlineToOrg il@(RawInline f str) | elem f ["tex", "latex"] && T.isPrefixOf "\\begin" str = return $ cr <> literal str <> cr | isRawFormat f = return $ literal str | otherwise = do report $ InlineNotRendered il return empty inlineToOrg LineBreak = return (text "\\\\" <> cr) inlineToOrg Space = return space inlineToOrg SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of WrapPreserve -> return cr WrapAuto -> return space WrapNone -> return space inlineToOrg (Link _ txt (src, _)) = case txt of [Str x] | escapeURI x == src -> -- autolink return $ "[[" <> literal (orgPath x) <> "]]" _ -> do contents <- nowrap <$> inlineListToOrg txt return $ "[[" <> literal (orgPath src) <> "][" <> contents <> "]]" inlineToOrg (Image _ _ (source, _)) = return $ "[[" <> literal (orgPath source) <> "]]" inlineToOrg (Note contents) = do -- add to notes in state notes <- gets stNotes modify $ \st -> st { stNotes = contents:notes } let ref = tshow $ length notes + 1 return $ "[fn:" <> literal ref <> "]" orgPath :: Text -> Text orgPath src = case T.uncons src of Nothing -> "" -- wiki link Just ('#', _) -> src -- internal link _ | isUrl src -> src _ | isFilePath src -> src _ -> "file:" <> src where isFilePath :: Text -> Bool isFilePath cs = any (`T.isPrefixOf` cs) ["/", "./", "../", "file:"] isUrl :: Text -> Bool isUrl cs = let (scheme, path) = T.break (== ':') cs in T.all (\c -> isAlphaNum c || T.any (== c) ".-") scheme && not (T.null path) -- | Translate from pandoc's programming language identifiers to those used by -- org-mode. pandocLangToOrg :: Text -> Text pandocLangToOrg cs = case cs of "c" -> "C" "commonlisp" -> "lisp" "r" -> "R" "bash" -> "sh" _ -> cs -- taken from oc-csl.el in the org source tree: locmap :: LocatorMap locmap = LocatorMap $ M.fromList [ ("bk." , "book") , ("bks." , "book") , ("book" , "book") , ("chap." , "chapter") , ("chaps." , "chapter") , ("chapter" , "chapter") , ("col." , "column") , ("cols." , "column") , ("column" , "column") , ("figure" , "figure") , ("fig." , "figure") , ("figs." , "figure") , ("folio" , "folio") , ("fol." , "folio") , ("fols." , "folio") , ("number" , "number") , ("no." , "number") , ("nos." , "number") , ("line" , "line") , ("l." , "line") , ("ll." , "line") , ("note" , "note") , ("n." , "note") , ("nn." , "note") , ("opus" , "opus") , ("op." , "opus") , ("opp." , "opus") , ("page" , "page") , ("p" , "page") , ("p." , "page") , ("pp." , "page") , ("paragraph" , "paragraph") , ("para." , "paragraph") , ("paras." , "paragraph") , ("¶" , "paragraph") , ("¶¶" , "paragraph") , ("part" , "part") , ("pt." , "part") , ("pts." , "part") , ("§" , "section") , ("§§" , "section") , ("section" , "section") , ("sec." , "section") , ("secs." , "section") , ("sub verbo" , "sub verbo") , ("s.v." , "sub verbo") , ("s.vv." , "sub verbo") , ("verse" , "verse") , ("v." , "verse") , ("vv." , "verse") , ("volume" , "volume") , ("vol." , "volume") , ("vols." , "volume") ] ================================================ FILE: src/Text/Pandoc/Writers/Powerpoint/Output.hs ================================================ {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc.Writers.Powerpoint.Output Copyright : Copyright (C) 2017-2020 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> Stability : alpha Portability : portable Conversion of Presentation datatype (defined in Text.Pandoc.Writers.Powerpoint.Presentation) to a zip archive. -} module Text.Pandoc.Writers.Powerpoint.Output ( presentationToArchive ) where import Control.Monad ( MonadPlus(mplus), foldM, unless ) import Control.Monad.Except (throwError, catchError) import Control.Monad.Reader ( asks, MonadReader(local), ReaderT(runReaderT) ) import Control.Monad.State ( StateT, gets, modify, evalStateT ) import Codec.Archive.Zip import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf, intersperse) import Data.Bifunctor (bimap) import Data.CaseInsensitive (CI) import qualified Data.CaseInsensitive as CI import Data.Default import Data.Foldable (toList) import Data.List.NonEmpty (nonEmpty, NonEmpty ((:|))) import Data.Ratio ((%), Ratio) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Read (decimal) import Data.Time (formatTime, defaultTimeLocale) import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) import Data.Traversable (for) import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension, takeFileName) import Text.Pandoc.XML.Light as XML import Text.Pandoc.Definition import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Error (PandocError(..)) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Data (readDataFile, readDefaultDataFile) import Text.Pandoc.Options import Text.Pandoc.MIME import qualified Data.ByteString.Lazy as BL import Text.Pandoc.Writers.Shared (metaToContext) import Text.Pandoc.Writers.OOXML import qualified Data.Map as M import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, maybeToList, catMaybes, isJust) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) import System.FilePath.Glob import Text.DocTemplates (FromContext(lookupContext), Context) import Text.DocLayout (literal) import Text.TeXMath import Text.Pandoc.Logging (LogMessage(PowerpointTemplateWarning)) import Text.Pandoc.Writers.Math (convertMath) import Text.Pandoc.Writers.Powerpoint.Presentation import Text.Pandoc.Shared (tshow, stringify) import Skylighting (fromColor) -- |The 'EMU' type is used to specify sizes in English Metric Units. type EMU = Integer -- |The 'pixelsToEmu' function converts a size in pixels to one -- in English Metric Units. It assumes a DPI of 72. pixelsToEmu :: Pixels -> EMU pixelsToEmu = (12700 *) -- This populates the global ids map with images already in the -- template, so the ids won't be used by images introduced by the -- user. initialGlobalIds :: Archive -> Archive -> M.Map FilePath Int initialGlobalIds refArchive distArchive = let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive mediaPaths = filter (isPrefixOf "ppt/media/image") archiveFiles go :: FilePath -> Maybe (FilePath, Int) go fp = do s <- stripPrefix "ppt/media/image" $ fst $ splitExtension fp (n, _) <- listToMaybe $ reads s return (fp, n) in M.fromList $ mapMaybe go mediaPaths getPresentationSize :: Archive -> Archive -> Maybe (Integer, Integer) getPresentationSize refArchive distArchive = do entry <- findEntryByPath "ppt/presentation.xml" refArchive `mplus` findEntryByPath "ppt/presentation.xml" distArchive presElement <- either (const Nothing) return $ parseXMLElement $ UTF8.toTextLazy $ fromEntry entry let ns = elemToNameSpaces presElement sldSize <- findChild (elemName ns "p" "sldSz") presElement cxS <- findAttr (QName "cx" Nothing Nothing) sldSize cyS <- findAttr (QName "cy" Nothing Nothing) sldSize cx <- readTextAsInteger cxS cy <- readTextAsInteger cyS return (cx `div` 12700, cy `div` 12700) readTextAsInteger :: Text -> Maybe Integer readTextAsInteger = either (const Nothing) (Just . fst) . Data.Text.Read.decimal data WriterEnv = WriterEnv { envRefArchive :: Archive , envDistArchive :: Archive , envUTCTime :: UTCTime , envOpts :: WriterOptions , envContext :: Context Text , envPresentationSize :: (Integer, Integer) , envSlideHasHeader :: Bool , envInList :: Bool , envInNoteSlide :: Bool , envCurSlideId :: Int , envPlaceholder :: Placeholder , envSlideIdMap :: M.Map SlideId Int -- maps the slide number to the -- corresponding notes id number. If there -- are no notes for a slide, there will be -- no entry in the map for it. , envSpeakerNotesIdMap :: M.Map Int Int , envInSpeakerNotes :: Bool , envSlideLayouts :: Maybe SlideLayouts , envOtherStyleIndents :: Maybe Indents } deriving (Show) instance Default WriterEnv where def = WriterEnv { envRefArchive = emptyArchive , envDistArchive = emptyArchive , envUTCTime = posixSecondsToUTCTime 0 , envOpts = def , envContext = mempty , envPresentationSize = (720, 540) , envSlideHasHeader = False , envInList = False , envInNoteSlide = False , envCurSlideId = 1 , envPlaceholder = Placeholder ObjType 0 , envSlideIdMap = mempty , envSpeakerNotesIdMap = mempty , envInSpeakerNotes = False , envSlideLayouts = Nothing , envOtherStyleIndents = Nothing } type SlideLayouts = SlideLayoutsOf SlideLayout data SlideLayoutsOf a = SlideLayouts { metadata :: a , title :: a , content :: a , twoColumn :: a , comparison :: a , contentWithCaption :: a , blank :: a } deriving (Show, Eq, Functor, Foldable, Traversable) data SlideLayout = SlideLayout { slElement :: Element , slInReferenceDoc :: Bool -- ^ True if the layout is in the provided reference doc, False if it's in -- the default reference doc. , slPath :: FilePath , slEntry :: Entry } deriving (Show) getSlideLayouts :: PandocMonad m => P m SlideLayouts getSlideLayouts = asks envSlideLayouts >>= maybe (throwError e) pure where e = PandocSomeError ("Slide layouts aren't defined, even though they should " <> "always be. This is a bug in pandoc.") -- | A placeholder within a layout, identified by type and index. -- -- E.g., @Placeholder ObjType 2@ is the third placeholder of type 'ObjType' in -- the layout. data Placeholder = Placeholder { placeholderType :: PHType , index :: Int } deriving (Show, Eq) -- | Paragraph indentation info. data Indents = Indents { level1 :: Maybe LevelIndents , level2 :: Maybe LevelIndents , level3 :: Maybe LevelIndents , level4 :: Maybe LevelIndents , level5 :: Maybe LevelIndents , level6 :: Maybe LevelIndents , level7 :: Maybe LevelIndents , level8 :: Maybe LevelIndents , level9 :: Maybe LevelIndents } deriving (Show, Eq) levelIndent :: Indents -> Int -> Maybe LevelIndents levelIndent is index = getter is where getter = case index of 0 -> level1 1 -> level2 2 -> level3 3 -> level4 4 -> level5 5 -> level6 6 -> level7 7 -> level8 8 -> level9 _ -> const Nothing data LevelIndents = LevelIndents { marL :: EMU , indent :: EMU } deriving (Show, Eq) data MediaInfo = MediaInfo { mInfoFilePath :: FilePath , mInfoLocalId :: Int , mInfoGlobalId :: Int , mInfoMimeType :: Maybe MimeType , mInfoExt :: Maybe T.Text , mInfoCaption :: Bool } deriving (Show, Eq) data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int LinkTarget) -- (FP, Local ID, Global ID, Maybe Mime) , stMediaIds :: M.Map Int [MediaInfo] , stMediaGlobalIds :: M.Map FilePath Int , stFooterInfo :: Maybe FooterInfo } deriving (Show, Eq) instance Default WriterState where def = WriterState { stLinkIds = mempty , stMediaIds = mempty , stMediaGlobalIds = mempty , stFooterInfo = Nothing } type P m = ReaderT WriterEnv (StateT WriterState m) runP :: Monad m => WriterEnv -> WriterState -> P m a -> m a runP env st p = evalStateT (runReaderT p env) st -------------------------------------------------------------------- monospaceFont :: Monad m => P m T.Text monospaceFont = do vars <- asks envContext case lookupContext "monofont" vars of Just s -> return s Nothing -> return "Courier" fontSizeAttributes :: Monad m => RunProps -> P m [(Text, Text)] fontSizeAttributes RunProps { rPropForceSize = Just sz } = return [("sz", tshow $ sz * 100)] fontSizeAttributes _ = return [] copyFileToArchive :: PandocMonad m => Archive -> FilePath -> P m Archive copyFileToArchive arch fp = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive case findEntryByPath fp refArchive `mplus` findEntryByPath fp distArchive of Nothing -> throwError $ PandocSomeError $ T.pack $ fp <> " missing in reference file" Just e -> return $ addEntryToArchive e arch alwaysInheritedPatterns :: [Pattern] alwaysInheritedPatterns = map compile [ "docProps/app.xml" , "ppt/slideLayouts/slideLayout*.xml" , "ppt/slideLayouts/_rels/slideLayout*.xml.rels" , "ppt/slideMasters/slideMaster1.xml" , "ppt/slideMasters/_rels/slideMaster1.xml.rels" , "ppt/theme/theme*.xml" , "ppt/theme/_rels/theme*.xml.rels" , "ppt/presProps.xml" , "ppt/tableStyles.xml" , "ppt/media/image*" , "ppt/fonts/*" ] -- We only look for these under special conditions contingentInheritedPatterns :: Presentation -> [Pattern] contingentInheritedPatterns pres = [] <> if presHasSpeakerNotes pres then map compile [ "ppt/notesMasters/notesMaster*.xml" , "ppt/notesMasters/_rels/notesMaster*.xml.rels" ] else [] inheritedPatterns :: Presentation -> [Pattern] inheritedPatterns pres = alwaysInheritedPatterns <> contingentInheritedPatterns pres patternToFilePaths :: PandocMonad m => Pattern -> P m [FilePath] patternToFilePaths pat = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive return $ filter (match pat) archiveFiles patternsToFilePaths :: PandocMonad m => [Pattern] -> P m [FilePath] patternsToFilePaths pats = concat <$> mapM patternToFilePaths pats -- Here are the files we'll require to make a Powerpoint document. If -- any of these are missing, we should error out of our build. requiredFiles :: [FilePath] requiredFiles = [ "docProps/app.xml" , "ppt/presProps.xml" , "ppt/slideLayouts/slideLayout1.xml" , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" , "ppt/slideLayouts/slideLayout2.xml" , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" , "ppt/slideLayouts/slideLayout3.xml" , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" , "ppt/slideLayouts/slideLayout4.xml" , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" , "ppt/slideMasters/slideMaster1.xml" , "ppt/slideMasters/_rels/slideMaster1.xml.rels" , "ppt/theme/theme1.xml" , "ppt/tableStyles.xml" ] presentationToArchiveP :: PandocMonad m => Presentation -> P m Archive presentationToArchiveP p@(Presentation docProps slides) = do filePaths <- patternsToFilePaths $ inheritedPatterns p -- make sure all required files are available: let missingFiles = filter (`notElem` filePaths) requiredFiles unless (null missingFiles) (throwError $ PandocSomeError $ "The following required files are missing:\n" <> T.unlines (map (T.pack . (" " <>)) missingFiles) ) newArch <- foldM copyFileToArchive emptyArchive filePaths -- Add any layouts taken from the default archive, -- overwriting any already added. slideLayouts <- getSlideLayouts let f layout = if not (slInReferenceDoc layout) then addEntryToArchive (slEntry layout) else id let newArch' = foldr f newArch slideLayouts master <- getMaster refArchive <- asks envRefArchive distArchive <- asks envDistArchive presentationElement <- parseXml refArchive distArchive "ppt/presentation.xml" modify (\s -> s {stFooterInfo = getFooterInfo (dcDate docProps) slideLayouts master presentationElement }) -- Update the master to make sure it includes any layouts we've just added masterRels <- getMasterRels let (updatedMasterElem, updatedMasterRelElem) = updateMasterElems slideLayouts master masterRels updatedMasterEntry <- elemToEntry "ppt/slideMasters/slideMaster1.xml" updatedMasterElem updatedMasterRelEntry <- elemToEntry "ppt/slideMasters/_rels/slideMaster1.xml.rels" updatedMasterRelElem -- we make a modified ppt/viewProps.xml out of the presentation viewProps viewPropsEntry <- makeViewPropsEntry -- we make a docProps/core.xml entry out of the presentation docprops docPropsEntry <- docPropsToEntry docProps -- we make a docProps/custom.xml entry out of the custom properties docCustomPropsEntry <- docCustomPropsToEntry docProps -- we make this ourself in case there's something unexpected in the -- one in the reference doc. relsEntry <- topLevelRelsEntry -- presentation entry and rels. (presentationRIdUpdateData, presRelsEntry) <- presentationToRelsEntry p presEntry <- presentationToPresEntry presentationRIdUpdateData p slideEntries <- mapM slideToEntry slides slideRelEntries <- mapM slideToSlideRelEntry slides spkNotesEntries <- catMaybes <$> mapM slideToSpeakerNotesEntry slides spkNotesRelEntries <- catMaybes <$> mapM slideToSpeakerNotesRelEntry slides -- These have to come after everything, because they need the info -- built up in the state. mediaEntries <- makeMediaEntries contentTypesEntry <- presentationToContentTypes p >>= contentTypesToEntry -- fold everything into our inherited archive and return it. return $ foldr addEntryToArchive newArch' $ slideEntries <> slideRelEntries <> spkNotesEntries <> spkNotesRelEntries <> mediaEntries <> [updatedMasterEntry, updatedMasterRelEntry] <> [contentTypesEntry, docPropsEntry, docCustomPropsEntry, relsEntry, presEntry, presRelsEntry, viewPropsEntry] updateMasterElems :: SlideLayouts -> Element -> Element -> (Element, Element) updateMasterElems layouts master masterRels = (updatedMaster, updatedMasterRels) where updatedMaster = master { elContent = updateSldLayoutIdLst <$> elContent master } (updatedRelationshipIds, updatedMasterRels) = addLayoutRels masterRels updateSldLayoutIdLst :: Content -> Content updateSldLayoutIdLst (Elem e) = case elName e of (QName "sldLayoutIdLst" _ _) -> let mkChild relationshipId (lastId, children) = let thisId = lastId + 1 newChild = Element { elName = QName "sldLayoutId" Nothing (Just "p") , elAttribs = [ Attr (QName "id" Nothing Nothing) (T.pack (show thisId)) , Attr (QName "id" Nothing (Just "r")) relationshipId ] , elContent = [] , elLine = Nothing } in (thisId, Elem newChild : children) newChildren = snd (foldr mkChild (maxIdNumber' e, []) updatedRelationshipIds) in Elem e { elContent = elContent e <> newChildren } _ -> Elem e updateSldLayoutIdLst c = c addLayoutRels :: Element -> ([Text], Element) addLayoutRels e = let layoutsToAdd = filter (\l -> not (slInReferenceDoc l) && isNew e l) (toList layouts) newRelationships = snd (foldr mkRelationship (maxIdNumber e, []) layoutsToAdd) newRelationshipIds = mapMaybe (findElemAttr (QName "Id" Nothing Nothing)) newRelationships mkRelationship layout (lastId, relationships) = let thisId = lastId + 1 slideLayoutPath = "../slideLayouts/" <> T.pack (takeFileName (slPath layout)) newRelationship = Element { elName = QName "Relationship" Nothing Nothing , elAttribs = [ Attr (QName "Id" Nothing Nothing) ("rId" <> T.pack (show thisId)) , Attr (QName "Type" Nothing Nothing) "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" , Attr (QName "Target" Nothing Nothing) slideLayoutPath ] , elContent = [] , elLine = Nothing } in (thisId, Elem newRelationship : relationships) in (newRelationshipIds, e {elContent = elContent e <> newRelationships}) -- Whether the layout needs to be added to the Relationships element. isNew :: Element -> SlideLayout -> Bool isNew relationships SlideLayout{..} = let toDetails = fmap (takeFileName . T.unpack) . findElemAttr (QName "Target" Nothing Nothing) in takeFileName slPath `notElem` mapMaybe toDetails (elContent relationships) findElemAttr :: QName -> Content -> Maybe Text findElemAttr attr (Elem e) = findAttr attr e findElemAttr _ _ = Nothing maxIdNumber :: Element -> Integer maxIdNumber relationships = maximum (0 : idNumbers) where idNumbers = mapMaybe (readTextAsInteger . T.drop 3) idAttributes idAttributes = mapMaybe getIdAttribute (elContent relationships) getIdAttribute (Elem e) = findAttr (QName "Id" Nothing Nothing) e getIdAttribute _ = Nothing maxIdNumber' :: Element -> Integer maxIdNumber' sldLayouts = maximum (0 : idNumbers) where idNumbers = mapMaybe readTextAsInteger idAttributes idAttributes = mapMaybe getIdAttribute (elContent sldLayouts) getIdAttribute (Elem e) = findAttr (QName "id" Nothing Nothing) e getIdAttribute _ = Nothing data FooterInfo = FooterInfo { fiDate :: SlideLayoutsOf (Maybe Element) , fiFooter :: SlideLayoutsOf (Maybe Element) , fiSlideNumber :: SlideLayoutsOf (Maybe Element) , fiShowOnFirstSlide :: Bool } deriving (Show, Eq) getFooterInfo :: Maybe Text -> SlideLayouts -> Element -> Element -> Maybe FooterInfo getFooterInfo date layouts master presentation = do let ns = elemToNameSpaces master hf <- findChild (elemName ns "p" "hf") master let fiDate = let f layoutDate = case date of Nothing -> layoutDate Just d -> if dateIsAutomatic (elemToNameSpaces layoutDate) layoutDate then layoutDate else replaceDate d layoutDate in fmap f . getShape "dt" hf . slElement <$> layouts fiFooter = getShape "ftr" hf . slElement <$> layouts fiSlideNumber = getShape "sldNum" hf . slElement <$> layouts fiShowOnFirstSlide = fromMaybe True (getBooleanAttribute "showSpecialPlsOnTitleSld" presentation) pure FooterInfo{..} where getShape t hf layout = if fromMaybe True (getBooleanAttribute t hf) then do let ns = elemToNameSpaces layout cSld <- findChild (elemName ns "p" "cSld") layout spTree <- findChild (elemName ns "p" "spTree") cSld let containsPlaceholder sp = fromMaybe False $ do nvSpPr <- findChild (elemName ns "p" "nvSpPr") sp nvPr <- findChild (elemName ns "p" "nvPr") nvSpPr ph <- findChild (elemName ns "p" "ph") nvPr placeholderType <- findAttr (QName "type" Nothing Nothing) ph pure (placeholderType == t) listToMaybe (filterChildren containsPlaceholder spTree) else Nothing dateIsAutomatic :: NameSpaces -> Element -> Bool dateIsAutomatic ns shape = isJust $ do txBody <- findChild (elemName ns "p" "txBody") shape p <- findChild (elemName ns "a" "p") txBody findChild (elemName ns "a" "fld") p replaceDate :: Text -> Element -> Element replaceDate newDate e = e { elContent = case (elName e) of QName "t" _ (Just "a") -> [ Text (CData { cdVerbatim = CDataText , cdData = newDate , cdLine = Nothing }) ] _ -> ifElem (replaceDate newDate) <$> elContent e } ifElem :: (Element -> Element) -> (Content -> Content) ifElem f (Elem e) = Elem (f e) ifElem _ c = c getBooleanAttribute t e = (`elem` ["1", "true"]) <$> (findAttr (QName t Nothing Nothing) e) footerElements :: PandocMonad m => (forall a. SlideLayoutsOf a -> a) -> P m [Content] footerElements layout = do footerInfo <- gets stFooterInfo pure $ Elem <$> (toList (footerInfo >>= layout . fiDate) <> toList (footerInfo >>= layout . fiFooter) <> toList (footerInfo >>= layout . fiSlideNumber)) makeSlideIdMap :: Presentation -> M.Map SlideId Int makeSlideIdMap (Presentation _ slides) = M.fromList $ map slideId slides `zip` [1..] makeSpeakerNotesMap :: Presentation -> M.Map Int Int makeSpeakerNotesMap (Presentation _ slides) = M.fromList $ mapMaybe f (slides `zip` [1..]) `zip` [1..] where f (Slide _ _ notes _, n) = if notes == mempty then Nothing else Just n presentationToArchive :: PandocMonad m => WriterOptions -> Meta -> Presentation -> m Archive presentationToArchive opts meta pres = do distArchive <- toArchive . BL.fromStrict <$> readDefaultDataFile "reference.pptx" refArchive <- case writerReferenceDoc opts of Just f -> toArchive . BL.fromStrict . fst <$> P.fetchItem (T.pack f) Nothing -> toArchive . BL.fromStrict <$> readDataFile "reference.pptx" let (referenceLayouts, defaultReferenceLayouts) = (getLayoutsFromArchive refArchive, getLayoutsFromArchive distArchive) let layoutTitles = SlideLayouts { metadata = "Title Slide" :: Text , title = "Section Header" , content = "Title and Content" , twoColumn = "Two Content" , comparison = "Comparison" , contentWithCaption = "Content with Caption" , blank = "Blank" } layouts <- for layoutTitles $ \layoutTitle -> do let layout = M.lookup (CI.mk layoutTitle) referenceLayouts let defaultLayout = M.lookup (CI.mk layoutTitle) defaultReferenceLayouts case (layout, defaultLayout) of (Nothing, Nothing) -> throwError (PandocSomeError ("Couldn't find layout named \"" <> layoutTitle <> "\" in the provided " <> "reference doc or in the default " <> "reference doc included with pandoc.")) (Nothing, Just ((element, path, entry) :| _)) -> do P.report (PowerpointTemplateWarning ("Couldn't find layout named \"" <> layoutTitle <> "\" in provided " <> "reference doc. Falling back to " <> "the default included with pandoc.")) pure SlideLayout { slElement = element , slPath = path , slEntry = entry , slInReferenceDoc = False } (Just ((element, path, entry) :| _), _ ) -> pure SlideLayout { slElement = element , slPath = path , slEntry = entry , slInReferenceDoc = True } master <- getMaster' refArchive distArchive let otherStyleIndents = do let ns = elemToNameSpaces master txStyles <- findChild (elemName ns "p" "txStyles") master otherStyle <- findChild (elemName ns "p" "otherStyle") txStyles let makeLevelIndents name = do e <- findChild (elemName ns "a" name) otherStyle pure LevelIndents { indent = fromMaybe (-342900) (findAttr (QName "indent" Nothing Nothing) e >>= readTextAsInteger) , marL = fromMaybe 347663 (findAttr (QName "marL" Nothing Nothing) e >>= readTextAsInteger) } pure Indents { level1 = makeLevelIndents "lvl1pPr" , level2 = makeLevelIndents "lvl2pPr" , level3 = makeLevelIndents "lvl3pPr" , level4 = makeLevelIndents "lvl4pPr" , level5 = makeLevelIndents "lvl5pPr" , level6 = makeLevelIndents "lvl6pPr" , level7 = makeLevelIndents "lvl7pPr" , level8 = makeLevelIndents "lvl8pPr" , level9 = makeLevelIndents "lvl9pPr" } utctime <- P.getTimestamp presSize <- case getPresentationSize refArchive distArchive of Just sz -> return sz Nothing -> throwError $ PandocSomeError "Could not determine presentation size" -- note, we need writerTemplate to be Just _ or metaToContext does -- nothing context <- metaToContext opts{ writerTemplate = writerTemplate opts <|> Just mempty } (return . literal . stringify) (return . literal . stringify) meta let env = def { envRefArchive = refArchive , envDistArchive = distArchive , envUTCTime = utctime , envOpts = opts , envContext = context , envPresentationSize = presSize , envSlideIdMap = makeSlideIdMap pres , envSpeakerNotesIdMap = makeSpeakerNotesMap pres , envSlideLayouts = Just layouts , envOtherStyleIndents = otherStyleIndents } let st = def { stMediaGlobalIds = initialGlobalIds refArchive distArchive } runP env st $ presentationToArchiveP pres -- | Get all slide layouts from an archive, as a map where the layout's name -- gives the map key. -- -- For each layout, the map contains its XML representation, its path within -- the archive, and the archive entry. getLayoutsFromArchive :: Archive -> M.Map (CI Text) (NonEmpty (Element, FilePath, Entry)) getLayoutsFromArchive archive = M.fromListWith (<>) ((\t@(e, _, _) -> (CI.mk (name e), pure t)) <$> layouts) where layouts :: [(Element, FilePath, Entry)] layouts = mapMaybe findElementByPath paths parseXml' entry = case parseXMLElement (UTF8.toTextLazy (fromEntry entry)) of Left _ -> Nothing Right element -> Just element findElementByPath :: FilePath -> Maybe (Element, FilePath, Entry) findElementByPath path = do entry <- findEntryByPath path archive element <- parseXml' entry pure (element, path, entry) paths = filter (match (compile "ppt/slideLayouts/slideLayout*.xml")) (filesInArchive archive) name element = fromMaybe "Untitled layout" $ do let ns = elemToNameSpaces element cSld <- findChild (elemName ns "p" "cSld") element findAttr (QName "name" Nothing Nothing) cSld -------------------------------------------------- -- Check to see if the presentation has speaker notes. This will -- influence whether we import the notesMaster template. presHasSpeakerNotes :: Presentation -> Bool presHasSpeakerNotes (Presentation _ slides) = not $ all ((mempty ==) . slideSpeakerNotes) slides curSlideHasSpeakerNotes :: PandocMonad m => P m Bool curSlideHasSpeakerNotes = M.member <$> asks envCurSlideId <*> asks envSpeakerNotesIdMap -------------------------------------------------- getLayout :: PandocMonad m => Layout -> P m Element getLayout layout = getElement <$> getSlideLayouts where getElement = slElement . case layout of MetadataSlide{} -> metadata TitleSlide{} -> title ContentSlide{} -> content TwoColumnSlide{} -> twoColumn ComparisonSlide{} -> comparison ContentWithCaptionSlide{} -> contentWithCaption BlankSlide{} -> blank shapeHasId :: NameSpaces -> T.Text -> Element -> Bool shapeHasId ns ident element = getShapeId ns element == Just ident getShapeId :: NameSpaces -> Element -> Maybe Text getShapeId ns element = do nvSpPr <- findChild (elemName ns "p" "nvSpPr") element cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr findAttr (QName "id" Nothing Nothing) cNvPr type ShapeId = Integer getContentShape :: PandocMonad m => NameSpaces -> Element -> P m (Maybe ShapeId, Element) getContentShape ns spTreeElem | isElem ns "p" "spTree" spTreeElem = do ph@Placeholder{index, placeholderType} <- asks envPlaceholder case drop index (getShapesByPlaceHolderType ns spTreeElem placeholderType) of sp : _ -> let shapeId = getShapeId ns sp >>= readTextAsInteger in return (shapeId, sp) [] -> throwError $ PandocSomeError $ missingPlaceholderMessage ph getContentShape _ _ = throwError $ PandocSomeError "Attempted to find content on non shapeTree" missingPlaceholderMessage :: Placeholder -> Text missingPlaceholderMessage Placeholder{..} = "Could not find a " <> ordinal <> " placeholder of type " <> placeholderText where ordinal = T.pack (show index) <> case (index `mod` 100, index `mod` 10) of (11, _) -> "th" (12, _) -> "th" (13, _) -> "th" (_, 1) -> "st" (_, 2) -> "nd" (_, 3) -> "rd" _ -> "th" placeholderText = case placeholderType of ObjType -> "obj (or nothing)" PHType t -> t getShapeDimensions :: NameSpaces -> Element -> Maybe ((Integer, Integer), (Integer, Integer)) getShapeDimensions ns element | isElem ns "p" "sp" element = do spPr <- findChild (elemName ns "p" "spPr") element xfrm <- findChild (elemName ns "a" "xfrm") spPr off <- findChild (elemName ns "a" "off") xfrm xS <- findAttr (QName "x" Nothing Nothing) off yS <- findAttr (QName "y" Nothing Nothing) off ext <- findChild (elemName ns "a" "ext") xfrm cxS <- findAttr (QName "cx" Nothing Nothing) ext cyS <- findAttr (QName "cy" Nothing Nothing) ext x <- readTextAsInteger xS y <- readTextAsInteger yS cx <- readTextAsInteger cxS cy <- readTextAsInteger cyS return ((x `div` 12700, y `div` 12700), (cx `div` 12700, cy `div` 12700)) | otherwise = Nothing getMasterShapeDimensionsById :: T.Text -> Element -> Maybe ((Integer, Integer), (Integer, Integer)) getMasterShapeDimensionsById ident master = do let ns = elemToNameSpaces master cSld <- findChild (elemName ns "p" "cSld") master spTree <- findChild (elemName ns "p" "spTree") cSld sp <- filterChild (\e -> isElem ns "p" "sp" e && shapeHasId ns ident e) spTree getShapeDimensions ns sp getContentShapeSize :: PandocMonad m => NameSpaces -> Element -> Element -> P m ((Integer, Integer), (Integer, Integer)) getContentShapeSize ns layout master | isElem ns "p" "sldLayout" layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do (_, sp) <- getContentShape ns spTree case getShapeDimensions ns sp of Just sz -> return sz Nothing -> do let mbSz = findChild (elemName ns "p" "nvSpPr") sp >>= findChild (elemName ns "p" "cNvPr") >>= findAttr (QName "id" Nothing Nothing) >>= flip getMasterShapeDimensionsById master case mbSz of Just sz' -> return sz' Nothing -> throwError $ PandocSomeError "Couldn't find necessary content shape size" getContentShapeSize _ _ _ = throwError $ PandocSomeError "Attempted to find content shape size in non-layout" buildSpTree :: NameSpaces -> Element -> [Content] -> Element buildSpTree ns spTreeElem newShapes = emptySpTreeElem { elContent = newContent } where newContent = elContent emptySpTreeElem <> newShapes emptySpTreeElem = spTreeElem { elContent = filter fn (elContent spTreeElem) } fn :: Content -> Bool fn (Elem e) = isElem ns "p" "nvGrpSpPr" e || isElem ns "p" "grpSpPr" e fn _ = True replaceNamedChildren :: NameSpaces -> Text -> Text -> [Element] -> Element -> Element replaceNamedChildren ns prefix name newKids element = element { elContent = concat $ fun True $ elContent element } where fun :: Bool -> [Content] -> [[Content]] fun _ [] = [] fun switch (Elem e : conts) | isElem ns prefix name e = if switch then map Elem newKids : fun False conts else fun False conts fun switch (cont : conts) = [cont] : fun switch conts ---------------------------------------------------------------- registerLink :: PandocMonad m => LinkTarget -> P m Int registerLink link = do curSlideId <- asks envCurSlideId linkReg <- gets stLinkIds mediaReg <- gets stMediaIds hasSpeakerNotes <- curSlideHasSpeakerNotes let maxLinkId = case M.lookup curSlideId linkReg >>= nonEmpty . M.keys of Just xs -> maximum xs Nothing | hasSpeakerNotes -> 2 | otherwise -> 1 maxMediaId = case M.lookup curSlideId mediaReg >>= nonEmpty of Just mInfos -> maximum $ fmap mInfoLocalId mInfos Nothing | hasSpeakerNotes -> 2 | otherwise -> 1 maxId = max maxLinkId maxMediaId slideLinks = case M.lookup curSlideId linkReg of Just mp -> M.insert (maxId + 1) link mp Nothing -> M.singleton (maxId + 1) link modify $ \st -> st{ stLinkIds = M.insert curSlideId slideLinks linkReg} return $ maxId + 1 registerMedia :: PandocMonad m => FilePath -> [ParaElem] -> P m MediaInfo registerMedia fp caption = do curSlideId <- asks envCurSlideId linkReg <- gets stLinkIds mediaReg <- gets stMediaIds globalIds <- gets stMediaGlobalIds hasSpeakerNotes <- curSlideHasSpeakerNotes let maxLinkId = case M.lookup curSlideId linkReg >>= nonEmpty . M.keys of Just ks -> maximum ks Nothing | hasSpeakerNotes -> 2 | otherwise -> 1 maxMediaId = case M.lookup curSlideId mediaReg >>= nonEmpty of Just mInfos -> maximum $ fmap mInfoLocalId mInfos Nothing | hasSpeakerNotes -> 2 | otherwise -> 1 maxLocalId = max maxLinkId maxMediaId maxGlobalId = maybe 0 maximum $ nonEmpty $ M.elems globalIds (imgBytes, mbMt) <- P.fetchItem $ T.pack fp let imgExt = (mbMt >>= extensionFromMimeType >>= (\x -> return $ "." <> x)) <|> case imageType imgBytes of Just Png -> Just ".png" Just Jpeg -> Just ".jpeg" Just Gif -> Just ".gif" Just Pdf -> Just ".pdf" Just Eps -> Just ".eps" Just Svg -> Just ".svg" Just Emf -> Just ".emf" Just Tiff -> Just ".tiff" Just Webp -> Just ".webp" Just Avif -> Just ".avif" Nothing -> Nothing let newGlobalId = fromMaybe (maxGlobalId + 1) (M.lookup fp globalIds) let newGlobalIds = M.insert fp newGlobalId globalIds let mediaInfo = MediaInfo { mInfoFilePath = fp , mInfoLocalId = maxLocalId + 1 , mInfoGlobalId = newGlobalId , mInfoMimeType = case mbMt of -- see #9113 Just t | ";base64" `T.isSuffixOf` t -> T.stripSuffix ";base64" t x -> x , mInfoExt = imgExt , mInfoCaption = (not . null) caption } let slideMediaInfos = case M.lookup curSlideId mediaReg of Just minfos -> mediaInfo : minfos Nothing -> [mediaInfo] modify $ \st -> st{ stMediaIds = M.insert curSlideId slideMediaInfos mediaReg , stMediaGlobalIds = newGlobalIds } return mediaInfo makeMediaEntry :: PandocMonad m => MediaInfo -> P m Entry makeMediaEntry mInfo = do epochtime <- floor . utcTimeToPOSIXSeconds <$> asks envUTCTime (imgBytes, _) <- P.fetchItem (T.pack $ mInfoFilePath mInfo) let ext = fromMaybe "" (mInfoExt mInfo) let fp = "ppt/media/image" <> show (mInfoGlobalId mInfo) <> T.unpack ext return $ toEntry fp epochtime $ BL.fromStrict imgBytes makeMediaEntries :: PandocMonad m => P m [Entry] makeMediaEntries = do mediaInfos <- gets stMediaIds let allInfos = mconcat $ M.elems mediaInfos mapM makeMediaEntry allInfos -- -- | Scales the image to fit the page -- -- sizes are passed in emu -- fitToPage' :: (Double, Double) -- image size in emu -- -> Integer -- pageWidth -- -> Integer -- pageHeight -- -> (Integer, Integer) -- imagesize -- fitToPage' (x, y) pageWidth pageHeight -- -- Fixes width to the page width and scales the height -- | x <= fromIntegral pageWidth && y <= fromIntegral pageHeight = -- (floor x, floor y) -- | x / fromIntegral pageWidth > y / fromIntegral pageWidth = -- (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) -- | otherwise = -- (floor $ ((fromIntegral pageHeight) / y) * x, pageHeight) -- positionImage :: (Double, Double) -> Integer -> Integer -> (Integer, Integer) -- positionImage (x, y) pageWidth pageHeight = -- let (x', y') = fitToPage' (x, y) pageWidth pageHeight -- in -- ((pageWidth - x') `div` 2, (pageHeight - y') `div` 2) getMaster :: PandocMonad m => P m Element getMaster = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive getMaster' refArchive distArchive getMaster' :: PandocMonad m => Archive -> Archive -> m Element getMaster' refArchive distArchive = parseXml refArchive distArchive "ppt/slideMasters/slideMaster1.xml" getMasterRels :: PandocMonad m => P m Element getMasterRels = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive parseXml refArchive distArchive "ppt/slideMasters/_rels/slideMaster1.xml.rels" -- We want to get the header dimensions, so we can make sure that the -- image goes underneath it. We only use this in a content slide if it -- has a header. -- getHeaderSize :: PandocMonad m => P m ((Integer, Integer), (Integer, Integer)) -- getHeaderSize = do -- master <- getMaster -- let ns = elemToNameSpaces master -- sps = [master] >>= -- findChildren (elemName ns "p" "cSld") >>= -- findChildren (elemName ns "p" "spTree") >>= -- findChildren (elemName ns "p" "sp") -- mbXfrm = -- listToMaybe (filter (shapeHasName ns "Title Placeholder 1") sps) >>= -- findChild (elemName ns "p" "spPr") >>= -- findChild (elemName ns "a" "xfrm") -- xoff = mbXfrm >>= -- findChild (elemName ns "a" "off") >>= -- findAttr (QName "x" Nothing Nothing) >>= -- (listToMaybe . (\s -> reads s :: [(Integer, String)])) -- yoff = mbXfrm >>= -- findChild (elemName ns "a" "off") >>= -- findAttr (QName "y" Nothing Nothing) >>= -- (listToMaybe . (\s -> reads s :: [(Integer, String)])) -- xext = mbXfrm >>= -- findChild (elemName ns "a" "ext") >>= -- findAttr (QName "cx" Nothing Nothing) >>= -- (listToMaybe . (\s -> reads s :: [(Integer, String)])) -- yext = mbXfrm >>= -- findChild (elemName ns "a" "ext") >>= -- findAttr (QName "cy" Nothing Nothing) >>= -- (listToMaybe . (\s -> reads s :: [(Integer, String)])) -- off = case xoff of -- Just (xoff', _) | Just (yoff',_) <- yoff -> (xoff', yoff') -- _ -> (1043490, 1027664) -- ext = case xext of -- Just (xext', _) | Just (yext',_) <- yext -> (xext', yext') -- _ -> (7024744, 1143000) -- return $ (off, ext) -- Hard-coded for now -- captionPosition :: ((Integer, Integer), (Integer, Integer)) -- captionPosition = ((457200, 6061972), (8229600, 527087)) captionHeight :: Integer captionHeight = 40 createCaption :: PandocMonad m => ((Integer, Integer), (Integer, Integer)) -> [ParaElem] -> P m (ShapeId, Element) createCaption contentShapeDimensions paraElements = do let para = Paragraph def{pPropAlign = Just AlgnCenter} paraElements elements <- mapM paragraphToElement [para] let ((x, y), (cx, cy)) = contentShapeDimensions let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] <> elements return ( 1 , surroundWithMathAlternate $ mknode "p:sp" [] [ mknode "p:nvSpPr" [] [ mknode "p:cNvPr" [("id","1"), ("name","TextBox 3")] () , mknode "p:cNvSpPr" [("txBox", "1")] () , mknode "p:nvPr" [] [mknode "p:ph" [("idx", "1")] ()] ] , mknode "p:spPr" [] [ mknode "a:xfrm" [] [ mknode "a:off" [("x", tshow $ 12700 * x), ("y", tshow $ 12700 * (y + cy - captionHeight))] () , mknode "a:ext" [("cx", tshow $ 12700 * cx), ("cy", tshow $ 12700 * captionHeight)] () ] , mknode "a:prstGeom" [("prst", "rect")] [ mknode "a:avLst" [] () ] , mknode "a:noFill" [] () ] , txBody ] ) makePicElements :: PandocMonad m => Element -> PicProps -> MediaInfo -> Text -> [ParaElem] -> P m [(ShapeId, Element)] makePicElements layout picProps mInfo titleText alt = do opts <- asks envOpts (pageWidth, pageHeight) <- asks envPresentationSize -- hasHeader <- asks envSlideHasHeader let hasCaption = mInfoCaption mInfo (imgBytes, _) <- P.fetchItem (T.pack $ mInfoFilePath mInfo) let (pxX, pxY) = case imageSize opts imgBytes of Right sz -> sizeInPixels sz Left _ -> sizeInPixels def master <- getMaster let ns = elemToNameSpaces layout ((x, y), (cx, cytmp)) <- getContentShapeSize ns layout master `catchError` (\_ -> return ((0, 0), (pageWidth, pageHeight))) let cy = if hasCaption then cytmp - captionHeight else cytmp let imgRatio = fromIntegral pxX / fromIntegral pxY :: Double boxRatio = fromIntegral cx / fromIntegral cy :: Double (dimX, dimY) = if imgRatio > boxRatio then (fromIntegral cx, fromIntegral cx / imgRatio) else (fromIntegral cy * imgRatio, fromIntegral cy) (dimX', dimY') = (round dimX * 12700, round dimY * 12700) :: (Integer, Integer) (xoff, yoff) = (fromIntegral x + (fromIntegral cx - dimX) / 2, fromIntegral y + (fromIntegral cy - dimY) / 2) (xoff', yoff') = (round xoff * 12700, round yoff * 12700) :: (Integer, Integer) let cNvPicPr = mknode "p:cNvPicPr" [] $ mknode "a:picLocks" [("noGrp","1") ,("noChangeAspect","1")] () -- cNvPr will contain the link information so we do that separately, -- and register the link if necessary. let description = (if T.null titleText then "" else titleText <> "\n\n") <> T.pack (mInfoFilePath mInfo) let cNvPrAttr = [("descr", description), ("id","0"), ("name","Picture 1")] cNvPr <- case picPropLink picProps of Just link -> do idNum <- registerLink link return $ mknode "p:cNvPr" cNvPrAttr $ mknode "a:hlinkClick" [("r:id", "rId" <> tshow idNum)] () Nothing -> return $ mknode "p:cNvPr" cNvPrAttr () let nvPicPr = mknode "p:nvPicPr" [] [ cNvPr , cNvPicPr , mknode "p:nvPr" [] ()] let blipFill = mknode "p:blipFill" [] [ mknode "a:blip" [("r:embed", "rId" <> tshow (mInfoLocalId mInfo))] () , mknode "a:stretch" [] $ mknode "a:fillRect" [] () ] let xfrm = mknode "a:xfrm" [] [ mknode "a:off" [("x", tshow xoff'), ("y", tshow yoff')] () , mknode "a:ext" [("cx", tshow dimX') ,("cy", tshow dimY')] () ] let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ mknode "a:avLst" [] () let ln = mknode "a:ln" [("w","9525")] [ mknode "a:noFill" [] () , mknode "a:headEnd" [] () , mknode "a:tailEnd" [] () ] let spPr = mknode "p:spPr" [("bwMode","auto")] [xfrm, prstGeom, mknode "a:noFill" [] (), ln] let picShape = ( 0 , mknode "p:pic" [] [ nvPicPr , blipFill , spPr ] ) -- And now, maybe create the caption: if hasCaption then do cap <- createCaption ((x, y), (cx, cytmp)) alt return [picShape, cap] else return [picShape] consolidateRuns :: [ParaElem] -> [ParaElem] consolidateRuns [] = [] consolidateRuns (Run pr1 s1 : Run pr2 s2 : xs) | pr1 == pr2 = consolidateRuns (Run pr1 (s1 <> s2) : xs) consolidateRuns (x:xs) = x : consolidateRuns xs paraElemToElements :: PandocMonad m => ParaElem -> P m [Content] paraElemToElements Break = return [Elem $ mknode "a:br" [] ()] paraElemToElements (Run rpr s) = do sizeAttrs <- fontSizeAttributes rpr let attrs = sizeAttrs <> ( [("b", "1") | rPropBold rpr]) <> ( [("i", "1") | rPropItalics rpr]) <> ( [("u", "sng") | rPropUnderline rpr]) <> (case rStrikethrough rpr of Just NoStrike -> [("strike", "noStrike")] Just SingleStrike -> [("strike", "sngStrike")] Just DoubleStrike -> [("strike", "dblStrike")] Nothing -> []) <> (case rBaseline rpr of Just n -> [("baseline", tshow n)] Nothing -> []) <> (case rCap rpr of Just NoCapitals -> [("cap", "none")] Just SmallCapitals -> [("cap", "small")] Just AllCapitals -> [("cap", "all")] Nothing -> []) <> [] linkProps <- case rLink rpr of Just link -> do idNum <- registerLink link -- first we have to make sure that if it's an -- anchor, it's in the anchor map. If not, there's -- no link. return $ case link of InternalTarget _ -> let linkAttrs = [ ("r:id", "rId" <> tshow idNum) , ("action", "ppaction://hlinksldjump") ] in [mknode "a:hlinkClick" linkAttrs ()] -- external ExternalTarget _ -> let linkAttrs = [ ("r:id", "rId" <> tshow idNum) ] in [mknode "a:hlinkClick" linkAttrs ()] Nothing -> return [] let colorContents = case rSolidFill rpr of Just color -> case fromColor color of '#':hx -> [mknode "a:solidFill" [] [mknode "a:srgbClr" [("val", T.toUpper $ T.pack hx)] ()]] _ -> [] Nothing -> [] codeFont <- monospaceFont let codeContents = [mknode "a:latin" [("typeface", codeFont)] () | rPropCode rpr] let propContents = linkProps <> colorContents <> codeContents return [Elem $ mknode "a:r" [] [ mknode "a:rPr" attrs propContents , mknode "a:t" [] s ]] paraElemToElements (MathElem mathType texStr) = do isInSpkrNotes <- asks envInSpeakerNotes if isInSpkrNotes then paraElemToElements $ Run def $ unTeXString texStr else do res <- convertMath writeOMML mathType (unTeXString texStr) case fromXLElement <$> res of Right r -> return [Elem $ mknode "a14:m" [] $ addMathInfo r] Left (Str s) -> paraElemToElements (Run def s) Left _ -> throwError $ PandocShouldNeverHappenError "non-string math fallback" paraElemToElements (RawOOXMLParaElem str) = return [Text (CData CDataRaw str Nothing)] -- This is a bit of a kludge -- really requires adding an option to -- TeXMath, but since that's a different package, we'll do this one -- step at a time. addMathInfo :: Element -> Element addMathInfo element = let mathspace = Attr { attrKey = QName "m" Nothing (Just "xmlns") , attrVal = "http://schemas.openxmlformats.org/officeDocument/2006/math" } in add_attr mathspace element -- We look through the element to see if it contains an a14:m -- element. If so, we surround it. This is a bit ugly, but it seems -- more dependable than looking through shapes for math. Plus this is -- an xml implementation detail, so it seems to make sense to do it at -- the xml level. surroundWithMathAlternate :: Element -> Element surroundWithMathAlternate element = case findElement (QName "m" Nothing (Just "a14")) element of Just _ -> mknode "mc:AlternateContent" [("xmlns:mc", "http://schemas.openxmlformats.org/markup-compatibility/2006") ] [ mknode "mc:Choice" [ ("xmlns:a14", "http://schemas.microsoft.com/office/drawing/2010/main") , ("Requires", "a14")] [ element ] ] Nothing -> element paragraphToElement :: PandocMonad m => Paragraph -> P m Element paragraphToElement par = do indents <- asks envOtherStyleIndents let lvl = pPropLevel (paraProps par) attrs = [("lvl", tshow lvl)] <> (case (pPropIndent (paraProps par), pPropMarginLeft (paraProps par)) of (Just px1, Just px2) -> [ ("indent", tshow $ pixelsToEmu px1) , ("marL", tshow $ pixelsToEmu px2) ] (Just px1, Nothing) -> [("indent", tshow $ pixelsToEmu px1)] (Nothing, Just px2) -> [("marL", tshow $ pixelsToEmu px2)] (Nothing, Nothing) -> fromMaybe [] $ do indents' <- indents thisLevel <- levelIndent indents' lvl nextLevel <- levelIndent indents' (lvl + 1) let (m, i) = case pPropBullet (paraProps par) of Nothing -> (Just (marL thisLevel), Just 0) Just (AutoNumbering _) -> ( Just (marL nextLevel) , Just (marL thisLevel - marL nextLevel) ) Just Bullet -> (Nothing, Nothing) pure ( toList ((,) "indent" . tshow <$> i) <> toList ((,) "marL" . tshow <$> m) ) ) <> (case pPropAlign (paraProps par) of Just AlgnLeft -> [("algn", "l")] Just AlgnRight -> [("algn", "r")] Just AlgnCenter -> [("algn", "ctr")] Nothing -> [] ) props = [] <> (case pPropSpaceBefore $ paraProps par of Just px -> [mknode "a:spcBef" [] [ mknode "a:spcPts" [("val", tshow $ 100 * px)] () ] ] Nothing -> [] ) <> (case pPropBullet $ paraProps par of Just Bullet -> [] Just (AutoNumbering attrs') -> [mknode "a:buAutoNum" (autoNumAttrs attrs') ()] Nothing -> [mknode "a:buNone" [] ()] ) paras <- mconcat <$> mapM paraElemToElements (consolidateRuns (paraElems par)) return $ mknode "a:p" [] $ [Elem $ mknode "a:pPr" attrs props] <> paras shapeToElement :: PandocMonad m => Element -> Shape -> P m (Maybe ShapeId, Element) shapeToElement layout (TextBox paras) | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do (shapeId, sp) <- getContentShape ns spTree elements <- mapM paragraphToElement paras let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] <> elements emptySpPr = mknode "p:spPr" [] () return . (shapeId,) . surroundWithMathAlternate . replaceNamedChildren ns "p" "txBody" [txBody] . replaceNamedChildren ns "p" "spPr" [emptySpPr] $ sp -- GraphicFrame and Pic should never reach this. shapeToElement _ _ = return (Nothing, mknode "p:sp" [] ()) shapeToElements :: PandocMonad m => Element -> Shape -> P m [(Maybe ShapeId, Content)] shapeToElements layout (Pic picProps fp titleText alt) = do mInfo <- registerMedia fp alt case mInfoExt mInfo of Just _ -> map (bimap Just Elem) <$> makePicElements layout picProps mInfo titleText alt Nothing -> shapeToElements layout $ TextBox [Paragraph def alt] shapeToElements layout (GraphicFrame tbls cptn) = map (bimap Just Elem) <$> graphicFrameToElements layout tbls cptn shapeToElements _ (RawOOXMLShape str) = return [(Nothing, Text (CData CDataRaw str Nothing))] shapeToElements layout shp@(TextBox _) = do (shapeId, element) <- shapeToElement layout shp return [(shapeId, Elem element)] shapesToElements :: PandocMonad m => Element -> [Shape] -> P m [(Maybe ShapeId, Content)] shapesToElements layout shps = concat <$> mapM (shapeToElements layout) shps graphicFrameToElements :: PandocMonad m => Element -> [Graphic] -> [ParaElem] -> P m [(ShapeId, Element)] graphicFrameToElements layout tbls caption = do -- get the sizing master <- getMaster (pageWidth, pageHeight) <- asks envPresentationSize let ns = elemToNameSpaces layout ((x, y), (cx, cytmp)) <- getContentShapeSize ns layout master `catchError` (\_ -> return ((0, 0), (pageWidth, pageHeight))) let cy = if not $ null caption then cytmp - captionHeight else cytmp elements <- mapM (graphicToElement cx) tbls let graphicFrameElts = ( 6 , mknode "p:graphicFrame" [] $ [ mknode "p:nvGraphicFramePr" [] [ mknode "p:cNvPr" [("id", "6"), ("name", "Content Placeholder 5")] () , mknode "p:cNvGraphicFramePr" [] [mknode "a:graphicFrameLocks" [("noGrp", "1")] ()] , mknode "p:nvPr" [] [mknode "p:ph" [("idx", "1")] ()] ] , mknode "p:xfrm" [] [ mknode "a:off" [("x", tshow $ 12700 * x), ("y", tshow $ 12700 * y)] () , mknode "a:ext" [("cx", tshow $ 12700 * cx), ("cy", tshow $ 12700 * cy)] () ] ] <> elements ) if not $ null caption then do capElt <- createCaption ((x, y), (cx, cytmp)) caption return [graphicFrameElts, capElt] else return [graphicFrameElts] getDefaultTableStyle :: PandocMonad m => P m (Maybe T.Text) getDefaultTableStyle = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive tblStyleLst <- parseXml refArchive distArchive "ppt/tableStyles.xml" return $ findAttr (QName "def" Nothing Nothing) tblStyleLst graphicToElement :: PandocMonad m => Integer -> Graphic -> P m Element graphicToElement tableWidth (Tbl widths tblPr hdrCells rows) = do let totalWidth = sum widths let colWidths = if 0.0 `elem` widths then if null hdrCells then case rows of r@(_:_) : _ -> replicate (length r) $ tableWidth `div` toInteger (length r) []: _ -> [] [] -> [] else replicate (length hdrCells) $ tableWidth `div` toInteger (length hdrCells) else map (\w -> round $ w / totalWidth * fromIntegral tableWidth) widths let cellToOpenXML paras = do elements <- mapM paragraphToElement paras let elements' = if null elements then [mknode "a:p" [] [mknode "a:endParaRPr" [] ()]] else elements return [mknode "a:txBody" [] $ [ mknode "a:bodyPr" [] () , mknode "a:lstStyle" [] ()] <> elements'] headers' <- mapM cellToOpenXML hdrCells rows' <- mapM (mapM cellToOpenXML) rows let borderProps = mknode "a:tcPr" [] () let emptyCell' = [mknode "a:p" [] [mknode "a:pPr" [] ()]] let mkcell border contents = mknode "a:tc" [] $ (if null contents then emptyCell' else contents) <> [ borderProps | border ] let mkrow border cells = mknode "a:tr" [("h", "0")] $ map (mkcell border) cells let mkgridcol w = mknode "a:gridCol" [("w", tshow ((12700 * w) :: Integer))] () let hasHeader = not (all null hdrCells) mbDefTblStyle <- getDefaultTableStyle let tblPrElt = mknode "a:tblPr" [ ("firstRow", if tblPrFirstRow tblPr then "1" else "0") , ("bandRow", if tblPrBandRow tblPr then "1" else "0") ] (case mbDefTblStyle of Nothing -> [] Just sty -> [mknode "a:tableStyleId" [] sty]) return $ mknode "a:graphic" [] [mknode "a:graphicData" [("uri", "http://schemas.openxmlformats.org/drawingml/2006/table")] [mknode "a:tbl" [] $ [ tblPrElt , mknode "a:tblGrid" [] (if all (==0) colWidths then [] else map mkgridcol colWidths) ] <> [ mkrow True headers' | hasHeader ] <> map (mkrow False) rows' ] ] -- We get the shape by placeholder type. If there is NO type, it -- defaults to a content placeholder. data PHType = PHType T.Text | ObjType deriving (Show, Eq) findPHType :: NameSpaces -> Element -> PHType -> Bool findPHType ns spElem phType | isElem ns "p" "sp" spElem = let mbPHElem = (Just spElem >>= findChild (elemName ns "p" "nvSpPr") >>= findChild (elemName ns "p" "nvPr") >>= findChild (elemName ns "p" "ph")) in case mbPHElem of -- if it's a named PHType, we want to check that the attribute -- value matches. Just phElem | (PHType tp) <- phType -> case findAttr (QName "type" Nothing Nothing) phElem of Just tp' -> tp == tp' Nothing -> False -- if it's an ObjType, we want to check that there is NO -- "type" attribute. In other words, a lookup should return nothing. Just phElem | ObjType <- phType -> case findAttr (QName "type" Nothing Nothing) phElem of Just _ -> False Nothing -> True Nothing -> False findPHType _ _ _ = False getShapesByPlaceHolderType :: NameSpaces -> Element -> PHType -> [Element] getShapesByPlaceHolderType ns spTreeElem phType | isElem ns "p" "spTree" spTreeElem = filterChildren (\e -> findPHType ns e phType) spTreeElem | otherwise = [] getShapeByPlaceHolderType :: NameSpaces -> Element -> PHType -> Maybe Element getShapeByPlaceHolderType ns spTreeElem phType = listToMaybe $ getShapesByPlaceHolderType ns spTreeElem phType -- Like the above, but it tries a number of different placeholder types getShapeByPlaceHolderTypes :: NameSpaces -> Element -> [PHType] -> Maybe Element getShapeByPlaceHolderTypes _ _ [] = Nothing getShapeByPlaceHolderTypes ns spTreeElem (s:ss) = case getShapeByPlaceHolderType ns spTreeElem s of Just element -> Just element Nothing -> getShapeByPlaceHolderTypes ns spTreeElem ss nonBodyTextToElement :: PandocMonad m => Element -> [PHType] -> [ParaElem] -> P m (Maybe ShapeId, Element) nonBodyTextToElement layout phTypes paraElements | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld , Just sp <- getShapeByPlaceHolderTypes ns spTree phTypes , Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") sp , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr , Just shapeId <- findAttr (nodename "id") cNvPr , Right (shapeIdNum, _) <- decimal shapeId = do let hdrPara = Paragraph def paraElements element <- paragraphToElement hdrPara let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] <> [element] return (Just shapeIdNum, surroundWithMathAlternate $ replaceNamedChildren ns "p" "txBody" [txBody] sp) -- XXX: TODO | otherwise = return (Nothing, mknode "p:sp" [] ()) data ContentShapeIds = ContentShapeIds { contentHeaderId :: Maybe ShapeId , contentContentIds :: [ShapeId] } contentToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> P m (Maybe ContentShapeIds, Element) contentToElement layout hdrShape shapes | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do (shapeId, element) <- nonBodyTextToElement layout [PHType "title"] hdrShape let hdrShapeElements = [Elem element | not (null hdrShape)] contentHeaderId = if null hdrShape then Nothing else shapeId content' <- local (\env -> env {envPlaceholder = Placeholder ObjType 0}) (shapesToElements layout shapes) let contentContentIds = mapMaybe fst content' contentElements = snd <$> content' footer <- footerElements content return ( Just ContentShapeIds{..} , buildSpTree ns spTree (hdrShapeElements <> contentElements <> footer) ) contentToElement _ _ _ = return (Nothing, mknode "p:sp" [] ()) data TwoColumnShapeIds = TwoColumnShapeIds { twoColumnHeaderId :: Maybe ShapeId , twoColumnLeftIds :: [ShapeId] , twoColumnRightIds :: [ShapeId] } twoColumnToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> [Shape] -> P m (Maybe TwoColumnShapeIds, Element) twoColumnToElement layout hdrShape shapesL shapesR | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do (headerId, element) <- nonBodyTextToElement layout [PHType "title"] hdrShape let hdrShapeElements = [Elem element | not (null hdrShape)] twoColumnHeaderId = if null hdrShape then Nothing else headerId contentL <- local (\env -> env {envPlaceholder = Placeholder ObjType 0}) (shapesToElements layout shapesL) let twoColumnLeftIds = mapMaybe fst contentL contentElementsL = snd <$> contentL contentR <- local (\env -> env {envPlaceholder = Placeholder ObjType 1}) (shapesToElements layout shapesR) let (twoColumnRightIds) = (mapMaybe fst contentR) contentElementsR = snd <$> contentR -- let contentElementsL' = map (setIdx ns "1") contentElementsL -- contentElementsR' = map (setIdx ns "2") contentElementsR footer <- footerElements twoColumn return $ (Just TwoColumnShapeIds{..}, ) $ buildSpTree ns spTree $ hdrShapeElements <> contentElementsL <> contentElementsR <> footer twoColumnToElement _ _ _ _ = return (Nothing, mknode "p:sp" [] ()) data ComparisonShapeIds = ComparisonShapeIds { comparisonHeaderId :: Maybe ShapeId , comparisonLeftTextIds :: [ShapeId] , comparisonLeftContentIds :: [ShapeId] , comparisonRightTextIds :: [ShapeId] , comparisonRightContentIds :: [ShapeId] } comparisonToElement :: PandocMonad m => Element -> [ParaElem] -> ([Shape], [Shape]) -> ([Shape], [Shape]) -> P m (Maybe ComparisonShapeIds, Element) comparisonToElement layout hdrShape (shapesL1, shapesL2) (shapesR1, shapesR2) | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do (headerShapeId, element) <- nonBodyTextToElement layout [PHType "title"] hdrShape let hdrShapeElements = [Elem element | not (null hdrShape)] comparisonHeaderId = if null hdrShape then Nothing else headerShapeId contentL1 <- local (\env -> env {envPlaceholder = Placeholder (PHType "body") 0}) (shapesToElements layout shapesL1) let comparisonLeftTextIds = mapMaybe fst contentL1 contentElementsL1 = snd <$> contentL1 contentL2 <- local (\env -> env {envPlaceholder = Placeholder ObjType 0}) (shapesToElements layout shapesL2) let comparisonLeftContentIds = mapMaybe fst contentL2 contentElementsL2 = snd <$> contentL2 contentR1 <- local (\env -> env {envPlaceholder = Placeholder (PHType "body") 1}) (shapesToElements layout shapesR1) let comparisonRightTextIds = mapMaybe fst contentR1 contentElementsR1 = snd <$> contentR1 contentR2 <- local (\env -> env {envPlaceholder = Placeholder ObjType 1}) (shapesToElements layout shapesR2) let comparisonRightContentIds = mapMaybe fst contentR2 contentElementsR2 = snd <$> contentR2 footer <- footerElements comparison return $ (Just ComparisonShapeIds{..}, ) $ buildSpTree ns spTree $ mconcat [ hdrShapeElements , contentElementsL1 , contentElementsL2 , contentElementsR1 , contentElementsR2 ] <> footer comparisonToElement _ _ _ _= return (Nothing, mknode "p:sp" [] ()) data ContentWithCaptionShapeIds = ContentWithCaptionShapeIds { contentWithCaptionHeaderId :: Maybe ShapeId , contentWithCaptionCaptionIds :: [ShapeId] , contentWithCaptionContentIds :: [ShapeId] } contentWithCaptionToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> [Shape] -> P m (Maybe ContentWithCaptionShapeIds, Element) contentWithCaptionToElement layout hdrShape textShapes contentShapes | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do (shapeId, element) <- nonBodyTextToElement layout [PHType "title"] hdrShape let hdrShapeElements = [Elem element | not (null hdrShape)] contentWithCaptionHeaderId = if null hdrShape then Nothing else shapeId text <- local (\env -> env {envPlaceholder = Placeholder (PHType "body") 0}) (shapesToElements layout textShapes) let contentWithCaptionCaptionIds = mapMaybe fst text textElements = snd <$> text content <- local (\env -> env {envPlaceholder = Placeholder ObjType 0}) (shapesToElements layout contentShapes) let contentWithCaptionContentIds = mapMaybe fst content contentElements = snd <$> content footer <- footerElements contentWithCaption return $ (Just ContentWithCaptionShapeIds{..}, ) $ buildSpTree ns spTree $ mconcat [ hdrShapeElements , textElements , contentElements ] <> footer contentWithCaptionToElement _ _ _ _ = return (Nothing, mknode "p:sp" [] ()) blankToElement :: PandocMonad m => Element -> P m Element blankToElement layout | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = buildSpTree ns spTree <$> footerElements blank blankToElement _ = return $ mknode "p:sp" [] () newtype TitleShapeIds = TitleShapeIds { titleHeaderId :: Maybe ShapeId } titleToElement :: PandocMonad m => Element -> [ParaElem] -> P m (Maybe TitleShapeIds, Element) titleToElement layout titleElems | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do (shapeId, element) <- nonBodyTextToElement layout [PHType "title", PHType "ctrTitle"] titleElems let titleShapeElements = [Elem element | not (null titleElems)] titleHeaderId = if null titleElems then Nothing else shapeId footer <- footerElements title return $ (Just TitleShapeIds{..}, ) $ buildSpTree ns spTree (titleShapeElements <> footer) titleToElement _ _ = return (Nothing, mknode "p:sp" [] ()) data MetadataShapeIds = MetadataShapeIds { metadataTitleId :: Maybe ShapeId , metadataSubtitleId :: Maybe ShapeId , metadataDateId :: Maybe ShapeId } metadataToElement :: PandocMonad m => Element -> [ParaElem] -> [ParaElem] -> [[ParaElem]] -> [ParaElem] -> P m (Maybe MetadataShapeIds, Element) metadataToElement layout titleElems subtitleElems authorsElems dateElems | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do let combinedAuthorElems = intercalate [Break] authorsElems subtitleAndAuthorElems = intercalate [Break, Break] $ filter (not . null) [subtitleElems, combinedAuthorElems] (titleId, titleElement) <- nonBodyTextToElement layout [PHType "ctrTitle"] titleElems (subtitleId, subtitleElement) <- nonBodyTextToElement layout [PHType "subTitle"] subtitleAndAuthorElems (dateId, dateElement) <- nonBodyTextToElement layout [PHType "dt"] dateElems let titleShapeElements = [titleElement | not (null titleElems)] metadataTitleId = if null titleElems then Nothing else titleId subtitleShapeElements = [subtitleElement | not (null subtitleAndAuthorElems)] metadataSubtitleId = if null subtitleAndAuthorElems then Nothing else subtitleId footerInfo <- gets stFooterInfo footer <- (if maybe False fiShowOnFirstSlide footerInfo then id else const []) <$> footerElements metadata let dateShapeElements = [dateElement | not (null dateElems || isJust (footerInfo >>= metadata . fiDate)) ] metadataDateId = if null dateElems then Nothing else dateId return $ (Just MetadataShapeIds{..}, ) $ buildSpTree ns spTree $ map Elem (titleShapeElements <> subtitleShapeElements <> dateShapeElements) <> footer metadataToElement _ _ _ _ _ = return (Nothing, mknode "p:sp" [] ()) slideToElement :: PandocMonad m => Slide -> P m Element slideToElement (Slide _ l@(ContentSlide hdrElems shapes) _ backgroundImage) = do layout <- getLayout l backgroundImageElement <- traverse backgroundImageToElement backgroundImage (shapeIds, spTree) <- local (\env -> if null hdrElems then env else env{envSlideHasHeader=True}) (contentToElement layout hdrElems shapes) let animations = case shapeIds of Nothing -> [] Just ContentShapeIds{..} -> slideToIncrementalAnimations (zip contentContentIds shapes) return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] (mknode "p:cSld" [] (toList backgroundImageElement <> [spTree]) : animations) slideToElement (Slide _ l@(TwoColumnSlide hdrElems shapesL shapesR) _ backgroundImage) = do layout <- getLayout l backgroundImageElement <- traverse backgroundImageToElement backgroundImage (shapeIds, spTree) <- local (\env -> if null hdrElems then env else env{envSlideHasHeader=True}) $ twoColumnToElement layout hdrElems shapesL shapesR let animations = case shapeIds of Nothing -> [] Just TwoColumnShapeIds{..} -> slideToIncrementalAnimations (zip twoColumnLeftIds shapesL <> zip twoColumnRightIds shapesR) return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] (mknode "p:cSld" [] (toList backgroundImageElement <> [spTree]) : animations) slideToElement (Slide _ l@(ComparisonSlide hdrElems shapesL shapesR) _ backgroundImage) = do layout <- getLayout l backgroundImageElement <- traverse backgroundImageToElement backgroundImage (shapeIds, spTree) <- local (\env -> if null hdrElems then env else env{envSlideHasHeader=True}) $ comparisonToElement layout hdrElems shapesL shapesR let animations = case shapeIds of Nothing -> [] Just ComparisonShapeIds{..} -> slideToIncrementalAnimations (zip comparisonLeftTextIds (fst shapesL) <> zip comparisonLeftContentIds (snd shapesL) <> zip comparisonRightTextIds (fst shapesR) <> zip comparisonRightContentIds (snd shapesR)) return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] (mknode "p:cSld" [] (toList backgroundImageElement <> [spTree]) : animations) slideToElement (Slide _ l@(TitleSlide hdrElems) _ backgroundImage) = do layout <- getLayout l backgroundImageElement <- traverse backgroundImageToElement backgroundImage (_, spTree) <- titleToElement layout hdrElems return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [mknode "p:cSld" [] (toList backgroundImageElement <> [spTree])] slideToElement (Slide _ l@(MetadataSlide titleElems subtitleElems authorElems dateElems) _ backgroundImage) = do layout <- getLayout l backgroundImageElement <- traverse backgroundImageToElement backgroundImage (_, spTree) <- metadataToElement layout titleElems subtitleElems authorElems dateElems return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [mknode "p:cSld" [] (toList backgroundImageElement <> [spTree])] slideToElement (Slide _ l@(ContentWithCaptionSlide hdrElems captionShapes contentShapes) _ backgroundImage) = do layout <- getLayout l backgroundImageElement <- traverse backgroundImageToElement backgroundImage (shapeIds, spTree) <- contentWithCaptionToElement layout hdrElems captionShapes contentShapes let animations = case shapeIds of Nothing -> [] Just ContentWithCaptionShapeIds{..} -> slideToIncrementalAnimations (zip contentWithCaptionCaptionIds captionShapes <> zip contentWithCaptionContentIds contentShapes) return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] (mknode "p:cSld" [] (toList backgroundImageElement <> [spTree]) : animations) slideToElement (Slide _ BlankSlide _ backgroundImage) = do layout <- getLayout BlankSlide backgroundImageElement <- traverse backgroundImageToElement backgroundImage spTree <- blankToElement layout return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [mknode "p:cSld" [] (toList backgroundImageElement <> [spTree])] backgroundImageToElement :: PandocMonad m => FilePath -> P m Element backgroundImageToElement path = do MediaInfo{mInfoLocalId, mInfoFilePath} <- registerMedia path [] (imgBytes, _) <- P.fetchItem (T.pack mInfoFilePath) opts <- asks envOpts let imageDimensions = either (const Nothing) (Just . sizeInPixels) (imageSize opts imgBytes) pageSize <- asks envPresentationSize let fillRectAttributes = maybe [] (offsetAttributes pageSize) imageDimensions let rId = "rId" <> T.pack (show mInfoLocalId) return $ mknode "p:bg" [] $ mknode "p:bgPr" [] [ mknode "a:blipFill" [("dpi", "0"), ("rotWithShape", "1")] [ mknode "a:blip" [("r:embed", rId)] $ mknode "a:lum" [] () , mknode "a:srcRect" [] () , mknode "a:stretch" [] $ mknode "a:fillRect" fillRectAttributes () ] , mknode "a:effectsLst" [] () ] where offsetAttributes :: (Integer, Integer) -> (Integer, Integer) -> [(Text, Text)] offsetAttributes (pageWidth, pageHeight) (pictureWidth, pictureHeight) = let widthRatio = pictureWidth % pageWidth heightRatio = pictureHeight % pageHeight getOffset :: Ratio Integer -> Text getOffset proportion = let percentageOffset = (proportion - 1) * (-100 % 2) integerOffset = round percentageOffset * 1000 :: Integer in T.pack (show integerOffset) in case compare widthRatio heightRatio of EQ -> [] LT -> let offset = getOffset ((pictureHeight % pageHeight) / widthRatio) in [ ("t", offset) , ("b", offset) ] GT -> let offset = getOffset ((pictureWidth % pageWidth) / heightRatio) in [ ("l", offset) , ("r", offset) ] slideToIncrementalAnimations :: [(ShapeId, Shape)] -> [Element] slideToIncrementalAnimations shapes = let incrementals :: [(ShapeId, [Bool])] incrementals = do (shapeId, TextBox ps) <- shapes pure . (shapeId,) $ do Paragraph ParaProps{pPropIncremental} _ <- ps pure pPropIncremental toIndices :: [Bool] -> Maybe (NonEmpty (Integer, Integer)) toIndices bs = do let indexed = zip [0..] bs ts <- nonEmpty (filter snd indexed) pure (fmap (\(n, _) -> (n, n)) ts) indices :: [(ShapeId, NonEmpty (Integer, Integer))] indices = do (shapeId, bs) <- incrementals toList ((,) shapeId <$> toIndices bs) in toList (incrementalAnimation <$> nonEmpty indices) -------------------------------------------------------------------- -- Notes: getNotesMaster :: PandocMonad m => P m Element getNotesMaster = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive parseXml refArchive distArchive "ppt/notesMasters/notesMaster1.xml" getSlideNumberFieldId :: PandocMonad m => Element -> P m T.Text getSlideNumberFieldId notesMaster | ns <- elemToNameSpaces notesMaster , Just cSld <- findChild (elemName ns "p" "cSld") notesMaster , Just spTree <- findChild (elemName ns "p" "spTree") cSld , Just sp <- getShapeByPlaceHolderType ns spTree (PHType "sldNum") , Just txBody <- findChild (elemName ns "p" "txBody") sp , Just p <- findChild (elemName ns "a" "p") txBody , Just fld <- findChild (elemName ns "a" "fld") p , Just fldId <- findAttr (QName "id" Nothing Nothing) fld = return fldId | otherwise = throwError $ PandocSomeError "No field id for slide numbers in notesMaster.xml" speakerNotesSlideImage :: Element speakerNotesSlideImage = mknode "p:sp" [] [ mknode "p:nvSpPr" [] [ mknode "p:cNvPr" [ ("id", "2") , ("name", "Slide Image Placeholder 1") ] () , mknode "p:cNvSpPr" [] [ mknode "a:spLocks" [ ("noGrp", "1") , ("noRot", "1") , ("noChangeAspect", "1") ] () ] , mknode "p:nvPr" [] [ mknode "p:ph" [("type", "sldImg")] ()] ] , mknode "p:spPr" [] () ] -- we want to wipe links from the speaker notes in the -- paragraphs. Powerpoint doesn't allow you to input them, and it -- would provide extra complications. removeParaLinks :: Paragraph -> Paragraph removeParaLinks paragraph = paragraph{paraElems = map f (paraElems paragraph)} where f (Run rProps s) = Run rProps{rLink=Nothing} s f pe = pe -- put an empty paragraph between paragraphs for more expected spacing. spaceParas :: [Paragraph] -> [Paragraph] spaceParas = intersperse (Paragraph def []) speakerNotesBody :: PandocMonad m => [Paragraph] -> P m Element speakerNotesBody paras = do elements <- local (\env -> env{envInSpeakerNotes = True}) $ mapM paragraphToElement $ spaceParas $ map removeParaLinks paras let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] <> elements return $ surroundWithMathAlternate $ mknode "p:sp" [] [ mknode "p:nvSpPr" [] [ mknode "p:cNvPr" [ ("id", "3") , ("name", "Notes Placeholder 2") ] () , mknode "p:cNvSpPr" [] [ mknode "a:spLocks" [("noGrp", "1")] ()] , mknode "p:nvPr" [] [ mknode "p:ph" [("type", "body"), ("idx", "1")] ()] ] , mknode "p:spPr" [] () , txBody ] speakerNotesSlideNumber :: Int -> T.Text -> Element speakerNotesSlideNumber pgNum fieldId = mknode "p:sp" [] [ mknode "p:nvSpPr" [] [ mknode "p:cNvPr" [ ("id", "4") , ("name", "Slide Number Placeholder 3") ] () , mknode "p:cNvSpPr" [] [ mknode "a:spLocks" [("noGrp", "1")] ()] , mknode "p:nvPr" [] [ mknode "p:ph" [ ("type", "sldNum") , ("sz", "quarter") , ("idx", "10") ] () ] ] , mknode "p:spPr" [] () , mknode "p:txBody" [] [ mknode "a:bodyPr" [] () , mknode "a:lstStyle" [] () , mknode "a:p" [] [ mknode "a:fld" [ ("id", fieldId) , ("type", "slidenum") ] [ mknode "a:rPr" [("lang", "en-US")] () , mknode "a:t" [] (tshow pgNum) ] , mknode "a:endParaRPr" [("lang", "en-US")] () ] ] ] slideToSpeakerNotesElement :: PandocMonad m => Slide -> P m (Maybe Element) slideToSpeakerNotesElement (Slide _ _ (SpeakerNotes []) _) = return Nothing slideToSpeakerNotesElement slide@(Slide _ _ (SpeakerNotes paras) _) = do master <- getNotesMaster fieldId <- getSlideNumberFieldId master num <- slideNum slide let imgShape = speakerNotesSlideImage sldNumShape = speakerNotesSlideNumber num fieldId bodyShape <- speakerNotesBody paras return $ Just $ mknode "p:notes" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main") , ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships") , ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [ mknode "p:cSld" [] [ mknode "p:spTree" [] [ mknode "p:nvGrpSpPr" [] [ mknode "p:cNvPr" [("id", "1"), ("name", "")] () , mknode "p:cNvGrpSpPr" [] () , mknode "p:nvPr" [] () ] , mknode "p:grpSpPr" [] [ mknode "a:xfrm" [] [ mknode "a:off" [("x", "0"), ("y", "0")] () , mknode "a:ext" [("cx", "0"), ("cy", "0")] () , mknode "a:chOff" [("x", "0"), ("y", "0")] () , mknode "a:chExt" [("cx", "0"), ("cy", "0")] () ] ] , imgShape , bodyShape , sldNumShape ] ] ] ----------------------------------------------------------------------- getSlideIdNum :: PandocMonad m => SlideId -> P m Int getSlideIdNum sldId = do slideIdMap <- asks envSlideIdMap case M.lookup sldId slideIdMap of Just n -> return n Nothing -> throwError $ PandocShouldNeverHappenError $ "Slide Id " <> tshow sldId <> " not found." slideNum :: PandocMonad m => Slide -> P m Int slideNum slide = getSlideIdNum $ slideId slide idNumToFilePath :: Int -> FilePath idNumToFilePath idNum = "slide" <> show idNum <> ".xml" slideToFilePath :: PandocMonad m => Slide -> P m FilePath slideToFilePath slide = do idNum <- slideNum slide return $ "slide" <> show idNum <> ".xml" slideToRelId :: PandocMonad m => MinimumRId -> Slide -> P m T.Text slideToRelId minSlideRId slide = do n <- slideNum slide return $ "rId" <> tshow (n + minSlideRId - 1) data Relationship = Relationship { relId :: Int , relType :: MimeType , relTarget :: FilePath } deriving (Show, Eq) elementToRel :: Element -> Maybe Relationship elementToRel element | elName element == QName "Relationship" (Just "http://schemas.openxmlformats.org/package/2006/relationships") Nothing = do rId <- findAttr (QName "Id" Nothing Nothing) element numStr <- T.stripPrefix "rId" rId num <- fromIntegral <$> readTextAsInteger numStr type' <- findAttr (QName "Type" Nothing Nothing) element target <- findAttr (QName "Target" Nothing Nothing) element return $ Relationship num type' (T.unpack target) | otherwise = Nothing slideToPresRel :: PandocMonad m => Int -> Slide -> P m Relationship slideToPresRel minimumSlideRId slide = do idNum <- slideNum slide let rId = idNum + minimumSlideRId - 1 fp = "slides/" <> idNumToFilePath idNum return $ Relationship { relId = rId , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" , relTarget = fp } getPresentationRels :: PandocMonad m => P m [Relationship] getPresentationRels = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive relsElem <- parseXml refArchive distArchive "ppt/_rels/presentation.xml.rels" let globalNS = "http://schemas.openxmlformats.org/package/2006/relationships" let relElems = findChildren (QName "Relationship" (Just globalNS) Nothing) relsElem return $ mapMaybe elementToRel relElems -- | Info required to update a presentation rId from the reference doc for the -- output. type PresentationRIdUpdateData = (ReferenceMinRIdAfterSlides, NewRIdBounds) -- | The minimum and maximum rIds for presentation relationships created from -- the presentation content (as opposed to from the reference doc). -- -- Relationships taken from the reference doc should have their rId number -- adjusted to make sure it sits outside this range. type NewRIdBounds = (MinimumRId, MaximumRId) -- | The minimum presentation rId from the reference doc which comes after the -- first slide rId (in the reference doc). type ReferenceMinRIdAfterSlides = Int type MinimumRId = Int type MaximumRId = Int -- | Given a presentation rId from the reference doc, return the value it should -- have in the output. updatePresentationRId :: PresentationRIdUpdateData -> Int -> Int updatePresentationRId (minOverlappingRId, (minNewId, maxNewId)) n | n < minNewId = n | otherwise = n - minOverlappingRId + maxNewId + 1 presentationToRels :: PandocMonad m => Presentation -> P m (PresentationRIdUpdateData, [Relationship]) presentationToRels pres@(Presentation _ slides) = do rels <- getPresentationRels -- We want to make room for the slides in the id space. We'll assume the slide -- masters come first (this seems to be what PowerPoint does by default, and -- is true of the reference doc), and we'll put the slides next. So we find -- the starting rId for the slides by finding the maximum rId for the masters -- and adding 1. -- -- Then: -- 1. We look to see what the minimum rId which is greater than or equal to -- the minimum slide rId is, in the rels we're keeping from the reference -- doc (i.e. the minimum rId which might overlap with the slides). -- 2. We increase this minimum overlapping rId to 1 higher than the last slide -- rId (or the notesMaster rel, if we're including one), and increase all -- rIds higher than this minimum by the same amount. let masterRels = filter (T.isSuffixOf "slideMaster" . relType) rels slideStartId = maybe 1 ((+ 1) . maximum . fmap relId) (nonEmpty masterRels) -- we remove the slide rels and the notesmaster (if it's -- there). We'll put these back in ourselves, if necessary. relsWeKeep = filter (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" && relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster") rels minOverlappingRel = maybe 0 minimum (nonEmpty (filter (slideStartId <=) (relId <$> relsWeKeep))) mySlideRels <- mapM (slideToPresRel slideStartId) slides let notesMasterRels = [Relationship { relId = slideStartId + length mySlideRels , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster" , relTarget = "notesMasters/notesMaster1.xml" } | presHasSpeakerNotes pres] insertedRels = mySlideRels <> notesMasterRels newRIdBounds = (slideStartId, slideStartId + length insertedRels - 1) updateRId = updatePresentationRId (minOverlappingRel, newRIdBounds) relsWeKeep' = map (\r -> r{relId = updateRId $ relId r}) relsWeKeep return ((minOverlappingRel, newRIdBounds), insertedRels <> relsWeKeep') -- We make this ourselves, in case there's a thumbnail in the one from -- the template. topLevelRels :: [Relationship] topLevelRels = [ Relationship { relId = 1 , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" , relTarget = "ppt/presentation.xml" } , Relationship { relId = 2 , relType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" , relTarget = "docProps/core.xml" } , Relationship { relId = 3 , relType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/extended-properties" , relTarget = "docProps/app.xml" } , Relationship { relId = 4 , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" , relTarget = "docProps/custom.xml" } ] topLevelRelsEntry :: PandocMonad m => P m Entry topLevelRelsEntry = elemToEntry "_rels/.rels" $ relsToElement topLevelRels relToElement :: Relationship -> Element relToElement rel = mknode "Relationship" [ ("Id", "rId" <> tshow (relId rel)) , ("Type", relType rel) , ("Target", T.pack $ relTarget rel) ] () relsToElement :: [Relationship] -> Element relsToElement rels = mknode "Relationships" [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] (map relToElement rels) presentationToRelsEntry :: PandocMonad m => Presentation -> P m (PresentationRIdUpdateData, Entry) presentationToRelsEntry pres = do (presentationRIdUpdateData, rels) <- presentationToRels pres element <- elemToEntry "ppt/_rels/presentation.xml.rels" $ relsToElement rels pure (presentationRIdUpdateData, element) elemToEntry :: PandocMonad m => FilePath -> Element -> P m Entry elemToEntry fp element = do epochtime <- floor . utcTimeToPOSIXSeconds <$> asks envUTCTime return $ toEntry fp epochtime $ renderXml element slideToEntry :: PandocMonad m => Slide -> P m Entry slideToEntry slide = do idNum <- slideNum slide local (\env -> env{envCurSlideId = idNum}) $ do element <- slideToElement slide elemToEntry ("ppt/slides/" <> idNumToFilePath idNum) element slideToSpeakerNotesEntry :: PandocMonad m => Slide -> P m (Maybe Entry) slideToSpeakerNotesEntry slide = do idNum <- slideNum slide local (\env -> env{envCurSlideId = idNum}) $ do mbElement <- slideToSpeakerNotesElement slide mbNotesIdNum <- do mp <- asks envSpeakerNotesIdMap return $ M.lookup idNum mp case mbElement of Just element | Just notesIdNum <- mbNotesIdNum -> Just <$> elemToEntry ("ppt/notesSlides/notesSlide" <> show notesIdNum <> ".xml") element _ -> return Nothing slideToSpeakerNotesRelElement :: PandocMonad m => Slide -> P m (Maybe Element) slideToSpeakerNotesRelElement (Slide _ _ (SpeakerNotes []) _) = return Nothing slideToSpeakerNotesRelElement slide@Slide{} = do idNum <- slideNum slide return $ Just $ mknode "Relationships" [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] [ mknode "Relationship" [ ("Id", "rId2") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") , ("Target", "../slides/slide" <> tshow idNum <> ".xml") ] () , mknode "Relationship" [ ("Id", "rId1") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster") , ("Target", "../notesMasters/notesMaster1.xml") ] () ] slideToSpeakerNotesRelEntry :: PandocMonad m => Slide -> P m (Maybe Entry) slideToSpeakerNotesRelEntry slide = do idNum <- slideNum slide mbElement <- slideToSpeakerNotesRelElement slide mp <- asks envSpeakerNotesIdMap let mbNotesIdNum = M.lookup idNum mp case mbElement of Just element | Just notesIdNum <- mbNotesIdNum -> Just <$> elemToEntry ("ppt/notesSlides/_rels/notesSlide" <> show notesIdNum <> ".xml.rels") element _ -> return Nothing slideToSlideRelEntry :: PandocMonad m => Slide -> P m Entry slideToSlideRelEntry slide = do idNum <- slideNum slide element <- slideToSlideRelElement slide elemToEntry ("ppt/slides/_rels/" <> idNumToFilePath idNum <> ".rels") element linkRelElement :: PandocMonad m => (Int, LinkTarget) -> P m Element linkRelElement (rIdNum, InternalTarget targetId) = do targetIdNum <- getSlideIdNum targetId return $ mknode "Relationship" [ ("Id", "rId" <> tshow rIdNum) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") , ("Target", "slide" <> tshow targetIdNum <> ".xml") ] () linkRelElement (rIdNum, ExternalTarget (url, _)) = return $ mknode "Relationship" [ ("Id", "rId" <> tshow rIdNum) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") , ("Target", url) , ("TargetMode", "External") ] () linkRelElements :: PandocMonad m => M.Map Int LinkTarget -> P m [Element] linkRelElements mp = mapM linkRelElement (M.toList mp) mediaRelElement :: MediaInfo -> Element mediaRelElement mInfo = let ext = fromMaybe "" (mInfoExt mInfo) in mknode "Relationship" [ ("Id", "rId" <> tshow (mInfoLocalId mInfo)) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") , ("Target", "../media/image" <> tshow (mInfoGlobalId mInfo) <> ext) ] () speakerNotesSlideRelElement :: PandocMonad m => Slide -> P m (Maybe Element) speakerNotesSlideRelElement slide = do idNum <- slideNum slide mp <- asks envSpeakerNotesIdMap return $ case M.lookup idNum mp of Nothing -> Nothing Just n -> let target = "../notesSlides/notesSlide" <> tshow n <> ".xml" in Just $ mknode "Relationship" [ ("Id", "rId2") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide") , ("Target", target) ] () slideToSlideRelElement :: PandocMonad m => Slide -> P m Element slideToSlideRelElement slide = do idNum <- slideNum slide target <- flip fmap getSlideLayouts $ T.pack . ("../slideLayouts/" <>) . takeFileName . slPath . case slide of (Slide _ MetadataSlide{} _ _) -> metadata (Slide _ TitleSlide{} _ _) -> title (Slide _ ContentSlide{} _ _) -> content (Slide _ TwoColumnSlide{} _ _) -> twoColumn (Slide _ ComparisonSlide{} _ _) -> comparison (Slide _ ContentWithCaptionSlide{} _ _) -> contentWithCaption (Slide _ BlankSlide _ _) -> blank speakerNotesRels <- maybeToList <$> speakerNotesSlideRelElement slide linkIds <- gets stLinkIds mediaIds <- gets stMediaIds linkRels <- case M.lookup idNum linkIds of Just mp -> linkRelElements mp Nothing -> return [] let mediaRels = case M.lookup idNum mediaIds of Just mInfos -> map mediaRelElement mInfos Nothing -> [] return $ mknode "Relationships" [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] ([mknode "Relationship" [ ("Id", "rId1") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") , ("Target", target)] () ] <> speakerNotesRels <> linkRels <> mediaRels) slideToSldIdElement :: PandocMonad m => MinimumRId -> Slide -> P m Element slideToSldIdElement minimumSlideRId slide = do n <- slideNum slide let id' = tshow $ n + 255 rId <- slideToRelId minimumSlideRId slide return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] () presentationToSldIdLst :: PandocMonad m => MinimumRId -> Presentation -> P m Element presentationToSldIdLst minimumSlideRId (Presentation _ slides) = do ids <- mapM (slideToSldIdElement minimumSlideRId) slides return $ mkNodeSldIdLst ids mkNodeSldIdLst :: [Element] -> Element mkNodeSldIdLst = mknode "p:sldIdLst" [] presentationToPresentationElement :: PandocMonad m => PresentationRIdUpdateData -> Presentation -> P m Element presentationToPresentationElement presentationUpdateRIdData pres = do let (_, (minSlideRId, maxSlideRId)) = presentationUpdateRIdData refArchive <- asks envRefArchive distArchive <- asks envDistArchive element <- parseXml refArchive distArchive "ppt/presentation.xml" sldIdLst <- presentationToSldIdLst minSlideRId pres let modifySldIdLst :: Content -> Content modifySldIdLst ct = if isSldIdLst ct then Elem sldIdLst else ct notesMasterRId = maxSlideRId notesMasterElem = mknode "p:notesMasterIdLst" [] [ mknode "p:notesMasterId" [("r:id", "rId" <> tshow notesMasterRId)] () ] -- if there's a notesMasterIdLst in the presentation.xml file, -- we want to remove it. We then want to put our own, if -- necessary, after the slideMasterIdLst element. We also remove -- handouts master, since we don't want it. removeUnwantedMaster' :: Content -> [Content] removeUnwantedMaster' (Elem e) = case elName e of (QName "notesMasterIdLst" _ _) -> [] (QName "handoutMasterIdLst" _ _) -> [] _ -> [Elem e] removeUnwantedMaster' ct = [ct] removeUnwantedMaster :: [Content] -> [Content] removeUnwantedMaster = concatMap removeUnwantedMaster' insertNotesMaster :: [Content] -> [Content] insertNotesMaster = if presHasSpeakerNotes pres then insertAfterSldMasterIdLst notesMasterElem else id updateRIds :: Content -> Content updateRIds (Elem el) = Elem (el { elAttribs = fmap updateRIdAttribute (elAttribs el) , elContent = fmap updateRIds (elContent el) }) updateRIds content = content updateRIdAttribute :: XML.Attr -> XML.Attr updateRIdAttribute attr = fromMaybe attr $ do oldValue <- case attrKey attr of QName "id" _ (Just "r") -> T.stripPrefix "rId" (attrVal attr) >>= fmap fromIntegral . readTextAsInteger _ -> Nothing let newValue = updatePresentationRId presentationUpdateRIdData oldValue pure attr {attrVal = "rId" <> T.pack (show newValue)} -- if there is no sldIdLst in the presentation.xml file, add an empty one -- after the sldMasterIdLst so modifySldIdLst can replace it. insertSldIdListIfMissing :: [Content] -> [Content] insertSldIdListIfMissing contentList = if any isSldIdLst contentList then contentList else insertAfterSldMasterIdLst (mkNodeSldIdLst []) contentList insertAfterSldMasterIdLst' :: Content -> Content -> [Content] insertAfterSldMasterIdLst' newElement ct = if isElemName "sldMasterIdLst" ct then [ct, newElement] else [ct] insertAfterSldMasterIdLst :: Element -> [Content] -> [Content] insertAfterSldMasterIdLst newElement = concatMap $ insertAfterSldMasterIdLst' $ Elem newElement isElemName :: T.Text -> Content -> Bool isElemName name (Elem e) = qName (elName e) == name isElemName _ _ = False isSldIdLst :: Content -> Bool isSldIdLst = isElemName "sldIdLst" newContent = insertNotesMaster $ removeUnwantedMaster $ (modifySldIdLst . updateRIds) <$> insertSldIdListIfMissing (elContent element) return $ element{elContent = newContent} presentationToPresEntry :: PandocMonad m => PresentationRIdUpdateData -> Presentation -> P m Entry presentationToPresEntry presentationRIdUpdateData pres = presentationToPresentationElement presentationRIdUpdateData pres >>= elemToEntry "ppt/presentation.xml" -- adapted from the Docx writer docPropsElement :: PandocMonad m => DocProps -> P m Element docPropsElement docProps = do utctime <- asks envUTCTime let keywords = case dcKeywords docProps of Just xs -> T.intercalate ", " xs Nothing -> "" return $ 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")] $ mknode "dc:title" [] (fromMaybe "" $ dcTitle docProps) : mknode "dc:creator" [] (fromMaybe "" $ dcCreator docProps) : mknode "cp:keywords" [] keywords : ( [mknode "dc:subject" [] $ fromMaybe "" $ dcSubject docProps | isJust (dcSubject docProps)]) <> ( [mknode "dc:description" [] $ fromMaybe "" $ dcDescription docProps | isJust (dcDescription docProps)]) <> ( [mknode "cp:category" [] $ fromMaybe "" $ cpCategory docProps | isJust (cpCategory docProps)]) <> (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x ]) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime) docPropsToEntry :: PandocMonad m => DocProps -> P m Entry docPropsToEntry docProps = docPropsElement docProps >>= elemToEntry "docProps/core.xml" -- adapted from the Docx writer docCustomPropsElement :: PandocMonad m => DocProps -> P m Element docCustomPropsElement docProps = do let mkCustomProp (k, v) pid = mknode "property" [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}") ,("pid", tshow pid) ,("name", k)] $ mknode "vt:lpwstr" [] v return $ mknode "Properties" [("xmlns","http://schemas.openxmlformats.org/officeDocument/2006/custom-properties") ,("xmlns:vt","http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes") ] $ zipWith mkCustomProp (fromMaybe [] $ customProperties docProps) [(2 :: Int)..] docCustomPropsToEntry :: PandocMonad m => DocProps -> P m Entry docCustomPropsToEntry docProps = docCustomPropsElement docProps >>= elemToEntry "docProps/custom.xml" -- We read from the template, but we remove the lastView, so it always -- opens on slide view. Templates will sometimes be open in master -- view for editing. viewPropsElement :: PandocMonad m => P m Element viewPropsElement = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive viewPrElement <- parseXml refArchive distArchive "ppt/viewProps.xml" -- remove "lastView" if it exists: let notLastView :: XML.Attr -> Bool notLastView attr = qName (attrKey attr) /= "lastView" return $ viewPrElement {elAttribs = filter notLastView (elAttribs viewPrElement)} makeViewPropsEntry :: PandocMonad m => P m Entry makeViewPropsEntry = viewPropsElement >>= elemToEntry "ppt/viewProps.xml" defaultContentTypeToElem :: DefaultContentType -> Element defaultContentTypeToElem dct = mknode "Default" [("Extension", defContentTypesExt dct), ("ContentType", defContentTypesType dct)] () overrideContentTypeToElem :: OverrideContentType -> Element overrideContentTypeToElem oct = mknode "Override" [("PartName", T.pack $ overrideContentTypesPart oct), ("ContentType", overrideContentTypesType oct)] () contentTypesToElement :: ContentTypes -> Element contentTypesToElement ct = let ns = "http://schemas.openxmlformats.org/package/2006/content-types" in mknode "Types" [("xmlns", ns)] $ map defaultContentTypeToElem (contentTypesDefaults ct) <> map overrideContentTypeToElem (contentTypesOverrides ct) data DefaultContentType = DefaultContentType { defContentTypesExt :: T.Text , defContentTypesType:: MimeType } deriving (Show, Eq) data OverrideContentType = OverrideContentType { overrideContentTypesPart :: FilePath , overrideContentTypesType :: MimeType } deriving (Show, Eq) data ContentTypes = ContentTypes { contentTypesDefaults :: [DefaultContentType] , contentTypesOverrides :: [OverrideContentType] } deriving (Show, Eq) contentTypesToEntry :: PandocMonad m => ContentTypes -> P m Entry contentTypesToEntry ct = elemToEntry "[Content_Types].xml" $ contentTypesToElement ct pathToOverride :: FilePath -> Maybe OverrideContentType pathToOverride fp = OverrideContentType ("/" <> fp) <$> getContentType fp mediaFileContentType :: FilePath -> Maybe DefaultContentType mediaFileContentType fp = case takeExtension fp of '.' : ext -> Just $ DefaultContentType { defContentTypesExt = T.pack ext , defContentTypesType = fromMaybe "application/octet-stream" (getMimeType fp) } _ -> Nothing mediaContentType :: MediaInfo -> Maybe DefaultContentType mediaContentType mInfo | Just t <- mInfoExt mInfo , Just ('.', ext) <- T.uncons t = Just $ DefaultContentType { defContentTypesExt = ext , defContentTypesType = fromMaybe "application/octet-stream" (mInfoMimeType mInfo) } | otherwise = Nothing getSpeakerNotesFilePaths :: PandocMonad m => P m [FilePath] getSpeakerNotesFilePaths = do mp <- asks envSpeakerNotesIdMap let notesIdNums = M.elems mp return $ map (\n -> "ppt/notesSlides/notesSlide" <> show n <> ".xml") notesIdNums presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes presentationToContentTypes p@(Presentation _ slides) = do mediaInfos <- mconcat . M.elems <$> gets stMediaIds filePaths <- patternsToFilePaths $ inheritedPatterns p let mediaFps = filter (match (compile "ppt/media/image*")) filePaths fontFps = filter (match (compile "ppt/fonts/*")) filePaths let defaults = [ DefaultContentType "xml" "application/xml" , DefaultContentType "rels" "application/vnd.openxmlformats-package.relationships+xml" ] mediaDefaults = nub $ mapMaybe mediaContentType mediaInfos <> mapMaybe mediaFileContentType mediaFps fontDefaults = [ DefaultContentType "fntdata" "application/x-fontdata" | any (\fp -> takeExtension fp == ".fntdata") fontFps ] inheritedOverrides = mapMaybe pathToOverride filePaths createdOverrides = mapMaybe pathToOverride [ "docProps/core.xml" , "docProps/custom.xml" , "ppt/presentation.xml" , "ppt/viewProps.xml" ] relativePaths <- mapM slideToFilePath slides let slideOverrides = mapMaybe (\fp -> pathToOverride $ "ppt/slides/" <> fp) relativePaths speakerNotesOverrides <- mapMaybe pathToOverride <$> getSpeakerNotesFilePaths return $ ContentTypes (defaults <> mediaDefaults <> fontDefaults) (inheritedOverrides <> createdOverrides <> slideOverrides <> speakerNotesOverrides) presML :: T.Text presML = "application/vnd.openxmlformats-officedocument.presentationml" noPresML :: T.Text noPresML = "application/vnd.openxmlformats-officedocument" getContentType :: FilePath -> Maybe MimeType getContentType fp | fp == "ppt/presentation.xml" = Just $ presML <> ".presentation.main+xml" | fp == "ppt/presProps.xml" = Just $ presML <> ".presProps+xml" | fp == "ppt/viewProps.xml" = Just $ presML <> ".viewProps+xml" | fp == "ppt/tableStyles.xml" = Just $ presML <> ".tableStyles+xml" | fp == "docProps/core.xml" = Just "application/vnd.openxmlformats-package.core-properties+xml" | fp == "docProps/custom.xml" = Just "application/vnd.openxmlformats-officedocument.custom-properties+xml" | fp == "docProps/app.xml" = Just $ noPresML <> ".extended-properties+xml" | ["ppt", "slideMasters", f] <- splitDirectories fp , (_, ".xml") <- splitExtension f = Just $ presML <> ".slideMaster+xml" | ["ppt", "slides", f] <- splitDirectories fp , (_, ".xml") <- splitExtension f = Just $ presML <> ".slide+xml" | ["ppt", "notesMasters", f] <- splitDirectories fp , (_, ".xml") <- splitExtension f = Just $ presML <> ".notesMaster+xml" | ["ppt", "notesSlides", f] <- splitDirectories fp , (_, ".xml") <- splitExtension f = Just $ presML <> ".notesSlide+xml" | ["ppt", "theme", f] <- splitDirectories fp , (_, ".xml") <- splitExtension f = Just $ noPresML <> ".theme+xml" | ["ppt", "slideLayouts", _] <- splitDirectories fp= Just $ presML <> ".slideLayout+xml" | otherwise = Nothing -- Kept as String for XML.Light autoNumAttrs :: ListAttributes -> [(Text, Text)] autoNumAttrs (startNum, numStyle, numDelim) = numAttr <> typeAttr where numAttr = [("startAt", tshow startNum) | startNum /= 1] typeAttr = [("type", typeString <> delimString)] typeString = case numStyle of Decimal -> "arabic" UpperAlpha -> "alphaUc" LowerAlpha -> "alphaLc" UpperRoman -> "romanUc" LowerRoman -> "romanLc" _ -> "arabic" delimString = case numDelim of Period -> "Period" OneParen -> "ParenR" TwoParens -> "ParenBoth" _ -> "Period" -- | The XML required to insert an "appear" animation for each of the given -- groups of paragraphs, identified by index. incrementalAnimation :: -- | (ShapeId, [(startParagraphIndex, endParagraphIndex)]) NonEmpty (ShapeId, NonEmpty (Integer, Integer)) -> Element incrementalAnimation indices = mknode "p:timing" [] [tnLst, bldLst] where triples :: NonEmpty (ShapeId, Integer, Integer) triples = do (shapeId, paragraphIds) <- indices (start, end) <- paragraphIds pure (shapeId, start, end) tnLst = mknode "p:tnLst" [] $ mknode "p:par" [] $ mknode "p:cTn" [ ("id", "1") , ("dur", "indefinite") , ("restart", "never") , ("nodeType", "tmRoot") ] $ mknode "p:childTnLst" [] $ mknode "p:seq" [ ("concurrent", "1") , ("nextAc", "seek") ] [ mknode "p:cTn" [ ("id", "2") , ("dur", "indefinite") , ("nodeType", "mainSeq") ] $ mknode "p:childTnLst" [] $ zipWith makePar [3, 7 ..] (toList triples) , mknode "p:prevCondLst" [] $ mknode "p:cond" ([("evt", "onPrev"), ("delay", "0")]) $ mknode "p:tgtEl" [] $ mknode "p:sldTgt" [] () , mknode "p:nextCondLst" [] $ mknode "p:cond" ([("evt", "onNext"), ("delay", "0")]) $ mknode "p:tgtEl" [] $ mknode "p:sldTgt" [] () ] bldLst = mknode "p:bldLst" [] [ mknode "p:bldP" [ ("spid", T.pack (show shapeId)) , ("grpId", "0") , ("uiExpand", "1") , ("build", "p") ] () | (shapeId, _) <- toList indices ] makePar :: Integer -> (ShapeId, Integer, Integer) -> Element makePar nextId (shapeId, start, end) = mknode "p:par" [] $ mknode "p:cTn" [("id", T.pack (show nextId)), ("fill", "hold")] [ mknode "p:stCondLst" [] $ mknode "p:cond" [("delay", "indefinite")] () , mknode "p:childTnLst" [] $ mknode "p:par" [] $ mknode "p:cTn" [ ("id", T.pack (show (nextId + 1))) , ("fill", "hold") ] [ mknode "p:stCondLst" [] $ mknode "p:cond" [("delay", "0")] () , mknode "p:childTnLst" [] $ mknode "p:par" [] $ mknode "p:cTn" [ ("id", T.pack (show (nextId + 2))) , ("presetID", "1") , ("presetClass", "entr") , ("presetSubtype", "0") , ("fill", "hold") , ("grpId", "0") , ("nodeType", "clickEffect") ] [ mknode "p:stCondLst" [] $ mknode "p:cond" [("delay", "0")] () , mknode "p:childTnLst" [] $ mknode "p:set" [] [ mknode "p:cBhvr" [] [ mknode "p:cTn" [ ("id", T.pack (show (nextId + 3))) , ("dur", "1") , ("fill", "hold") ] $ mknode "p:stCondLst" [] $ mknode "p:cond" [("delay", "0")] () , mknode "p:tgtEl" [] $ mknode "p:spTgt" [("spid", T.pack (show shapeId))] $ mknode "p:txEl" [] $ mknode "p:pRg" [ ("st", T.pack (show start)) , ("end", T.pack (show end))] () , mknode "p:attrNameLst" [] $ mknode "p:attrName" [] ("style.visibility" :: Text) ] , mknode "p:to" [] $ mknode "p:strVal" [("val", "visible")] () ] ] ] ] ================================================ FILE: src/Text/Pandoc/Writers/Powerpoint/Presentation.hs ================================================ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.Powerpoint.Presentation Copyright : Copyright (C) 2017-2020 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> Stability : alpha Portability : portable Definition of Presentation datatype, modeling a MS Powerpoint (pptx) document, and functions for converting a Pandoc document to Presentation. -} module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation , Presentation(..) , DocProps(..) , Slide(..) , Layout(..) , SpeakerNotes(..) , SlideId(..) , Shape(..) , Graphic(..) , BulletType(..) , Algnment(..) , Paragraph(..) , ParaElem(..) , ParaProps(..) , RunProps(..) , TableProps(..) , Strikethrough(..) , Capitals(..) , Pixels , PicProps(..) , URL , TeXString(..) , LinkTarget(..) ) where import Control.Monad import Control.Monad.Reader import Control.Monad.State import Data.List (intercalate) import Data.List.NonEmpty (nonEmpty) import Data.Default import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Slides (getSlideLevel) import Text.Pandoc.Options import Text.Pandoc.Logging import Text.Pandoc.Walk import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element" import Text.Pandoc.Shared (tshow) import Text.Pandoc.Writers.Shared (lookupMetaInlines, lookupMetaBlocks , lookupMetaString, toTableOfContents , toLegacyTable) import qualified Data.Map as M import qualified Data.Set as S import Data.Maybe (maybeToList, fromMaybe, listToMaybe, isNothing) import Text.Pandoc.Highlighting import qualified Data.Text as T import Control.Applicative ((<|>)) import Skylighting import Data.Bifunctor (bimap) import Data.Char (isSpace) data WriterEnv = WriterEnv { envMetadata :: Meta , envRunProps :: RunProps , envParaProps :: ParaProps , envSlideLevel :: Int , envOpts :: WriterOptions , envSlideHasHeader :: Bool , envInList :: Bool , envInNoteSlide :: Bool , envCurSlideId :: SlideId , envInSpeakerNotes :: Bool , envInIncrementalDiv :: Maybe InIncrementalDiv , envInListInBlockQuote :: Bool } deriving (Show) instance Default WriterEnv where def = WriterEnv { envMetadata = mempty , envRunProps = def , envParaProps = def , envSlideLevel = 2 , envOpts = def , envSlideHasHeader = False , envInList = False , envInNoteSlide = False , envCurSlideId = SlideId "Default" , envInSpeakerNotes = False , envInIncrementalDiv = Nothing , envInListInBlockQuote = False } data WriterState = WriterState { stNoteIds :: M.Map Int [Block] -- associate anchors with slide id , stAnchorMap :: M.Map T.Text SlideId , stSlideIdSet :: S.Set SlideId , stLog :: [LogMessage] , stSpeakerNotes :: SpeakerNotes } deriving (Show, Eq) instance Default WriterState where def = WriterState { stNoteIds = mempty , stAnchorMap = mempty -- we reserve this s , stSlideIdSet = reservedSlideIds , stLog = [] , stSpeakerNotes = mempty } data InIncrementalDiv = InIncremental -- ^ The current content is contained within an "incremental" div. | InNonIncremental -- ^ The current content is contained within a "nonincremental" div. deriving (Show) listShouldBeIncremental :: Pres Bool listShouldBeIncremental = do incrementalOption <- asks (writerIncremental . envOpts) inIncrementalDiv <- asks envInIncrementalDiv inBlockQuote <- asks envInListInBlockQuote let toBoolean = (\case InIncremental -> True InNonIncremental -> False) maybeInvert = if inBlockQuote then not else id pure (maybeInvert (maybe incrementalOption toBoolean inIncrementalDiv)) metadataSlideId :: SlideId metadataSlideId = SlideId "Metadata" tocSlideId :: SlideId tocSlideId = SlideId "TOC" endNotesSlideId :: SlideId endNotesSlideId = SlideId "EndNotes" reservedSlideIds :: S.Set SlideId reservedSlideIds = S.fromList [ metadataSlideId , tocSlideId , endNotesSlideId ] uniqueSlideId' :: Integer -> S.Set SlideId -> T.Text -> SlideId uniqueSlideId' n idSet s = let s' = if n == 0 then s else s <> "-" <> tshow n in if SlideId s' `S.member` idSet then uniqueSlideId' (n+1) idSet s else SlideId s' uniqueSlideId :: S.Set SlideId -> T.Text -> SlideId uniqueSlideId = uniqueSlideId' 0 runUniqueSlideId :: T.Text -> Pres SlideId runUniqueSlideId s = do idSet <- gets stSlideIdSet let sldId = uniqueSlideId idSet s modify $ \st -> st{stSlideIdSet = S.insert sldId idSet} return sldId addLogMessage :: LogMessage -> Pres () addLogMessage msg = modify $ \st -> st{stLog = msg : stLog st} type Pres = ReaderT WriterEnv (State WriterState) runPres :: WriterEnv -> WriterState -> Pres a -> (a, [LogMessage]) runPres env st p = (pres, reverse $ stLog finalSt) where (pres, finalSt) = runState (runReaderT p env) st type Pixels = Integer data Presentation = Presentation DocProps [Slide] deriving (Show) data DocProps = DocProps { dcTitle :: Maybe T.Text , dcSubject :: Maybe T.Text , dcCreator :: Maybe T.Text , dcKeywords :: Maybe [T.Text] , dcDescription :: Maybe T.Text , cpCategory :: Maybe T.Text , dcDate :: Maybe T.Text , customProperties :: Maybe [(T.Text, T.Text)] } deriving (Show, Eq) data Slide = Slide { slideId :: SlideId , slideLayout :: Layout , slideSpeakerNotes :: SpeakerNotes , slideBackgroundImage :: Maybe FilePath } deriving (Show, Eq) newtype SlideId = SlideId T.Text deriving (Show, Eq, Ord) -- In theory you could have anything on a notes slide but it seems -- designed mainly for one textbox, so we'll just put in the contents -- of that textbox, to avoid other shapes that won't work as well. newtype SpeakerNotes = SpeakerNotes {fromSpeakerNotes :: [Paragraph]} deriving (Show, Eq, Monoid, Semigroup) data Layout = MetadataSlide [ParaElem] [ParaElem] [[ParaElem]] [ParaElem] -- title subtitle authors date | TitleSlide [ParaElem] -- heading | ContentSlide [ParaElem] [Shape] -- heading content | TwoColumnSlide [ParaElem] [Shape] [Shape] -- heading left right | ComparisonSlide [ParaElem] ([Shape], [Shape]) ([Shape], [Shape]) -- heading left@(text, content) right@(text, content) | ContentWithCaptionSlide [ParaElem] [Shape] [Shape] -- heading text content | BlankSlide deriving (Show, Eq) data Shape = Pic PicProps FilePath T.Text [ParaElem] -- title alt-text | GraphicFrame [Graphic] [ParaElem] | TextBox [Paragraph] | RawOOXMLShape T.Text deriving (Show, Eq) type TableCell = [Paragraph] -- TODO: remove when better handling of new -- tables is implemented type SimpleCell = [Block] data TableProps = TableProps { tblPrFirstRow :: Bool , tblPrBandRow :: Bool } deriving (Show, Eq) data Graphic = Tbl [Double] TableProps [TableCell] [[TableCell]] deriving (Show, Eq) data Paragraph = Paragraph { paraProps :: ParaProps , paraElems :: [ParaElem] } deriving (Show, Eq) data BulletType = Bullet | AutoNumbering ListAttributes deriving (Show, Eq) data Algnment = AlgnLeft | AlgnRight | AlgnCenter deriving (Show, Eq) data ParaProps = ParaProps { pPropMarginLeft :: Maybe Pixels , pPropMarginRight :: Maybe Pixels , pPropLevel :: Int , pPropBullet :: Maybe BulletType , pPropAlign :: Maybe Algnment , pPropSpaceBefore :: Maybe Pixels , pPropIndent :: Maybe Pixels , pPropIncremental :: Bool } deriving (Show, Eq) instance Default ParaProps where def = ParaProps { pPropMarginLeft = Just 0 , pPropMarginRight = Just 0 , pPropLevel = 0 , pPropBullet = Nothing , pPropAlign = Nothing , pPropSpaceBefore = Nothing , pPropIndent = Just 0 , pPropIncremental = False } newtype TeXString = TeXString {unTeXString :: T.Text} deriving (Eq, Show) data ParaElem = Break | Run RunProps T.Text -- It would be more elegant to have native TeXMath -- Expressions here, but this allows us to use -- `convertmath` from T.P.Writers.Math. Will perhaps -- revisit in the future. | MathElem MathType TeXString | RawOOXMLParaElem T.Text deriving (Show, Eq) data Strikethrough = NoStrike | SingleStrike | DoubleStrike deriving (Show, Eq) data Capitals = NoCapitals | SmallCapitals | AllCapitals deriving (Show, Eq) type URL = T.Text data LinkTarget = ExternalTarget (URL, T.Text) | InternalTarget SlideId deriving (Show, Eq) data RunProps = RunProps { rPropBold :: Bool , rPropItalics :: Bool , rStrikethrough :: Maybe Strikethrough , rBaseline :: Maybe Int , rCap :: Maybe Capitals , rLink :: Maybe LinkTarget , rPropCode :: Bool , rPropBlockQuote :: Bool , rPropForceSize :: Maybe Pixels , rSolidFill :: Maybe Color -- TODO: Make a full underline data type with -- the different options. , rPropUnderline :: Bool } deriving (Show, Eq) instance Default RunProps where def = RunProps { rPropBold = False , rPropItalics = False , rStrikethrough = Nothing , rBaseline = Nothing , rCap = Nothing , rLink = Nothing , rPropCode = False , rPropBlockQuote = False , rPropForceSize = Nothing , rSolidFill = Nothing , rPropUnderline = False } data PicProps = PicProps { picPropLink :: Maybe LinkTarget , picWidth :: Maybe Dimension , picHeight :: Maybe Dimension } deriving (Show, Eq) instance Default PicProps where def = PicProps { picPropLink = Nothing , picWidth = Nothing , picHeight = Nothing } -------------------------------------------------- inlinesToParElems :: [Inline] -> Pres [ParaElem] inlinesToParElems = fmap mconcat . mapM inlineToParElems inlineToParElems :: Inline -> Pres [ParaElem] inlineToParElems (Str s) = do pr <- asks envRunProps return [Run pr s] inlineToParElems (Emph ils) = local (\r -> r{envRunProps = (envRunProps r){rPropItalics=True}}) $ inlinesToParElems ils inlineToParElems (Underline ils) = local (\r -> r{envRunProps = (envRunProps r){rPropUnderline=True}}) $ inlinesToParElems ils inlineToParElems (Strong ils) = local (\r -> r{envRunProps = (envRunProps r){rPropBold=True}}) $ inlinesToParElems ils inlineToParElems (Strikeout ils) = local (\r -> r{envRunProps = (envRunProps r){rStrikethrough=Just SingleStrike}}) $ inlinesToParElems ils inlineToParElems (Superscript ils) = local (\r -> r{envRunProps = (envRunProps r){rBaseline=Just 30000}}) $ inlinesToParElems ils inlineToParElems (Subscript ils) = local (\r -> r{envRunProps = (envRunProps r){rBaseline=Just (-25000)}}) $ inlinesToParElems ils inlineToParElems (SmallCaps ils) = local (\r -> r{envRunProps = (envRunProps r){rCap = Just SmallCapitals}}) $ inlinesToParElems ils inlineToParElems Space = inlineToParElems (Str " ") inlineToParElems SoftBreak = inlineToParElems (Str " ") inlineToParElems LineBreak = return [Break] inlineToParElems (Link _ ils (url, title)) = local (\r ->r{envRunProps = (envRunProps r){rLink = Just $ ExternalTarget (url, title)}}) $ inlinesToParElems ils inlineToParElems (Code _ str) = local (\r ->r{envRunProps = (envRunProps r){rPropCode = True}}) $ inlineToParElems $ Str str inlineToParElems (Math mathtype str) = return [MathElem mathtype (TeXString str)] -- We ignore notes if we're in a speaker notes div. Otherwise this -- would add an entry to the endnotes slide, which would put speaker -- notes in the public presentation. In the future, we can entertain a -- way of adding a speakernotes-specific note that would just add -- paragraphs to the bottom of the notes page. inlineToParElems (Note blks) = do inSpNotes <- asks envInSpeakerNotes if inSpNotes then return [] else do notes <- gets stNoteIds let maxNoteId = maybe 0 maximum $ nonEmpty $ M.keys notes curNoteId = maxNoteId + 1 modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes } local (\env -> env{envRunProps = (envRunProps env){rLink = Just $ InternalTarget endNotesSlideId}}) $ inlineToParElems $ Superscript [Str $ tshow curNoteId] inlineToParElems (Span _ ils) = inlinesToParElems ils inlineToParElems (Quoted quoteType ils) = inlinesToParElems $ [Str open] ++ ils ++ [Str close] where (open, close) = case quoteType of SingleQuote -> ("\x2018", "\x2019") DoubleQuote -> ("\x201C", "\x201D") inlineToParElems il@(RawInline fmt s) = case fmt of Format "openxml" -> return [RawOOXMLParaElem s] _ -> do addLogMessage $ InlineNotRendered il return [] inlineToParElems (Cite _ ils) = inlinesToParElems ils -- Note: we shouldn't reach this, because images should be handled at -- the shape level, but should that change in the future, we render -- the alt text. inlineToParElems (Image _ alt _) = inlinesToParElems alt isListType :: Block -> Bool isListType (OrderedList _ _) = True isListType (BulletList _) = True isListType (DefinitionList _) = True isListType _ = False registerAnchorId :: T.Text -> Pres () registerAnchorId anchor = do anchorMap <- gets stAnchorMap sldId <- asks envCurSlideId unless (T.null anchor) $ modify $ \st -> st {stAnchorMap = M.insert anchor sldId anchorMap} -- Currently hardcoded, until I figure out how to make it dynamic. blockQuoteSize :: Pixels blockQuoteSize = 20 noteSize :: Pixels noteSize = 18 blockToParagraphs :: Block -> Pres [Paragraph] blockToParagraphs (Plain ils) = blockToParagraphs (Para ils) blockToParagraphs (Para ils) = do parElems <- inlinesToParElems ils pProps <- asks envParaProps return [Paragraph pProps parElems] blockToParagraphs (LineBlock ilsList) = do parElems <- inlinesToParElems $ intercalate [LineBreak] ilsList pProps <- asks envParaProps return [Paragraph pProps parElems] -- TODO: work out the attributes blockToParagraphs (CodeBlock attr str) = do pProps <- asks envParaProps local (\r -> r{ envParaProps = def{ pPropMarginLeft = Nothing , pPropBullet = Nothing , pPropLevel = pPropLevel pProps , pPropIndent = Just 0 } , envRunProps = (envRunProps r){rPropCode = True}}) $ do highlightOpt <- writerHighlightMethod <$> asks envOpts synMap <- writerSyntaxMap <$> asks envOpts let highlightWithStyle style = do case highlight synMap (formatSourceLines style) attr str of Right pElems -> do pPropsNew <- asks envParaProps return [Paragraph pPropsNew pElems] Left _ -> blockToParagraphs $ Para [Str str] case highlightOpt of Skylighting sty -> highlightWithStyle sty DefaultHighlighting -> highlightWithStyle defaultStyle _ -> blockToParagraphs $ Para [Str str] -- We can't yet do incremental lists, but we should render a -- (BlockQuote List) as a list to maintain compatibility with other -- formats. blockToParagraphs (BlockQuote (blk : blks)) | isListType blk = do ps <- local (\env -> env { envInListInBlockQuote = True }) (blockToParagraphs blk) ps' <- blockToParagraphs $ BlockQuote blks return $ ps ++ ps' blockToParagraphs (BlockQuote blks) = local (\r -> r{ envParaProps = (envParaProps r){ pPropMarginLeft = Just 100 , pPropIndent = Just 0 } , envRunProps = (envRunProps r){rPropForceSize = Just blockQuoteSize}})$ mconcat <$> mapM blockToParagraphs blks -- TODO: work out the format blockToParagraphs blk@(RawBlock _ _) = do addLogMessage $ BlockNotRendered blk return [] blockToParagraphs (Header _ (ident, _, _) ils) = do -- Note that this function only deals with content blocks, so it -- will only touch headers that are above the current slide level -- -- slides at or below the slidelevel will be taken care of by -- `blocksToSlide'`. We have the register anchors in both of them. registerAnchorId ident -- we set the subeader to bold parElems <- local (\e->e{envRunProps = (envRunProps e){rPropBold=True}}) $ inlinesToParElems ils -- and give it a bit of space before it. return [Paragraph def{pPropSpaceBefore = Just 30} parElems] blockToParagraphs (BulletList blksLst) = do pProps <- asks envParaProps incremental <- listShouldBeIncremental local (\env -> env{ envInList = True , envParaProps = pProps{ pPropBullet = Just Bullet , pPropMarginLeft = Nothing , pPropIndent = Nothing , pPropIncremental = incremental }}) $ mconcat <$> mapM multiParList blksLst blockToParagraphs (OrderedList listAttr blksLst) = do pProps <- asks envParaProps incremental <- listShouldBeIncremental local (\env -> env{ envInList = True , envParaProps = pProps{ pPropBullet = Just (AutoNumbering listAttr) , pPropMarginLeft = Nothing , pPropIndent = Nothing , pPropIncremental = incremental }}) $ mconcat <$> mapM multiParList blksLst blockToParagraphs (DefinitionList entries) = do incremental <- listShouldBeIncremental let go :: ([Inline], [[Block]]) -> Pres [Paragraph] go (ils, blksLst) = do term <-blockToParagraphs $ Para [Strong ils] -- For now, we'll treat each definition term as a -- blockquote. We can extend this further later. definition <- mconcat <$> mapM (blockToParagraphs . BlockQuote) blksLst return $ term ++ definition local (\env -> env {envParaProps = (envParaProps env) {pPropIncremental = incremental}}) $ mconcat <$> mapM go entries blockToParagraphs (Div (_, classes, _) blks) = let hasIncremental = "incremental" `elem` classes hasNonIncremental = "nonincremental" `elem` classes incremental = if | hasIncremental -> Just InIncremental | hasNonIncremental -> Just InNonIncremental | otherwise -> Nothing addIncremental env = env { envInIncrementalDiv = incremental } 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 blockToParagraphs tbl@Table{} = notRendered tbl -- | Report that a block cannot be rendered. notRendered :: Block -> Pres [Paragraph] notRendered blk = do addLogMessage $ BlockNotRendered blk return [] -- | Make sure the bullet env gets turned off after the first para. multiParList :: [Block] -> Pres [Paragraph] multiParList [] = return [] multiParList (b:bs) = do pProps <- asks envParaProps p <- blockToParagraphs b let level = pPropLevel pProps ps <- local (\env -> env { envParaProps = pProps { pPropBullet = Nothing , pPropLevel = level + 1 } }) $ mconcat <$> mapM blockToParagraphs bs return $ p ++ ps cellToParagraphs :: Alignment -> SimpleCell -> Pres [Paragraph] cellToParagraphs algn tblCell = do paras <- mapM blockToParagraphs tblCell let alignment = case algn of AlignLeft -> Just AlgnLeft AlignRight -> Just AlgnRight AlignCenter -> Just AlgnCenter AlignDefault -> Nothing paras' = map (map (\p -> p{paraProps = (paraProps p){pPropAlign = alignment}})) paras return $ concat paras' rowToParagraphs :: [Alignment] -> [SimpleCell] -> Pres [[Paragraph]] rowToParagraphs algns tblCells = do -- We have to make sure we have the right number of alignments let pairs = zip (algns ++ repeat AlignDefault) tblCells mapM (uncurry cellToParagraphs) pairs withAttr :: Attr -> Shape -> Shape withAttr attr (Pic picPr url title caption) = let picPr' = picPr { picWidth = dimension Width attr , picHeight = dimension Height attr } in Pic picPr' url title caption withAttr _ sp = sp blockToShape :: Block -> Pres Shape blockToShape (Plain ils) = blockToShape (Para ils) blockToShape (Para (il:_)) | Image attr ils (url, title) <- il = withAttr attr . Pic def (T.unpack url) title <$> inlinesToParElems ils blockToShape (Para (il:_)) | Link _ (il':_) target <- il , Image attr ils (url, title) <- il' = withAttr attr . Pic def{picPropLink = Just $ ExternalTarget target} (T.unpack url) title <$> inlinesToParElems ils blockToShape (Figure _figattr _caption [b]) = blockToShape b blockToShape (Table _ blkCapt specs thead tbody tfoot) = do let (caption, algn, widths, hdrCells, rows) = toLegacyTable blkCapt specs thead tbody tfoot caption' <- inlinesToParElems caption hdrCells' <- rowToParagraphs algn hdrCells rows' <- mapM (rowToParagraphs algn) rows let tblPr = if null hdrCells then TableProps { tblPrFirstRow = False , tblPrBandRow = True } else TableProps { tblPrFirstRow = True , tblPrBandRow = True } return $ GraphicFrame [Tbl widths tblPr hdrCells' rows'] caption' -- If the format isn't openxml, we fall through to blockToPargraphs blockToShape (RawBlock (Format "openxml") str) = return $ RawOOXMLShape str blockToShape blk = do paras <- blockToParagraphs blk let paras' = map (\par -> par{paraElems = combineParaElems $ paraElems par}) paras return $ TextBox paras' combineShapes :: [Shape] -> [Shape] combineShapes [] = [] combineShapes (pic@Pic{} : ss) = pic : combineShapes ss combineShapes (TextBox [] : ss) = combineShapes ss combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) combineShapes (TextBox (p:ps) : TextBox (p':ps') : ss) = combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss combineShapes (s:ss) = s : combineShapes ss isNotesDiv :: Block -> Bool isNotesDiv (Div (_, ["notes"], _) _) = True isNotesDiv _ = False blocksToShapes :: [Block] -> Pres [Shape] blocksToShapes blks = combineShapes <$> mapM blockToShape blks isImage :: Inline -> Bool isImage Image{} = True isImage (Link _ (Image{} : _) _) = True isImage _ = False plainOrPara :: Block -> Maybe [Inline] plainOrPara (Plain ils) = Just ils plainOrPara (Para ils) = Just ils plainOrPara _ = Nothing notText :: Block -> Bool notText block | startsWithImage block = True notText Table{} = True notText Figure{} = True notText _ = False startsWithImage :: Block -> Bool startsWithImage block = fromMaybe False $ do inline <- plainOrPara block >>= listToMaybe pure (isImage inline) -- | Group blocks into a number of "splits" splitBlocks' :: -- | Blocks so far in the current split [Block] -> -- | Splits so far [[Block]] -> -- | All remaining blocks [Block] -> Pres [[Block]] splitBlocks' cur acc [] = return $ acc ++ ([cur | not (null cur)]) splitBlocks' cur acc (HorizontalRule : blks) = splitBlocks' [] (acc ++ ([cur | not (null cur)])) blks splitBlocks' cur acc (h@(Header n _ _) : blks) = do slideLevel <- asks envSlideLevel let (nts, blks') = span isNotesDiv blks case compare n slideLevel of LT -> splitBlocks' [] (acc ++ ([cur | not (null cur)]) ++ [h : nts]) blks' EQ -> splitBlocks' (h:nts) (acc ++ ([cur | not (null cur)])) blks' GT -> splitBlocks' (cur ++ (h:nts)) acc blks' -- `blockToParagraphs` treats Plain and Para the same, so we can save -- some code duplication by treating them the same here. splitBlocks' cur acc (Plain ils : blks) = splitBlocks' cur acc (Para ils : blks) splitBlocks' cur acc (Para (il:ils) : blks) | isImage il = do slideLevel <- asks envSlideLevel let (nts, blks') = if null ils then span isNotesDiv blks else ([], blks) case cur of [Header n _ _] | n == slideLevel || slideLevel == 0 -> splitBlocks' [] (acc ++ [cur ++ [Para [il]] ++ nts]) (if null ils then blks' else Para ils : blks') _ -> splitBlocks' [] (if any notText cur then acc ++ ([cur | not (null cur)]) ++ [Para [il] : nts] else acc ++ [cur ++ [Para [il]] ++ nts]) (if null ils then blks' else Para ils : blks') splitBlocks' cur acc (tbl@Table{} : blks) = do slideLevel <- asks envSlideLevel let (nts, blks') = span isNotesDiv blks case cur of [Header n _ _] | n == slideLevel || slideLevel == 0 -> splitBlocks' [] (acc ++ [cur ++ [tbl] ++ nts]) blks' _ -> splitBlocks' [] (if any notText cur then acc ++ ([cur | not (null cur)]) ++ [tbl : nts] else acc ++ ([cur ++ [tbl] ++ nts])) blks' splitBlocks' cur acc (d@(Div (_, classes, _) _): blks) | "columns" `elem` classes = do slideLevel <- asks envSlideLevel let (nts, blks') = span isNotesDiv blks case cur of [Header n _ _] | n == slideLevel || slideLevel == 0 -> splitBlocks' [] (acc ++ [cur ++ [d] ++ nts]) blks' _ -> splitBlocks' [] (acc ++ ([cur | not (null cur)]) ++ [d : nts]) blks' splitBlocks' cur acc (blk : blks) = splitBlocks' (cur ++ [blk]) acc blks splitBlocks :: [Block] -> Pres [[Block]] splitBlocks = splitBlocks' [] [] -- | Assuming the slide title is already handled, convert these blocks to the -- body content for the slide. bodyBlocksToSlide :: Int -> [Block] -> SpeakerNotes -> Pres Slide bodyBlocksToSlide _ (blk : blks) spkNotes | Div (_, classes, _) divBlks <- blk , "columns" `elem` classes , Div (_, clsL, _) blksL : Div (_, clsR, _) blksR : remaining <- divBlks , "column" `elem` clsL, "column" `elem` clsR = do -- At least 2 column elements mapM_ (addLogMessage . BlockNotRendered) (blks ++ remaining) let mkTwoColumn left right = do blksL' <- join . take 1 <$> splitBlocks left blksR' <- join . take 1 <$> splitBlocks right shapesL <- blocksToShapes blksL' shapesR <- blocksToShapes blksR' sldId <- asks envCurSlideId return $ Slide sldId (TwoColumnSlide [] shapesL shapesR) spkNotes Nothing let mkComparison blksL1 blksL2 blksR1 blksR2 = do shapesL1 <- blocksToShapes blksL1 shapesL2 <- blocksToShapes blksL2 shapesR1 <- blocksToShapes blksR1 shapesR2 <- blocksToShapes blksR2 sldId <- asks envCurSlideId return $ Slide sldId (ComparisonSlide [] (shapesL1, shapesL2) (shapesR1, shapesR2)) spkNotes Nothing let (blksL1, blksL2) = break notText blksL (blksR1, blksR2) = break notText blksR if (any null [blksL1, blksL2]) && (any null [blksR1, blksR2]) then mkTwoColumn blksL blksR else mkComparison blksL1 blksL2 blksR1 blksR2 | Div (_, classes, _) divBlks <- blk , "columns" `elem` classes , Div (_, cls, _) columnBlks : remaining <- divBlks , "column" `elem` cls = do -- Only 1 column element. mapM_ (addLogMessage . BlockNotRendered) (blks ++ remaining) clBlks' <- join . take 1 <$> splitBlocks columnBlks shapes <- blocksToShapes clBlks' sldId <- asks envCurSlideId return $ Slide sldId (ContentSlide [] shapes) spkNotes Nothing bodyBlocksToSlide _ (blk : blks) spkNotes = do sldId <- asks envCurSlideId inNoteSlide <- asks envInNoteSlide let mkSlide s = Slide sldId s spkNotes Nothing if inNoteSlide then mkSlide . ContentSlide [] <$> forceFontSize noteSize (blocksToShapes (blk : blks)) else let contentOrBlankSlide = if makesBlankSlide (blk : blks) then pure (mkSlide BlankSlide) else mkSlide . ContentSlide [] <$> blocksToShapes (blk : blks) in case break notText (blk : blks) of ([], _) -> contentOrBlankSlide (_, []) -> contentOrBlankSlide (textBlocks, contentBlocks) -> do textShapes <- blocksToShapes textBlocks contentShapes <- blocksToShapes contentBlocks return (mkSlide (ContentWithCaptionSlide [] textShapes contentShapes)) bodyBlocksToSlide _ [] spkNotes = do sldId <- asks envCurSlideId return $ Slide sldId BlankSlide spkNotes Nothing blocksToSlide' :: Int -> [Block] -> SpeakerNotes -> Pres Slide blocksToSlide' lvl (Header n (ident, _, attributes) ils : blks) spkNotes | n < lvl = do registerAnchorId ident sldId <- asks envCurSlideId hdr <- inlinesToParElems ils return $ Slide sldId (TitleSlide hdr) spkNotes backgroundImage | n == lvl || lvl == 0 = do registerAnchorId ident hdr <- inlinesToParElems ils -- Now get the slide without the header, and then add the header -- in. slide <- bodyBlocksToSlide lvl blks spkNotes let layout = case slideLayout slide of ContentSlide _ cont -> ContentSlide hdr cont TwoColumnSlide _ contL contR -> TwoColumnSlide hdr contL contR ComparisonSlide _ contL contR -> ComparisonSlide hdr contL contR ContentWithCaptionSlide _ text content -> ContentWithCaptionSlide hdr text content BlankSlide -> if all inlineIsBlank ils then BlankSlide else ContentSlide hdr [] layout' -> layout' return $ slide{slideLayout = layout, slideBackgroundImage = backgroundImage} where backgroundImage = T.unpack <$> (lookup "background-image" attributes <|> lookup "data-background-image" attributes) blocksToSlide' lvl blks spkNotes = bodyBlocksToSlide lvl blks spkNotes blockToSpeakerNotes :: Block -> Pres SpeakerNotes blockToSpeakerNotes (Div (_, ["notes"], _) blks) = local (\env -> env{envInSpeakerNotes=True}) $ SpeakerNotes . mconcat <$> mapM blockToParagraphs blks blockToSpeakerNotes _ = return mempty handleSpeakerNotes :: Block -> Pres () handleSpeakerNotes blk = do spNotes <- blockToSpeakerNotes blk modify $ \st -> st{stSpeakerNotes = stSpeakerNotes st <> spNotes} handleAndFilterSpeakerNotes' :: [Block] -> Pres [Block] handleAndFilterSpeakerNotes' blks = do mapM_ handleSpeakerNotes blks return $ filter (not . isNotesDiv) blks handleAndFilterSpeakerNotes :: [Block] -> Pres ([Block], SpeakerNotes) handleAndFilterSpeakerNotes blks = do modify $ \st -> st{stSpeakerNotes = mempty} blks' <- walkM handleAndFilterSpeakerNotes' blks spkNotes <- gets stSpeakerNotes return (blks', spkNotes) blocksToSlide :: [Block] -> Pres Slide blocksToSlide blks = do (blks', spkNotes) <- handleAndFilterSpeakerNotes blks slideLevel <- asks envSlideLevel blocksToSlide' slideLevel blks' spkNotes makeNoteEntry :: (Int, [Block]) -> [Block] makeNoteEntry (n, blks) = let enum = Str (tshow n <> ".") in case blks of (Para ils : blks') -> Para (enum : Space : ils) : blks' _ -> Para [enum] : blks forceFontSize :: Pixels -> Pres a -> Pres a forceFontSize px x = do rpr <- asks envRunProps local (\r -> r {envRunProps = rpr{rPropForceSize = Just px}}) x -- We leave these as blocks because we will want to include them in -- the TOC. makeEndNotesSlideBlocks :: Pres [Block] makeEndNotesSlideBlocks = do noteIds <- gets stNoteIds slideLevel <- asks envSlideLevel exts <- writerExtensions <$> asks envOpts meta <- asks envMetadata -- Get identifiers so we can give the notes section a unique ident. anchorSet <- M.keysSet <$> gets stAnchorMap if M.null noteIds then return [] else let title = case lookupMetaInlines "notes-title" meta of [] -> [Str "Notes"] ls -> ls ident = Shared.uniqueIdent exts title anchorSet hdr = Header slideLevel (ident, [], []) title blks = concatMap makeNoteEntry $ M.toList noteIds in return $ hdr : blks getMetaSlide :: Pres (Maybe Slide) getMetaSlide = do meta <- asks envMetadata title <- inlinesToParElems $ docTitle meta 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 $ Just $ Slide metadataSlideId (MetadataSlide title subtitle authors date) speakerNotes Nothing addSpeakerNotesToMetaSlide :: Slide -> [Block] -> Pres (Slide, [Block]) addSpeakerNotesToMetaSlide (Slide sldId layout@MetadataSlide{} spkNotes backgroundImage) blks = do let (ntsBlks, blks') = span isNotesDiv blks spkNotes' <- mconcat <$> mapM blockToSpeakerNotes ntsBlks return (Slide sldId layout (spkNotes <> spkNotes') backgroundImage, blks') addSpeakerNotesToMetaSlide sld blks = return (sld, blks) makeTOCSlide :: [Block] -> Pres Slide makeTOCSlide blks = local (\env -> env{envCurSlideId = tocSlideId}) $ do opts <- asks envOpts let contents = toTableOfContents opts blks meta <- asks envMetadata slideLevel <- asks envSlideLevel let tocTitle = case lookupMetaInlines "toc-title" meta of [] -> [Str "Table of Contents"] ls -> ls hdr = Header slideLevel nullAttr tocTitle blocksToSlide [hdr, contents] combineParaElems' :: Maybe ParaElem -> [ParaElem] -> [ParaElem] combineParaElems' mbPElem [] = maybeToList mbPElem combineParaElems' Nothing (pElem : pElems) = combineParaElems' (Just pElem) pElems combineParaElems' (Just pElem') (pElem : pElems) | Run rPr' s' <- pElem' , Run rPr s <- pElem , rPr == rPr' = combineParaElems' (Just $ Run rPr' $ s' <> s) pElems | otherwise = pElem' : combineParaElems' (Just pElem) pElems combineParaElems :: [ParaElem] -> [ParaElem] combineParaElems = combineParaElems' Nothing applyToParagraph :: Monad m => (ParaElem -> m ParaElem) -> Paragraph -> m Paragraph applyToParagraph f para = do paraElems' <- mapM f $ paraElems para return $ para {paraElems = paraElems'} applyToShape :: Monad m => (ParaElem -> m ParaElem) -> Shape -> m Shape applyToShape f (Pic pPr fp title pes) = Pic pPr fp title <$> mapM f pes applyToShape f (GraphicFrame gfx pes) = GraphicFrame gfx <$> mapM f pes applyToShape f (TextBox paras) = TextBox <$> mapM (applyToParagraph f) paras applyToShape _ (RawOOXMLShape str) = return $ RawOOXMLShape str applyToLayout :: Monad m => (ParaElem -> m ParaElem) -> Layout -> m Layout applyToLayout f (MetadataSlide title subtitle authors date) = do title' <- mapM f title subtitle' <- mapM f subtitle authors' <- mapM (mapM f) authors date' <- mapM f date return $ MetadataSlide title' subtitle' authors' date' applyToLayout f (TitleSlide title) = TitleSlide <$> mapM f title applyToLayout f (ContentSlide hdr content) = do hdr' <- mapM f hdr content' <- mapM (applyToShape f) content return $ ContentSlide hdr' content' applyToLayout f (TwoColumnSlide hdr contentL contentR) = do hdr' <- mapM f hdr contentL' <- mapM (applyToShape f) contentL contentR' <- mapM (applyToShape f) contentR return $ TwoColumnSlide hdr' contentL' contentR' applyToLayout f (ComparisonSlide hdr (contentL1, contentL2) (contentR1, contentR2)) = do hdr' <- mapM f hdr contentL1' <- mapM (applyToShape f) contentL1 contentL2' <- mapM (applyToShape f) contentL2 contentR1' <- mapM (applyToShape f) contentR1 contentR2' <- mapM (applyToShape f) contentR2 return $ ComparisonSlide hdr' (contentL1', contentL2') (contentR1', contentR2') applyToLayout f (ContentWithCaptionSlide hdr textShapes contentShapes) = do hdr' <- mapM f hdr textShapes' <- mapM (applyToShape f) textShapes contentShapes' <- mapM (applyToShape f) contentShapes return $ ContentWithCaptionSlide hdr' textShapes' contentShapes' applyToLayout _ BlankSlide = pure BlankSlide applyToSlide :: Monad m => (ParaElem -> m ParaElem) -> Slide -> m Slide applyToSlide f slide = do layout' <- applyToLayout f $ slideLayout slide let paras = fromSpeakerNotes $ slideSpeakerNotes slide notes' <- SpeakerNotes <$> mapM (applyToParagraph f) paras return slide{slideLayout = layout', slideSpeakerNotes = notes'} replaceAnchor :: ParaElem -> Pres ParaElem replaceAnchor (Run rProps s) | Just (ExternalTarget (T.uncons -> Just ('#', anchor), _)) <- rLink rProps = do anchorMap <- gets stAnchorMap -- If the anchor is not in the anchormap, we just remove the -- link. let rProps' = case M.lookup anchor anchorMap of Just n -> rProps{rLink = Just $ InternalTarget n} Nothing -> rProps{rLink = Nothing} return $ Run rProps' s replaceAnchor pe = return pe emptyParaElem :: ParaElem -> Bool emptyParaElem (Run _ s) = T.null $ Shared.trim s emptyParaElem (MathElem _ ts) = T.null $ Shared.trim $ unTeXString ts emptyParaElem _ = False emptyParagraph :: Paragraph -> Bool emptyParagraph para = all emptyParaElem $ paraElems para emptyShape :: Shape -> Bool emptyShape (TextBox paras) = all emptyParagraph paras emptyShape _ = False emptyLayout :: Layout -> Bool emptyLayout layout = case layout of MetadataSlide title subtitle authors date -> all emptyParaElem title && all emptyParaElem subtitle && all (all emptyParaElem) authors && all emptyParaElem date TitleSlide hdr -> all emptyParaElem hdr ContentSlide hdr shapes -> all emptyParaElem hdr && all emptyShape shapes TwoColumnSlide hdr shapes1 shapes2 -> all emptyParaElem hdr && all emptyShape shapes1 && all emptyShape shapes2 ComparisonSlide hdr (shapesL1, shapesL2) (shapesR1, shapesR2) -> all emptyParaElem hdr && all emptyShape shapesL1 && all emptyShape shapesL2 && all emptyShape shapesR1 && all emptyShape shapesR2 ContentWithCaptionSlide hdr textShapes contentShapes -> all emptyParaElem hdr && all emptyShape textShapes && all emptyShape contentShapes BlankSlide -> False emptySlide :: Slide -> Bool emptySlide (Slide _ layout notes backgroundImage) = (notes == mempty) && emptyLayout layout && isNothing backgroundImage makesBlankSlide :: [Block] -> Bool makesBlankSlide = all blockIsBlank blockIsBlank :: Block -> Bool blockIsBlank = \case Plain ins -> all inlineIsBlank ins Para ins -> all inlineIsBlank ins LineBlock inss -> all (all inlineIsBlank) inss CodeBlock _ txt -> textIsBlank txt RawBlock _ txt -> textIsBlank txt BlockQuote bls -> all blockIsBlank bls OrderedList _ blss -> all (all blockIsBlank) blss BulletList blss -> all (all blockIsBlank) blss DefinitionList ds -> all (uncurry (&&) . bimap (all inlineIsBlank) (all (all blockIsBlank))) ds Header _ _ ils -> all inlineIsBlank ils HorizontalRule -> True Figure _ _ bls -> all blockIsBlank bls Table{} -> False Div _ bls -> all blockIsBlank bls textIsBlank :: T.Text -> Bool textIsBlank = T.all isSpace inlineIsBlank :: Inline -> Bool inlineIsBlank = \case (Str txt) -> textIsBlank txt (Emph ins) -> all inlineIsBlank ins (Underline ins) -> all inlineIsBlank ins (Strong ins) -> all inlineIsBlank ins (Strikeout ins) -> all inlineIsBlank ins (Superscript ins) -> all inlineIsBlank ins (Subscript ins) -> all inlineIsBlank ins (SmallCaps ins) -> all inlineIsBlank ins (Quoted _ ins) -> all inlineIsBlank ins (Cite _ _) -> False (Code _ txt) -> textIsBlank txt Space -> True SoftBreak -> True LineBreak -> True (Math _ txt) -> textIsBlank txt (RawInline _ txt) -> textIsBlank txt (Link _ ins (t1, t2)) -> all inlineIsBlank ins && textIsBlank t1 && textIsBlank t2 (Image _ ins (t1, t2)) -> all inlineIsBlank ins && textIsBlank t1 && textIsBlank t2 (Note bls) -> all blockIsBlank bls (Span _ ins) -> all inlineIsBlank ins blocksToPresentationSlides :: [Block] -> Pres [Slide] blocksToPresentationSlides blks = do opts <- asks envOpts mbMetadataSlide <- getMetaSlide -- if the metadata slide exists, we try to add any speakerNotes -- which immediately follow it. We also convert from maybe to a -- list, so that it will be able to add together more easily with -- the other lists of slides. (metadataslides, blks') <- case mbMetadataSlide of Just sld -> do (s, bs) <- addSpeakerNotesToMetaSlide sld blks return ([s], bs) Nothing -> return ([], blks) -- As far as I can tell, if we want to have a variable-length toc in -- the future, we'll have to make it twice. Once to get the length, -- and a second time to include the notes slide. We can't make the -- notes slide before the body slides because we need to know if -- there are notes, and we can't make either before the toc slide, -- because we need to know its length to get slide numbers right. -- -- For now, though, since the TOC slide is only length 1, if it -- exists, we'll just get the length, and then come back to make the -- slide later blksLst <- splitBlocks blks' bodySlideIds <- mapM (\n -> runUniqueSlideId $ "BodySlide" <> tshow n) (take (length blksLst) [1..] :: [Integer]) bodyslides <- mapM (\(bs, ident) -> local (\st -> st{envCurSlideId = ident}) (blocksToSlide bs)) (zip blksLst bodySlideIds) endNotesSlideBlocks <- makeEndNotesSlideBlocks -- now we come back and make the real toc... tocSlides <- if writerTableOfContents opts then do toc <- makeTOCSlide $ blks' ++ endNotesSlideBlocks return [toc] else return [] -- ... and the notes slide. We test to see if the blocks are empty, -- because we don't want to make an empty slide. endNotesSlides <- if null endNotesSlideBlocks then return [] else do endNotesSlide <- local (\env -> env { envCurSlideId = endNotesSlideId , envInNoteSlide = True }) (blocksToSlide endNotesSlideBlocks) return [endNotesSlide] let slides = metadataslides ++ tocSlides ++ bodyslides ++ endNotesSlides slides' = filter (not . emptySlide) slides mapM (applyToSlide replaceAnchor) slides' metaToDocProps :: Meta -> DocProps metaToDocProps meta = let keywords = case lookupMeta "keywords" meta of Just (MetaList xs) -> Just $ map Shared.stringify xs _ -> Nothing authors = case map Shared.stringify $ docAuthors meta of [] -> Nothing ss -> Just $ T.intercalate "; " ss description = case map Shared.stringify $ lookupMetaBlocks "description" meta of [] -> Nothing ss -> Just $ T.intercalate "_x000d_\n" ss customProperties' = case [(k, lookupMetaString k meta) | k <- M.keys (unMeta meta) , k `notElem` ["title", "author", "keywords", "description" , "subject","lang","category"]] of [] -> Nothing ss -> Just ss in DocProps{ dcTitle = Shared.stringify <$> lookupMeta "title" meta , dcSubject = Shared.stringify <$> lookupMeta "subject" meta , dcCreator = authors , dcKeywords = keywords , dcDescription = description , cpCategory = Shared.stringify <$> lookupMeta "category" meta , dcDate = let t = Shared.stringify (docDate meta) in if T.null t then Nothing else Just t , customProperties = customProperties' } documentToPresentation :: WriterOptions -> Pandoc -> (Presentation, [LogMessage]) documentToPresentation opts (Pandoc meta blks) = let env = def { envOpts = opts , envMetadata = meta , envSlideLevel = fromMaybe (getSlideLevel blks) (writerSlideLevel opts) } (presSlides, msgs) = runPres env def $ blocksToPresentationSlides blks docProps = metaToDocProps meta in (Presentation docProps presSlides, msgs) -- -------------------------------------------------------------- applyTokStyToRunProps :: TokenStyle -> RunProps -> RunProps applyTokStyToRunProps tokSty rProps = rProps{ rSolidFill = tokenColor tokSty <|> rSolidFill rProps , rPropBold = tokenBold tokSty || rPropBold rProps , rPropItalics = tokenItalic tokSty || rPropItalics rProps , rPropUnderline = tokenUnderline tokSty || rPropUnderline rProps } formatToken :: Style -> Token -> ParaElem formatToken sty (tokType, txt) = let rProps = def{rPropCode = True, rSolidFill = defaultColor sty} rProps' = case M.lookup tokType (tokenStyles sty) of Just tokSty -> applyTokStyToRunProps tokSty rProps Nothing -> rProps in Run rProps' txt formatSourceLine :: Style -> FormatOptions -> SourceLine -> [ParaElem] formatSourceLine sty _ srcLn = map (formatToken sty) srcLn formatSourceLines :: Style -> FormatOptions -> [SourceLine] -> [ParaElem] formatSourceLines sty opts srcLns = intercalate [Break] $ map (formatSourceLine sty opts) srcLns ================================================ FILE: src/Text/Pandoc/Writers/Powerpoint.hs ================================================ {- | Module : Text.Pandoc.Writers.Powerpoint Copyright : Copyright (C) 2017-2020 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to powerpoint (pptx). -} {- This is a wrapper around two modules: - Text.Pandoc.Writers.Powerpoint.Presentation (which converts a pandoc document into a Presentation datatype), and - Text.Pandoc.Writers.Powerpoint.Output (which converts a Presentation into a zip archive, which can be output). -} module Text.Pandoc.Writers.Powerpoint (writePowerpoint) where import Codec.Archive.Zip import Text.Pandoc.Definition import Text.Pandoc.Walk import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Options (WriterOptions) import Text.Pandoc.Writers.Shared (fixDisplayMath) import Text.Pandoc.Writers.Powerpoint.Presentation (documentToPresentation) import Text.Pandoc.Writers.Powerpoint.Output (presentationToArchive) import qualified Data.ByteString.Lazy as BL writePowerpoint :: (PandocMonad m) => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> m BL.ByteString writePowerpoint opts (Pandoc meta blks) = do let blks' = walk fixDisplayMath blks let (pres, logMsgs) = documentToPresentation opts (Pandoc meta blks') mapM_ report logMsgs archv <- presentationToArchive opts meta pres return $ fromArchive archv ================================================ FILE: src/Text/Pandoc/Writers/RST.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Writers.RST Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to reStructuredText. reStructuredText: <http://docutils.sourceforge.net/rst.html> -} module Text.Pandoc.Writers.RST ( writeRST, flatten ) where import Control.Monad.State.Strict ( StateT, gets, modify, evalStateT ) import Control.Monad (zipWithM, liftM) import Data.Char (isSpace, generalCategory, isAscii, isAlphaNum, GeneralCategory( ClosePunctuation, OpenPunctuation, InitialQuote, FinalQuote, DashPunctuation, OtherPunctuation)) 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 import Data.Text (Text) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared import Text.Pandoc.Walk import Safe (lastMay, headMay) type Refs = [([Inline], Target)] data WriterState = WriterState { stNotes :: [[Block]] , stLinks :: Refs , stImages :: [([Inline], (Attr, Text, Text, Maybe Text))] , stHasMath :: Bool , stHasRawTeX :: Bool , stOptions :: WriterOptions , stTopLevel :: Bool , stImageId :: Int } type RST = StateT WriterState -- | Convert Pandoc to RST. writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeRST opts document = do let st = WriterState { stNotes = [], stLinks = [], stImages = [], stHasMath = False, stHasRawTeX = False, stOptions = opts, stTopLevel = True, stImageId = 1 } evalStateT (pandocToRST document) st -- | Return RST representation of document. pandocToRST :: PandocMonad m => Pandoc -> RST m Text pandocToRST (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing let subtit = lookupMetaInlines "subtitle" meta title <- titleToRST (docTitle meta) subtit metadata <- metaToContext opts blockListToRST (fmap chomp . inlineListToRST) meta body <- blockListToRST' True $ case writerTemplate opts of Just _ -> normalizeHeadings 1 blocks Nothing -> blocks notes <- gets (reverse . stNotes) >>= notesToRST -- note that the notes may contain refs, so we do them first refs <- gets (reverse . stLinks) >>= refsToRST pics <- gets (reverse . stImages) >>= pictRefsToRST hasMath <- gets stHasMath rawTeX <- gets stHasRawTeX let main = vsep [body, notes, refs, pics] let context = defField "body" main $ defField "toc" (writerTableOfContents opts) $ defField "toc-depth" (tshow $ writerTOCDepth opts) $ defField "number-sections" (writerNumberSections opts) $ defField "math" hasMath $ defField "titleblock" (render Nothing title :: Text) $ defField "math" hasMath $ defField "rawtex" rawTeX metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context where normalizeHeadings lev (Header l a i:bs) = Header lev a i:normalizeHeadings (lev+1) cont ++ normalizeHeadings lev bs' where (cont,bs') = break (headerLtEq l) bs headerLtEq level (Header l' _ _) = l' <= level headerLtEq _ _ = False normalizeHeadings lev (b:bs) = b:normalizeHeadings lev bs normalizeHeadings _ [] = [] -- | Return RST representation of reference key table. refsToRST :: PandocMonad m => Refs -> RST m (Doc Text) refsToRST refs = vcat <$> mapM keyToRST refs -- | Return RST representation of a reference key. keyToRST :: PandocMonad m => ([Inline], (Text, Text)) -> RST m (Doc Text) keyToRST (label, (src, _)) = do label' <- inlineListToRST label let label'' = if (==':') `T.any` (render Nothing label' :: Text) then char '`' <> label' <> char '`' else label' return $ nowrap $ ".. _" <> label'' <> ": " <> literal src -- | Return RST representation of notes. notesToRST :: PandocMonad m => [[Block]] -> RST m (Doc Text) notesToRST notes = vsep <$> zipWithM noteToRST [1..] notes -- | Return RST representation of a note. noteToRST :: PandocMonad m => Int -> [Block] -> RST m (Doc Text) noteToRST num note = do contents <- blockListToRST note let marker = ".. [" <> text (show num) <> "]" return $ nowrap $ marker $$ nest 3 contents -- | Return RST representation of picture reference table. pictRefsToRST :: PandocMonad m => [([Inline], (Attr, Text, Text, Maybe Text))] -> RST m (Doc Text) pictRefsToRST refs = vcat <$> mapM pictToRST refs -- | Return RST representation of a picture substitution reference. pictToRST :: PandocMonad m => ([Inline], (Attr, Text, Text, Maybe Text)) -> RST m (Doc Text) pictToRST (label, (attr, src, _, mbtarget)) = do label' <- inlineListToRST label dims <- imageDimsToRST attr let (_, cls, _) = attr classes = case cls of [] -> empty ["align-top"] -> ":align: top" ["align-middle"] -> ":align: middle" ["align-bottom"] -> ":align: bottom" ["align-center"] -> empty ["align-right"] -> empty ["align-left"] -> empty _ -> ":class: " <> literal (T.unwords cls) return $ nowrap $ ".. |" <> label' <> "| image:: " <> literal src $$ hang 3 empty (classes $$ dims) $$ case mbtarget of Nothing -> empty Just t -> " :target: " <> literal t -- | Escape special characters for RST. escapeText :: WriterOptions -> Text -> Text escapeText opts t = if T.any isSpecial t then T.pack . escapeString' True . T.unpack $ t else t -- optimization where isSmart = isEnabled Ext_smart opts isSpecial c = c == '\\' || c == '_' || c == '`' || c == '*' || c == '|' || (isSmart && (c == '-' || c == '.' || c == '"' || c == '\'')) canFollowInlineMarkup c = c == '-' || c == '.' || c == ',' || c == ':' || c == ';' || c == '!' || c == '?' || c == '\'' || c == '"' || c == ')' || c == ']' || c == '}' || c == '>' || isSpace c || (not (isAscii c) && generalCategory c `elem` [OpenPunctuation, InitialQuote, FinalQuote, DashPunctuation, OtherPunctuation]) canPrecedeInlineMarkup c = c == '-' || c == ':' || c == '/' || c == '\'' || c == '"' || c == '<' || c == '(' || c == '[' || c == '{' || isSpace c || (not (isAscii c) && generalCategory c `elem` [ClosePunctuation, InitialQuote, FinalQuote, DashPunctuation, OtherPunctuation]) escapeString' canStart cs = case cs of [] -> [] d:ds | d == '\\' -> '\\' : d : escapeString' False ds '\'':ds | isSmart -> '\\' : '\'' : escapeString' True ds '"':ds | isSmart -> '\\' : '"' : escapeString' True ds '-':'-':ds | isSmart -> '\\' : '-' : escapeString' False ('-':ds) '.':'.':'.':ds | isSmart -> '\\' : '.' : escapeString' False ('.':'.':ds) [e] | e == '*' || e == '_' || e == '|' || e == '`' -> ['\\',e] d:ds | canPrecedeInlineMarkup d -> d : escapeString' True ds e:d:ds | e == '*' || e == '_' || e == '|' || e == '`' , (not canStart && canFollowInlineMarkup d) || (canStart && not (isSpace d)) -> '\\' : e : escapeString' False (d:ds) '_':d:ds | not (isAlphaNum d) -> '\\' : '_' : escapeString' False (d:ds) d:ds -> d : escapeString' False ds titleToRST :: PandocMonad m => [Inline] -> [Inline] -> RST m (Doc Text) titleToRST [] _ = return empty titleToRST tit subtit = do title <- inlineListToRST tit subtitle <- inlineListToRST subtit return $ bordered title '=' $$ bordered subtitle '-' bordered :: Doc Text -> Char -> Doc Text bordered contents c = if len > 0 then border $$ contents $$ border else empty where len = offset contents border = literal (T.replicate len $ T.singleton c) -- | Convert Pandoc block element to RST. blockToRST :: PandocMonad m => Block -- ^ Block element -> RST m (Doc Text) blockToRST (Div ("",["title"],[]) _) = return empty -- this is generated by the rst reader and can safely be -- omitted when we're generating rst blockToRST (Div (ident,classes,_kvs) bs) = do contents <- blockListToRST bs let admonitions = ["attention","caution","danger","error","hint", "important","note","tip","warning","admonition"] let admonition = case classes of (cl:_) | cl `elem` admonitions -> blankline <> ".. " <> literal cl <> "::" cls -> blankline <> ".. container::" <> space <> literal (T.unwords (filter (/= "container") cls)) -- if contents start with block quote, we need to insert -- an empty comment to fix the indentation point (#10236) let contents' = case bs of BlockQuote{}:_-> ".." $+$ contents _ -> contents return $ blankline $$ admonition $$ (if T.null ident then blankline else " :name: " <> literal ident $$ blankline) $$ nest 3 contents' $$ blankline blockToRST (Plain inlines) = inlineListToRST inlines blockToRST (Para inlines) | LineBreak `elem` inlines = linesToLineBlock $ splitBy (==LineBreak) inlines | otherwise = do contents <- inlineListToRST inlines return $ contents <> blankline blockToRST (LineBlock lns) = linesToLineBlock lns blockToRST (RawBlock f@(Format f') str) | f == "rst" = return $ literal str | f == "tex" = blockToRST (RawBlock (Format "latex") str) | otherwise = return $ blankline <> ".. raw:: " <> literal (T.toLower f') $+$ nest 3 (literal str) $$ blankline blockToRST HorizontalRule = return $ blankline $$ "--------------" $$ blankline blockToRST (Header level (name,classes,_) inlines) = do contents <- inlineListToRST inlines -- we calculate the id that would be used by auto_identifiers -- so we know whether to print an explicit identifier opts <- gets stOptions let autoId = uniqueIdent (writerExtensions opts) inlines mempty isTopLevel <- gets stTopLevel if isTopLevel then do let headerChar = if level > 5 then ' ' else "=-~^'" !! (level - 1) let border = literal $ T.replicate (offset contents) $ T.singleton headerChar let anchor | T.null name || name == autoId = empty | otherwise = ".. _" <> (if T.any (==':') name || T.take 1 name == "_" then "`" <> literal name <> "`" else literal name) <> ":" $$ blankline return $ nowrap $ anchor $$ contents $$ border $$ blankline else do let rub = "rubric:: " <> contents let name' | T.null name = empty | otherwise = ":name: " <> literal name let cls | null classes = empty | otherwise = ":class: " <> literal (T.unwords classes) return $ nowrap $ hang 3 ".. " (rub $$ name' $$ cls) $$ blankline blockToRST (CodeBlock (_,classes,kvs) str) = do opts <- gets stOptions let startnum = maybe "" (\x -> " " <> literal x) $ lookup "startFrom" kvs let numberlines = if "numberLines" `elem` classes then " :number-lines:" <> startnum else empty if "haskell" `elem` classes && "literate" `elem` classes && isEnabled Ext_literate_haskell opts then return $ prefixed "> " (literal str) $$ blankline else return $ (case [c | c <- classes, c `notElem` ["sourceCode","literate","numberLines", "number-lines","example"]] of [] -> "::" (lang:_) -> (blankline <> ".. code:: " <> literal lang) $$ numberlines) $+$ nest 3 (literal str) $$ blankline blockToRST (BlockQuote blocks) = do contents <- blockListToRST blocks return $ nest 3 contents <> blankline blockToRST (Table _attrs blkCapt specs thead@(TableHead _ theadRows) tbody tfoot@(TableFoot _ tfootRows)) = do let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot caption' <- inlineListToRST caption let blocksToDoc opts bs = do oldOpts <- gets stOptions modify $ \st -> st{ stOptions = opts } result <- blockListToRST bs modify $ \st -> st{ stOptions = oldOpts } return result opts <- gets stOptions let specs' = map (\(_,width) -> (AlignDefault, width)) specs renderGrid = gridTable opts blocksToDoc specs' thead tbody tfoot rowHasRowSpan (Row _ cells) = any cellHasRowSpan cells cellHasRowSpan (Cell _ _ rowSpan _ _) = rowSpan > 1 isSimple = all (== 0) widths && length widths > 1 && not (any rowHasRowSpan $ theadRows ++ tableBodiesToRows tbody ++ tfootRows) renderSimple = do tbl' <- simpleTable opts blocksToDoc thead tbody tfoot if offset tbl' > writerColumns opts then renderGrid else return tbl' isList = writerListTables opts renderList = tableToRSTList caption (map (const AlignDefault) aligns) widths headers rows rendered | isList = renderList | isSimple = renderSimple | otherwise = renderGrid tbl <- rendered return $ blankline $$ (if null caption || isList then tbl else (blankline <> ".. table:: " <> caption') $$ blankline $$ nest 3 tbl) $$ blankline blockToRST (BulletList items) = do contents <- mapM bulletListItemToRST items -- ensure that sublists have preceding blank line return $ blankline $$ (if isTightList items then vcat else vsep) contents $$ blankline blockToRST (OrderedList (start, style', delim) items) = do let markers = if start == 1 && style' == DefaultStyle && delim == DefaultDelim then replicate (length items) "#." else take (length items) $ orderedListMarkers (start, style', delim) let maxMarkerLength = maybe 0 maximum $ NE.nonEmpty $ map T.length markers let markers' = map (\m -> let s = maxMarkerLength - T.length m in m <> T.replicate s " ") markers contents <- zipWithM orderedListItemToRST markers' items -- ensure that sublists have preceding blank line return $ blankline $$ (if isTightList items then vcat else vsep) contents $$ blankline blockToRST (DefinitionList items) = do contents <- mapM definitionListItemToRST items -- ensure that sublists have preceding blank line return $ blankline $$ vcat contents $$ blankline blockToRST (Figure (ident, classes, _kvs) (Caption _ longCapt) body) = do let figure attr txt (src, tit) = do description <- inlineListToRST txt capt <- blockListToRST longCapt dims <- imageDimsToRST attr let fig = "figure::" <+> literal src alt = if null txt then if T.null tit then empty else ":alt:" <+> literal tit else ":alt:" <+> description name = if T.null ident then empty else "name:" <+> literal ident (_,cls,_) = attr align = case cls of [] -> empty ["align-right"] -> ":align: right" ["align-left"] -> ":align: left" ["align-center"] -> ":align: center" _ -> ":figclass: " <> literal (T.unwords cls) return $ hang 3 ".. " (fig $$ name $$ alt $$ align $$ dims $+$ capt) $$ blankline case body of [Para [Image attr txt tgt]] -> figure attr txt tgt [Plain [Image attr txt tgt]] -> figure attr txt tgt _ -> do content <- blockListToRST body return $ blankline $$ ( ".. container:: float" <> space <> literal (T.unwords (filter (/= "container") classes))) $$ (if T.null ident then blankline else " :name: " <> literal ident $$ blankline) $$ nest 3 content $$ blankline -- | Convert bullet list item (list of blocks) to RST. bulletListItemToRST :: PandocMonad m => [Block] -> RST m (Doc Text) bulletListItemToRST items = do contents <- blockListToRST items -- if a list item starts with block quote, we need to insert -- an empty comment to fix the indentation point (#10236) let contents' = case items of BlockQuote{}:_-> ".." $+$ contents _ -> contents return $ hang 2 "- " contents' $$ if null items || (endsWithPlain items && not (endsWithList items)) then cr else blankline -- | Convert ordered list item (a list of blocks) to RST. orderedListItemToRST :: PandocMonad m => Text -- ^ marker for list item -> [Block] -- ^ list item (list of blocks) -> RST m (Doc Text) orderedListItemToRST marker items = do contents <- blockListToRST items let marker' = marker <> " " -- if a list item starts with block quote, we need to insert -- an empty comment to fix the indentation point (#10236) let contents' = case items of BlockQuote{}:_-> ".." $+$ contents _ -> contents return $ hang (T.length marker') (literal marker') contents' $$ if null items || (endsWithPlain items && not (endsWithList items)) then cr else blankline endsWithList :: [Block] -> Bool endsWithList bs = case lastMay bs of Just (BulletList{}) -> True Just (OrderedList{}) -> True _ -> False -- | Convert definition list item (label, list of blocks) to RST. definitionListItemToRST :: PandocMonad m => ([Inline], [[Block]]) -> RST m (Doc Text) definitionListItemToRST (label, defs) = do label' <- inlineListToRST label contents <- liftM vcat $ mapM blockListToRST defs -- if definition list starts with block quote, we need to insert -- an empty comment to fix the indentation point (#10236) let contents' = case defs of (BlockQuote{}:_):_ -> ".." $+$ contents _ -> contents return $ nowrap label' $$ nest 3 (nestle contents') $$ if isTightList defs then cr else blankline -- | Format a list of lines as line block. linesToLineBlock :: PandocMonad m => [[Inline]] -> RST m (Doc Text) linesToLineBlock inlineLines = do lns <- mapM inlineListToRST inlineLines return $ vcat (map (hang 2 (literal "| ")) lns) <> blankline -- | Convert list of Pandoc block elements to RST. blockListToRST' :: PandocMonad m => Bool -> [Block] -- ^ List of block elements -> RST m (Doc Text) blockListToRST' topLevel blocks = do -- insert comment between list and quoted blocks, see #4248 and #3675 let fixBlocks (b1:b2@(BlockQuote _):bs) | toClose b1 = b1 : commentSep : b2 : fixBlocks bs where toClose Plain{} = False toClose Header{} = False toClose LineBlock{} = False toClose HorizontalRule = False toClose SimpleFigure{} = True toClose Para{} = False toClose _ = True commentSep = RawBlock "rst" "..\n\n" fixBlocks (b:bs) = b : fixBlocks bs fixBlocks [] = [] tl <- gets stTopLevel modify (\s->s{stTopLevel=topLevel}) res <- vcat `fmap` mapM blockToRST (fixBlocks blocks) modify (\s->s{stTopLevel=tl}) return res blockListToRST :: PandocMonad m => [Block] -- ^ List of block elements -> RST m (Doc Text) blockListToRST = blockListToRST' False {- http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#directives According to the terminology used in the spec, a marker includes a final whitespace and a block includes the directive arguments. Here the variable names have slightly different meanings because we don't want to finish the line with a space if there are no arguments, it would produce rST that differs from what users expect in a way that's not easy to detect -} toRSTDirective :: Doc Text -> Doc Text -> [(Doc Text, Doc Text)] -> Doc Text -> Doc Text toRSTDirective typ args options content = marker <> spaceArgs <> cr <> block where marker = ".. " <> typ <> "::" block = nest 3 (fieldList $$ blankline $$ content $$ blankline) spaceArgs = if isEmpty args then "" else " " <> args -- a field list could end up being an empty doc thus being -- omitted by $$ fieldList = foldl ($$) "" $ map joinField options -- a field body can contain multiple lines joinField (name, body) = ":" <> name <> ": " <> body tableToRSTList :: PandocMonad m => [Inline] -> [Alignment] -> [Double] -> [[Block]] -> [[[Block]]] -> RST m (Doc Text) tableToRSTList caption _ propWidths headers rows = do captionRST <- inlineListToRST caption opts <- gets stOptions content <- listTableContent toWrite pure $ toRSTDirective "list-table" captionRST (directiveOptions opts) content where directiveOptions opts = widths (writerColumns opts) propWidths <> headerRows toWrite = if noHeaders then rows else headers:rows headerRows = [("header-rows", text $ show (1 :: Int)) | not noHeaders] widths tot pro = [("widths", showWidths tot pro) | not (null propWidths || all (==0.0) propWidths)] noHeaders = all null headers -- >>> showWidths 70 [0.5, 0.5] -- "35 35" showWidths :: Int -> [Double] -> Doc Text showWidths tot = text . unwords . map (show . toColumns tot) -- toColumns converts a width expressed as a proportion of the -- total into a width expressed as a number of columns toColumns :: Int -> Double -> Int toColumns t p = round (p * fromIntegral t) listTableContent :: PandocMonad m => [[[Block]]] -> RST m (Doc Text) listTableContent = fmap vcat . mapM (fmap (hang 2 (text "* ") . vcat) . mapM bulletListItemToRST) transformInlines :: [Inline] -> [Inline] transformInlines = insertBS . filter hasContents . removeSpaceAfterDisplayMath . concatMap (transformNested . flatten) where -- empty inlines are not valid RST syntax hasContents :: Inline -> Bool hasContents (Str "") = False hasContents (Emph []) = False hasContents (Underline []) = False hasContents (Strong []) = False hasContents (Strikeout []) = False hasContents (Superscript []) = False hasContents (Subscript []) = False hasContents (SmallCaps []) = False hasContents (Quoted _ []) = False hasContents (Cite _ []) = False hasContents (Span _ []) = False hasContents (Link _ [] ("", "")) = False hasContents (Image _ [] ("", "")) = False hasContents _ = True -- remove spaces after displaymath, as they screw up indentation: removeSpaceAfterDisplayMath (Math DisplayMath x : zs) = Math DisplayMath x : dropWhile (==Space) zs removeSpaceAfterDisplayMath (x:xs) = x : removeSpaceAfterDisplayMath xs removeSpaceAfterDisplayMath [] = [] insertBS :: [Inline] -> [Inline] -- insert '\ ' where needed insertBS (x:y:z:zs) | isComplex y && surroundComplex x z = x : y : insertBS (z : zs) insertBS (x:y:zs) | isComplex x && not (okAfterComplex y) = x : RawInline "rst" "\\ " : insertBS (y : zs) | isComplex y && not (okBeforeComplex x) = x : RawInline "rst" "\\ " : insertBS (y : zs) | otherwise = x : insertBS (y : zs) insertBS (x:ys) = x : insertBS ys insertBS [] = [] transformNested :: [Inline] -> [Inline] transformNested = concatMap exportLeadingTrailingSpace exportLeadingTrailingSpace :: Inline -> [Inline] exportLeadingTrailingSpace il | isComplex il = let contents = dropInlineParent il headSpace = headMay contents == Just Space lastSpace = lastMay contents == Just Space in (if headSpace then (Space:) else id) . (if lastSpace then (++ [Space]) else id) $ [setInlineChildren il (stripLeadingTrailingSpace contents)] | otherwise = [il] surroundComplex :: Inline -> Inline -> Bool surroundComplex (Str s) (Str s') | Just (_, c) <- T.unsnoc s , Just (c', _) <- T.uncons s' = case (c, c') of ('\'','\'') -> True ('"','"') -> True ('<','>') -> True ('[',']') -> True ('{','}') -> True _ -> False surroundComplex _ _ = False okAfterComplex :: Inline -> Bool okAfterComplex Space = True okAfterComplex SoftBreak = True okAfterComplex LineBreak = True okAfterComplex (Str (T.uncons -> Just (c,_))) = isSpace c || T.any (== c) "-.,:;!?\\/'\")]}>–—" okAfterComplex _ = False okBeforeComplex :: Inline -> Bool okBeforeComplex Space = True okBeforeComplex SoftBreak = True okBeforeComplex LineBreak = True okBeforeComplex (Str (T.unsnoc -> Just (_,c))) = isSpace c || T.any (== c) "-:/'\"<([{–—" okBeforeComplex _ = False isComplex :: Inline -> Bool isComplex (Emph _) = True isComplex (Underline _) = True isComplex (Strong _) = True isComplex (SmallCaps _) = True isComplex (Strikeout _) = True isComplex (Superscript _) = True isComplex (Subscript _) = True isComplex Link{} = True isComplex Image{} = True isComplex (Code _ _) = True isComplex (Math _ _) = True isComplex (Cite _ (x:_)) = isComplex x isComplex (Span _ (x:_)) = isComplex x isComplex _ = False -- | Flattens nested inlines. Extracts nested inlines and goes through -- them either collapsing them in the outer inline container or -- pulling them out of it flatten :: Inline -> [Inline] flatten outer | null contents = [outer] | otherwise = combineAll contents where contents = dropInlineParent outer combineAll = L.foldl' combine [] combine :: [Inline] -> Inline -> [Inline] combine f i = case (outer, i) of -- quotes are not rendered using RST inlines, so we can keep -- them and they will be readable and parsable (Quoted _ _, _) -> keep f i (_, Quoted _ _) -> keep f i -- spans are not rendered using RST inlines, so we can keep them (Span (_,_,[]) _, _) -> keep f i (_, Span (_,_,[]) _) -> keep f i -- inlineToRST handles this case properly so it's safe to keep ( Link{}, Image{}) -> keep f i -- parent inlines would prevent links from being correctly -- parsed, in this case we prioritise the content over the -- style (_, Link{}) -> emerge f i -- always give priority to strong text over emphasis (Emph _, Strong _) -> emerge f i -- drop all other nested styles (_, _) -> collapse f i emerge f i = f <> [i] keep f i = appendToLast f [i] collapse f i = appendToLast f $ dropInlineParent i appendToLast :: [Inline] -> [Inline] -> [Inline] appendToLast flattened toAppend = case NE.nonEmpty flattened of Nothing -> [setInlineChildren outer toAppend] Just xs -> if isOuter lastFlat then NE.init xs <> [appendTo lastFlat toAppend] else flattened <> [setInlineChildren outer toAppend] where lastFlat = NE.last xs appendTo o i = mapNested (<> i) o isOuter i = emptyParent i == emptyParent outer emptyParent i = setInlineChildren i [] mapNested :: ([Inline] -> [Inline]) -> Inline -> Inline mapNested f i = setInlineChildren i (f (dropInlineParent i)) dropInlineParent :: Inline -> [Inline] dropInlineParent (Link _ i _) = i dropInlineParent (Emph i) = i dropInlineParent (Underline i) = i dropInlineParent (Strong i) = i dropInlineParent (Strikeout i) = i dropInlineParent (Superscript i) = i dropInlineParent (Subscript i) = i dropInlineParent (SmallCaps i) = i dropInlineParent (Cite _ i) = i dropInlineParent (Image _ i _) = i dropInlineParent (Span _ i) = i dropInlineParent (Quoted _ i) = i dropInlineParent i = [i] -- not a parent, like Str or Space setInlineChildren :: Inline -> [Inline] -> Inline setInlineChildren (Link a _ t) i = Link a i t setInlineChildren (Emph _) i = Emph i setInlineChildren (Underline _) i = Underline i setInlineChildren (Strong _) i = Strong i setInlineChildren (Strikeout _) i = Strikeout i setInlineChildren (Superscript _) i = Superscript i setInlineChildren (Subscript _) i = Subscript i setInlineChildren (SmallCaps _) i = SmallCaps i setInlineChildren (Quoted q _) i = Quoted q i setInlineChildren (Cite c _) i = Cite c i setInlineChildren (Image a _ t) i = Image a i t setInlineChildren (Span a _) i = Span a i setInlineChildren leaf _ = leaf inlineListToRST :: PandocMonad m => [Inline] -> RST m (Doc Text) inlineListToRST = writeInlines . walk transformInlines -- | Convert list of Pandoc inline elements to RST. writeInlines :: PandocMonad m => [Inline] -> RST m (Doc Text) writeInlines lst = hcat <$> mapM inlineToRST lst -- | Convert Pandoc inline element to RST. inlineToRST :: PandocMonad m => Inline -> RST m (Doc Text) inlineToRST (Span ("",["mark"],[]) ils) = do contents <- writeInlines ils return $ ":mark:`" <> contents <> "`" inlineToRST (Span (_,_,kvs) ils) = do contents <- writeInlines ils return $ case lookup "role" kvs of Just role -> ":" <> literal role <> ":`" <> contents <> "`" Nothing -> contents inlineToRST (Emph lst) = do contents <- writeInlines lst return $ "*" <> contents <> "*" -- Underline is not supported, fall back to Emph inlineToRST (Underline lst) = inlineToRST (Emph lst) inlineToRST (Strong lst) = do contents <- writeInlines lst return $ "**" <> contents <> "**" inlineToRST (Strikeout lst) = do contents <- writeInlines lst return $ "[STRIKEOUT:" <> contents <> "]" inlineToRST (Superscript lst) = do contents <- writeInlines lst return $ ":sup:`" <> contents <> "`" inlineToRST (Subscript lst) = do contents <- writeInlines lst return $ ":sub:`" <> contents <> "`" inlineToRST (SmallCaps lst) = writeInlines lst inlineToRST (Quoted SingleQuote lst) = do contents <- writeInlines lst opts <- gets stOptions if isEnabled Ext_smart opts then return $ "'" <> contents <> "'" else return $ "‘" <> contents <> "’" inlineToRST (Quoted DoubleQuote lst) = do contents <- writeInlines lst opts <- gets stOptions if isEnabled Ext_smart opts then return $ "\"" <> contents <> "\"" else return $ "“" <> contents <> "”" inlineToRST (Cite _ lst) = writeInlines lst inlineToRST (Code (_,["interpreted-text"],[("role",role)]) str) = return $ ":" <> literal role <> ":`" <> literal str <> "`" inlineToRST (Code _ str) = do opts <- gets stOptions -- we trim the string because the delimiters must adjoin a -- non-space character; see #3496 -- we use :literal: when the code contains backticks, since -- :literal: allows backslash-escapes; see #3974 return $ if T.any (== '`') str then ":literal:`" <> literal (escapeText opts (trim str)) <> "`" else "``" <> literal (trim str) <> "``" inlineToRST (Str str) = do opts <- gets stOptions return $ literal $ (if isEnabled Ext_smart opts then unsmartify opts else id) $ escapeText opts str inlineToRST (Math t str) = do modify $ \st -> st{ stHasMath = True } return $ if t == InlineMath then ":math:`" <> literal str <> "`" else if T.any (== '\n') str then blankline $$ ".. math::" $$ blankline $$ nest 3 (literal str) $$ blankline else blankline $$ (".. math:: " <> literal str) $$ blankline inlineToRST il@(RawInline f x) | f == "rst" = return $ literal x | f == "latex" || f == "tex" = do modify $ \st -> st{ stHasRawTeX = True } return $ ":raw-latex:`" <> literal x <> "`" | otherwise = empty <$ report (InlineNotRendered il) inlineToRST LineBreak = return cr -- there's no line break in RST (see Para) inlineToRST Space = return space inlineToRST SoftBreak = do wrapText <- gets $ writerWrapText . stOptions case wrapText of WrapPreserve -> return cr WrapAuto -> return space WrapNone -> return space -- autolink inlineToRST (Link _ [Str str] (src, _)) | isURI src && if "mailto:" `T.isPrefixOf` src then src == escapeURI ("mailto:" <> str) else src == escapeURI str = do let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src) return $ literal srcSuffix inlineToRST (Link _ [Image attr alt (imgsrc,imgtit)] (src, _tit)) = do label <- registerImage attr alt (imgsrc,imgtit) (Just src) return $ "|" <> label <> "|" inlineToRST (Link _ txt (src, tit)) = do useReferenceLinks <- gets $ writerReferenceLinks . stOptions linktext <- writeInlines $ B.toList . B.trimInlines . B.fromList $ txt if useReferenceLinks then do refs <- gets stLinks case lookup txt refs of Just (src',tit') -> if src == src' && tit == tit' then return $ "`" <> linktext <> "`_" else return $ "`" <> linktext <> " <" <> literal src <> ">`__" Nothing -> do modify $ \st -> st { stLinks = (txt,(src,tit)):refs } return $ "`" <> linktext <> "`_" else return $ "`" <> linktext <> " <" <> literal src <> ">`__" inlineToRST (Image attr alternate (source, tit)) = do label <- registerImage attr alternate (source,tit) Nothing return $ "|" <> label <> "|" inlineToRST (Note contents) = do -- add to notes in state notes <- gets stNotes modify $ \st -> st { stNotes = contents:notes } let ref = show $ length notes + 1 return $ " [" <> text ref <> "]_" registerImage :: PandocMonad m => Attr -> [Inline] -> Target -> Maybe Text -> RST m (Doc Text) registerImage attr alt (src,tit) mbtarget = do pics <- gets stImages imgId <- gets stImageId let getImageName = do modify $ \st -> st{ stImageId = imgId + 1 } return [Str ("image" <> tshow imgId)] txt <- case lookup alt pics of Just (a,s,t,mbt) -> if (a,s,t,mbt) == (attr,src,tit,mbtarget) then return alt else do alt' <- getImageName modify $ \st -> st { stImages = (alt', (attr,src,tit, mbtarget)):stImages st } return alt' Nothing -> do alt' <- if null alt || alt == [Str ""] then getImageName else return alt modify $ \st -> st { stImages = (alt', (attr,src,tit, mbtarget)):stImages st } return alt' inlineListToRST txt imageDimsToRST :: PandocMonad m => Attr -> RST m (Doc Text) imageDimsToRST attr = do let (ident, _, _) = attr name = if T.null ident then empty else ":name: " <> literal ident showDim dir = let cols d = ":" <> text (show dir) <> ": " <> text (show d) in case dimension dir attr of Just (Percent a) -> case dir of Height -> empty Width -> cols (Percent a) Just dim -> cols dim Nothing -> empty return $ cr <> name $$ showDim Width $$ showDim Height simpleTable :: PandocMonad m => WriterOptions -> (WriterOptions -> [Block] -> m (Doc Text)) -> TableHead -> [TableBody] -> TableFoot -> m (Doc Text) simpleTable opts blocksToDoc (TableHead _ headers) tbody (TableFoot _ footers) = do headerDocs <- if allRowsEmpty headers then return [] else fixEmpties <$> mapM rowToDoc headers rowDocs <- fixEmpties <$> mapM rowToDoc ((tableBodiesToRows tbody) ++ footers) let numChars = maybe 0 maximum . NE.nonEmpty . map (offset . fst) let colWidths = map numChars $ transpose (headerDocs ++ rowDocs) let hline = nowrap $ hsep (map (\n -> literal (T.replicate n "=")) colWidths) let hdr = if allRowsEmpty headers then mempty else hline $$ mapToRow colWidths headerDocs let bdy = mapToRow colWidths rowDocs return $ hdr $$ hline $$ bdy $$ hline where -- can't have empty cells in first column: fixEmpties (d:ds) = fixEmpties' d : ds fixEmpties [] = [] fixEmpties' ((d, colSpan):ds) = if isEmpty d then (literal "\\ ", colSpan) : ds else (d, colSpan) : ds fixEmpties' [] = [] rowToDoc (Row _ cells) = concat <$> mapM cellToDocs cells cellToDocs (Cell _ _ _ colSpan blocks) = applyColSpan colSpan <$> (blocksToDoc opts) blocks applyColSpan col@(ColSpan colSpan) doc | colSpan > 1 = -- Fill up columns for the col spans by adding empty docs without a ColSpan. let emptyDoc = (literal "", Nothing) in (doc, Just col) : replicate (colSpan - 1) emptyDoc | otherwise = [(doc, Just col)] mapToRow colWidths = vcat . concatMap (toRow colWidths) toRow colWidths rowDocsWithColSpans = let (rowDocs, colSpans) = unzip rowDocsWithColSpans row = intersperseDivider $ zipWith lblock colWidths rowDocs colSpanRow = intersperseDivider $ writeColSpans colSpans colWidths in if any (maybe False (> 1)) colSpans then [row, colSpanRow] else [row] -- Don't write out col spans if they are all just 1. intersperseDivider = mconcat . intersperse (lblock 1 " ") -- Write col span dashes to match the length of the col widths. writeColSpans [] _ = [] writeColSpans _ [] = [] writeColSpans (Nothing : remainingColSpans) colWidths = writeColSpans remainingColSpans colWidths writeColSpans (Just (ColSpan colSpan) : remainingColSpans) colWidths = let (colWidths', remainingColWidths) = splitAt colSpan colWidths in writeColSpanDashes colWidths' : writeColSpans remainingColSpans remainingColWidths writeColSpanDashes colWidths = let colWidthsLength = length colWidths colWidthsSum = sum colWidths dashLength = if colWidthsLength > 1 -- Offset by 1 for the white spaces between columns so that the col -- span dashes align with the end of the columns correctly. then colWidthsSum + colWidthsLength - 1 else colWidthsSum in literal $ T.replicate dashLength "-" ================================================ FILE: src/Text/Pandoc/Writers/RTF.hs ================================================ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.RTF Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to RTF (rich text format). -} module Text.Pandoc.Writers.RTF ( writeRTF ) where import Control.Monad.Except (catchError, throwError) import Control.Monad import qualified Data.ByteString as B import Data.Char (chr, isDigit, ord, isAlphaNum) import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.DocLayout (render, literal) import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Printf (printf) -- | Convert Image inlines into a raw RTF embedded image, read from a file, -- or a MediaBag, or the internet. -- If file not found or filetype not jpeg or png, leave the inline unchanged. rtfEmbedImage :: PandocMonad m => WriterOptions -> Inline -> m Inline rtfEmbedImage opts x@(Image attr _ (src,_)) = catchError (do result <- P.fetchItem src case result of (imgdata, Just mime) | mime' <- T.takeWhile (/=';') mime , mime' == "image/jpeg" || mime' == "image/png" -> do let bytes = map (T.pack . printf "%02x") $ B.unpack imgdata filetype <- case mime' of "image/jpeg" -> return "\\jpegblip" "image/png" -> return "\\pngblip" _ -> throwError $ PandocShouldNeverHappenError $ "Unknown file type " <> mime sizeSpec <- case imageSize opts imgdata of Left msg -> do report $ CouldNotDetermineImageSize src msg return "" Right sz -> return $ "\\picw" <> tshow xpx <> "\\pich" <> tshow ypx <> "\\picwgoal" <> tshow (floor (xpt * 20) :: Integer) <> "\\pichgoal" <> tshow (floor (ypt * 20) :: Integer) -- twip = 1/1440in = 1/20pt where (xpx, ypx) = sizeInPixels sz (xpt, ypt) = desiredSizeInPoints opts attr sz let raw = "{\\pict" <> filetype <> sizeSpec <> " " <> T.concat bytes <> "}" if B.null imgdata then do report $ CouldNotFetchResource src "image contained no data" return x else return $ RawInline (Format "rtf") raw | otherwise -> do report $ CouldNotFetchResource src "image is not a jpeg or png" return x (_, Nothing) -> do report $ CouldNotDetermineMimeType src return x) (\e -> do report $ CouldNotFetchResource src $ tshow e return x) rtfEmbedImage _ x = return x -- | Convert Pandoc to a string in rich text format. writeRTF :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeRTF options doc = do -- handle images Pandoc meta@(Meta metamap) blocks <- walkM (rtfEmbedImage options) doc let spacer = not $ all null $ docTitle meta : docDate meta : docAuthors meta let toPlain (MetaBlocks [Para ils]) = MetaInlines ils toPlain x = x -- adjust title, author, date so we don't get para inside para let meta' = Meta $ M.adjust toPlain "title" . M.adjust toPlain "author" . M.adjust toPlain "date" $ metamap metadata <- metaToContext options (fmap (literal . T.concat) . mapM (blockToRTF 0 AlignDefault)) (fmap literal . inlinesToRTF) meta' body <- blocksToRTF 0 AlignDefault blocks toc <- blocksToRTF 0 AlignDefault [toTableOfContents options blocks] let context = defField "body" body $ defField "spacer" spacer $ (if writerTableOfContents options then defField "table-of-contents" toc -- for backwards compatibility, -- we populate toc with the contents -- of the toc rather than a boolean: . defField "toc" toc else id) metadata return $ case writerTemplate options of Just tpl -> render Nothing $ renderTemplate tpl context Nothing -> case T.unsnoc body of Just (_,'\n') -> body _ -> body <> T.singleton '\n' -- | Convert unicode characters (> 127) into rich text format representation. handleUnicode :: Text -> Text handleUnicode = T.concatMap $ \c -> if ord c > 127 then if surrogate c then let x = ord c - 0x10000 (q, r) = x `divMod` 0x400 upper = q + 0xd800 lower = r + 0xDC00 in enc (chr upper) <> enc (chr lower) else enc c else T.singleton c where surrogate x = not ( (0x0000 <= ord x && ord x <= 0xd7ff) || (0xe000 <= ord x && ord x <= 0xffff) ) enc x = "\\u" <> tshow (ord x) <> " ?" -- | Escape special characters. escapeSpecial :: Text -> Text escapeSpecial t | T.all isAlphaNum t = t | otherwise = T.concatMap escChar t where escChar '\t' = "\\tab " escChar '\8216' = "\\u8216'" escChar '\8217' = "\\u8217'" escChar '\8220' = "\\u8220\"" escChar '\8221' = "\\u8221\"" escChar '\8211' = "\\u8211-" escChar '\8212' = "\\u8212-" escChar '{' = "\\{" escChar '}' = "\\}" escChar '\\' = "\\\\" escChar c = T.singleton c -- | Escape strings as needed for rich text format. stringToRTF :: Text -> Text stringToRTF = handleUnicode . escapeSpecial -- | Escape things as needed for code block in RTF. codeStringToRTF :: Text -> Text codeStringToRTF str = T.intercalate "\\line\n" $ T.lines (stringToRTF str) -- | Make a paragraph with first-line indent, block indent, and space after. rtfParSpaced :: Int -- ^ space after (in twips) -> Int -- ^ block indent (in twips) -> Int -- ^ first line indent (relative to block) (in twips) -> Alignment -- ^ alignment -> Text -- ^ string with content -> Text rtfParSpaced spaceAfter indent firstLineIndent alignment content = let alignString = case alignment of AlignLeft -> "\\ql " AlignRight -> "\\qr " AlignCenter -> "\\qc " AlignDefault -> "\\ql " in "{\\pard " <> alignString <> "\\f0 \\sa" <> tshow spaceAfter <> " \\li" <> T.pack (show indent) <> " \\fi" <> tshow firstLineIndent <> " " <> content <> "\\par}\n" -- | Default paragraph. rtfPar :: Int -- ^ block indent (in twips) -> Int -- ^ first line indent (relative to block) (in twips) -> Alignment -- ^ alignment -> Text -- ^ string with content -> Text rtfPar = rtfParSpaced 180 -- | Compact paragraph (e.g. for compact list items). rtfCompact :: Int -- ^ block indent (in twips) -> Int -- ^ first line indent (relative to block) (in twips) -> Alignment -- ^ alignment -> Text -- ^ string with content -> Text rtfCompact = rtfParSpaced 0 -- number of twips to indent indentIncrement :: Int indentIncrement = 720 listIncrement :: Int listIncrement = 360 -- | Returns appropriate bullet list marker for indent level. bulletMarker :: Int -> Text bulletMarker indent = case indent `mod` 720 of 0 -> "\\bullet " _ -> "\\endash " -- | Returns appropriate (list of) ordered list markers for indent level. orderedMarkers :: Int -> ListAttributes -> [Text] orderedMarkers indent (start, style, delim) = if style == DefaultStyle && delim == DefaultDelim then case indent `mod` 720 of 0 -> orderedListMarkers (start, Decimal, Period) _ -> orderedListMarkers (start, LowerAlpha, Period) else orderedListMarkers (start, style, delim) blocksToRTF :: PandocMonad m => Int -> Alignment -> [Block] -> m Text blocksToRTF indent align = fmap T.concat . mapM (blockToRTF indent align) -- | Convert Pandoc block element to RTF. blockToRTF :: PandocMonad m => Int -- ^ indent level -> Alignment -- ^ alignment -> Block -- ^ block to convert -> m Text blockToRTF indent alignment (Div _ bs) = blocksToRTF indent alignment bs blockToRTF indent alignment (Plain lst) = rtfCompact indent 0 alignment <$> inlinesToRTF lst blockToRTF indent alignment (Para lst) = rtfPar indent 0 alignment <$> inlinesToRTF lst blockToRTF indent alignment (LineBlock lns) = blockToRTF indent alignment $ linesToPara lns blockToRTF indent alignment (BlockQuote lst) = blocksToRTF (indent + indentIncrement) alignment lst blockToRTF indent _ (CodeBlock _ str) = return $ rtfPar indent 0 AlignLeft ("\\f1 " <> codeStringToRTF str) blockToRTF _ _ b@(RawBlock f str) | f == Format "rtf" = return str | otherwise = do report $ BlockNotRendered b return "" blockToRTF indent alignment (BulletList lst) = spaceAtEnd . T.concat <$> mapM (listItemToRTF alignment indent (bulletMarker indent)) lst blockToRTF indent alignment (OrderedList attribs lst) = spaceAtEnd . T.concat <$> zipWithM (listItemToRTF alignment indent) (orderedMarkers indent attribs) lst blockToRTF indent alignment (DefinitionList lst) = spaceAtEnd . T.concat <$> mapM (definitionListItemToRTF alignment indent) lst blockToRTF indent _ HorizontalRule = return $ rtfPar indent 0 AlignCenter "\\emdash\\emdash\\emdash\\emdash\\emdash" blockToRTF indent alignment (Header level _ lst) = do contents <- inlinesToRTF lst return $ rtfPar indent 0 alignment $ "\\outlinelevel" <> tshow (level - 1) <> " \\b \\fs" <> tshow (40 - (level * 4)) <> " " <> contents blockToRTF indent alignment (Table _ blkCapt specs thead tbody tfoot) = do let (caption, aligns, sizes, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot caption' <- inlinesToRTF caption header' <- if all null headers then return "" else tableRowToRTF True indent aligns sizes headers rows' <- T.concat <$> mapM (tableRowToRTF False indent aligns sizes) rows return $ header' <> rows' <> rtfPar indent 0 alignment caption' blockToRTF indent alignment (Figure attr capt body) = blockToRTF indent alignment $ figureDiv attr capt body tableRowToRTF :: PandocMonad m => Bool -> Int -> [Alignment] -> [Double] -> [[Block]] -> m Text tableRowToRTF header indent aligns sizes' cols = do let totalTwips = 6 * 1440 -- 6 inches let sizes = if all (== 0) sizes' then replicate (length cols) (1.0 / fromIntegral (length cols)) else sizes' columns <- T.concat <$> zipWithM (tableItemToRTF indent) aligns cols let rightEdges = drop 1 $ scanl (\sofar new -> sofar + floor (new * totalTwips)) (0 :: Integer) sizes let cellDefs = map (\edge -> (if header then "\\clbrdrb\\brdrs" else "") <> "\\cellx" <> tshow edge) rightEdges let start = "{\n\\trowd \\trgaph120\n" <> T.concat cellDefs <> "\n" <> "\\trkeep\\intbl\n{\n" let end = "}\n\\intbl\\row}\n" return $ start <> columns <> end tableItemToRTF :: PandocMonad m => Int -> Alignment -> [Block] -> m Text tableItemToRTF indent alignment item = do contents <- blocksToRTF indent alignment item return $ "{" <> T.replace "\\pard" "\\pard\\intbl" contents <> "\\cell}\n" -- | Ensure that there's the same amount of space after compact -- lists as after regular lists. spaceAtEnd :: Text -> Text spaceAtEnd str = maybe str (<> "\\sa180\\par}\n") $ T.stripSuffix "\\par}\n" str -- | Convert list item (list of blocks) to RTF. listItemToRTF :: PandocMonad m => Alignment -- ^ alignment -> Int -- ^ indent level -> Text -- ^ list start marker -> [Block] -- ^ list item (list of blocks) -> m Text listItemToRTF alignment indent marker [] = return $ rtfCompact (indent + listIncrement) (negate listIncrement) alignment (marker <> "\\tx" <> tshow listIncrement <> "\\tab ") listItemToRTF alignment indent marker (listFirst:listRest) = do let f = blockToRTF (indent + listIncrement) alignment first <- f listFirst rest <- mapM f listRest let listMarker = "\\fi" <> tshow (negate listIncrement) <> " " <> marker <> "\\tx" <> tshow listIncrement <> "\\tab" -- Find the first occurrence of \\fi or \\fi-, then replace it and the following -- digits with the list marker. let insertListMarker t = case popDigit $ optionDash $ T.drop 3 suff of Just suff' -> pref <> listMarker <> T.dropWhile isDigit suff' Nothing -> t where (pref, suff) = T.breakOn "\\fi" t optionDash x = case T.uncons x of Just ('-', xs) -> xs _ -> x popDigit x | Just (d, xs) <- T.uncons x , isDigit d = Just xs | otherwise = Nothing -- insert the list marker into the (processed) first block return $ insertListMarker first <> T.concat rest -- | Convert definition list item (label, list of blocks) to RTF. definitionListItemToRTF :: PandocMonad m => Alignment -- ^ alignment -> Int -- ^ indent level -> ([Inline],[[Block]]) -- ^ list item (list of blocks) -> m Text definitionListItemToRTF alignment indent (label, defs) = do labelText <- blockToRTF indent alignment (Plain label) itemsText <- blocksToRTF (indent + listIncrement) alignment (concat defs) return $ labelText <> itemsText -- | Convert list of inline items to RTF. inlinesToRTF :: PandocMonad m => [Inline] -- ^ list of inlines to convert -> m Text inlinesToRTF lst = T.concat <$> mapM inlineToRTF lst -- | Convert inline item to RTF. inlineToRTF :: PandocMonad m => Inline -- ^ inline to convert -> m Text inlineToRTF (Span _ lst) = inlinesToRTF lst inlineToRTF (Emph lst) = do contents <- inlinesToRTF lst return $ "{\\i " <> contents <> "}" inlineToRTF (Underline lst) = do contents <- inlinesToRTF lst return $ "{\\ul " <> contents <> "}" inlineToRTF (Strong lst) = do contents <- inlinesToRTF lst return $ "{\\b " <> contents <> "}" inlineToRTF (Strikeout lst) = do contents <- inlinesToRTF lst return $ "{\\strike " <> contents <> "}" inlineToRTF (Superscript lst) = do contents <- inlinesToRTF lst return $ "{\\super " <> contents <> "}" inlineToRTF (Subscript lst) = do contents <- inlinesToRTF lst return $ "{\\sub " <> contents <> "}" inlineToRTF (SmallCaps lst) = do contents <- inlinesToRTF lst return $ "{\\scaps " <> contents <> "}" inlineToRTF (Quoted SingleQuote lst) = do contents <- inlinesToRTF lst return $ "\\u8216'" <> contents <> "\\u8217'" inlineToRTF (Quoted DoubleQuote lst) = do contents <- inlinesToRTF lst return $ "\\u8220\"" <> contents <> "\\u8221\"" inlineToRTF (Code _ str) = return $ "{\\f1 " <> codeStringToRTF str <> "}" inlineToRTF (Str str) = return $ stringToRTF str inlineToRTF (Math t str) = texMathToInlines t str >>= inlinesToRTF inlineToRTF (Cite _ lst) = inlinesToRTF lst inlineToRTF il@(RawInline f str) | f == Format "rtf" = return str | otherwise = do report $ InlineNotRendered il return "" inlineToRTF LineBreak = return "\\line " inlineToRTF SoftBreak = return " " inlineToRTF Space = return " " inlineToRTF (Link _ text (src, _)) = do contents <- inlinesToRTF text return $ "{\\field{\\*\\fldinst{HYPERLINK \"" <> codeStringToRTF src <> "\"}}{\\fldrslt{\\ul\n" <> contents <> "\n}}}\n" inlineToRTF (Image _ _ (source, _)) = return $ "{\\cf1 [image: " <> source <> "]\\cf0}" inlineToRTF (Note contents) = do body <- T.concat <$> mapM (blockToRTF 0 AlignDefault) contents return $ "{\\super\\chftn}{\\*\\footnote\\chftn\\~\\plain\\pard " <> body <> "}" ================================================ FILE: src/Text/Pandoc/Writers/Roff.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.Roff Copyright : Copyright (C) 2007-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Common functions for roff writers (man, ms). -} module Text.Pandoc.Writers.Roff ( WriterState(..) , defaultWriterState , MS , Note , EscapeMode(..) , escapeString , withFontFeature ) where import Data.Char (ord, isAscii) import Control.Monad.State.Strict import qualified Data.Map as Map import Data.Text (Text) import qualified Data.Text as Text import Data.String import Data.Maybe (fromMaybe) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.DocLayout import Text.Printf (printf) import Text.Pandoc.RoffChar (standardEscapes, characterCodes) data WriterState = WriterState { stHasInlineMath :: Bool , stFirstPara :: Bool , stNotes :: [Note] , stSmallCaps :: Bool , stHighlighting :: Bool , stInHeader :: Bool , stFontFeatures :: Map.Map Char Bool , stHasTables :: Bool } defaultWriterState :: WriterState defaultWriterState = WriterState{ stHasInlineMath = False , stFirstPara = True , stNotes = [] , stSmallCaps = False , stHighlighting = False , stInHeader = False , stFontFeatures = Map.fromList [ ('I',False) , ('B',False) , ('C',False) , ('V',False) ] , stHasTables = False } type Note = [Block] type MS = StateT WriterState data EscapeMode = AllowUTF8 -- ^ use preferred man escapes | AsciiOnly -- ^ escape everything deriving Show essentialEscapes :: Map.Map Char Text essentialEscapes = Map.fromList standardEscapes -- | Escape special characters for roff. If the first parameter is -- True, escape @-@ as @\-@, as required by current versions of groff man; -- otherwise leave it unescaped, as neededfor ms. escapeString :: Bool -> EscapeMode -> Text -> Text escapeString escapeHyphen e = Text.concat . escapeString' e . Text.unpack where escapeString' _ [] = [] escapeString' escapeMode ('\n':'.':xs) = "\n\\&." : escapeString' escapeMode xs -- see #10533; we need to escape hyphens as \- in man but not in ms: escapeString' escapeMode ('-':xs) | escapeHyphen = "\\-" : escapeString' escapeMode xs escapeString' escapeMode (x:xs) = case Map.lookup x essentialEscapes of Just s -> s : escapeString' escapeMode xs Nothing | isAscii x -> Text.singleton x : escapeString' escapeMode xs | otherwise -> (case escapeMode of AllowUTF8 -> Text.singleton x AsciiOnly -> case Map.lookup x characterCodeMap of Just t | Text.length t == 2 -> "\\(" <> t -- see #10716 | otherwise -> "\\C'" <> t <> "'" Nothing -> "\\C'" <> Text.pack (printf "u%04X" (ord x)) <> "'") : escapeString' escapeMode xs characterCodeMap :: Map.Map Char Text characterCodeMap = Map.fromList characterCodes fontChange :: (HasChars a, IsString a, PandocMonad m) => MS m (Doc a) fontChange = do features <- gets stFontFeatures inHeader <- gets stInHeader let filling = ['C' | fromMaybe False $ Map.lookup 'C' features] ++ ['V' | fromMaybe False $ Map.lookup 'V' features] ++ ['B' | inHeader || fromMaybe False (Map.lookup 'B' features)] ++ ['I' | fromMaybe False $ Map.lookup 'I' features] return $ case filling of [] -> text "\\f[R]" -- see #9020. C is not a font, use CR. ['C'] -> text "\\f[CR]" _ -> text $ "\\f[" ++ filling ++ "]" withFontFeature :: (HasChars a, IsString a, PandocMonad m) => Char -> MS m (Doc a) -> MS m (Doc a) withFontFeature c action = do modify $ \st -> st{ stFontFeatures = Map.adjust not c $ stFontFeatures st } begin <- fontChange d <- action modify $ \st -> st{ stFontFeatures = Map.adjust not c $ stFontFeatures st } end <- fontChange return $ begin <> d <> end ================================================ FILE: src/Text/Pandoc/Writers/Shared.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE LambdaCase #-} {- | Module : Text.Pandoc.Writers.Shared Copyright : Copyright (C) 2013-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Shared utility functions for pandoc writers. -} module Text.Pandoc.Writers.Shared ( metaToContext , metaToContext' , addVariablesToContext , getField , setField , resetField , defField , getLang , tagWithAttrs , htmlAddStyle , htmlAlignmentToString , htmlAttrs , isDisplayMath , fixDisplayMath , unsmartify , gridTable , lookupMetaBool , lookupMetaBlocks , lookupMetaInlines , lookupMetaString , stripLeadingTrailingSpace , toSubscript , toSuperscript , toSubscriptInline , toSuperscriptInline , toTableOfContents , endsWithPlain , toLegacyTable , splitSentences , ensureValidXmlIdentifiers , removeLinks , setupTranslations , isOrderedListMarker , toTaskListItem , delimited , allRowsEmpty , tableBodiesToRows , insertCurrentSpansAtColumn , takePreviousSpansAtColumn , decrementTrailingRowSpans ) where import Safe (lastMay, maximumMay) import qualified Data.ByteString.Lazy as BL import Control.Monad (MonadPlus, mzero) import Data.Either (isRight) import Data.Aeson (ToJSON (..), encode) import Data.Char (chr, ord, isSpace, isLetter, isUpper) 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 import qualified Data.Text as T import Data.Text (Text) import qualified Text.Pandoc.Builder as Builder import Text.Pandoc.CSS (cssAttributes) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing (runParser, eof, defaultParserState, anyOrderedListMarker) import Text.DocLayout import Text.Pandoc.Shared (stringify, makeSections, blocksToInlines) import Text.Pandoc.Walk (Walkable(..)) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (escapeStringForXML) import Text.DocTemplates (Context(..), Val(..), TemplateTarget, ToContext(..), FromContext(..)) import Text.Pandoc.Chunks (tocToList, toTOCTree) import Text.Collate.Lang (Lang (..)) import Text.Pandoc.Class (PandocMonad, toLang) import Text.Pandoc.Translations (setTranslations) import Data.Maybe (fromMaybe) import qualified Text.Pandoc.Writers.AnnotatedTable as Ann -- import Debug.Trace -- | Create template Context from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- Variables overwrite metadata fields with the same names. -- If multiple variables are set with the same name, a list is -- assigned. Does nothing if 'writerTemplate' is Nothing. metaToContext :: (Monad m, TemplateTarget a) => WriterOptions -> ([Block] -> m (Doc a)) -> ([Inline] -> m (Doc a)) -> Meta -> m (Context a) metaToContext opts blockWriter inlineWriter meta = case writerTemplate opts of Nothing -> return mempty Just _ -> addVariablesToContext opts <$> metaToContext' blockWriter inlineWriter meta -- | Like 'metaToContext, but does not include variables and is -- not sensitive to 'writerTemplate'. metaToContext' :: (Monad m, TemplateTarget a) => ([Block] -> m (Doc a)) -- ^ block writer -> ([Inline] -> m (Doc a)) -- ^ inline writer -> Meta -> m (Context a) metaToContext' blockWriter inlineWriter (Meta metamap) = Context <$> mapM (metaValueToVal blockWriter inlineWriter) metamap -- | Add variables to a template Context, using monoidal append. -- Also add `meta-json`. Note that metadata values are used -- in template contexts only when like-named variables aren't set. addVariablesToContext :: TemplateTarget a => WriterOptions -> Context a -> Context a addVariablesToContext opts c1 = c2 <> (fromText <$> writerVariables opts) <> c1 where c2 = Context $ M.insert "meta-json" (SimpleVal $ literal $ fromText jsonrep) mempty jsonrep = UTF8.toText $ BL.toStrict $ encode $ toJSON c1 -- | Converts a 'MetaValue' into a doctemplate 'Val', using the given -- converter functions. metaValueToVal :: (Monad m, TemplateTarget a) => ([Block] -> m (Doc a)) -- ^ block writer -> ([Inline] -> m (Doc a)) -- ^ inline writer -> MetaValue -> m (Val a) metaValueToVal blockWriter inlineWriter (MetaMap metamap) = MapVal . Context <$> mapM (metaValueToVal blockWriter inlineWriter) metamap metaValueToVal blockWriter inlineWriter (MetaList xs) = ListVal <$> mapM (metaValueToVal blockWriter inlineWriter) xs metaValueToVal _ _ (MetaBool b) = return $ BoolVal b metaValueToVal _ inlineWriter (MetaString s) = SimpleVal <$> inlineWriter (Builder.toList (Builder.text s)) metaValueToVal blockWriter _ (MetaBlocks bs) = SimpleVal <$> blockWriter bs metaValueToVal _ inlineWriter (MetaInlines is) = SimpleVal <$> inlineWriter is -- | Retrieve a field value from a template context. getField :: FromContext a b => Text -> Context a -> Maybe b getField field (Context m) = M.lookup field m >>= fromVal -- | Set a field of a template context. If the field already has a value, -- convert it into a list with the new value appended to the old value(s). -- This is a utility function to be used in preparing template contexts. setField :: ToContext a b => Text -> b -> Context a -> Context a setField field val (Context m) = Context $ M.insertWith combine field (toVal val) m where combine newval (ListVal xs) = ListVal (xs ++ [newval]) combine newval x = ListVal [x, newval] -- | Reset a field of a template context. If the field already has a -- value, the new value replaces it. -- This is a utility function to be used in preparing template contexts. resetField :: ToContext a b => Text -> b -> Context a -> Context a resetField field val (Context m) = Context (M.insert field (toVal val) m) -- | Set a field of a template context if it currently has no value. -- If it has a value, do nothing. -- This is a utility function to be used in preparing template contexts. defField :: ToContext a b => Text -> b -> Context a -> Context a defField field val (Context m) = Context (M.insertWith f field (toVal val) m) where f _newval oldval = oldval -- | Get the contents of the `lang` metadata field or variable. getLang :: WriterOptions -> Meta -> Maybe Text getLang opts meta = case lookupContext "lang" (writerVariables opts) of Just s -> Just s _ -> case lookupMeta "lang" meta of Just (MetaBlocks [Para [Str s]]) -> Just s Just (MetaBlocks [Plain [Str s]]) -> Just s Just (MetaInlines [Str s]) -> Just s Just (MetaString s) -> Just s _ -> Nothing -- | Produce an HTML tag with the given pandoc attributes. tagWithAttrs :: HasChars a => a -> Attr -> Doc a tagWithAttrs tag attr = "<" <> literal tag <> (htmlAttrs attr) <> ">" -- | Produce HTML for the given pandoc attributes, to be used in HTML tags htmlAttrs :: HasChars a => Attr -> Doc a htmlAttrs (ident, classes, kvs) = addSpaceIfNotEmpty (hsep [ if T.null ident then empty else "id=" <> doubleQuotes (text $ T.unpack ident) ,if null classes then empty else "class=" <> doubleQuotes (text $ T.unpack (T.unwords classes)) ,hsep (map (\(k,v) -> text (T.unpack k) <> "=" <> doubleQuotes (text $ T.unpack (escapeStringForXML v))) kvs) ]) addSpaceIfNotEmpty :: HasChars a => Doc a -> Doc a addSpaceIfNotEmpty f = if isEmpty f then f else " " <> f -- | Adds a key-value pair to the @style@ attribute. htmlAddStyle :: (Text, Text) -> [(Text, Text)] -> [(Text, Text)] htmlAddStyle (key, value) kvs = let cssToStyle = T.intercalate " " . map (\(k, v) -> k <> ": " <> v <> ";") in case break ((== "style") . fst) kvs of (_, []) -> -- no style attribute yet, add new one ("style", cssToStyle [(key, value)]) : kvs (xs, (_,cssStyles):rest) -> -- modify the style attribute xs ++ ("style", cssToStyle modifiedCssStyles) : rest where modifiedCssStyles = case break ((== key) . fst) $ cssAttributes cssStyles of (cssAttribs, []) -> (key, value) : cssAttribs (pre, _:post) -> pre ++ (key, value) : post -- | Get the html representation of an alignment key htmlAlignmentToString :: Alignment -> Maybe Text htmlAlignmentToString = \case AlignLeft -> Just "left" AlignRight -> Just "right" AlignCenter -> Just "center" AlignDefault -> Nothing -- | Returns 'True' iff the argument is an inline 'Math' element of type -- 'DisplayMath'. isDisplayMath :: Inline -> Bool isDisplayMath (Math DisplayMath _) = True isDisplayMath (Span _ [Math DisplayMath _]) = True isDisplayMath _ = False -- | Remove leading and trailing 'Space' and 'SoftBreak' elements. stripLeadingTrailingSpace :: [Inline] -> [Inline] stripLeadingTrailingSpace = go . reverse . go . reverse where go (Space:xs) = xs go (SoftBreak:xs) = xs go xs = xs -- | Put display math in its own block (for ODT/DOCX). fixDisplayMath :: Block -> Block fixDisplayMath (Plain lst) | any isDisplayMath lst && not (all isDisplayMath lst) = -- chop into several paragraphs so each displaymath is its own Div ("",["math"],[]) $ map Plain $ filter (not . null) $ map stripLeadingTrailingSpace $ groupBy (\x y -> (isDisplayMath x && isDisplayMath y) || not (isDisplayMath x || isDisplayMath y)) lst fixDisplayMath (Para lst) | any isDisplayMath lst && not (all isDisplayMath lst) = -- chop into several paragraphs so each displaymath is its own Div ("",["math"],[]) $ map Para $ filter (not . null) $ map stripLeadingTrailingSpace $ groupBy (\x y -> (isDisplayMath x && isDisplayMath y) || not (isDisplayMath x || isDisplayMath y)) lst fixDisplayMath x = x -- | Converts a Unicode character into the ASCII sequence used to -- represent the character in "smart" Markdown. unsmartify :: WriterOptions -> Text -> Text unsmartify opts = T.concatMap $ \c -> case c of '\8217' -> "'" '\8230' -> "..." '\8211' | isEnabled Ext_old_dashes opts -> "-" | otherwise -> "--" '\8212' | isEnabled Ext_old_dashes opts -> "--" | otherwise -> "---" '\8220' -> "\"" '\8221' -> "\"" '\8216' -> "'" _ -> T.singleton c -- | Writes a grid table. gridTable :: Monad m => WriterOptions -> (WriterOptions -> [Block] -> m (Doc Text)) -- ^ format Doc writer -> [ColSpec] -> TableHead -> [TableBody] -> TableFoot -> m (Doc Text) gridTable opts blocksToDoc colspecs' thead' tbodies' tfoot' = do let Ann.Table _ _ colspecs thead tbodies tfoot = Ann.toTable mempty (Caption Nothing mempty) colspecs' thead' tbodies' tfoot' let renderRows = fmap addDummies . mapM (gridRow opts blocksToDoc) let getHeadCells (Ann.HeaderRow _ _ cells) = cells let getHeadRows (Ann.TableHead _ rs) = map getHeadCells rs headCells <- renderRows (getHeadRows thead) let getFootRows (Ann.TableFoot _ xs) = map getHeadCells xs footCells <- renderRows (getFootRows tfoot) -- We don't distinguish between row head and regular cells here: let getBodyCells (Ann.BodyRow _ _ rhcells cells) = rhcells ++ cells let getBody (Ann.TableBody _ _ hs xs) = map getHeadCells hs <> map getBodyCells xs bodyCells <- mapM (renderRows . getBody) tbodies let rows = (setTopBorder SingleLine . setBottomBorder DoubleHeaderLine) headCells ++ (setTopBorder (if null headCells then SingleHeaderLine else SingleLine) . setBottomBorder SingleLine) (mconcat bodyCells) ++ (setTopBorder DoubleLine . setBottomBorder DoubleLine) footCells pure $ gridRows $ redoWidths opts colspecs rows -- Returns (current widths, full widths, min widths) extractColWidths :: WriterOptions -> [[RenderedCell Text]] -> ([Int], [Int], [Int]) extractColWidths opts rows = (currentwidths, fullwidths, minwidths) where getWidths calcOffset = map (fromMaybe 0 . maximumMay) (transpose (map (concatMap (getCellWidths calcOffset)) rows)) getCellWidths calcOffset c = replicate (cellColSpan c) (calcOffset c `div` (cellColSpan c) + calcOffset c `rem` (cellColSpan c)) fullwidths = getWidths (max 1 . offset . cellContents) currentwidths = getWidths cellWidth minwidths = case writerWrapText opts of WrapNone -> fullwidths _ -> getWidths (minOffset . cellContents) resetWidths :: [Int] -> [RenderedCell Text] -> [RenderedCell Text] resetWidths _ [] = [] resetWidths [] cs = cs resetWidths (w:ws) (c:cs) = case cellColSpan c of 1 -> c{ cellWidth = w } : resetWidths ws cs n | n < 1 -> c : resetWidths ws cs | otherwise -> c{ cellWidth = w + sum (take (n - 1) ws) + (3 * (n-1)) } : resetWidths (drop (n - 1) ws) cs redoWidths :: WriterOptions -> [ColSpec] -> [[RenderedCell Text]] -> [[RenderedCell Text]] redoWidths _ _ [] = [] redoWidths opts colspecs rows = map (resetWidths newwidths) rows where numcols = length colspecs isSimple = all ((== ColWidthDefault) . snd) colspecs (actualwidths, fullwidths, minwidths) = extractColWidths opts rows totwidth = writerColumns opts - (3 * numcols) - 1 evenwidth = totwidth `div` numcols + totwidth `rem` numcols keepwidths = filter (< evenwidth) fullwidths evenwidth' = (totwidth - sum keepwidths) `div` (numcols - length keepwidths) ensureMinWidths = zipWith max minwidths newwidths = ensureMinWidths $ case isSimple of True | sum fullwidths <= totwidth -> fullwidths | otherwise -> map (\w -> if w < evenwidth then w else evenwidth') fullwidths False -> actualwidths makeDummy :: RenderedCell Text -> RenderedCell Text makeDummy c = RenderedCell{ cellColNum = cellColNum c, cellColSpan = cellColSpan c, cellColSpecs = cellColSpecs c, cellAlign = AlignDefault, cellRowSpan = cellRowSpan c - 1, cellWidth = cellWidth c, cellContents = mempty, cellBottomBorder = NoLine, cellTopBorder = NoLine } addDummies :: [[RenderedCell Text]] -> [[RenderedCell Text]] addDummies = reverse . L.foldl' go [] where go [] cs = [cs] go (prevRow:rs) cs = addDummiesToRow prevRow cs : prevRow : rs addDummiesToRow [] cs = cs addDummiesToRow ds [] = map makeDummy ds addDummiesToRow (d:ds) (c:cs) = if cellColNum d < cellColNum c then makeDummy d : addDummiesToRow ds (c:cs) else c : addDummiesToRow (dropWhile (\x -> cellColNum x < cellColNum c + cellColSpan c) (d:ds)) cs setTopBorder :: LineStyle -> [[RenderedCell Text]] -> [[RenderedCell Text]] setTopBorder _ [] = [] setTopBorder sty (cs:rest) = (map (\c -> c{ cellTopBorder = sty }) cs) : rest setBottomBorder :: LineStyle -> [[RenderedCell Text]] -> [[RenderedCell Text]] setBottomBorder _ [] = [] setBottomBorder sty [cs] = [map (\c -> c{ cellBottomBorder = sty }) cs] setBottomBorder sty (c:cs) = c : setBottomBorder sty cs gridRows :: [[RenderedCell Text]] -> Doc Text gridRows [] = mempty gridRows (x:xs) = (case x of [] -> mempty (c:_) | isHeaderStyle (cellTopBorder c) -> formatHeaderLine (cellTopBorder c) (x:xs) | otherwise -> formatBorder cellTopBorder False x) $$ vcat (zipWith (rowAndBottom (x:xs)) (x:xs) (xs ++ [[]])) where -- generate wrapped contents. include pipe borders, bottom and left renderCellContents c = -- we don't use cblock or lblock because the content might -- be interpreted as an indented code block...even though it -- would look better to right-align right-aligned cells... -- (TODO: change this on parsing side?) lblock (cellWidth c) (cellContents c) formatRow cs = vfill "| " <> hcat (intersperse (vfill " | ") (map renderCellContents cs)) <> vfill " |" rowAndBottom allRows thisRow nextRow = let isLastRow = null nextRow border1 = case thisRow of [] -> mempty (c:_) -> if isHeaderStyle (cellBottomBorder c) then formatHeaderLine (cellBottomBorder c) allRows else formatBorder cellBottomBorder False thisRow border2 = case nextRow of [] -> mempty (c:_) -> if isHeaderStyle (cellTopBorder c) then formatHeaderLine (cellTopBorder c) allRows else formatBorder cellTopBorder False nextRow combinedBorder = if isLastRow then border1 else literal $ combineBorders (render Nothing border1) (render Nothing border2) in formatRow thisRow $$ combinedBorder combineBorders :: Text -> Text -> Text combineBorders t1 t2 = if T.null t1 then t2 else T.zipWith go t1 t2 where go '+' _ = '+' go _ '+' = '+' go ':' _ = ':' go _ ':' = ':' go '|' '-' = '+' go '-' '|' = '+' go '|' '=' = '+' go '=' '|' = '+' go '=' _ = '=' go _ '=' = '=' go ' ' d = d go c _ = c formatHeaderLine :: Show a => LineStyle -> [[RenderedCell a]] -> Doc Text formatHeaderLine lineStyle rows = literal $ L.foldl' (\t row -> combineBorders t (render Nothing $ formatBorder (const lineStyle) True row)) mempty rows formatBorder :: Show a => (RenderedCell a -> LineStyle) -> Bool -> [RenderedCell a] -> Doc Text formatBorder borderStyle alignMarkers cs = borderParts <> if lastBorderStyle == NoLine then char '|' else char '+' where (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 (NoLine, NoLine) -> '|' _ -> '+' toBorderSection c = text $ leftalign : replicate (cellWidth c) lineChar ++ [rightalign] where lineChar = case borderStyle c of NoLine -> ' ' SingleLine -> '-' SingleHeaderLine -> '-' DoubleLine -> '=' DoubleHeaderLine -> '=' (leftalign, rightalign) = case cellAlign c of _ | not alignMarkers -> (lineChar,lineChar) AlignLeft -> (':',lineChar) AlignCenter -> (':',':') AlignRight -> (lineChar,':') AlignDefault -> (lineChar,lineChar) data LineStyle = NoLine | SingleLine | DoubleLine | SingleHeaderLine | DoubleHeaderLine deriving (Show, Ord, Eq) isHeaderStyle :: LineStyle -> Bool isHeaderStyle SingleHeaderLine = True isHeaderStyle DoubleHeaderLine = True isHeaderStyle _ = False data RenderedCell a = RenderedCell{ cellColNum :: Int , cellColSpan :: Int , cellColSpecs :: NonEmpty ColSpec , cellAlign :: Alignment , cellRowSpan :: Int , cellWidth :: Int , cellContents :: Doc a , cellBottomBorder :: LineStyle , cellTopBorder :: LineStyle } deriving (Show) getColWidth :: ColSpec -> Double getColWidth (_, ColWidth n) = n getColWidth (_, ColWidthDefault) = 0 -- TODO? toCharWidth :: WriterOptions -> Double -> Int toCharWidth opts width = max 1 (floor (width * fromIntegral (writerColumns opts)) - 3) gridRow :: (Monad m, HasChars a) => WriterOptions -> (WriterOptions -> [Block] -> m (Doc a)) -- ^ format Doc writer -> [Ann.Cell] -> m [RenderedCell a] gridRow opts blocksToDoc = mapM renderCell where renderer = blocksToDoc opts renderCell (Ann.Cell cellcolspecs (Ann.ColNumber colnum) (Cell _ _ (RowSpan rowspan) _ blocks)) = do let ((align,_):|_) = cellcolspecs let width = toCharWidth opts $ sum (fmap getColWidth cellcolspecs) rendered <- renderer blocks pure $ RenderedCell{ cellColNum = colnum, cellColSpan = length cellcolspecs, cellColSpecs = cellcolspecs, cellAlign = align, cellRowSpan = rowspan, cellWidth = width, cellContents = rendered, cellBottomBorder = if rowspan < 2 then SingleLine else NoLine, cellTopBorder = SingleLine } -- | Retrieve the metadata value for a given @key@ -- and convert to Bool. lookupMetaBool :: Text -> Meta -> Bool lookupMetaBool key meta = case lookupMeta key meta of Just (MetaBlocks _) -> True Just (MetaInlines _) -> True Just (MetaString x) -> not (T.null x) Just (MetaBool True) -> True _ -> False -- | Retrieve the metadata value for a given @key@ -- and extract blocks. -- -- Note that an empty list is returned for maps, lists, and booleans. lookupMetaBlocks :: Text -> Meta -> [Block] lookupMetaBlocks key meta = case lookupMeta key meta of Just (MetaBlocks bs) -> bs Just (MetaInlines ils) -> [Plain ils] Just (MetaString s) -> [Plain [Str s]] _ -> [] -- | Retrieve the metadata value for a given @key@ -- and extract inlines. -- -- Note that an empty list is returned for maps and lists. lookupMetaInlines :: Text -> Meta -> [Inline] lookupMetaInlines key meta = case lookupMeta key meta of Just (MetaString s) -> [Str s] Just (MetaInlines ils) -> ils Just (MetaBlocks [Plain ils]) -> ils Just (MetaBlocks [Para ils]) -> ils _ -> [] -- | Retrieve the metadata value for a given @key@ -- and convert to String. -- -- Note that an empty list is returned for maps, lists, and booleans. lookupMetaString :: Text -> Meta -> Text lookupMetaString key meta = case lookupMeta key meta of Just (MetaString s) -> s Just (MetaInlines ils) -> stringify ils Just (MetaBlocks bs) -> stringify bs Just (MetaBool b) -> T.pack (show b) _ -> "" -- | Tries to convert a character into a unicode superscript version of -- the character. toSuperscript :: Char -> Maybe Char toSuperscript '1' = Just '\x00B9' toSuperscript '2' = Just '\x00B2' toSuperscript '3' = Just '\x00B3' toSuperscript '+' = Just '\x207A' toSuperscript '-' = Just '\x207B' toSuperscript '\x2212' = Just '\x207B' -- unicode minus toSuperscript '=' = Just '\x207C' toSuperscript '(' = Just '\x207D' toSuperscript ')' = Just '\x207E' toSuperscript c | c >= '0' && c <= '9' = Just $ chr (0x2070 + (ord c - 48)) | isSpace c = Just c | otherwise = Nothing -- | Tries to convert a character into a unicode subscript version of -- the character. toSubscript :: Char -> Maybe Char toSubscript '+' = Just '\x208A' toSubscript '-' = Just '\x208B' toSubscript '=' = Just '\x208C' toSubscript '(' = Just '\x208D' toSubscript ')' = Just '\x208E' toSubscript c | c >= '0' && c <= '9' = Just $ chr (0x2080 + (ord c - 48)) | isSpace c = Just c | otherwise = Nothing toSubscriptInline :: Inline -> Maybe Inline toSubscriptInline Space = Just Space toSubscriptInline (Span attr ils) = Span attr <$> traverse toSubscriptInline ils toSubscriptInline (Str s) = Str . T.pack <$> traverse toSubscript (T.unpack s) toSubscriptInline LineBreak = Just LineBreak toSubscriptInline SoftBreak = Just SoftBreak toSubscriptInline _ = Nothing toSuperscriptInline :: Inline -> Maybe Inline toSuperscriptInline Space = Just Space toSuperscriptInline (Span attr ils) = Span attr <$> traverse toSuperscriptInline ils toSuperscriptInline (Str s) = Str . T.pack <$> traverse toSuperscript (T.unpack s) toSuperscriptInline LineBreak = Just LineBreak toSuperscriptInline SoftBreak = Just SoftBreak toSuperscriptInline _ = Nothing -- | Construct table of contents (as a bullet list) from document body. toTableOfContents :: WriterOptions -> [Block] -> Block toTableOfContents opts = tocToList (writerNumberSections opts) (writerTOCDepth opts) . toTOCTree . makeSections (writerNumberSections opts) Nothing -- | Returns 'True' iff the list of blocks has a @'Plain'@ as its last -- element. endsWithPlain :: [Block] -> Bool endsWithPlain xs = case lastMay xs of Just Plain{} -> True Just (BulletList is) -> maybe False endsWithPlain (lastMay is) Just (OrderedList _ is) -> maybe False endsWithPlain (lastMay is) _ -> False -- | Convert the relevant components of a new-style table (with block -- caption, row headers, row and column spans, and so on) to those of -- an old-style table (inline caption, table head with one row, no -- foot, and so on). Cells with a 'RowSpan' and 'ColSpan' of @(h, w)@ -- will be cut up into @h * w@ cells of dimension @(1, 1)@, with the -- content placed in the upper-left corner. toLegacyTable :: Caption -> [ColSpec] -> TableHead -> [TableBody] -> TableFoot -> ([Inline], [Alignment], [Double], [[Block]], [[[Block]]]) toLegacyTable (Caption _ cbody) specs thead tbodies tfoot = (cbody', aligns, widths, th', tb') where numcols = length specs (aligns, mwidths) = unzip specs fromWidth (ColWidth w) | w > 0 = w fromWidth _ = 0 widths = map fromWidth mwidths unRow (Row _ x) = x unBody (TableBody _ _ hd bd) = hd <> bd unBodies = concatMap unBody TableHead _ th = Builder.normalizeTableHead numcols thead tb = map (Builder.normalizeTableBody numcols) tbodies TableFoot _ tf = Builder.normalizeTableFoot numcols tfoot cbody' = blocksToInlines cbody (th', tb') = case th of r:rs -> let (pendingPieces, r') = placeCutCells [] $ unRow r rs' = cutRows pendingPieces $ rs <> unBodies tb <> tf in (r', rs') [] -> ([], cutRows [] $ unBodies tb <> tf) -- Adapted from placeRowSection in Builders. There is probably a -- more abstract foldRowSection that unifies them both. placeCutCells pendingPieces cells -- If there are any pending pieces for a column, add -- them. Pending pieces have preference over cells due to grid -- layout rules. | (p:ps):pendingPieces' <- pendingPieces = let (pendingPieces'', rowPieces) = placeCutCells pendingPieces' cells in (ps : pendingPieces'', p : rowPieces) -- Otherwise cut up a cell on the row and deal with its pieces. | c:cells' <- cells = let (h, w, cBody) = getComponents c cRowPieces = cBody : replicate (w - 1) mempty cPendingPieces = replicate w $ replicate (h - 1) mempty pendingPieces' = drop w pendingPieces (pendingPieces'', rowPieces) = placeCutCells pendingPieces' cells' in (cPendingPieces <> pendingPieces'', cRowPieces <> rowPieces) | otherwise = ([], []) cutRows pendingPieces (r:rs) = let (pendingPieces', r') = placeCutCells pendingPieces $ unRow r rs' = cutRows pendingPieces' rs in r' : rs' cutRows _ [] = [] getComponents (Cell _ _ (RowSpan h) (ColSpan w) body) = (h, w, body) splitSentences :: Doc Text -> Doc Text splitSentences = go . toList where go [] = mempty go (Text len t : AfterBreak _ : BreakingSpace : xs) | isSentenceEnding t = Text len t <> NewLine <> go xs go (Text len t : BreakingSpace : xs) | isSentenceEnding t = Text len t <> NewLine <> go xs go (x:xs) = x <> go xs toList (Concat (Concat a b) c) = toList (Concat a (Concat b c)) toList (Concat a b) = a : toList b toList x = [x] isSentenceEnding t = case T.unsnoc t of Just (t',c) | c == '.' || c == '!' || c == '?' , not (isInitial t') -> True | c == ')' || c == ']' || c == '"' || c == '\x201D' -> case T.unsnoc t' of Just (t'',d) -> d == '.' || d == '!' || d == '?' && not (isInitial t'') _ -> False _ -> False where isInitial x = T.length x == 1 && T.all isUpper x -- | Ensure that all identifiers start with a letter, -- and modify internal links accordingly. (Yes, XML allows an -- underscore, but HTML 4 doesn't, so we are more conservative.) ensureValidXmlIdentifiers :: Pandoc -> Pandoc ensureValidXmlIdentifiers = walk fixLinks . walkAttr fixIdentifiers where fixIdentifiers (ident, classes, kvs) = (case T.uncons ident of Nothing -> ident Just (c, _) | isLetter c -> ident _ -> "id_" <> ident, classes, kvs) needsFixing src = case T.uncons src of Just ('#',t) -> case T.uncons t of Just (c,_) | not (isLetter c) -> Just ("#id_" <> t) _ -> Nothing _ -> Nothing fixLinks (Link attr ils (src, tit)) | Just src' <- needsFixing src = Link attr ils (src', tit) fixLinks (Image attr ils (src, tit)) | Just src' <- needsFixing src = Image attr ils (src', tit) fixLinks x = x -- | Walk Pandoc document, modifying attributes. walkAttr :: (Attr -> Attr) -> Pandoc -> Pandoc walkAttr f = walk goInline . walk goBlock where goInline (Span attr ils) = Span (f attr) ils goInline (Link attr ils target) = Link (f attr) ils target goInline (Image attr ils target) = Image (f attr) ils target goInline (Code attr txt) = Code (f attr) txt goInline x = x goBlock (Header lev attr ils) = Header lev (f attr) ils goBlock (CodeBlock attr txt) = CodeBlock (f attr) txt goBlock (Table attr cap colspecs thead tbodies tfoot) = Table (f attr) cap colspecs thead tbodies tfoot goBlock (Div attr bs) = Div (f attr) bs goBlock x = x -- | Convert links to spans; most useful when writing elements that must not -- contain links, e.g. to avoid nested links. removeLinks :: [Inline] -> [Inline] removeLinks = walk go where go (Link attr ils _) = Span attr ils go x = x -- | Set translations based on the `lang` in metadata. setupTranslations :: PandocMonad m => Meta -> m () setupTranslations meta = do let defLang = Lang "en" (Just "US") Nothing [] [] [] lang <- case lookupMetaString "lang" meta of "" -> pure defLang s -> fromMaybe defLang <$> toLang (Just s) setTranslations lang -- True if the string would count as a Markdown ordered list marker. isOrderedListMarker :: Text -> Bool isOrderedListMarker xs = not (T.null xs) && (T.last xs `elem` ['.',')']) && isRight (runParser (anyOrderedListMarker >> eof) defaultParserState "" xs) toTaskListItem :: MonadPlus m => [Block] -> m (Bool, [Block]) toTaskListItem (Plain (Str "☐":Space:ils):xs) = pure (False, Plain ils:xs) toTaskListItem (Plain (Str "☒":Space:ils):xs) = pure (True, Plain ils:xs) toTaskListItem (Para (Str "☐":Space:ils):xs) = pure (False, Para ils:xs) toTaskListItem (Para (Str "☒":Space:ils):xs) = pure (True, Para ils:xs) toTaskListItem _ = mzero -- | Add an opener and closer to a Doc. If the Doc begins or ends -- with whitespace, export this outside the opener or closer. -- This is used for formats, like Markdown, which don't allow spaces -- after opening or before closing delimiters. delimited :: Doc Text -> Doc Text -> Doc Text -> Doc Text delimited opener closer content = mconcat initialWS <> opener <> mconcat middle <> closer <> mconcat finalWS where contents = toList content (initialWS, rest) = span isWS contents (reverseFinalWS, reverseMiddle) = span isWS (reverse rest) finalWS = reverse reverseFinalWS middle = reverse reverseMiddle isWS NewLine = True isWS CarriageReturn = True isWS BreakingSpace = True isWS BlankLines{} = True isWS _ = False toList (Concat (Concat a b) c) = toList (Concat a (Concat b c)) toList (Concat a b) = a : toList b toList x = [x] -- | Determine whether all rows and their cells are empty. allRowsEmpty :: [Row] -> Bool allRowsEmpty = all isEmptyRow where isEmptyRow (Row _ cells) = all isEmptyCell cells isEmptyCell (Cell _ _ _ _ blocks) = null blocks -- | Concatenates the header and body Rows of a List of TableBody into a flat -- List of Rows. tableBodiesToRows :: [TableBody] -> [Row] tableBodiesToRows = concatMap tableBodyToRows where tableBodyToRows (TableBody _ _ headerRows bodyRows) = headerRows ++ bodyRows -- | Insert the current span information of a table cell to keep track of it in -- subsequent rows. -- -- If 'RowSpan' @> 1@, the current span information will be inserted. Otherwise -- the previous span information will be left unchanged. -- -- Use 'takePreviousSpansAtColumn' to take previous span information at -- subsequent rows. Use 'decrementTrailingRowSpans' to handle previous trailing -- spans at the end of a row. -- -- For writers that need to manually apply the 'RowSpan' of cells over multiple -- rows or otherwise have to keep track of it. insertCurrentSpansAtColumn :: Int -> M.Map Int (RowSpan, ColSpan) -> RowSpan -> ColSpan -> M.Map Int (RowSpan, ColSpan) insertCurrentSpansAtColumn columnPosition previousSpans (RowSpan rowSpan) colSpan = if (rowSpan > 1) then M.insert columnPosition (RowSpan rowSpan - 1, colSpan) previousSpans -- Minus its own row. else previousSpans -- | Take previous span information at a column position that was added with -- 'insertCurrentSpansAtColumn' if available. -- -- If the previous 'RowSpan' @>= 1@, this will return 'Just' the previous -- 'ColSpan' and an adjusted span information where that 'RowSpan' is either -- decremented or deleted if it would fall to 0. Otherwise this will return -- 'Nothing'. takePreviousSpansAtColumn :: Int -> M.Map Int (RowSpan, ColSpan) -> Maybe (ColSpan, M.Map Int (RowSpan, ColSpan)) takePreviousSpansAtColumn columnPosition previousSpans | Just previous@(RowSpan previousRowSpan, previousColSpan) <- M.lookup columnPosition previousSpans , previousRowSpan >= 1 = Just (previousColSpan, decrementPreviousRowSpans previous) | otherwise = Nothing where decrementPreviousRowSpans (RowSpan previousRowSpan, previousColSpan) = if previousRowSpan > 1 then M.insert columnPosition (RowSpan previousRowSpan - 1, previousColSpan) previousSpans else M.delete columnPosition previousSpans -- | Decrement all previously tracked trailing 'RowSpan' elements at or after a -- column position. -- -- For handling previous row spans that are next to the end of a row's cells -- that were previously added with 'insertCurrentSpansAtColumn'. decrementTrailingRowSpans :: Int -> M.Map Int (RowSpan, ColSpan) -> M.Map Int (RowSpan, ColSpan) decrementTrailingRowSpans columnPosition = M.mapWithKey decrementTrailing where decrementTrailing previousColumnPosition previousSpan@(RowSpan rowSpan, colSpan) = if previousColumnPosition >= columnPosition && rowSpan >= 1 then (RowSpan rowSpan - 1, colSpan) else previousSpan ================================================ FILE: src/Text/Pandoc/Writers/TEI.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- | Module : Text.Pandoc.Writers.TEI Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to TEI XML -} module Text.Pandoc.Writers.TEI (writeTEI) where import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (languages, languagesByExtension) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared import Text.Pandoc.XML -- | Convert Pandoc document to string in TEI XML format. writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTEI opts doc = do let Pandoc meta blocks = ensureValidXmlIdentifiers doc let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing let startLvl = case writerTopLevelDivision opts of TopLevelPart -> -1 TopLevelChapter -> 0 TopLevelSection -> 1 TopLevelDefault -> 1 let fromBlocks = blocksToTEI opts . makeSections False (Just startLvl) metadata <- metaToContext opts fromBlocks (fmap chomp . inlinesToTEI opts) meta main <- fromBlocks blocks let context = defField "body" main $ defField "mathml" (case writerHTMLMathMethod opts of MathML -> True _ -> False) metadata return $ render colwidth $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate tpl context -- | Convert a list of Pandoc blocks to TEI. blocksToTEI :: PandocMonad m => WriterOptions -> [Block] -> m (Doc Text) blocksToTEI opts bs = vcat <$> mapM (blockToTEI opts) bs -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block plainToPara (Plain x) = Para x plainToPara x = x -- | Convert a list of pairs of terms and definitions into a TEI -- list with labels and items. deflistItemsToTEI :: PandocMonad m => WriterOptions -> [([Inline],[[Block]])] -> m (Doc Text) deflistItemsToTEI opts items = vcat <$> mapM (uncurry (deflistItemToTEI opts)) items -- | Convert a term and a list of blocks into a TEI varlistentry. deflistItemToTEI :: PandocMonad m => WriterOptions -> [Inline] -> [[Block]] -> m (Doc Text) deflistItemToTEI opts term defs = do term' <- inlinesToTEI opts term defs' <- blocksToTEI opts $ concatMap (map plainToPara) defs return $ inTagsIndented "label" term' $$ inTagsIndented "item" defs' -- | Convert a list of lists of blocks to a list of TEI list items. listItemsToTEI :: PandocMonad m => WriterOptions -> [[Block]] -> m (Doc Text) listItemsToTEI opts items = vcat <$> mapM (listItemToTEI opts) items -- | Convert a list of blocks into a TEI list item. listItemToTEI :: PandocMonad m => WriterOptions -> [Block] -> m (Doc Text) listItemToTEI opts item = inTagsIndented "item" <$> blocksToTEI opts (map plainToPara item) imageToTEI :: PandocMonad m => WriterOptions -> Attr -> Text -> m (Doc Text) imageToTEI opts attr src = return $ selfClosingTag "graphic" $ ("url", src) : idFromAttr opts attr ++ dims where dims = go Width "width" ++ go Height "height" go dir dstr = case dimension dir attr of Just a -> [(dstr, tshow a)] Nothing -> [] -- | Convert a Pandoc block element to TEI. blockToTEI :: PandocMonad m => WriterOptions -> Block -> m (Doc Text) blockToTEI opts (Div attr@(_,"section":_,_) (Header lvl _ ils : xs)) = do -- TEI doesn't allow sections with no content, so insert some if needed let xs' = if null xs then [Para []] else xs -- level numbering correspond to LaTeX internals divType = case lvl of n | n == -1 -> "part" | n == 0 -> "chapter" | n >= 1 && n <= 5 -> "level" <> tshow n | otherwise -> "section" titleContents <- inlinesToTEI opts ils contents <- blocksToTEI opts xs' return $ inTags True "div" (("type", divType) : idFromAttr opts attr) $ inTagsSimple "head" titleContents $$ contents -- Add ids to paragraphs in divs with ids - this is needed for -- pandoc-citeproc to get link anchors in bibliographies: blockToTEI opts (Div attr [Para lst]) = do let attribs = idFromAttr opts attr inTags False "p" attribs <$> inlinesToTEI opts lst blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs blockToTEI _ h@Header{} = do -- should not occur after makeSections, except inside lists/blockquotes report $ BlockNotRendered h return empty -- For TEI simple, text must be within containing block element, so -- we use treat as Para to ensure that Plain text ends up contained by -- something: blockToTEI opts (Plain lst) = blockToTEI opts $ Para lst blockToTEI opts (Para lst) = inTags False "p" [] <$> inlinesToTEI opts lst blockToTEI opts (LineBlock lns) = blockToTEI opts $ linesToPara lns blockToTEI opts (BlockQuote blocks) = inTagsIndented "quote" <$> blocksToTEI opts blocks blockToTEI opts (CodeBlock (_,classes,_) str) = return $ literal ("<ab type='codeblock " <> lang <> "'>") <> cr <> flush (literal (escapeStringForXML str) <> cr <> text "</ab>") where lang = case langs of [] -> "" (l:_) -> escapeStringForXML l syntaxMap = writerSyntaxMap opts isLang l = T.toLower l `elem` map T.toLower (languages syntaxMap) langsFrom s = if isLang s then [s] else (languagesByExtension syntaxMap) . T.toLower $ s langs = concatMap langsFrom classes blockToTEI opts (BulletList lst) = do let attribs = [("type", "unordered")] inTags True "list" attribs <$> listItemsToTEI opts lst blockToTEI _ (OrderedList _ []) = return empty blockToTEI opts (OrderedList (start, numstyle, _) (first:rest)) = do let attribs = case numstyle of DefaultStyle -> [] Decimal -> [("type", "ordered:arabic")] Example -> [("type", "ordered:arabic")] UpperAlpha -> [("type", "ordered:upperalpha")] LowerAlpha -> [("type", "ordered:loweralpha")] UpperRoman -> [("type", "ordered:upperroman")] LowerRoman -> [("type", "ordered:lowerroman")] items <- if start == 1 then listItemsToTEI opts (first:rest) else do fi <- blocksToTEI opts $ map plainToPara first re <- listItemsToTEI opts rest return $ inTags True "item" [("n",tshow start)] fi $$ re return $ inTags True "list" attribs items blockToTEI opts (DefinitionList lst) = do let attribs = [("type", "definition")] inTags True "list" attribs <$> deflistItemsToTEI opts lst blockToTEI _ b@(RawBlock f str) | f == "tei" = return $ literal str -- raw TEI block (should such a thing exist). | otherwise = do report $ BlockNotRendered b return empty blockToTEI _ HorizontalRule = return $ selfClosingTag "milestone" [("unit","undefined") ,("type","separator") ,("rendition","line")] blockToTEI opts (Figure attr capt bs) = blockToTEI opts (figureDiv attr capt bs) -- TEI Tables -- TEI Simple's tables are composed of cells and rows; other -- table info in the AST is here lossily discard. blockToTEI opts (Table _ blkCapt specs thead tbody tfoot) = do let (_, _, _, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot headers' <- if null headers then pure mempty else tableHeadersToTEI opts headers rows' <- mapM (tableRowToTEI opts) rows return $ inTags True "table" [] $ headers' $$ vcat rows' tableRowToTEI :: PandocMonad m => WriterOptions -> [[Block]] -> m (Doc Text) tableRowToTEI opts cols = inTagsIndented "row" . vcat <$> mapM (tableItemToTEI opts) cols tableHeadersToTEI :: PandocMonad m => WriterOptions -> [[Block]] -> m (Doc Text) tableHeadersToTEI opts cols = inTags True "row" [("role","label")] . vcat <$> mapM (tableItemToTEI opts) cols tableItemToTEI :: PandocMonad m => WriterOptions -> [Block] -> m (Doc Text) tableItemToTEI opts item = inTags False "cell" [] . vcat <$> mapM (blockToTEI opts) item -- | Convert a list of inline elements to TEI. inlinesToTEI :: PandocMonad m => WriterOptions -> [Inline] -> m (Doc Text) inlinesToTEI opts lst = hcat <$> mapM (inlineToTEI opts) lst -- | Convert an inline element to TEI. inlineToTEI :: PandocMonad m => WriterOptions -> Inline -> m (Doc Text) inlineToTEI _ (Str str) = return $ literal $ escapeStringForXML str inlineToTEI opts (Emph lst) = inTags False "hi" [("rendition","simple:italic")] <$> inlinesToTEI opts lst inlineToTEI opts (Underline lst) = inTags False "hi" [("rendition","simple:underline")] <$> inlinesToTEI opts lst inlineToTEI opts (Strong lst) = inTags False "hi" [("rendition", "simple:bold")] <$> inlinesToTEI opts lst inlineToTEI opts (Strikeout lst) = inTags False "hi" [("rendition", "simple:strikethrough")] <$> inlinesToTEI opts lst inlineToTEI opts (Superscript lst) = inTags False "hi" [("rendition", "simple:superscript")] <$> inlinesToTEI opts lst inlineToTEI opts (Subscript lst) = inTags False "hi" [("rendition", "simple:subscript")] <$> inlinesToTEI opts lst inlineToTEI opts (SmallCaps lst) = inTags False "hi" [("rendition", "simple:smallcaps")] <$> inlinesToTEI opts lst inlineToTEI opts (Quoted _ lst) = inTagsSimple "quote" <$> inlinesToTEI opts lst inlineToTEI opts (Cite _ lst) = inlinesToTEI opts lst inlineToTEI opts (Span _ ils) = inlinesToTEI opts ils inlineToTEI _ (Code _ str) = return $ inTags False "seg" [("type","code")] $ literal (escapeStringForXML str) -- Distinguish display from inline math by wrapping the former in a "figure." inlineToTEI _ (Math t str) = return $ case t of InlineMath -> inTags False "formula" [("notation","TeX")] $ literal str DisplayMath -> inTags True "figure" [("type","math")] $ inTags False "formula" [("notation","TeX")] $ literal str inlineToTEI _ il@(RawInline f x) | f == "tei" = return $ literal x | otherwise = empty <$ report (InlineNotRendered il) inlineToTEI _ LineBreak = return $ selfClosingTag "lb" [] inlineToTEI _ Space = return space -- because we use \n for LineBreak, we can't do soft breaks: inlineToTEI _ SoftBreak = return space inlineToTEI opts (Link attr txt (src, _)) | Just email <- T.stripPrefix "mailto:" src = do let emailLink = literal $ escapeStringForXML email case txt of [Str s] | escapeURI s == email -> return emailLink _ -> do linktext <- inlinesToTEI opts txt return $ linktext <+> char '(' <> emailLink <> char ')' | otherwise = inTags False "ref" (("target", src) : idFromAttr opts attr) <$> inlinesToTEI opts txt inlineToTEI opts (Image attr description (src, tit)) = do let titleDoc = if T.null tit then empty else inTags False "figDesc" [] (literal $ escapeStringForXML tit) imageDesc <- if null description then return empty else inTags False "head" [] <$> inlinesToTEI opts description img <- imageToTEI opts attr src return $ inTagsIndented "figure" $ imageDesc $$ img $$ titleDoc inlineToTEI opts (Note contents) = inTagsIndented "note" <$> blocksToTEI opts contents idFromAttr :: WriterOptions -> Attr -> [(Text, Text)] idFromAttr opts (id',_,_) = [("xml:id", writerIdentifierPrefix opts <> id') | not (T.null id')] ================================================ FILE: src/Text/Pandoc/Writers/Texinfo.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.Texinfo Copyright : Copyright (C) 2008-2024 John MacFarlane 2012 Peter Wang License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' format into Texinfo. -} module Text.Pandoc.Writers.Texinfo ( writeTexinfo ) where import Control.Monad (zipWithM, unless) import Control.Monad.Except (throwError) import Control.Monad.State.Strict ( StateT, MonadState(get), gets, modify, evalStateT ) import Data.Char (chr, ord, isAlphaNum) 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 import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) import System.FilePath import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Walk (walkM) import Text.Pandoc.Error import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared import Text.Printf (printf) data WriterState = WriterState { stStrikeout :: Bool -- document contains strikeout , stContext :: Context , stNodes :: M.Map Text Int -- maps node to number of duplicates , stHeadings :: M.Map Text Text -- header ids to node texts , stOptions :: WriterOptions -- writer options } data Context = NormalContext | NodeContext deriving (Eq, Show) withContext :: PandocMonad m => Context -> TI m a -> TI m a withContext context pa = do oldContext <- gets stContext modify $ \s -> s{ stContext = context } res <- pa modify $ \s -> s{ stContext = oldContext } pure res disallowedInNode :: Char -> Bool disallowedInNode c = c `elem` ['.',':',',','(',')'] {- TODO: - internal cross references a la HTML - generated .texi files don't work when run through texi2dvi -} type TI m = StateT WriterState m -- | Convert Pandoc to Texinfo. writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTexinfo options document = evalStateT (pandocToTexinfo options $ wrapTop document) WriterState { stStrikeout = False, stContext = NormalContext, stNodes = mempty, stHeadings = mempty, stOptions = options} -- | Add a "Top" node around the document, needed by Texinfo. wrapTop :: Pandoc -> Pandoc wrapTop (Pandoc meta blocks) = Pandoc meta (Header 0 nullAttr (docTitle meta) : blocks) addNodeText :: PandocMonad m => Block -> TI m Block addNodeText (Header lev (ident,_,_) ils) | lev >= 1 && lev <= 4 = do node <- render Nothing <$> withContext NodeContext (inlineListToTexinfo ils) nodes <- gets stNodes node' <- case M.lookup node nodes of Just i -> do modify $ \st -> st{ stNodes = M.adjust (+ 1) node nodes } pure $ node <> " " <> tshow (i + 1) Nothing -> do modify $ \st -> st{ stNodes = M.insert node 1 nodes } pure node unless (T.null ident) $ modify $ \st -> st{ stHeadings = M.insert ident node' (stHeadings st) } pure $ Header lev (ident,[],[("node", node')]) ils addNodeText x = pure x pandocToTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> TI m Text pandocToTexinfo options (Pandoc meta blocks') = do blocks <- walkM addNodeText blocks' let titlePage = not $ all null $ docTitle meta : docDate meta : docAuthors meta let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing metadata <- metaToContext options blockListToTexinfo (fmap chomp .inlineListToTexinfo) meta body <- blockListToTexinfo blocks st <- get let context = defField "body" body $ defField "toc" (writerTableOfContents options) $ defField "titlepage" titlePage $ defField "strikeout" (stStrikeout st) metadata return $ render colwidth $ case writerTemplate options of Nothing -> body Just tpl -> renderTemplate tpl context -- | Escape things as needed for Texinfo. stringToTexinfo :: PandocMonad m => Text -> TI m Text stringToTexinfo t | T.all isAlphaNum t = pure t | otherwise = do context <- gets stContext let escChar '{' = "@{" escChar '}' = "@}" escChar '@' = "@@" escChar '\160' = "@ " escChar '\x2014' = "---" escChar '\x2013' = "--" escChar '\x2026' = "@dots{}" escChar '\x2019' = "'" escChar ',' | context == NodeContext = "" escChar ':' | context == NodeContext = "" escChar '.' | context == NodeContext = "" escChar '(' | context == NodeContext = "" escChar ')' | context == NodeContext = "" escChar c = T.singleton c pure $ T.concatMap escChar t -- | Puts contents into Texinfo command. 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 -> TI m (Doc Text) blockToTexinfo (Div _ bs) = blockListToTexinfo bs blockToTexinfo (Plain lst) = inlineListToTexinfo lst -- 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 blockToTexinfo (BlockQuote lst) = do contents <- blockListToTexinfo lst return $ text "@quotation" $$ contents $$ text "@end quotation" blockToTexinfo (CodeBlock _ str) = return $ blankline $$ text "@verbatim" $$ flush (literal str) $$ text "@end verbatim" <> blankline blockToTexinfo b@(RawBlock f str) | f == "texinfo" = return $ literal str | f == "latex" || f == "tex" = return $ text "@tex" $$ literal str $$ text "@end tex" | otherwise = do report $ BlockNotRendered b return empty blockToTexinfo (BulletList lst) = do items <- mapM listItemToTexinfo lst return $ text "@itemize" $$ vcat items $$ text "@end itemize" <> blankline blockToTexinfo (OrderedList (start, numstyle, _) lst) = do items <- mapM listItemToTexinfo lst return $ text "@enumerate " <> exemplar $$ vcat items $$ text "@end enumerate" <> blankline where exemplar = case numstyle of DefaultStyle -> decimal Decimal -> decimal Example -> decimal UpperRoman -> decimal -- Roman numerals not supported LowerRoman -> decimal UpperAlpha -> upperAlpha LowerAlpha -> lowerAlpha decimal = if start == 1 then empty else text (show start) upperAlpha = text [chr $ ord 'A' + start - 1] lowerAlpha = text [chr $ ord 'a' + start - 1] blockToTexinfo (DefinitionList lst) = do items <- mapM defListItemToTexinfo lst return $ text "@table @asis" $$ vcat items $$ text "@end table" <> blankline blockToTexinfo HorizontalRule = -- XXX can't get the equivalent from LaTeX.hs to work return $ text "@iftex" $$ text "@bigskip@hrule@bigskip" $$ text "@end iftex" $$ text "@ifnottex" $$ text (replicate 72 '-') $$ text "@end ifnottex" blockToTexinfo (Header 0 _ lst) = do txt <- if null lst then return $ text "Top" else inlineListToTexinfo lst return $ text "@node Top" $$ text "@top " <> txt <> blankline blockToTexinfo (Header level (_,_,[("node",node)]) lst) = do txt <- inlineListToTexinfo lst sec <- seccmd level return $ if (level > 0) && (level <= 4) then blankline <> text "@node " <> literal node $$ literal sec <> txt else txt where seccmd :: PandocMonad m => Int -> TI m Text seccmd 1 = return "@chapter " seccmd 2 = return "@section " seccmd 3 = return "@subsection " seccmd 4 = return "@subsubsection " seccmd _ = throwError $ PandocSomeError "illegal seccmd level" -- non-node header: blockToTexinfo (Header _ _ lst) = blockToTexinfo (Para lst) blockToTexinfo (Table _ blkCapt specs thead tbody tfoot) = do let (caption, aligns, widths, heads, rows) = toLegacyTable blkCapt specs thead tbody tfoot headers <- if all null heads then return empty else tableHeadToTexinfo aligns heads captionText <- inlineListToTexinfo caption rowsText <- mapM (tableRowToTexinfo aligns) rows colDescriptors <- if all (== 0) widths then do -- use longest entry instead of column widths cols <- mapM (mapM (fmap (T.unpack . render Nothing . hcat) . mapM blockToTexinfo)) $ transpose $ heads : rows return $ concatMap ((\x -> "{"++x++"} ") . maybe "" (maximumBy (comparing length)) . nonEmpty) cols else return $ "@columnfractions " ++ concatMap (printf "%.2f ") widths let tableBody = text ("@multitable " ++ colDescriptors) $$ headers $$ vcat rowsText $$ text "@end multitable" return $ if isEmpty captionText then tableBody <> blankline else text "@float Table" $$ tableBody $$ inCmd "caption" captionText $$ text "@end float" blockToTexinfo (Figure _ caption [SimpleFigure attr figCaption tgt]) = do let capt = if null figCaption then let (Caption _ cblks) = caption in blocksToInlines cblks else figCaption captionText <- if null capt then return empty else (text "@caption" <>) . braces <$> inlineListToTexinfo capt img <- inlineToTexinfo (Image attr figCaption tgt) return $ text "@float Figure" $$ img $$ captionText $$ text "@end float" blockToTexinfo (Figure _ fCaption [ Table attr tCaption@(Caption _ cbody) specs thead tbody tfoot]) = do let caption = case cbody of [] -> fCaption _ -> tCaption blockToTexinfo (Table attr caption specs thead tbody tfoot) blockToTexinfo (Figure _ (Caption _ caption) body) = do captionText <- inlineListToTexinfo $ blocksToInlines caption content <- blockListToTexinfo body return $ text ("@float" ++ floatType body) $$ content $$ ( if isEmpty captionText then empty else inCmd "caption" captionText ) $$ text "@end float" where -- floatType according to -- https://www.gnu.org/software/texinfo/manual/texinfo/html_node/_0040float.html floatType [SimpleFigure {}] = " Figure" floatType [Table {}] = " Table" floatType _ = "" tableHeadToTexinfo :: PandocMonad m => [Alignment] -> [[Block]] -> TI m (Doc Text) tableHeadToTexinfo = tableAnyRowToTexinfo "@headitem " tableRowToTexinfo :: PandocMonad m => [Alignment] -> [[Block]] -> TI m (Doc Text) tableRowToTexinfo = tableAnyRowToTexinfo "@item " tableAnyRowToTexinfo :: PandocMonad m => Text -> [Alignment] -> [[Block]] -> TI m (Doc Text) tableAnyRowToTexinfo itemtype aligns cols = (literal itemtype $$) . L.foldl' (\row item -> row $$ (if isEmpty row then empty else text " @tab ") <> item) empty <$> zipWithM alignedBlock aligns cols alignedBlock :: PandocMonad m => Alignment -> [Block] -> TI m (Doc Text) -- XXX @flushleft and @flushright text won't get word wrapped. Since word -- wrapping is more important than alignment, we ignore the alignment. alignedBlock _ = blockListToTexinfo {- alignedBlock AlignLeft col = do b <- blockListToTexinfo col return $ text "@flushleft" $$ b $$ text "@end flushleft" alignedBlock AlignRight col = do b <- blockListToTexinfo col return $ text "@flushright" $$ b $$ text "@end flushright" alignedBlock _ col = blockListToTexinfo col -} -- | Convert Pandoc block elements to Texinfo. blockListToTexinfo :: PandocMonad m => [Block] -> TI m (Doc Text) blockListToTexinfo [] = return empty blockListToTexinfo (x:xs) = do x' <- blockToTexinfo x case x of Header level _ _ -> do -- We need need to insert a menu for this node. let (before, after) = break isHeaderBlock xs before' <- blockListToTexinfo before let menu = if level < 4 then collectNodes (level + 1) after else [] lines' <- mapM makeMenuLine menu let menu' = if null lines' then empty else blankline $$ text "@menu" $$ vcat lines' $$ text "@end menu" after' <- blockListToTexinfo after return $ x' $$ before' $$ menu' $$ after' Para _ -> do xs' <- blockListToTexinfo xs case xs of (CodeBlock _ _:_) -> return $ x' $$ xs' _ -> return $ x' $+$ xs' _ -> do xs' <- blockListToTexinfo xs return $ x' $$ xs' collectNodes :: Int -> [Block] -> [Block] collectNodes _ [] = [] collectNodes level (x:xs) = case x of (Header hl _ _) | hl < level -> [] | hl == level -> x : collectNodes level xs | otherwise -> collectNodes level xs _ -> collectNodes level xs makeMenuLine :: PandocMonad m => Block -> TI m (Doc Text) makeMenuLine (Header _ (_,_,[("node", node)]) lst) = do txt <- withContext NodeContext $ inlineListToTexinfo lst pure $ nowrap $ text "* " <> if render Nothing txt == node then literal node <> "::" else txt <> ": " <> literal node <> "." makeMenuLine _ = throwError $ PandocSomeError "makeMenuLine called with non-node" listItemToTexinfo :: PandocMonad m => [Block] -> TI m (Doc Text) listItemToTexinfo lst = do contents <- blockListToTexinfo lst let spacer = case reverse lst of (Para{}:_) -> blankline _ -> empty return $ text "@item" $$ contents <> spacer defListItemToTexinfo :: PandocMonad m => ([Inline], [[Block]]) -> TI m (Doc Text) defListItemToTexinfo (term, defs) = do term' <- inlineListToTexinfo term let defToTexinfo bs = do d <- blockListToTexinfo bs case reverse bs of (Para{}:_) -> return $ d <> blankline _ -> return d defs' <- mapM defToTexinfo defs return $ text "@item " <> term' $+$ vcat defs' -- | Convert list of inline elements to Texinfo. inlineListToTexinfo :: PandocMonad m => [Inline] -- ^ Inlines to convert -> TI m (Doc Text) inlineListToTexinfo lst = hcat <$> mapM inlineToTexinfo lst -- | Convert inline element to Texinfo inlineToTexinfo :: PandocMonad m => Inline -- ^ Inline to convert -> TI m (Doc Text) inlineToTexinfo (Span _ lst) = inlineListToTexinfo lst inlineToTexinfo (Emph lst) = inCmd "emph" <$> inlineListToTexinfo lst -- Underline isn't supported, fall back to Emph inlineToTexinfo (Underline lst) = inlineToTexinfo (Emph lst) inlineToTexinfo (Strong lst) = inCmd "strong" <$> inlineListToTexinfo lst inlineToTexinfo (Strikeout lst) = do modify $ \st -> st{ stStrikeout = True } contents <- inlineListToTexinfo lst return $ text "@textstrikeout{" <> contents <> text "}" inlineToTexinfo (Superscript lst) = do contents <- inlineListToTexinfo lst return $ text "@sup{" <> contents <> char '}' inlineToTexinfo (Subscript lst) = do contents <- inlineListToTexinfo lst return $ text "@sub{" <> contents <> char '}' inlineToTexinfo (SmallCaps lst) = inCmd "sc" <$> inlineListToTexinfo lst inlineToTexinfo (Code (_, cls , _) str) | T.pack "variable" `elem` cls = do code <- stringToTexinfo str return $ literal $ "@code{@var{" <> code <> "}}" inlineToTexinfo (Code _ str) = do code <- stringToTexinfo str return $ literal $ "@code{" <> code <> "}" inlineToTexinfo (Quoted SingleQuote lst) = do contents <- inlineListToTexinfo lst return $ char '`' <> contents <> char '\'' inlineToTexinfo (Quoted DoubleQuote lst) = do contents <- inlineListToTexinfo lst return $ text "``" <> contents <> text "''" inlineToTexinfo (Cite _ lst) = inlineListToTexinfo lst inlineToTexinfo (Str str) = literal <$> stringToTexinfo str inlineToTexinfo (Math _ str) = return $ inCmd "math" $ literal str inlineToTexinfo il@(RawInline f str) | f == "latex" || f == "tex" = return $ text "@tex" $$ literal str $$ text "@end tex" | f == "texinfo" = return $ literal str | otherwise = do report $ InlineNotRendered il return empty inlineToTexinfo LineBreak = return $ text "@*" <> cr inlineToTexinfo SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of WrapAuto -> return space WrapNone -> return space WrapPreserve -> return cr inlineToTexinfo Space = return space inlineToTexinfo (Link _ txt (src, _)) | Just ('#', ident) <- T.uncons src = do headings <- gets stHeadings target <- case M.lookup ident headings of Nothing -> literal <$> stringToTexinfo (T.filter (not . disallowedInNode) src) Just node -> pure $ literal node contents <- withContext NodeContext $ inlineListToTexinfo txt return $ text "@ref" <> braces (target <> if contents == target then mempty else text ",," <> contents) | otherwise = case txt of [Str x] | escapeURI x == src -> -- autolink return $ literal $ "@url{" <> x <> "}" _ -> do contents <- withContext NodeContext $ inlineListToTexinfo txt src1 <- stringToTexinfo src return $ literal ("@uref{" <> src1 <> ",") <> contents <> char '}' inlineToTexinfo (Image attr alternate (source, _)) = do content <- withContext NodeContext $ inlineListToTexinfo alternate opts <- gets stOptions let showDim dim = case dimension dim attr of (Just (Pixel a)) -> showInInch opts (Pixel a) <> "in" (Just (Percent _)) -> "" (Just d) -> tshow d Nothing -> "" return $ literal ("@image{" <> base <> "," <> showDim Width <> "," <> showDim Height <> ",") <> content <> text "," <> literal (ext <> "}") where ext = T.drop 1 $ T.pack $ takeExtension source' base = T.pack $ dropExtension source' source' = if isURI source then T.unpack source else unEscapeString $ T.unpack source inlineToTexinfo (Note contents) = do contents' <- blockListToTexinfo contents return $ text "@footnote" <> braces contents' ================================================ FILE: src/Text/Pandoc/Writers/Textile.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.Textile Copyright : Copyright (C) 2010-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to Textile markup. Textile: <http://thresholdstate.com/articles/4312/the-textile-reference-manual> -} module Text.Pandoc.Writers.Textile ( writeTextile ) where import Control.Monad (zipWithM, liftM) import Control.Monad.State.Strict ( StateT, gets, modify, evalStateT ) import Data.Char (isSpace) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.DocLayout (render, literal) import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared import Text.Pandoc.XML (escapeStringForXML) data WriterState = WriterState { stNotes :: [Text] -- Footnotes , stListLevel :: [Char] -- String at beginning of list items, e.g. "**" , stStartNum :: Maybe Int -- Start number if first list item , stUseTags :: Bool -- True if we should use HTML tags because we're in a complex list } type TW = StateT WriterState -- | Convert Pandoc to Textile. writeTextile :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTextile opts document = evalStateT (pandocToTextile opts document) WriterState { stNotes = [], stListLevel = [], stStartNum = Nothing, stUseTags = False } -- | Return Textile representation of document. pandocToTextile :: PandocMonad m => WriterOptions -> Pandoc -> TW m Text pandocToTextile opts (Pandoc meta blocks) = do metadata <- metaToContext opts (fmap literal . blockListToTextile opts) (fmap literal . inlineListToTextile opts) meta body <- blockListToTextile opts blocks notes <- gets $ T.unlines . reverse . stNotes let main = body <> if T.null notes then "" else "\n\n" <> notes let context = defField "body" main metadata return $ case writerTemplate opts of Nothing -> main Just tpl -> render Nothing $ renderTemplate tpl context withUseTags :: PandocMonad m => TW m a -> TW m a withUseTags action = do oldUseTags <- gets stUseTags modify $ \s -> s { stUseTags = True } result <- action modify $ \s -> s { stUseTags = oldUseTags } return result -- | Escape one character as needed for Textile. escapeCharForTextile :: Char -> Text escapeCharForTextile x = case x of '&' -> "&" '<' -> "<" '>' -> ">" '"' -> """ '*' -> "*" '_' -> "_" '@' -> "@" '+' -> "+" '-' -> "-" '|' -> "|" '\x2014' -> " -- " '\x2013' -> " - " '\x2019' -> "'" '\x2026' -> "..." c -> T.singleton c -- | Escape string as needed for Textile. escapeTextForTextile :: Text -> Text escapeTextForTextile = T.concatMap escapeCharForTextile -- | Convert Pandoc block element to Textile. blockToTextile :: PandocMonad m => WriterOptions -- ^ Options -> Block -- ^ Block element -> TW m Text blockToTextile opts (Div attr bs) = do let startTag = render Nothing $ tagWithAttrs "div" attr let endTag = "</div>" contents <- blockListToTextile opts bs return $ startTag <> "\n\n" <> contents <> "\n\n" <> endTag <> "\n" blockToTextile opts (Plain inlines) = inlineListToTextile opts inlines blockToTextile opts (Para inlines) = do useTags <- gets stUseTags listLevel <- gets stListLevel contents <- inlineListToTextile opts inlines return $ if useTags then "<p>" <> contents <> "</p>" else contents <> if null listLevel then "\n" else "" blockToTextile opts (LineBlock lns) = blockToTextile opts $ linesToPara lns blockToTextile _ b@(RawBlock f str) | f == Format "html" || f == Format "textile" = return str | otherwise = do report $ BlockNotRendered b return "" blockToTextile _ HorizontalRule = return "<hr />\n" blockToTextile opts (Header level (ident,classes,keyvals) inlines) = do contents <- inlineListToTextile opts inlines let identAttr = if T.null ident then "" else "#" <> ident let attribs = if T.null identAttr && null classes then "" else "(" <> T.unwords classes <> identAttr <> ")" let lang = maybe "" (\x -> "[" <> x <> "]") $ lookup "lang" keyvals let styles = maybe "" (\x -> "{" <> x <> "}") $ lookup "style" keyvals let prefix = "h" <> tshow level <> attribs <> styles <> lang <> ". " return $ prefix <> contents <> "\n" blockToTextile _ (CodeBlock (_,classes,_) str) | any (T.all isSpace) (T.lines str) = return $ "<pre" <> classes' <> ">\n" <> escapeStringForXML str <> "\n</pre>\n" where classes' = if null classes then "" else " class=\"" <> T.unwords classes <> "\"" blockToTextile _ (CodeBlock (_,classes,_) str) = return $ "bc" <> classes' <> ". " <> str <> "\n\n" where classes' = if null classes then "" else "(" <> T.unwords classes <> ")" blockToTextile opts (BlockQuote bs@[Para _]) = do contents <- blockListToTextile opts bs return $ "bq. " <> contents <> "\n\n" blockToTextile opts (BlockQuote blocks) = do contents <- blockListToTextile opts blocks return $ "<blockquote>\n\n" <> contents <> "\n</blockquote>\n" blockToTextile opts (Table _ blkCapt specs thead tbody tfoot) = case toLegacyTable blkCapt specs thead tbody tfoot of ([], aligns, widths, headers, rows') | all (==0) widths -> do hs <- mapM (liftM (("_. " <>) . stripTrailingNewlines) . blockListToTextile opts) headers let cellsToRow cells = "|" <> T.intercalate "|" cells <> "|" let header = if all null headers then "" else cellsToRow hs <> "\n" let blocksToCell (align, bs) = do contents <- stripTrailingNewlines <$> blockListToTextile opts bs let alignMarker = case align of AlignLeft -> "<. " AlignRight -> ">. " AlignCenter -> "=. " AlignDefault -> "" return $ alignMarker <> contents let rowToCells = mapM blocksToCell . zip aligns bs <- mapM rowToCells rows' let body = T.unlines $ map cellsToRow bs return $ header <> body (capt, aligns, widths, headers, rows') -> do let alignStrings = map alignmentToText aligns captionDoc <- if null capt then return "" else do c <- inlineListToTextile opts capt return $ "<caption>" <> c <> "</caption>\n" let percent w = tshow (truncate (100*w) :: Integer) <> "%" let coltags = if all (== 0.0) widths then "" else T.unlines $ map (\w -> "<col width=\"" <> percent w <> "\" />") widths head' <- if all null headers then return "" else do hs <- tableRowToTextile opts alignStrings 0 headers return $ "<thead>\n" <> hs <> "\n</thead>\n" body' <- zipWithM (tableRowToTextile opts alignStrings) [1..] rows' return $ "<table>\n" <> captionDoc <> coltags <> head' <> "<tbody>\n" <> T.unlines body' <> "</tbody>\n</table>\n" blockToTextile opts x@(BulletList items) = do oldUseTags <- gets stUseTags let useTags = oldUseTags || not (isSimpleList x) if useTags then do contents <- withUseTags $ mapM (listItemToTextile opts) items return $ "<ul>\n" <> vcat contents <> "\n</ul>\n" else do modify $ \s -> s { stListLevel = stListLevel s <> "*" } level <- gets $ length . stListLevel contents <- mapM (listItemToTextile opts) items modify $ \s -> s { stListLevel = init (stListLevel s) } return $ vcat contents <> (if level > 1 then "" else "\n") blockToTextile opts x@(OrderedList attribs@(start, _, _) items) = do oldUseTags <- gets stUseTags let useTags = oldUseTags || not (isSimpleList x) if useTags then do contents <- withUseTags $ mapM (listItemToTextile opts) items return $ "<ol" <> listAttribsToString attribs <> ">\n" <> vcat contents <> "\n</ol>\n" else do modify $ \s -> s { stListLevel = stListLevel s <> "#" , stStartNum = if start > 1 then Just start else Nothing } level <- gets $ length . stListLevel contents <- mapM (listItemToTextile opts) items modify $ \s -> s { stListLevel = init (stListLevel s), stStartNum = Nothing } return $ vcat contents <> (if level > 1 then "" else "\n") blockToTextile opts (DefinitionList items) = do contents <- withUseTags $ mapM (definitionListItemToTextile opts) items return $ "<dl>\n" <> vcat contents <> "\n</dl>\n" blockToTextile opts (Figure attr (Caption _ caption) body) = do let startTag = render Nothing $ tagWithAttrs "figure" attr let endTag = "</figure>" let captionInlines = blocksToInlines caption captionMarkup <- if null captionInlines then return "" else ((<> "\n\n</figcaption>\n\n") . ("<figcaption>\n\n" <>)) <$> inlineListToTextile opts (blocksToInlines caption) contents <- blockListToTextile opts body return $ startTag <> "\n\n" <> captionMarkup <> contents <> "\n\n" <> endTag <> "\n" -- Auxiliary functions for lists: -- | Convert ordered list attributes to HTML attribute string listAttribsToString :: ListAttributes -> Text listAttribsToString (startnum, numstyle, _) = let numstyle' = camelCaseToHyphenated $ tshow numstyle in (if startnum /= 1 then " start=\"" <> tshow startnum <> "\"" else "") <> (if numstyle /= DefaultStyle then " style=\"list-style-type: " <> numstyle' <> ";\"" else "") -- | Convert bullet or ordered list item (list of blocks) to Textile. listItemToTextile :: PandocMonad m => WriterOptions -> [Block] -> TW m Text listItemToTextile opts items = do contents <- blockListToTextile opts items useTags <- gets stUseTags if useTags then return $ "<li>" <> contents <> "</li>" else do marker <- gets stListLevel mbstart <- gets stStartNum case mbstart of Just n -> do modify $ \s -> s{ stStartNum = Nothing } return $ T.pack marker <> tshow n <> " " <> contents Nothing -> return $ T.pack marker <> " " <> contents -- | Convert definition list item (label, list of blocks) to Textile. definitionListItemToTextile :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> TW m Text definitionListItemToTextile opts (label, items) = do labelText <- inlineListToTextile opts label contents <- mapM (blockListToTextile opts) items return $ "<dt>" <> labelText <> "</dt>\n" <> T.intercalate "\n" (map (\d -> "<dd>" <> d <> "</dd>") contents) -- | True if the list can be handled by simple wiki markup, False if HTML tags will be needed. isSimpleList :: Block -> Bool isSimpleList x = case x of BulletList items -> all isSimpleListItem items OrderedList (_, sty, _) items -> all isSimpleListItem items && sty `elem` [DefaultStyle, Decimal] _ -> False -- | True if list item can be handled with the simple wiki syntax. False if -- HTML tags will be needed. isSimpleListItem :: [Block] -> Bool isSimpleListItem [] = True isSimpleListItem [x] = case x of Plain _ -> True Para _ -> True BulletList _ -> isSimpleList x OrderedList _ _ -> isSimpleList x _ -> False isSimpleListItem [x, y] | isPlainOrPara x = case y of BulletList _ -> isSimpleList y OrderedList _ _ -> isSimpleList y _ -> False isSimpleListItem _ = False isPlainOrPara :: Block -> Bool isPlainOrPara (Plain _) = True isPlainOrPara (Para _) = True isPlainOrPara _ = False -- | Concatenates strings with line breaks between them. vcat :: [Text] -> Text vcat = T.intercalate "\n" -- Auxiliary functions for tables. (TODO: these are common to HTML, MediaWiki, -- and Textile writers, and should be abstracted out.) tableRowToTextile :: PandocMonad m => WriterOptions -> [Text] -> Int -> [[Block]] -> TW m Text tableRowToTextile opts alignStrings rownum cols' = do let celltype = if rownum == 0 then "th" else "td" cols'' <- zipWithM (\alignment item -> tableItemToTextile opts celltype alignment item) alignStrings cols' return $ "<tr>\n" <> T.unlines cols'' <> "</tr>" alignmentToText :: Alignment -> Text alignmentToText alignment = case alignment of AlignLeft -> "left" AlignRight -> "right" AlignCenter -> "center" AlignDefault -> "left" tableItemToTextile :: PandocMonad m => WriterOptions -> Text -> Text -> [Block] -> TW m Text tableItemToTextile opts celltype align' item = do let mkcell x = "<" <> celltype <> " align=\"" <> align' <> "\">" <> x <> "</" <> celltype <> ">" contents <- blockListToTextile opts item return $ mkcell contents -- | Convert list of Pandoc block elements to Textile. blockListToTextile :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements -> TW m Text blockListToTextile opts blocks = vcat <$> mapM (blockToTextile opts) blocks -- | Convert list of Pandoc inline elements to Textile. inlineListToTextile :: PandocMonad m => WriterOptions -> [Inline] -> TW m Text inlineListToTextile opts lst = T.concat <$> mapM (inlineToTextile opts) lst -- | Convert Pandoc inline element to Textile. inlineToTextile :: PandocMonad m => WriterOptions -> Inline -> TW m Text inlineToTextile opts (Span _ lst) = inlineListToTextile opts lst inlineToTextile opts (Emph lst) = do contents <- inlineListToTextile opts lst return $ if T.any (== '_') contents then "<em>" <> contents <> "</em>" else "_" <> contents <> "_" inlineToTextile opts (Underline lst) = do contents <- inlineListToTextile opts lst return $ if T.any (== '+') contents then "<u>" <> contents <> "</u>" else "+" <> contents <> "+" inlineToTextile opts (Strong lst) = do contents <- inlineListToTextile opts lst return $ if T.any (== '*') contents then "<strong>" <> contents <> "</strong>" else "*" <> contents <> "*" inlineToTextile opts (Strikeout lst) = do contents <- inlineListToTextile opts lst return $ if T.any (== '-') contents then "<del>" <> contents <> "</del>" else "-" <> contents <> "-" inlineToTextile opts (Superscript lst) = do contents <- inlineListToTextile opts lst return $ if T.any (== '^') contents then "<sup>" <> contents <> "</sup>" else "[^" <> contents <> "^]" inlineToTextile opts (Subscript lst) = do contents <- inlineListToTextile opts lst return $ if T.any (== '~') contents then "<sub>" <> contents <> "</sub>" else "[~" <> contents <> "~]" inlineToTextile opts (SmallCaps lst) = inlineListToTextile opts lst inlineToTextile opts (Quoted SingleQuote lst) = do contents <- inlineListToTextile opts lst return $ "'" <> contents <> "'" inlineToTextile opts (Quoted DoubleQuote lst) = do contents <- inlineListToTextile opts lst return $ "\"" <> contents <> "\"" inlineToTextile opts (Cite _ lst) = inlineListToTextile opts lst inlineToTextile _ (Code _ str) = return $ if T.any (== '@') str then "<tt>" <> escapeStringForXML str <> "</tt>" else "@" <> str <> "@" inlineToTextile _ (Str str) = return $ escapeTextForTextile str inlineToTextile _ (Math _ str) = return $ "<span class=\"math\">" <> escapeStringForXML str <> "</span>" inlineToTextile opts il@(RawInline f str) | f == Format "html" || f == Format "textile" = return str | (f == Format "latex" || f == Format "tex") && isEnabled Ext_raw_tex opts = return str | otherwise = do report $ InlineNotRendered il return "" inlineToTextile _ LineBreak = return "\n" inlineToTextile _ SoftBreak = return " " inlineToTextile _ Space = return " " inlineToTextile opts (Link (_, cls, _) txt (src, _)) = do label <- case txt of [Code _ s] | s == src -> return "$" [Str s] | s == src -> return "$" _ -> inlineListToTextile opts txt let classes = if null cls || cls == ["uri"] && label == "$" then "" else "(" <> T.unwords cls <> ")" return $ "\"" <> classes <> label <> "\":" <> src inlineToTextile opts (Image attr@(_, cls, _) alt (source, tit)) = do alt' <- inlineListToTextile opts alt let txt = if T.null tit then if T.null alt' then "" else "(" <> alt' <> ")" else "(" <> tit <> ")" classes = if null cls then "" else "(" <> T.unwords cls <> ")" showDim dir = let toCss str = Just $ tshow dir <> ":" <> str <> ";" in case dimension dir attr of Just (Percent a) -> toCss $ tshow (Percent a) Just dim -> toCss $ showInPixel opts dim <> "px" Nothing -> Nothing styles = case (showDim Width, showDim Height) of (Just w, Just h) -> "{" <> w <> h <> "}" (Just w, Nothing) -> "{" <> w <> "height:auto;}" (Nothing, Just h) -> "{" <> "width:auto;" <> h <> "}" (Nothing, Nothing) -> "" return $ "!" <> classes <> styles <> source <> txt <> "!" inlineToTextile opts (Note contents) = do curNotes <- gets stNotes let newnum = length curNotes + 1 contents' <- blockListToTextile opts contents let thisnote = "fn" <> tshow newnum <> ". " <> contents' <> "\n" modify $ \s -> s { stNotes = thisnote : curNotes } return $ "[" <> tshow newnum <> "]" -- note - may not work for notes with multiple blocks ================================================ FILE: src/Text/Pandoc/Writers/Typst.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.Typst Copyright : Copyright (C) 2023 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Conversion of 'Pandoc' format into Typst markup (<https://typst.app>). -} module Text.Pandoc.Writers.Typst ( writeTypst ) where import Text.Pandoc.Definition import Text.Pandoc.Class ( PandocMonad, report, runPure, fetchItem ) import Text.Pandoc.ImageSize ( dimension, Dimension(Pixel), Direction(..), showInInch ) import Text.Pandoc.Options ( WriterOptions(..), WrapOption(..), isEnabled, CaptionPosition(..), HighlightMethod(..) ) import Data.Text (Text) import Data.List (intercalate, intersperse) import Data.Bifunctor (first, second) import Network.URI (unEscapeString) import qualified Data.Text as T import Control.Monad (unless) import Control.Monad.State ( StateT, evalStateT, gets, modify ) import Text.Pandoc.Writers.Shared ( lookupMetaInlines, lookupMetaString, metaToContext, defField, resetField, setupTranslations ) import Text.Pandoc.Shared (isTightList, orderedListMarkers, tshow, stringify) import Text.Pandoc.Highlighting (highlight, formatTypstBlock, formatTypstInline, styleToTypst) import Text.Pandoc.Translations (Term(Abstract), translateTerm) import Text.Pandoc.Error (PandocError(PandocSomeError)) import Text.Pandoc.Walk (query) import Text.Pandoc.Writers.Math (convertMath) import qualified Text.TeXMath as TM import Text.DocLayout import Text.DocTemplates (renderTemplate) import Text.Pandoc.Extensions (Extension(..)) import Text.Pandoc.Logging (LogMessage(..)) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Collate.Lang (Lang(..), parseLang) import Text.Printf (printf) import Data.Char (isDigit) import Data.Maybe (fromMaybe) import Unicode.Char (isXIDContinue) import qualified Data.ByteString as B -- | Convert Pandoc to Typst. writeTypst :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTypst options document = evalStateT (pandocToTypst options document) WriterState{ stOptions = options, stEscapeContext = NormalContext, stHighlighting = False } data EscapeContext = NormalContext | TermContext deriving (Show, Eq) data WriterState = WriterState { stOptions :: WriterOptions, stEscapeContext :: EscapeContext, stHighlighting :: Bool } type TW m = StateT WriterState m pandocToTypst :: PandocMonad m => WriterOptions -> Pandoc -> TW m Text pandocToTypst options (Pandoc meta blocks) = do let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing setupTranslations meta metadata <- metaToContext options blocksToTypst (fmap chomp . inlinesToTypst) meta main <- blocksToTypst blocks abstractTitle <- translateTerm Abstract let toPosition :: CaptionPosition -> Text toPosition CaptionAbove = "top" toPosition CaptionBelow = "bottom" let nociteIds = query (\case Cite cs _ -> map citationId cs _ -> []) $ lookupMetaInlines "nocite" meta hasHighlighting <- gets stHighlighting let context = defField "body" main $ defField "toc" (writerTableOfContents options) $ (if isEnabled Ext_citations options then defField "citations" True . defField "nocite-ids" (filter (/= "*") nociteIds) . defField "full-bibliography" ("*" `elem` nociteIds) else id) $ (case lookupMetaString "lang" meta of "" -> id lang -> case parseLang lang of Left _ -> id Right l -> resetField "lang" (langLanguage l) . maybe id (resetField "region") (langRegion l)) $ defField "csl" (lookupMetaString "citation-style" meta) -- #10661 $ defField "smart" (isEnabled Ext_smart options) $ defField "abstract-title" abstractTitle $ defField "toc-depth" (tshow $ writerTOCDepth options) $ (if hasHighlighting then case writerHighlightMethod options of Skylighting sty -> defField "highlighting-definitions" (T.stripEnd $ styleToTypst sty) _ -> id else id) $ defField "figure-caption-position" (toPosition $ writerFigureCaptionPosition options) $ defField "table-caption-position" (toPosition $ writerTableCaptionPosition options) $ defField "page-numbering" ("1" :: Text) $ (if writerNumberSections options then defField "section-numbering" ("1.1.1.1.1" :: Text) else id) metadata return $ render colwidth $ case writerTemplate options of Nothing -> main Just tpl -> renderTemplate tpl context pickTypstAttrs :: [(Text, Text)] -> ([(Text, Text)],[(Text, Text)]) pickTypstAttrs = foldr go ([],[]) where go (k,v) = case T.splitOn ":" k of ["typst", "text", x] -> second ((x,v):) ["typst", x] -> first ((x,v):) _ -> id formatTypstProp :: (Text, Text) -> Text formatTypstProp (k,v) = k <> ": " <> v toTypstPropsListSep :: [(Text, Text)] -> Doc Text toTypstPropsListSep = hsep . intersperse "," . map (literal . formatTypstProp) toTypstPropsListTerm :: [(Text, Text)] -> Doc Text toTypstPropsListTerm [] = "" toTypstPropsListTerm typstAttrs = toTypstPropsListSep typstAttrs <> "," toTypstPropsListParens :: [(Text, Text)] -> Doc Text toTypstPropsListParens [] = "" toTypstPropsListParens typstAttrs = parens $ toTypstPropsListSep typstAttrs toTypstTextElement :: [(Text, Text)] -> Doc Text -> Doc Text toTypstTextElement [] content = content toTypstTextElement typstTextAttrs content = "#text" <> toTypstPropsListParens typstTextAttrs <> brackets content toTypstSetText :: [(Text, Text)] -> Doc Text toTypstSetText [] = "" toTypstSetText typstTextAttrs = "set text" <> parens (toTypstPropsListSep typstTextAttrs) <> "; " toTypstPoundSetText :: [(Text, Text)] -> Doc Text toTypstPoundSetText [] = "" toTypstPoundSetText typstTextAttrs = "#" <> toTypstSetText typstTextAttrs toTypstBracesSetText :: [(Text, Text)] -> Doc Text -> Doc Text toTypstBracesSetText [] x = "#" <> x toTypstBracesSetText typstTextAttrs x = "#" <> braces (toTypstSetText typstTextAttrs <> x) blocksToTypst :: PandocMonad m => [Block] -> TW m (Doc Text) blocksToTypst blocks = vcat <$> mapM blockToTypst blocks blockToTypst :: PandocMonad m => Block -> TW m (Doc Text) blockToTypst block = case block of Plain inlines -> inlinesToTypst inlines Para inlines -> do ($$ blankline) <$> inlinesToTypst inlines Header level (ident,cls,_) inlines -> do contents <- inlinesToTypst inlines let lab = toLabel FreestandingLabel ident let headingAttrs = ["outlined: false" | "unlisted" `elem` cls] ++ ["numbering: none" | "unnumbered" `elem` cls] return $ if null headingAttrs then nowrap (literal (T.replicate level "=") <> space <> contents) <> cr <> lab else literal "#heading" <> parens (literal (T.intercalate ", " ("level: " <> tshow level : headingAttrs))) <> brackets contents <> cr <> lab RawBlock fmt str -> case fmt of Format "typst" -> return $ literal str _ -> return mempty CodeBlock (ident,cls,kvs) code -> do let go :: Char -> (Int, Int) -> (Int, Int) go '`' (longest, current) = let !new = current + 1 in (max longest new, new) go _ (longest, _) = (longest, 0) let (longestBacktickSequence, _) = T.foldr go (0,0) code let fence = literal $ T.replicate (max 3 (longestBacktickSequence + 1)) "`" let lang = case cls of (cl:_) -> literal cl _ -> mempty opts <- gets stOptions case writerHighlightMethod opts of Skylighting _ -> case highlight (writerSyntaxMap opts) formatTypstBlock (ident,cls ++ ["default"],kvs) code of Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg return $ fence <> lang <> cr <> literal code <> cr <> fence <> blankline Right h -> do modify (\s -> s{ stHighlighting = True }) return (literal h) NoHighlighting -> return $ fence <> cr <> literal code <> cr <> fence <> blankline _ -> return $ fence <> lang <> cr <> literal code <> cr <> fence <> blankline LineBlock lns -> do contents <- inlinesToTypst (intercalate [LineBreak] lns) return $ contents <> blankline BlockQuote blocks -> do contents <- blocksToTypst blocks return $ "#quote(block: true)[" $$ chomp contents $$ "]" $$ blankline HorizontalRule -> return $ blankline <> "#horizontalrule" <> blankline OrderedList attribs items -> do let addBlock = case attribs of (1, DefaultStyle, DefaultDelim) -> id (1, Decimal, Period) -> id (start, sty, delim) -> \x -> "#block[" $$ ("#set enum" <> parens ( "numbering: " <> doubleQuoted (case orderedListMarkers (1, sty, delim) of (m:_) -> m [] -> "1.") <> ", start: " <> text (show start) )) $$ x $$ "]" items' <- mapM (fmap chomp . listItemToTypst 2 ("+")) items return $ addBlock (if isTightList items then vcat items' else vsep items') $$ blankline BulletList items -> do items' <- mapM (fmap chomp . listItemToTypst 2 "-") items return $ (if isTightList items then vcat items' else vsep items') $$ blankline DefinitionList items -> ($$ blankline) . vsep <$> mapM defListItemToTypst items Table (ident,tabclasses,tabkvs) (Caption _ caption) colspecs thead tbodies tfoot -> do let lab = toLabel FreestandingLabel ident capt' <- if null caption then return mempty else do captcontents <- blocksToTypst caption return $ ", caption: " <> brackets captcontents let typstFigureKind = literal (", kind: " <> fromMaybe "table" (lookup "typst:figure:kind" tabkvs)) let numcols = length colspecs let (aligns, widths) = unzip colspecs let commaSep = hcat . intersperse ", " let toPercentage (ColWidth w) = literal $ (T.dropWhileEnd (== '.') . T.dropWhileEnd (== '0')) (T.pack (printf "%0.2f" (w * 100))) <> "%" toPercentage ColWidthDefault = literal "auto" let columns = if all (== ColWidthDefault) widths then literal $ tshow numcols else parens (commaSep (map toPercentage widths)) let formatalign AlignLeft = "left," formatalign AlignRight = "right," formatalign AlignCenter = "center," formatalign AlignDefault = "auto," let alignarray = parens $ mconcat $ map formatalign aligns let fromCell (Cell (_,_,kvs) alignment rowspan colspan bs) = do let (typstAttrs, typstTextAttrs) = pickTypstAttrs kvs let valign = (case lookup "align" typstAttrs of Just va -> [va] _ -> []) let typstAttrs2 = filter ((/="align") . fst) typstAttrs let halign = (case alignment of AlignDefault -> [] AlignLeft -> [ "left" ] AlignRight -> [ "right" ] AlignCenter -> [ "center" ]) let cellaligns = valign ++ halign let cellattrs = (case cellaligns of [] -> [] _ -> [ "align: " <> T.intercalate " + " cellaligns ]) ++ (case rowspan of RowSpan 1 -> [] RowSpan n -> [ "rowspan: " <> tshow n ]) ++ (case colspan of ColSpan 1 -> [] ColSpan n -> [ "colspan: " <> tshow n ]) ++ map formatTypstProp typstAttrs2 cellContents <- blocksToTypst bs let contents2 = brackets (toTypstPoundSetText typstTextAttrs <> cellContents) pure $ if null cellattrs then contents2 else "table.cell" <> parens (literal (T.intercalate ", " cellattrs)) <> contents2 let fromRow (Row _ cs) = (<> ",") . commaSep <$> mapM fromCell cs let fromHead (TableHead _attr headRows) = if null headRows then pure mempty else (($$ "table.hline(),") . (<> ",") . ("table.header" <>) . parens . nest 2 . vcat) <$> mapM fromRow headRows let fromFoot (TableFoot _attr footRows) = if null footRows then pure mempty else (("table.hline()," $$) . (<> ",") . ("table.footer" <>) . parens . nest 2 . vcat) <$> mapM fromRow footRows let fromTableBody (TableBody _attr _rowHeadCols headRows bodyRows) = do hrows <- mapM fromRow headRows brows <- mapM fromRow bodyRows pure $ vcat (hrows ++ ["table.hline()," | not (null hrows)] ++ brows) let (typstAttrs, typstTextAttrs) = pickTypstAttrs tabkvs header <- fromHead thead footer <- fromFoot tfoot body <- vcat <$> mapM fromTableBody tbodies let table = "table(" $$ nest 2 ( "columns: " <> columns <> "," $$ "align: " <> alignarray <> "," $$ toTypstPropsListTerm typstAttrs $$ header $$ body $$ footer ) $$ ")" return $ if "typst:no-figure" `elem` tabclasses then toTypstBracesSetText typstTextAttrs table else "#figure(" $$ nest 2 ("align(center)[" <> toTypstPoundSetText typstTextAttrs <> "#" <> table <> "]" $$ capt' $$ typstFigureKind $$ ")") $$ lab $$ blankline Figure (ident,_,_) (Caption _mbshort capt) blocks -> do caption <- blocksToTypst capt opts <- gets stOptions let toImage (Image attr inlines (src, _)) = Just $ mkImage opts False src attr (getAlt attr inlines) toImage _ = Nothing contents <- case blocks of -- don't need #box around block-level image [Para [img]] | Just i <- toImage img -> pure i [Plain [img]] | Just i <- toImage img -> pure i _ -> brackets <$> blocksToTypst blocks let lab = toLabel FreestandingLabel ident return $ "#figure(" <> nest 2 ((contents <> ",") $$ ("caption: [" $$ nest 2 caption $$ "]") ) $$ ")" $$ lab $$ blankline Div (ident,_,_) (Header lev ("",cls,kvs) ils:rest) -> blocksToTypst (Header lev (ident,cls,kvs) ils:rest) Div (ident,_,kvs) blocks -> do let lab = toLabel FreestandingLabel ident let (typstAttrs,typstTextAttrs) = pickTypstAttrs kvs -- Handle lang attribute for Div elements let langAttrs = case lookup "lang" kvs of Nothing -> [] Just lang -> case parseLang lang of Left _ -> [] Right l -> [("lang", tshow (langLanguage l))] let allTypstTextAttrs = typstTextAttrs ++ langAttrs contents <- blocksToTypst blocks return $ "#block" <> toTypstPropsListParens typstAttrs <> "[" $$ toTypstPoundSetText allTypstTextAttrs <> contents $$ ("]" <+> lab) defListItemToTypst :: PandocMonad m => ([Inline], [[Block]]) -> TW m (Doc Text) defListItemToTypst (term, defns) = do modify $ \st -> st{ stEscapeContext = TermContext } term' <- inlinesToTypst term modify $ \st -> st{ stEscapeContext = NormalContext } defns' <- mapM blocksToTypst defns return $ nowrap ("/ " <> term' <> ": " <> "#block[") $$ chomp (vsep defns') $$ "]" listItemToTypst :: PandocMonad m => Int -> Doc Text -> [Block] -> TW m (Doc Text) listItemToTypst ind marker blocks = do contents <- blocksToTypst blocks return $ hang ind (marker <> space) contents inlinesToTypst :: PandocMonad m => [Inline] -> TW m (Doc Text) inlinesToTypst ils = hcat <$> mapM inlineToTypst (escapeParens ils) -- Add an escape before a parenthesis right after a non-space element. -- Otherwise we risk `#emph[test](3)` which will error. See #11210. escapeParens :: [Inline] -> [Inline] escapeParens [] = [] escapeParens (s : x : xs) | isSpacey s = s : x : escapeParens xs escapeParens (Str t : xs) | Just ('(',_) <- T.uncons t = RawInline (Format "typst") "\\" : Str t : escapeParens xs escapeParens (x : xs) = x : escapeParens xs isSpacey :: Inline -> Bool isSpacey Space = True isSpacey SoftBreak = True isSpacey LineBreak = True isSpacey _ = False inlineToTypst :: PandocMonad m => Inline -> TW m (Doc Text) inlineToTypst inline = case inline of Str txt -> do opts <- gets stOptions context <- gets stEscapeContext return $ escapeTypst (isEnabled Ext_smart opts) context txt Space -> return space SoftBreak -> do wrapText <- gets $ writerWrapText . stOptions case wrapText of WrapPreserve -> return cr WrapAuto -> return space WrapNone -> return space LineBreak -> return (space <> "\\" <> space) Math mathType str -> do res <- convertMath TM.writeTypst mathType str case res of Left il -> inlineToTypst il Right r -> (case extractLabel str of -- #10805 Nothing -> id Just lab -> (<> (toLabel FreestandingLabel lab))) <$> case mathType of InlineMath -> return $ "$" <> literal r <> "$" DisplayMath -> return $ "$ " <> literal r <> " $" Code (ident,cls,kvs) code -> do opts <- gets stOptions let defaultHighlightedCode = case cls of (lang:_) | writerHighlightMethod opts /= NoHighlighting -> "#raw(lang:" <> doubleQuoted lang <> ", " <> doubleQuoted code <> ")" _ | T.any (=='`') code -> "#raw(" <> doubleQuoted code <> ")" | otherwise -> "`" <> literal code <> "`" case writerHighlightMethod opts of Skylighting _ -> case highlight (writerSyntaxMap opts) formatTypstInline (ident,cls ++ ["default"],kvs) code of Left msg -> do unless (T.null msg) $ report $ CouldNotHighlight msg return defaultHighlightedCode Right h -> do modify (\s -> s{ stHighlighting = True }) return (literal h) _ -> return defaultHighlightedCode RawInline fmt str -> case fmt of Format "typst" -> return $ literal str _ -> return mempty Strikeout inlines -> textstyle "#strike" inlines Emph inlines -> textstyle "#emph" inlines Underline inlines -> textstyle "#underline" inlines Strong inlines -> textstyle "#strong" inlines Superscript inlines -> textstyle "#super" inlines Subscript inlines -> textstyle "#sub" inlines SmallCaps inlines -> textstyle "#smallcaps" inlines Span (ident,cls,kvs) inlines -> do let lab = toLabel FreestandingLabel ident let (_, typstTextAttrs) = pickTypstAttrs kvs contents <- inlinesToTypst inlines let addHl x = "#highlight" <> brackets x return $ (if "mark" `elem` cls then addHl else id) (toTypstTextElement typstTextAttrs contents) <> lab Quoted quoteType inlines -> do opts <- gets stOptions let smart = isEnabled Ext_smart opts contents <- inlinesToTypst inlines return $ case quoteType of DoubleQuote | smart -> "\"" <> contents <> "\"" | otherwise -> "“" <> contents <> "”" SingleQuote | smart -> "'" <> contents <> "'" | otherwise -> "‘" <> contents <> "’" Cite citations inlines -> do opts <- gets stOptions if isEnabled Ext_citations opts -- Note: this loses prefix then mconcat <$> mapM toCite citations else inlinesToTypst inlines Link (_,_,kvs) inlines (src,_tit) -> do case lookup "reference-type" kvs of Just "ref" | Just ('#', ident) <- T.uncons src -> if T.all isIdentChar ident then pure $ literal $ "@" <> ident else pure $ "#ref" <> parens (toLabel ArgumentLabel ident) _ -> do contents <- inlinesToTypst inlines let dest = case T.uncons src of Just ('#', ident) -> toLabel ArgumentLabel ident _ -> doubleQuoted src pure $ "#link" <> parens dest <> (if inlines == [Str src] then mempty else nowrap $ brackets contents) Image attr inlines (src,_tit) -> do opts <- gets stOptions pure $ mkImage opts True src attr (getAlt attr inlines) Note blocks -> do contents <- blocksToTypst blocks return $ "#footnote" <> brackets (chomp contents) -- see #9104; need box or image is treated as block-level mkImage :: WriterOptions -> Bool -> Text -> Attr -> Maybe Text -> Doc Text mkImage opts useBox src attr mbAlt | useBox = "#box" <> parens coreImage | otherwise = coreImage where src' = T.pack $ unEscapeString $ T.unpack src -- #9389 showDim (Pixel a) = literal (showInInch opts (Pixel a) <> "in") showDim dim = text (show dim) dimAttrs = (case dimension Height attr of Nothing -> mempty Just dim -> ", height: " <> showDim dim) <> (case dimension Width attr of Nothing -> mempty Just dim -> ", width: " <> showDim dim) altAttr = case mbAlt of Just alt -> ", alt: " <> doubleQuoted alt Nothing -> mempty isData = "data:" `T.isPrefixOf` src' eitherImageData = if isData then runPure (fetchItem src) else Left $ PandocSomeError "not a data URI" toArray = parens . hcat . intersperse "," . map (literal . tshow) . B.unpack attrs = dimAttrs <> altAttr coreImage = "image" <> parens (case eitherImageData of Right (contents, Just "image/svg+xml") -> "bytes" <> parens (doubleQuoted (UTF8.toText contents)) <> attrs Right (bytes, _mime) -> "bytes" <> parens (toArray bytes) <> attrs Left _ -> doubleQuoted src' <> attrs) -- | Extract alt text from image attributes and inlines. -- Use explicit alt attribute if present; otherwise use inlines. -- Empty alt="" means decorative image (no alt text). getAlt :: Attr -> [Inline] -> Maybe Text getAlt (_, _, kvs) imgInlines = case lookup "alt" kvs of Just "" -> Nothing -- decorative Just alt -> Just alt Nothing -> case imgInlines of [] -> Nothing _ -> Just (stringify imgInlines) textstyle :: PandocMonad m => Doc Text -> [Inline] -> TW m (Doc Text) textstyle s inlines = do (s <>) . brackets . fixInitialAfterBreakEscape <$> inlinesToTypst inlines fixInitialAfterBreakEscape :: Doc Text -> Doc Text fixInitialAfterBreakEscape (Concat x y) = Concat (fixInitialAfterBreakEscape x) y -- make an initial AfterBreak escape unconditional (it will be rendered -- in a block [..] and there won't be an actual break to trigger it, but -- typst still needs the escape) fixInitialAfterBreakEscape (AfterBreak "\\") = Text 1 "\\" fixInitialAfterBreakEscape x = x isOrderedListMarker :: Text -> Bool isOrderedListMarker t = not (T.null ds) && rest == "." where (ds, rest) = T.span isDigit t escapeTypst :: Bool -> EscapeContext -> Text -> Doc Text escapeTypst smart context t = (case T.uncons t of Just (c, rest) | c == ';' -> char '\\' -- see #9252 | c == '.' , not (T.null rest) -> char '\\' -- see #11511 | needsEscapeAtLineStart c || isOrderedListMarker t -> afterBreak "\\" _ -> mempty) <> 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 == '\8216', smart = "'" -- left quote | c == '\8217', smart = "'" -- apostrophe | c == '\8220', smart = "\"" -- left double quote | c == '\8221', smart = "\"" -- right double quote | c == '\8212', smart = "---" -- em dash | c == '\8211', smart = "--" -- en dash | needsEscape c = "\\" <> T.singleton c | otherwise = T.singleton c needsEscape '\160' = True needsEscape '\8216' = smart needsEscape '\8217' = smart needsEscape '\8220' = smart needsEscape '\8221' = smart needsEscape '\8212' = smart needsEscape '\8211' = smart needsEscape '\'' = smart needsEscape '"' = smart needsEscape '[' = True needsEscape ']' = True needsEscape '#' = True needsEscape '<' = True needsEscape '>' = True needsEscape '@' = True needsEscape '$' = True needsEscape '\\' = True needsEscape '`' = True needsEscape '_' = True needsEscape '*' = True needsEscape '~' = True needsEscape ':' = context == TermContext needsEscape _ = False needsEscapeAtLineStart :: Char -> Bool needsEscapeAtLineStart '/' = True needsEscapeAtLineStart '+' = True needsEscapeAtLineStart '-' = True needsEscapeAtLineStart '=' = True needsEscapeAtLineStart _ = False data LabelType = FreestandingLabel | ArgumentLabel deriving (Show, Eq) toLabel :: LabelType -> Text -> Doc Text toLabel labelType ident | T.null ident = mempty | T.all isIdentChar ident' = "<" <> literal ident' <> ">" | otherwise = case labelType of FreestandingLabel -> "#label" <> parens (doubleQuoted ident') ArgumentLabel -> "label" <> parens (doubleQuoted ident') where ident' = T.pack $ unEscapeString $ T.unpack ident isIdentChar :: Char -> Bool isIdentChar c = isXIDContinue c || c == '_' || c == '-' || c == '.' || c == ':' toCite :: PandocMonad m => Citation -> TW m (Doc Text) toCite cite = do let ident' = T.pack $ unEscapeString $ T.unpack $ citationId cite -- typst inserts comma and we get a doubled one if supplement contains it: let eatComma (Str "," : Space : xs) = xs eatComma xs = xs if citationMode cite == NormalCitation && T.all isIdentChar ident' then do suppl <- case citationSuffix cite of [] -> pure mempty suff -> brackets <$> inlinesToTypst (eatComma suff) pure $ "@" <> literal ident' <> suppl else do let label = if T.all isIdentChar ident' then "<" <> literal ident' <> ">" else "label" <> parens (doubleQuoted ident') let form = case citationMode cite of NormalCitation -> mempty SuppressAuthor -> ", form: \"year\"" AuthorInText -> ", form: \"prose\"" suppl <- case citationSuffix cite of [] -> pure mempty suff -> (", supplement: " <>) . brackets <$> inlinesToTypst (eatComma suff) pure $ (if citationMode cite == SuppressAuthor -- see #11044 then parens else id) $ "#cite" <> parens (label <> form <> suppl) doubleQuoted :: Text -> Doc Text doubleQuoted = doubleQuotes . literal . escape where escape = T.concatMap escapeChar escapeChar '\\' = "\\\\" escapeChar '"' = "\\\"" escapeChar c = T.singleton c extractLabel :: Text -> Maybe Text extractLabel = go . T.unpack where go [] = Nothing go ('\\':'l':'a':'b':'e':'l':'{':xs) = Just (T.pack (takeWhile (/='}') xs)) go (_:xs) = go xs ================================================ FILE: src/Text/Pandoc/Writers/Vimdoc.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Strict #-} module Text.Pandoc.Writers.Vimdoc (writeVimdoc) where import Control.Applicative (optional, (<|>)) import Control.Monad (forM) import Control.Monad.Reader (MonadReader (..), ReaderT (..), asks) import Control.Monad.State (MonadState (..), StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, intersperse, transpose) import Data.List.NonEmpty (NonEmpty (..), nonEmpty) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import Text.DocLayout hiding (char, link, text) import Text.Pandoc.Class.PandocMonad ( report, PandocMonad ) import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError) import Text.Pandoc.Logging (LogMessage (..)) import Text.Pandoc.Options (WrapOption (..), WriterOptions (..)) import Text.Pandoc.Parsing.General (many1Till, many1TillChar, readWith) import Text.Pandoc.Shared (capitalize, onlySimpleTableCells, orderedListMarkers, isTightList, makeSections, removeFormatting, tshow) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.URI (escapeURI, isURI) import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable) import Text.Parsec (anyChar, char, eof, string, try) import Text.Read (readMaybe) import Text.Pandoc.Chunks (toTOCTree, SecInfo (..)) import Data.Tree (Tree(..)) import Data.Functor ((<&>)) import Data.Sequence (Seq, (|>), (<|)) import qualified Data.Sequence as Seq import Data.Foldable (toList) data WriterState = WriterState { indentLevel :: Int -- How much to indent the block. Inlines shouldn't -- be concerned with indent level (I guess?) , shiftWidth :: Int -- spaces per indentation level , writerOptions :: WriterOptions , vimdocPrefix :: Maybe Text } instance Default WriterState where def = WriterState { indentLevel = 0 , shiftWidth = 4 , writerOptions = def , vimdocPrefix = Nothing } indent :: (Monad m) => Int -> (VW m a) -> (VW m a) indent n = local (\s -> s{indentLevel = indentLevel s + n}) type VW m = StateT (Seq (Doc Text)) (ReaderT WriterState m) runRR :: (Monad m) => Seq (Doc Text) -> WriterState -> VW m a -> m a runRR footnotes opts action = runReaderT (evalStateT action footnotes) opts docShiftWidth :: Meta -> Maybe Int docShiftWidth meta = case lookupMeta "shiftwidth" meta of Just (MetaInlines [Str sw]) -> readMaybe (T.unpack sw) Just (MetaString sw) -> readMaybe (T.unpack sw) _ -> Nothing docVimdocPrefix :: Meta -> Maybe Text docVimdocPrefix meta = case lookupMeta "vimdoc-prefix" meta of Just (MetaInlines [Str pref]) -> Just pref Just (MetaString pref) -> Just pref _ -> Nothing {- | Build a vim modeline >>> makeModeLine def "vim:tw=72:sw=4:ts=4:ft=help:norl:et:" -} makeModeLine :: WriterState -> Text makeModeLine ws = T.pack . intercalate ":" $ [ "vim" , "tw=" <> show tw , "sw=" <> show sw , "ts=" <> show sw , "ft=help" , "norl" -- left-to-right text , "et:" -- expandtab and finishing ":" ] where tw = writerColumns . writerOptions $ ws sw = shiftWidth ws -- | Build a single formatted TOC line tocEntryToLine :: (PandocMonad m) => SecInfo -> VW m Text tocEntryToLine secinfo = do rightRef <- mkVimdocRef (secId secinfo) let numberStr = case secNumber secinfo of Nothing -> "" Just x | '.' `T.elem` x -> x <> " " Just x -> x <> ". " title <- inlineListToVimdoc $ removeFormatting (secTitle secinfo) let titlePlain = render Nothing (title <> " ") -- length sub 2 because vertical bars are concealed let rightRefLen = max 0 (T.length rightRef - 2) let numberLen = T.length numberStr let leftLen = numberLen + T.length titlePlain let padForRight = 1 textWidth <- asks (writerColumns . writerOptions) il <- asks indentLevel -- positive when we lack space (i.e. content is too long) let lack = (il + leftLen + padForRight + rightRefLen) - textWidth -- when lacking, truncate title reserving 3+ chars for ellipsis let finalTitle = if lack >= 0 then let trunc = T.dropEnd (lack + 3) titlePlain stripped = T.stripEnd trunc ellipsis = T.replicate (3 + T.length trunc - T.length stripped) "." in stripped <> ellipsis else titlePlain -- Negative lack means we have an excess of space, so we fill it with dots let dots = T.replicate (negate lack) "." pure . T.concat $ [numberStr, finalTitle, dots, " ", rightRef] vimdocTOC :: (PandocMonad m) => WriterState -> [Block] -> VW m (Doc Text) vimdocTOC (WriterState{writerOptions = opts}) blocks = do let (Node _ subtrees) = toTOCTree $ makeSections (writerNumberSections opts) Nothing blocks let tocDepth = writerTOCDepth opts let isBelowTocDepth (Node sec _) = secLevel sec <= tocDepth let makeItem :: (PandocMonad m) => Tree SecInfo -> VW m (Doc Text) makeItem (Node secinfo xs) = do line <- tocEntryToLine secinfo -- When unnumbered, indent constantly by two, -- otherwise indent by (length of marker + 1) let markerLen = 1 + maybe 1 T.length (secNumber secinfo) childItems <- indent markerLen $ traverse makeItem (filter isBelowTocDepth xs) pure (literal line $$ nest markerLen (vcat childItems)) items <- traverse makeItem (filter isBelowTocDepth subtrees) pure $ vcat items writeVimdoc :: (PandocMonad m) => WriterOptions -> Pandoc -> m Text writeVimdoc opts document@(Pandoc meta _) = let sw = fromMaybe (shiftWidth def) $ docShiftWidth meta vp = docVimdocPrefix meta footnotes = Seq.empty initialEnv = def{shiftWidth = sw, writerOptions = opts, vimdocPrefix = vp} in runRR footnotes initialEnv $ pandocToVimdoc document pandocToVimdoc :: (PandocMonad m) => Pandoc -> VW m Text pandocToVimdoc (Pandoc meta body) = do st <- ask let opts = writerOptions st metadata <- metaToContext opts blockListToVimdoc inlineListToVimdoc meta main <- do body' <- blockListToVimdoc body footnotes <- get rule <- blockToVimdoc HorizontalRule let footnotes' = if Seq.null footnotes then Empty else vsep (toList $ rule <| footnotes) pure $ body' <> blankline <> footnotes' title <- inlineListToVimdoc $ docTitle meta authors <- traverse inlineListToVimdoc $ docAuthors meta let authors' = mconcat $ intersperse ("," <> space) (fmap nowrap authors) let tw = writerColumns . writerOptions $ st let combinedTitle = render (Just tw) . cblock tw $ (title <> space) <> (if null authors' then "" else "by" <> space <> authors') -- This is placed here because I couldn't find a way to right-align text -- inside template to the width specified by a variable let toc_reminder = render Nothing . rblock tw $ ("Type |gO| to see the table of contents." :: Doc Text) toc <- render (Just tw) <$> vimdocTOC st body let modeline = makeModeLine st let context = defField "body" main . defField "toc" (if writerTableOfContents opts then toc else "") . defField "modeline" modeline . defField "combined-title" combinedTitle . defField "toc-reminder" toc_reminder $ metadata pure $ case writerTemplate opts of Just tpl -> render (Just tw) $ renderTemplate tpl context Nothing -> render (Just tw) main blockListToVimdoc :: (PandocMonad m) => [Block] -> VW m (Doc Text) blockListToVimdoc blocks = vcat <$> mapM blockToVimdoc blocks blockToVimdoc :: (PandocMonad m) => Block -> VW m (Doc Text) blockToVimdoc (Plain inlines) = inlineListToVimdoc inlines blockToVimdoc (Para inlines) = do contents <- inlineListToVimdoc inlines pure $ contents <> blankline blockToVimdoc (LineBlock inliness) = vcat <$> mapM inlineListToVimdoc inliness blockToVimdoc (CodeBlock (_, cls, _) code) = do sw <- asks shiftWidth let lang = case cls of (lang' : _) -> lang' _ -> "" -- NOTE: No blankline after the codeblock because closing `<` is concealed pure . vcat $ [ ">" <> literal lang , nest sw (literal code) , flush "<" ] blockToVimdoc block@(RawBlock format raw) = case format of "vimdoc" -> pure $ literal raw _ -> "" <$ report (BlockNotRendered block) blockToVimdoc (BlockQuote blocks) = do content <- blockListToVimdoc blocks pure $ nest 2 content <> blankline blockToVimdoc (OrderedList listAttr items) = do let itemSpacer = if isTightList items then empty else blankline let itemsWithMarkers = zip (orderedListMarkers listAttr) items items' <- forM itemsWithMarkers $ \(marker, blocks) -> do let markerLen = T.length marker item' <- indent (markerLen + 1) $ blockListToVimdoc blocks pure $ literal marker <> space <> nest (markerLen + 1) item' <> itemSpacer pure $ vcat items' <> blankline blockToVimdoc (BulletList items) = do let itemSpacer = if isTightList items then empty else blankline items' <- forM items $ \blocks -> do let marker = "-" item <- indent 2 $ blockListToVimdoc blocks pure $ marker <> " " <> nest 2 item <> itemSpacer pure $ vcat items' <> blankline blockToVimdoc (DefinitionList items) = do sw <- asks shiftWidth let sepAll = if all (isTightList . snd) items then vcat else vsep items' <- forM items $ \(term, definitions) -> do let sepCur = if isTightList definitions then vcat else vsep labeledTerm <- mkVimdocDefinitionTerm term definitions' <- indent sw $ traverse blockListToVimdoc definitions pure $ labeledTerm <> cr <> nest sw (sepCur definitions') pure $ sepAll items' <> blankline blockToVimdoc (Header level (ref, _, _) inlines) = do tw <- asks (writerColumns . writerOptions) let rule = case level of 1 -> T.replicate tw "=" 2 -> T.replicate tw "-" _ -> "" title <- fmap (render Nothing) . inlineListToVimdoc $ case level of 3 -> capitalize inlines _ -> inlines label <- mkVimdocTag ref -- One manual space that ensures that even if spaceLeft is 0, title and ref -- don't touch each other let label' = " " <> label -- (+ 2) due to stars concealment let spaceLeft = tw - T.length title + 2 pure $ vcat [ blankline , literal rule , literal $ title <> T.justifyRight spaceLeft ' ' label' , blankline ] blockToVimdoc HorizontalRule = do tw <- asks (writerColumns . writerOptions) pure $ literal (T.replicate (tw `div` 2) " *") <> blankline -- Based on blockToMarkdown' from Text.Pandoc.Writers.Markdown blockToVimdoc t@(Table (_, _, _) blkCapt specs thead tbody tfoot) = do let isColRowSpans (Cell _ _ rs cs _) = rs > 1 || cs > 1 let rowHasColRowSpans (Row _ cs) = any isColRowSpans cs let tbodyHasColRowSpans (TableBody _ _ rhs rs) = any rowHasColRowSpans rhs || any rowHasColRowSpans rs let theadHasColRowSpans (TableHead _ rs) = any rowHasColRowSpans rs let tfootHasColRowSpans (TableFoot _ rs) = any rowHasColRowSpans rs let hasColRowSpans = theadHasColRowSpans thead || any tbodyHasColRowSpans tbody || tfootHasColRowSpans tfoot let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot let numcols = maximum $ length aligns :| length widths : map length (headers : rows) caption' <- inlineListToVimdoc caption let caption'' | null caption = blankline | otherwise = blankline $$ caption' $$ blankline let hasSimpleCells = onlySimpleTableCells $ headers : rows let isSimple = hasSimpleCells && all (== 0) widths && not hasColRowSpans let isPlainBlock (Plain _) = True isPlainBlock _ = False let hasBlocks = not (all (all (all isPlainBlock)) $ headers : rows) let padRow r = r ++ replicate x empty where x = numcols - length r let aligns' = aligns ++ replicate x AlignDefault where x = numcols - length aligns let widths' = widths ++ replicate x 0.0 where x = numcols - length widths sw <- asks shiftWidth rawHeaders <- padRow <$> mapM blockListToVimdoc headers rawRows <- mapM (fmap padRow . mapM blockListToVimdoc) rows let hasHeader = all null headers if | isSimple -> do -- Simple table tbl <- indent sw $ vimdocTable False hasHeader aligns' widths' rawHeaders rawRows pure $ nest sw (tbl $$ caption'') $$ blankline | not (hasBlocks || hasColRowSpans) -> do -- Multiline table tbl <- indent sw $ vimdocTable True hasHeader aligns' widths' rawHeaders rawRows pure $ nest sw (tbl $$ caption'') $$ blankline | otherwise -> ("[TABLE]" $$ caption'') <$ report (BlockNotRendered t) blockToVimdoc (Figure _ _ blocks) = blockListToVimdoc blocks blockToVimdoc (Div _ blocks) = blockListToVimdoc blocks {- | Create a vimdoc tag. Tag is prefixed with "$vimdocPrefix-" if vimdocPrefix is a Just value. >>> runReader (mkVimdocTag "abc") def "*abc*" >>> runReader (mkVimdocTag "abc") (def{vimdocPrefix = Just "myCoolProject"}) "*myCoolProject-abc*" -} mkVimdocTag :: (Monad m) => Text -> VW m Text mkVimdocTag tag = do asks vimdocPrefix <&> \case _ | T.null tag -> "" Nothing -> "*" <> tag <> "*" Just pref' -> "*" <> pref' <> "-" <> tag <> "*" {- | Create a hotlink for a tag, ie. a followable vimdoc link. Tag is prefixed - with "$vimdocPrefix-" if vimdocPrefix is a Just value >>> runReader (mkVimdocRef "abc") def "|abc|" >>> runReader (mkVimdocRef "abc") (def{vimdocPrefix = Just "myCoolProject"}) "|myCoolProject-abc|" -} mkVimdocRef :: (Monad m) => Text -> VW m Text mkVimdocRef ref = asks vimdocPrefix <&> \case _ | T.null ref -> "" Nothing -> "|" <> ref <> "|" Just pref' -> "|" <> pref' <> "-" <> ref <> "|" mkVimdocDefinitionTerm :: (PandocMonad m) => [Inline] -> VW m (Doc Text) mkVimdocDefinitionTerm inlines = do il <- asks indentLevel tw <- asks (writerColumns . writerOptions) label <- case inlines of -- NOTE: commands in vim are unique, so they get no prefix [Code (ref, _, _) code] | T.isPrefixOf ":" code -> pure . Just $ "*" <> ref <> "*" [Code (ref, _, _) _] | not (T.null ref) -> Just <$> mkVimdocTag ref [Span (ref, _, _) _] | not (T.null ref) -> Just <$> mkVimdocTag ref _ -> pure Nothing term <- case inlines of [Code _ code] | T.isPrefixOf ":" code -> pure $ literal code _ -> inlineListToVimdoc inlines let termLen = offset term let labelLen = maybe 0 T.length label if il + termLen + labelLen > tw then pure . mconcat $ [ case label of Nothing -> empty -- (+2) due to stars concealment Just l -> flush (rblock (tw + 2) $ literal l) <> cr , term ] else pure . mconcat $ [ -- Since we calculated that label fits on the same line as -- term and since label actually must exceed textwidth to align -- properly, we disable wrapping. -- vvvvvvvv nowrap term , case label of Nothing -> empty -- (+2) due to stars concealment Just l -> rblock (tw - termLen - il + 2) (literal l) ] -- | Write a vimdoc table vimdocTable :: (Monad m) => -- | whether this is a multiline table Bool -> -- | whether the table has a header Bool -> -- | column alignments [Alignment] -> -- | column widths [Double] -> -- | table header cells [Doc Text] -> -- | table body rows [[Doc Text]] -> VW m (Doc Text) vimdocTable multiline headless aligns widths rawHeaders rawRows = do let isSimple = all (== 0) widths let alignHeader alignment = case alignment of AlignLeft -> lblock AlignCenter -> cblock AlignRight -> rblock AlignDefault -> lblock -- Number of characters per column necessary to output every cell -- without requiring a line break. -- The @+2@ is needed for specifying the alignment. let numChars = (+ 2) . maybe 0 maximum . nonEmpty . map offset -- Number of characters per column necessary to output every cell -- without requiring a line break *inside a word*. -- The @+2@ is needed for specifying the alignment. let minNumChars = (+ 2) . maybe 0 maximum . nonEmpty . map minOffset let columns = transpose (rawHeaders : rawRows) il <- asks indentLevel -- x = (2 * length columns) -- spaces for specifying the alignment -- y = (length columns - 1) -- spaces between the columns -- x + y = (3 * length columns - 1) -- total needed correction tw <- asks (writerColumns . writerOptions) let tw' = tw - il - 3 * length columns + 1 wrap <- asks (writerWrapText . writerOptions) -- minimal column width without wrapping a single word let relWidth w col = max (floor $ fromIntegral (tw' - 1) * w) ( if wrap == WrapAuto then minNumChars col else numChars col ) let widthsInChars | isSimple = map numChars columns | otherwise = zipWith relWidth widths columns let makeRow = hcat . intersperse (lblock 1 (literal " ")) . zipWith3 alignHeader aligns widthsInChars let rows' = map makeRow rawRows -- TODO: reduce tw in case head is not empty let head' = makeRow rawHeaders <> " ~" let head'' = if headless then empty else head' let body = if multiline then vsep rows' $$ if length rows' < 2 then blankline else empty else vcat rows' return $ blankline $$ head'' $$ (if multiline then blankline else empty) $$ body -- | Replace Unicode characters with their ASCII representation replaceSpecialStrings :: Text -> Text replaceSpecialStrings = let expand c = case c of '\x00ad' -> "" '\x2013' -> "--" '\x2014' -> "---" '\x2019' -> "'" '\x2026' -> "..." _ -> T.singleton c in T.concatMap expand inlineListToVimdoc :: (PandocMonad m) => [Inline] -> VW m (Doc Text) inlineListToVimdoc inlines = hcat <$> mapM inlineToVimdoc inlines inlineToVimdoc :: (PandocMonad m) => Inline -> VW m (Doc Text) inlineToVimdoc (Str str) = pure . literal $ replaceSpecialStrings str -- Neither `:h help-writing`, nor neovim's grammar.js for vimdoc and -- highlights.scm say anything about styling text, so we strip all the -- formatting inlineToVimdoc (Emph inlines) = inlineListToVimdoc inlines inlineToVimdoc (Underline inlines) = inlineListToVimdoc inlines inlineToVimdoc (Strong inlines) = inlineListToVimdoc inlines inlineToVimdoc (Strikeout inlines) = inlineListToVimdoc inlines inlineToVimdoc (Superscript inlines) = inlineListToVimdoc inlines inlineToVimdoc (Subscript inlines) = inlineListToVimdoc inlines inlineToVimdoc (SmallCaps inlines) = inlineListToVimdoc inlines inlineToVimdoc (Quoted typ inlines) = let quote = case typ of SingleQuote -> "'"; DoubleQuote -> "\"" in inlineListToVimdoc inlines >>= \text -> pure (quote <> text <> quote) inlineToVimdoc (Cite _citations inlines) = inlineListToVimdoc inlines inlineToVimdoc (Code (_, cls, _) code) = do let hasNoLang = null cls pure . literal $ case T.words code of [":help", ref] | hasNoLang -> "|" <> ref <> "|" [":h", ref] | hasNoLang -> "|" <> ref <> "|" _ -> "`" <> code <> "`" inlineToVimdoc Space = pure space inlineToVimdoc SoftBreak = asks (writerWrapText . writerOptions) >>= \case WrapAuto -> pure space WrapNone -> pure " " WrapPreserve -> pure "\n" inlineToVimdoc LineBreak = pure "\n" inlineToVimdoc (Math _ math) = pure . literal $ "`$" <> math <> "$`" inlineToVimdoc inline@(RawInline (Format format) text) = case format of "vimdoc" -> pure $ literal text _ -> "" <$ report (InlineNotRendered inline) inlineToVimdoc (Link _ txt (src, _)) = do let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src) linkText <- render Nothing <$> inlineListToVimdoc txt let isAutolink = case txt of [Str x] | escapeURI x `elem` [src, srcSuffix] -> True _ -> False pure $ case refdocLinkToLink src of Right link | isAutolink -> "|" <> literal link <> "|" Right link -> literal (T.stripEnd linkText) <> space <> "|" <> literal link <> "|" Left _ | isURI src, isAutolink -> literal srcSuffix Left _ -> literal (T.stripEnd linkText) <> space <> literal srcSuffix inlineToVimdoc (Image {}) = pure "" inlineToVimdoc (Note blocks) = do newN <- gets (succ . Seq.length) contents <- blockListToVimdoc blocks tag <- mkVimdocTag ("footnote" <> tshow newN) tw <- asks (writerColumns . writerOptions) -- (+2) due to concealment of stars -- vvvvvvvv let taggedContents = rblock (tw + 2) (literal tag) $$ contents modify (|> taggedContents) ref <- mkVimdocRef ("footnote" <> tshow newN) pure $ space <> literal ref inlineToVimdoc (Span _ inlines) = inlineListToVimdoc inlines refdocLinkToLink :: Text -> Either PandocError Text refdocLinkToLink x = (\parser -> readWith parser Nothing x) $ do string "http" >> optional (char 's') >> string "://" let vimhelpP = do try (string "vimhelp.org/") <|> string "neo.vimhelp.org/" try (many1Till anyChar (char '#') >> many1TillChar anyChar eof) <|> many1TillChar anyChar (try $ string ".html" >> eof) let neovimP = do string "neovim.io/doc/user/" try (many1Till anyChar (char '#') >> many1TillChar anyChar eof) <|> do base <- many1TillChar anyChar (try $ string ".html" >> eof) pure $ base <> ".txt" try vimhelpP <|> neovimP ================================================ FILE: src/Text/Pandoc/Writers/XML.hs ================================================ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -- | -- Module : Text.Pandoc.Writers.XML -- Copyright : Copyright (C) 2025- Massimiliano Farinella and John MacFarlane -- License : GNU GPL, version 2 or above -- -- Maintainer : Massimiliano Farinella <massifrg@gmail.com> -- Stability : WIP -- Portability : portable -- -- Conversion of 'Pandoc' documents to (pandoc specific) xml markup. module Text.Pandoc.Writers.XML (writeXML) where import Data.Map (Map, toList) import Data.Maybe (mapMaybe) import qualified Data.Text as T import Data.Version (versionBranch) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions (..)) import Text.Pandoc.XML.Light import qualified Text.Pandoc.XML.Light as XML import Text.Pandoc.XMLFormat import Text.XML.Light (xml_header) type PandocAttr = Text.Pandoc.Definition.Attr writeXML :: (PandocMonad m) => WriterOptions -> Pandoc -> m T.Text writeXML _ doc = do return $ pandocToXmlText doc text_node :: T.Text -> Content text_node text = Text (CData CDataText text Nothing) emptyElement :: T.Text -> Element emptyElement tag = Element { elName = unqual tag, elAttribs = [], elContent = [], elLine = Nothing } elementWithContents :: T.Text -> [Content] -> Element elementWithContents tag contents = Element { elName = unqual tag, elAttribs = [], elContent = contents, elLine = Nothing } elementWithAttributes :: T.Text -> [XML.Attr] -> Element elementWithAttributes tag attributes = Element { elName = unqual tag, elAttribs = attributes, elContent = [], elLine = Nothing } elementWithAttrAndContents :: T.Text -> PandocAttr -> [Content] -> Element elementWithAttrAndContents tag attr contents = addAttrAttributes attr $ elementWithContents tag contents asBlockOfInlines :: Element -> [Content] asBlockOfInlines el = [Elem el, text_node "\n"] asBlockOfBlocks :: Element -> [Content] asBlockOfBlocks el = [Elem newline_before_first, newline] where newline = text_node "\n" newline_before_first = if null (elContent el) then el else prependContents [newline] el itemName :: (Show a) => a -> T.Text itemName a = T.pack $ takeWhile (/= ' ') (show a) intAsText :: Int -> T.Text intAsText i = T.pack $ show i itemAsEmptyElement :: (Show a) => a -> Element itemAsEmptyElement item = emptyElement $ itemName item pandocToXmlText :: Pandoc -> T.Text pandocToXmlText (Pandoc (Meta meta) blocks) = with_header . with_blocks . with_meta . with_version $ el where el = prependContents [text_node "\n"] $ emptyElement "Pandoc" with_version = addAttribute atNameApiVersion (T.intercalate "," $ map (T.pack . show) $ versionBranch pandocTypesVersion) with_meta = appendContents (metaMapToXML meta "meta") with_blocks = appendContents (asBlockOfBlocks $ elementWithContents "blocks" $ blocksToXML blocks) with_header :: Element -> T.Text with_header e = T.concat [T.pack xml_header, "\n", showElement e] metaMapToXML :: Map T.Text MetaValue -> T.Text -> [Content] metaMapToXML mmap tag = asBlockOfBlocks $ elementWithContents tag entries where entries = concatMap to_entry $ toList mmap to_entry :: (T.Text, MetaValue) -> [Content] to_entry (text, metavalue) = asBlockOfBlocks with_key where entry = elementWithContents tgNameMetaMapEntry $ metaValueToXML metavalue with_key = addAttribute atNameMetaMapEntryKey text entry metaValueToXML :: MetaValue -> [Content] metaValueToXML value = let name = itemName value el = itemAsEmptyElement value in case (value) of MetaBool b -> asBlockOfInlines $ addAttribute atNameMetaBoolValue bool_value el where bool_value = if b then "true" else "false" MetaString s -> asBlockOfInlines $ appendContents [text_node s] el MetaInlines inlines -> asBlockOfInlines $ appendContents (inlinesToXML inlines) el MetaBlocks blocks -> asBlockOfBlocks $ appendContents (blocksToXML blocks) el MetaList items -> asBlockOfBlocks $ appendContents (concatMap metaValueToXML items) el MetaMap mm -> metaMapToXML mm name blocksToXML :: [Block] -> [Content] blocksToXML blocks = concatMap blockToXML blocks inlinesToXML :: [Inline] -> [Content] inlinesToXML inlines = concatMap inlineContentToContents (ilsToIlsContent inlines []) data InlineContent = NormalInline Inline | ElSpace Int | ElStr T.Text ilsToIlsContent :: [Inline] -> [InlineContent] -> [InlineContent] ilsToIlsContent (Space : xs) [] = ilsToIlsContent xs [ElSpace 1] ilsToIlsContent (Space : xs) (NormalInline Space : cs) = ilsToIlsContent xs (ElSpace 2 : cs) ilsToIlsContent (Space : xs) (ElSpace n : cs) = ilsToIlsContent xs (ElSpace (n + 1) : cs) -- empty Str are always encoded as <Str /> ilsToIlsContent (Str "" : xs) ilct = ilsToIlsContent xs (ElStr "" : ilct) -- Str s1, Str s2 -> s1<Str content="s2"> ilsToIlsContent (Str s2 : xs) (NormalInline str1@(Str _) : ilct) = ilsToIlsContent xs (ElStr s2 : NormalInline str1 : ilct) -- ilsToIlsContent (Str s : xs) ilct = if T.any (== ' ') s then ilsToIlsContent xs (ElStr s : ilct) else ilsToIlsContent xs (NormalInline (Str s) : ilct) ilsToIlsContent (x : xs) ilct = ilsToIlsContent xs (NormalInline x : ilct) ilsToIlsContent [] ilct = reverse $ lastSpaceAsElem ilct where lastSpaceAsElem :: [InlineContent] -> [InlineContent] lastSpaceAsElem (NormalInline Space : xs) = ElSpace 1 : xs lastSpaceAsElem ilcts = ilcts inlineContentToContents :: InlineContent -> [Content] inlineContentToContents (NormalInline il) = inlineToXML il inlineContentToContents (ElSpace 1) = [Elem $ emptyElement "Space"] inlineContentToContents (ElSpace n) = [Elem $ addAttribute atNameSpaceCount (intAsText n) (emptyElement "Space")] inlineContentToContents (ElStr "") = [Elem $ emptyElement "Str"] inlineContentToContents (ElStr s) = [Elem $ addAttribute atNameStrContent s (emptyElement "Str")] asContents :: Element -> [Content] asContents el = [Elem el] wrapBlocks :: T.Text -> [Block] -> [Content] wrapBlocks tag blocks = asBlockOfBlocks $ elementWithContents tag $ blocksToXML blocks wrapArrayOfBlocks :: T.Text -> [[Block]] -> [Content] wrapArrayOfBlocks tag array = concatMap (wrapBlocks tag) array -- wrapInlines :: T.Text -> [Inline] -> [Content] -- wrapInlines tag inlines = asBlockOfInlines $ element_with_contents tag $ inlinesToXML inlines blockToXML :: Block -> [Content] blockToXML block = let el = itemAsEmptyElement block in case (block) of Para inlines -> asBlockOfInlines $ appendContents (inlinesToXML inlines) el Header level (idn, cls, attrs) inlines -> asBlockOfInlines $ appendContents (inlinesToXML inlines) with_attr where with_attr = addAttrAttributes (idn, cls, attrs ++ [(atNameLevel, intAsText level)]) el Plain inlines -> asBlockOfInlines $ appendContents (inlinesToXML inlines) el Div attr blocks -> asBlockOfBlocks $ appendContents (blocksToXML blocks) with_attr where with_attr = addAttrAttributes attr el BulletList items -> asBlockOfBlocks $ appendContents (wrapArrayOfBlocks tgNameListItem items) el OrderedList (start, style, delim) items -> asBlockOfBlocks $ with_contents . with_attrs $ el where with_attrs = addAttributes ( validAttributes [ (atNameStart, intAsText start), (atNameNumberStyle, itemName style), (atNameNumberDelim, itemName delim) ] ) with_contents = appendContents (wrapArrayOfBlocks tgNameListItem items) BlockQuote blocks -> asBlockOfBlocks $ appendContents (blocksToXML blocks) el HorizontalRule -> asBlockOfInlines el CodeBlock attr text -> asBlockOfInlines $ with_contents . with_attr $ el where with_contents = appendContents [text_node text] with_attr = addAttrAttributes attr LineBlock lins -> asBlockOfBlocks $ appendContents (concatMap wrapInlines lins) el where wrapInlines inlines = asContents $ appendContents (inlinesToXML inlines) $ emptyElement tgNameLineItem Table attr caption colspecs thead tbodies tfoot -> asBlockOfBlocks $ with_foot . with_bodies . with_head . with_colspecs . with_caption . with_attr $ el where with_attr = addAttrAttributes attr with_caption = appendContents (captionToXML caption) with_colspecs = appendContents (colSpecsToXML colspecs) with_head = appendContents (tableHeadToXML thead) with_bodies = appendContents (concatMap tableBodyToXML tbodies) with_foot = appendContents (tableFootToXML tfoot) Figure attr caption blocks -> asBlockOfBlocks $ with_contents . with_caption . with_attr $ el where with_attr = addAttrAttributes attr with_caption = appendContents (captionToXML caption) with_contents = appendContents (blocksToXML blocks) RawBlock (Format format) text -> asContents $ appendContents [text_node text] raw where raw = addAttribute atNameFormat format el DefinitionList items -> asBlockOfBlocks $ appendContents (map definitionListItemToXML items) el inlineToXML :: Inline -> [Content] inlineToXML inline = let el = itemAsEmptyElement inline wrapInlines inlines = asContents $ appendContents (inlinesToXML inlines) el in case (inline) of Space -> [text_node " "] Str s -> [text_node s] Emph inlines -> wrapInlines inlines Strong inlines -> wrapInlines inlines Quoted quote_type inlines -> asContents $ appendContents (inlinesToXML inlines) quoted where quoted = addAttribute atNameQuoteType (itemName quote_type) el Underline inlines -> wrapInlines inlines Strikeout inlines -> wrapInlines inlines SmallCaps inlines -> wrapInlines inlines Superscript inlines -> wrapInlines inlines Subscript inlines -> wrapInlines inlines SoftBreak -> asContents el LineBreak -> asContents el Span attr inlines -> asContents $ appendContents (inlinesToXML inlines) with_attr where with_attr = addAttrAttributes attr el Link (idn, cls, attrs) inlines (url, title) -> asContents $ appendContents (inlinesToXML inlines) with_attr where with_attr = addAttrAttributes (idn, cls, attrs ++ [(atNameLinkUrl, url), (atNameTitle, title)]) el Image (idn, cls, attrs) inlines (url, title) -> asContents $ appendContents (inlinesToXML inlines) with_attr where with_attr = addAttrAttributes (idn, cls, attrs ++ [(atNameImageUrl, url), (atNameTitle, title)]) el RawInline (Format format) text -> asContents $ appendContents [text_node text] raw where raw = addAttribute atNameFormat format el Math math_type text -> asContents $ appendContents [text_node text] math where math = addAttribute atNameMathType (itemName math_type) el Code attr text -> asContents $ appendContents [text_node text] with_attr where with_attr = addAttrAttributes attr el Note blocks -> asContents $ appendContents (blocksToXML blocks) el Cite citations inlines -> asContents $ appendContents (inlinesToXML inlines) with_citations where with_citations = addCitations citations el -- TODO: don't let an attribute overwrite id or class maybeAttribute :: (T.Text, T.Text) -> Maybe XML.Attr maybeAttribute (_, "") = Nothing maybeAttribute ("", _) = Nothing maybeAttribute (name, value) = Just $ XML.Attr (unqual name) value validAttributes :: [(T.Text, T.Text)] -> [XML.Attr] validAttributes pairs = mapMaybe maybeAttribute pairs appendContents :: [Content] -> Element -> Element appendContents newContents el = el {elContent = (elContent el) ++ newContents} prependContents :: [Content] -> Element -> Element prependContents newContents el = el {elContent = newContents ++ (elContent el)} addAttributes :: [XML.Attr] -> Element -> Element addAttributes newAttrs el = el {elAttribs = newAttrs ++ elAttribs el} addAttribute :: T.Text -> T.Text -> Element -> Element addAttribute attr_name attr_value el = el {elAttribs = new_attr : elAttribs el} where new_attr = XML.Attr (unqual attr_name) attr_value addAttrAttributes :: PandocAttr -> Element -> Element addAttrAttributes (identifier, classes, attributes) el = addAttributes attrs' el where attrs' = mapMaybe maybeAttribute (("id", identifier) : ("class", T.intercalate " " classes) : attributes) addCitations :: [Citation] -> Element -> Element addCitations citations el = appendContents [Elem $ elementWithContents tgNameCitations $ (text_node "\n") : concatMap citation_to_elem citations] el where citation_to_elem :: Citation -> [Content] citation_to_elem citation = asBlockOfInlines with_suffix where cit_elem = elementWithAttributes (itemName citation) attrs prefix = citationPrefix citation suffix = citationSuffix citation with_prefix = if null prefix then cit_elem else appendContents [Elem $ elementWithContents tgNameCitationPrefix $ inlinesToXML prefix] cit_elem with_suffix = if null suffix then with_prefix else appendContents [Elem $ elementWithContents tgNameCitationSuffix $ inlinesToXML suffix] with_prefix attrs = map (\(n, v) -> XML.Attr (unqual n) v) [ ("id", citationId citation), (atNameCitationMode, T.pack $ show $ citationMode citation), (atNameCitationNoteNum, intAsText $ citationNoteNum citation), (atNameCitationHash, intAsText $ citationHash citation) ] definitionListItemToXML :: ([Inline], [[Block]]) -> Content definitionListItemToXML (inlines, defs) = Elem $ elementWithContents tgNameDefListItem $ term ++ wrapArrayOfBlocks tgNameDefListDef defs where term = asBlockOfInlines $ appendContents (inlinesToXML inlines) $ emptyElement tgNameDefListTerm captionToXML :: Caption -> [Content] captionToXML (Caption short blocks) = asBlockOfBlocks with_short_caption where el = elementWithContents "Caption" $ blocksToXML blocks with_short_caption = case (short) of Just inlines -> prependContents (asBlockOfInlines $ elementWithContents tgNameShortCaption $ inlinesToXML inlines) el _ -> el colSpecToXML :: (Alignment, ColWidth) -> [Content] colSpecToXML (align, cw) = asBlockOfInlines colspec where colspec = elementWithAttributes "ColSpec" $ validAttributes [(atNameAlignment, itemName align), (atNameColWidth, colwidth)] colwidth = case (cw) of ColWidth d -> T.pack $ show d ColWidthDefault -> "0" colSpecsToXML :: [(Alignment, ColWidth)] -> [Content] colSpecsToXML colspecs = asBlockOfBlocks $ elementWithContents tgNameColspecs $ concatMap colSpecToXML colspecs tableHeadToXML :: TableHead -> [Content] tableHeadToXML (TableHead attr rows) = asBlockOfBlocks $ elementWithAttrAndContents "TableHead" attr $ concatMap rowToXML rows tableBodyToXML :: TableBody -> [Content] tableBodyToXML (TableBody (idn, cls, attrs) (RowHeadColumns headcols) hrows brows) = asBlockOfBlocks $ elementWithAttrAndContents "TableBody" attr children where attr = (idn, cls, (atNameRowHeadColumns, intAsText headcols) : attrs) header_rows = asBlockOfBlocks $ elementWithContents tgNameBodyHeader $ concatMap rowToXML hrows body_rows = asBlockOfBlocks $ elementWithContents tgNameBodyBody $ concatMap rowToXML brows children = header_rows ++ body_rows tableFootToXML :: TableFoot -> [Content] tableFootToXML (TableFoot attr rows) = asBlockOfBlocks $ elementWithAttrAndContents "TableFoot" attr $ concatMap rowToXML rows rowToXML :: Row -> [Content] rowToXML (Row attr cells) = asBlockOfBlocks $ elementWithAttrAndContents "Row" attr $ concatMap cellToXML cells cellToXML :: Cell -> [Content] cellToXML (Cell (idn, cls, attrs) alignment (RowSpan rowspan) (ColSpan colspan) blocks) = asBlockOfBlocks $ elementWithAttrAndContents "Cell" attr $ blocksToXML blocks where with_alignment a = (atNameAlignment, itemName alignment) : a with_rowspan a = if rowspan > 1 then (atNameRowspan, intAsText rowspan) : a else a with_colspan a = if colspan > 1 then (atNameColspan, intAsText colspan) : a else a attrs' = (with_colspan . with_rowspan . with_alignment) attrs attr = (idn, cls, attrs') ================================================ FILE: src/Text/Pandoc/Writers/XWiki.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2008-2024 John MacFarlane <jgm@berkeley.edu> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Writers.XWiki Copyright : Copyright (C) 2008-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Derek Chen-Becker <dchenbecker@gmail.com> Stability : alpha Portability : portable Conversion of 'Pandoc' documents to XWiki markup. XWiki: <http://www.xwiki.org/> XWiki Syntax: <http://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/Features/XWikiSyntax/> -} module Text.Pandoc.Writers.XWiki ( writeXWiki ) where import Control.Monad.Reader (ReaderT, asks, local, runReaderT) import qualified Data.Set as Set import qualified Data.Text as Text import Data.Text (Text, intercalate, replace, split) import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Shared import Text.Pandoc.URI import Text.Pandoc.Writers.MediaWiki (highlightingLangs) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable) import Text.DocLayout (render, literal) newtype WriterState = WriterState { listLevel :: Text -- String at the beginning of items } type XWikiReader m = ReaderT WriterState m -- | Convert Pandoc to XWiki. writeXWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeXWiki opts (Pandoc meta blocks) = do let env = WriterState { listLevel = "" } metadata <- metaToContext opts (fmap (literal . trimr) . (\bs -> runReaderT (blockListToXWiki bs) env)) (fmap (literal . trimr) . (\is -> runReaderT (inlineListToXWiki is) env)) meta body <- runReaderT (blockListToXWiki blocks) env let context = defField "body" body $ defField "toc" (writerTableOfContents opts) metadata return $ case writerTemplate opts of Just tpl -> render Nothing $ renderTemplate tpl context Nothing -> body -- | Concatenates strings with line breaks between them. vcat :: [Text] -> Text vcat = intercalate "\n" -- If an id is provided, we can generate an anchor using the id macro -- https://extensions.xwiki.org/xwiki/bin/view/Extension/Id%20Macro genAnchor :: Text -> Text genAnchor id' = if Text.null id' then "" else "{{id name=\"" <> id' <> "\" /}}" blockListToXWiki :: PandocMonad m => [Block] -> XWikiReader m Text blockListToXWiki blocks = vcat <$> mapM blockToXWiki blocks blockToXWiki :: PandocMonad m => Block -> XWikiReader m Text blockToXWiki (Div (id', _, _) blocks) = do content <- blockListToXWiki blocks return $ genAnchor id' <> content blockToXWiki (Plain inlines) = inlineListToXWiki inlines blockToXWiki (Para inlines) = do contents <- inlineListToXWiki inlines return $ contents <> "\n" blockToXWiki (LineBlock lns) = blockToXWiki $ linesToPara lns blockToXWiki b@(RawBlock f str) | f == Format "xwiki" = return str | otherwise = "" <$ report (BlockNotRendered b) blockToXWiki HorizontalRule = return "\n----\n" blockToXWiki (Header level (id', _, _) inlines) = do contents <- inlineListToXWiki inlines let eqs = Text.replicate level "=" return $ eqs <> " " <> contents <> " " <> genAnchor id' <> eqs <> "\n" -- XWiki doesn't appear to differentiate between inline and block-form code, so we delegate -- We do amend the text to ensure that the code markers are on their own lines, since this is a block blockToXWiki (CodeBlock attrs str) = do contents <- inlineToXWiki (Code attrs ("\n" <> str <> "\n")) return $ "\n" <> contents <> "\n" blockToXWiki (BlockQuote blocks) = do blockText <- blockListToXWiki blocks let quoteLines = split (== '\n') blockText let prefixed = map (">" <>) quoteLines return $ vcat prefixed blockToXWiki (BulletList contents) = blockToXWikiList "*" contents blockToXWiki (OrderedList _ contents) = blockToXWikiList "1" contents blockToXWiki (DefinitionList items) = do lev <- asks listLevel contents <- local (\s -> s { listLevel = listLevel s <> ";" }) $ mapM definitionListItemToMediaWiki items return $ vcat contents <> if Text.null lev then "\n" else "" -- Create a group according to -- https://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/Features/XWikiSyntax/?syntax=2.1§ion=Groups blockToXWiki (Figure attr _ body) = do content <- blockToXWiki $ Div attr body return $ intercalate content ["(((\n", "\n)))"] -- TODO: support more features blockToXWiki (Table _ blkCapt specs thead tbody tfoot) = do let (_, _, _, headers, rows') = toLegacyTable blkCapt specs thead tbody tfoot headers' <- mapM (tableCellXWiki True) $ take (length specs) $ headers ++ repeat [] otherRows <- mapM formRow rows' return $ Text.unlines (Text.unwords headers':otherRows) formRow :: PandocMonad m => [[Block]] -> XWikiReader m Text formRow row = do cellStrings <- mapM (tableCellXWiki False) row return $ Text.unwords cellStrings tableCellXWiki :: PandocMonad m => Bool -> [Block] -> XWikiReader m Text tableCellXWiki isHeader cell = do contents <- blockListToXWiki cell let isMultiline = (length . split (== '\n')) contents > 1 let contents' = intercalate contents $ if isMultiline then ["(((", ")))"] else [mempty, mempty] let cellBorder = if isHeader then "|=" else "|" return $ cellBorder <> contents' inlineListToXWiki :: PandocMonad m => [Inline] -> XWikiReader m Text inlineListToXWiki lst = mconcat <$> mapM inlineToXWiki lst inlineToXWiki :: PandocMonad m => Inline -> XWikiReader m Text inlineToXWiki (Str str) = return $ escapeXWikiString str inlineToXWiki Space = return " " -- Special syntax for XWiki 2.0. This won't break table cells inlineToXWiki LineBreak = return "\\\\" inlineToXWiki SoftBreak = return " " inlineToXWiki (Emph lst) = do contents <- inlineListToXWiki lst return $ "//" <> contents <> "//" inlineToXWiki (Underline lst) = do contents <- inlineListToXWiki lst return $ "__" <> contents <> "__" inlineToXWiki (Strong lst) = do contents <- inlineListToXWiki lst return $ "**" <> contents <> "**" inlineToXWiki (Strikeout lst) = do contents <- inlineListToXWiki lst return $ "--" <> contents <> "--" inlineToXWiki (Superscript lst) = do contents <- inlineListToXWiki lst return $ "^^" <> contents <> "^^" inlineToXWiki (Subscript lst) = do contents <- inlineListToXWiki lst return $ ",," <> contents <> ",," -- TODO: Not supported. Maybe escape to HTML? inlineToXWiki (SmallCaps lst) = inlineListToXWiki lst inlineToXWiki (Quoted SingleQuote lst) = do contents <- inlineListToXWiki lst return $ "‘" <> contents <> "’" inlineToXWiki (Quoted DoubleQuote lst) = do contents <- inlineListToXWiki lst return $ "“" <> contents <> "”" inlineToXWiki (Code (_,classes,_) contents) = do let at = Set.fromList classes `Set.intersection` highlightingLangs return $ case Set.toList at of [] -> "{{code}}" <> contents <> "{{/code}}" (l:_) -> "{{code language=\"" <> l <> "\"}}" <> contents <> "{{/code}}" inlineToXWiki (Cite _ lst) = inlineListToXWiki lst -- FIXME: optionally support this (plugin?) inlineToXWiki (Math _ str) = return $ "{{formula}}" <> str <> "{{/formula}}" inlineToXWiki il@(RawInline frmt str) | frmt == Format "xwiki" = return str | otherwise = "" <$ report (InlineNotRendered il) -- TODO: Handle anchors inlineToXWiki (Link (id', _, _) txt (src, _)) = do label <- inlineListToXWiki txt case txt of [Str s] | isURI src && escapeURI s == src -> return $ src <> genAnchor id' _ -> return $ "[[" <> label <> ">>" <> src <> "]]" <> genAnchor id' inlineToXWiki (Image _ alt (source, tit)) = do alt' <- inlineListToXWiki alt let params = Text.unwords $ filter (not . Text.null) [ if Text.null alt' then "" else "alt=\"" <> alt' <> "\"", if Text.null tit then "" else "title=\"" <> tit <> "\"" ] return $ "[[image:" <> source <> (if Text.null params then "" else "||" <> params) <> "]]" inlineToXWiki (Note contents) = do contents' <- blockListToXWiki contents return $ "{{footnote}}" <> Text.strip contents' <> "{{/footnote}}" -- TODO: support attrs other than id (anchor) inlineToXWiki (Span (id', _, _) contents) = do contents' <- inlineListToXWiki contents return $ genAnchor id' <> contents' -- Utility method since (for now) all lists are handled the same way blockToXWikiList :: PandocMonad m => Text -> [[Block]] -> XWikiReader m Text blockToXWikiList marker contents = do lev <- asks listLevel contents' <- local (\s -> s { listLevel = listLevel s <> marker } ) $ mapM listItemToXWiki contents return $ vcat contents' <> if Text.null lev then "\n" else "" listItemToXWiki :: PandocMonad m => [Block] -> XWikiReader m Text listItemToXWiki contents = do marker <- asks listLevel contents' <- blockListToXWiki contents return $ marker <> ". " <> Text.strip contents' -- | Convert definition list item (label, list of blocks) to MediaWiki. definitionListItemToMediaWiki :: PandocMonad m => ([Inline],[[Block]]) -> XWikiReader m Text definitionListItemToMediaWiki (label, items) = do labelText <- inlineListToXWiki label contents <- mapM blockListToXWiki items marker <- asks listLevel return $ marker <> " " <> labelText <> "\n" <> intercalate "\n" (map (\d -> Text.init marker <> ": " <> d) contents) -- Escape the escape character, as well as formatting pairs escapeXWikiString :: Text -> Text escapeXWikiString s = foldr (uncurry replace) s $ zip ["--", "**", "//", "^^", ",,", "~"] ["~-~-", "~*~*", "~/~/", "~^~^", "~,~,", "~~"] ================================================ FILE: src/Text/Pandoc/Writers/ZimWiki.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Writers.ZimWiki Copyright : © 2008-2024 John MacFarlane, 2017-2019 Alex Ivkin License : GNU GPL, version 2 or above Maintainer : Alex Ivkin <alex@ivkin.net> Stability : beta Portability : portable Conversion of 'Pandoc' documents to ZimWiki markup. http://zim-wiki.org/manual/Help/Wiki_Syntax.html -} module Text.Pandoc.Writers.ZimWiki ( writeZimWiki ) where import Control.Monad (zipWithM) import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (transpose) import Data.List.NonEmpty (nonEmpty) import qualified Data.Map as Map import Text.DocLayout (render, literal) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) import Text.Pandoc.Shared (figureDiv, linesToPara, removeFormatting, trimr) import Text.Pandoc.URI (escapeURI, isURI) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable) data WriterState = WriterState { stIndent :: Text, -- Indent after the marker at the beginning of list items stInTable :: Bool, -- Inside a table stInLink :: Bool -- Inside a link description } instance Default WriterState where def = WriterState { stIndent = "", stInTable = False, stInLink = False } type ZW = StateT WriterState -- | Convert Pandoc to ZimWiki. writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeZimWiki opts document = evalStateT (pandocToZimWiki opts document) def -- | Return ZimWiki representation of document. pandocToZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> ZW m Text pandocToZimWiki opts (Pandoc meta blocks) = do metadata <- metaToContext opts (fmap (literal . trimr) . blockListToZimWiki opts) (fmap (literal . trimr) . inlineListToZimWiki opts) meta main <- blockListToZimWiki opts blocks --let header = "Content-Type: text/x-zim-wiki\nWiki-Format: zim 0.4\n" let context = defField "body" main $ defField "toc" (writerTableOfContents opts) metadata return $ case writerTemplate opts of Just tpl -> render Nothing $ renderTemplate tpl context Nothing -> main -- | Escape special characters for ZimWiki. escapeText :: Text -> Text escapeText = T.replace "__" "''__''" . T.replace "**" "''**''" . T.replace "~~" "''~~''" . T.replace "//" "''//''" -- | Convert Pandoc block element to ZimWiki. blockToZimWiki :: PandocMonad m => WriterOptions -> Block -> ZW m Text blockToZimWiki opts (Div _attrs bs) = do contents <- blockListToZimWiki opts bs return $ contents <> "\n" blockToZimWiki opts (Plain inlines) = inlineListToZimWiki opts inlines blockToZimWiki opts (Para inlines) = do indent <- gets stIndent -- useTags <- gets stUseTags contents <- inlineListToZimWiki opts inlines return $ contents <> if T.null indent then "\n" else "" blockToZimWiki opts (LineBlock lns) = blockToZimWiki opts $ linesToPara lns blockToZimWiki opts b@(RawBlock f str) | f == Format "zimwiki" = return str | f == Format "html" = indentFromHTML opts str | otherwise = do report $ BlockNotRendered b return "" blockToZimWiki _ HorizontalRule = return "\n----\n" blockToZimWiki opts (Header level _ inlines) = do contents <- inlineListToZimWiki opts inlines let eqs = T.replicate ( 7 - level ) "=" return $ eqs <> " " <> contents <> " " <> eqs <> "\n" blockToZimWiki _ (CodeBlock (_,classes,_) str) = do -- Remap languages into the gtksourceview2 convention that ZimWiki source code plugin is using let langal = [("javascript", "js"), ("bash", "sh"), ("winbatch", "dosbatch")] let langmap = Map.fromList langal return $ case classes of [] -> "'''\n" <> cleanupCode str <> "\n'''\n" -- turn no lang block into a quote block (x:_) -> "{{{code: lang=\"" <> fromMaybe x (Map.lookup x langmap) <> "\" linenumbers=\"True\"\n" <> str <> "\n}}}\n" -- for zim's code plugin, go verbatim on the lang spec blockToZimWiki opts (BlockQuote blocks) = do contents <- blockListToZimWiki opts blocks return $ T.unlines $ map ("> " <>) $ T.lines contents blockToZimWiki opts (Table _ blkCapt specs thead tbody tfoot) = do let (capt, aligns, _, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot captionDoc <- if null capt then return "" else do c <- inlineListToZimWiki opts capt return $ "" <> c <> "\n" headers' <- if all null headers then case rows of [] -> pure mempty (r:_) -> zipWithM (tableItemToZimWiki opts) aligns r else mapM (inlineListToZimWiki opts . removeFormatting)headers -- emphasis, links etc. are not allowed in table headers rows' <- mapM (zipWithM (tableItemToZimWiki opts) aligns) rows let widths = map (maybe 0 maximum . nonEmpty . map T.length) $ transpose (headers':rows') let padTo (width, al) s = case width - T.length s of x | x > 0 -> if al == AlignLeft || al == AlignDefault then s <> T.replicate x " " else if al == AlignRight then T.replicate x " " <> s else T.replicate (x `div` 2) " " <> s <> T.replicate (x - x `div` 2) " " | otherwise -> s let borderCell (width, al) _ | al == AlignLeft = ":"<> T.replicate (width-1) "-" | al == AlignDefault = T.replicate width "-" | al == AlignRight = T.replicate (width-1) "-" <> ":" | otherwise = ":" <> T.replicate (width-2) "-" <> ":" let underheader = "|" <> T.intercalate "|" (zipWith borderCell (zip widths aligns) headers') <> "|" let renderRow cells = "|" <> T.intercalate "|" (zipWith padTo (zip widths aligns) cells) <> "|" return $ captionDoc <> (if null headers' then "" else renderRow headers' <> "\n") <> underheader <> "\n" <> T.unlines (map renderRow rows') blockToZimWiki opts (BulletList items) = do contents <- mapM (listItemToZimWiki opts) items indent <- gets stIndent return $ vcat contents <> if T.null indent then "\n" else "" blockToZimWiki opts (OrderedList _ items) = do contents <- zipWithM (orderedListItemToZimWiki opts) [1..] items indent <- gets stIndent return $ vcat contents <> if T.null indent then "\n" else "" blockToZimWiki opts (DefinitionList items) = do contents <- mapM (definitionListItemToZimWiki opts) items return $ vcat contents blockToZimWiki opts (Figure attr capt body) = do blockToZimWiki opts (figureDiv attr capt body) definitionListItemToZimWiki :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) -> ZW m Text definitionListItemToZimWiki opts (label, items) = do labelText <- inlineListToZimWiki opts label contents <- mapM (blockListToZimWiki opts) items indent <- gets stIndent return $ indent <> "* **" <> labelText <> "** " <> T.concat contents -- Auxiliary functions for lists: indentFromHTML :: PandocMonad m => WriterOptions -> Text -> ZW m Text indentFromHTML _ str = do indent <- gets stIndent if "<li>" `T.isInfixOf` str then return indent else if "</li>" `T.isInfixOf` str then return "\n" else if "<li value=" `T.isInfixOf` str then return "" else if "<ol>" `T.isInfixOf` str then do let olcount=countSubStrs "<ol>" str modify $ \s -> s { stIndent = stIndent s <> T.replicate olcount "\t" } return "" else if "</ol>" `T.isInfixOf` str then do let olcount=countSubStrs "/<ol>" str modify $ \s -> s{ stIndent = T.drop olcount (stIndent s) } return "" else return "" countSubStrs :: Text -> Text -> Int countSubStrs sub str = length $ T.breakOnAll sub str cleanupCode :: Text -> Text cleanupCode = T.replace "<nowiki>" "" . T.replace "</nowiki>" "" vcat :: [Text] -> Text vcat = T.intercalate "\n" -- | Convert bullet list item (list of blocks) to ZimWiki. listItemToZimWiki :: PandocMonad m => WriterOptions -> [Block] -> ZW m Text listItemToZimWiki opts items = do indent <- gets stIndent modify $ \s -> s { stIndent = indent <> "\t" } contents <- blockListToZimWiki opts items modify $ \s -> s{ stIndent = indent } return $ indent <> "* " <> contents -- | Convert ordered list item (list of blocks) to ZimWiki. orderedListItemToZimWiki :: PandocMonad m => WriterOptions -> Int -> [Block] -> ZW m Text orderedListItemToZimWiki opts itemnum items = do indent <- gets stIndent modify $ \s -> s { stIndent = indent <> "\t" } contents <- blockListToZimWiki opts items modify $ \s -> s{ stIndent = indent } return $ indent <> T.pack (show itemnum) <> ". " <> contents -- Auxiliary functions for tables: tableItemToZimWiki :: PandocMonad m => WriterOptions -> Alignment -> [Block] -> ZW m Text tableItemToZimWiki opts align' item = do let mkcell x = (if align' == AlignRight || align' == AlignCenter then " " else "") <> x <> (if align' == AlignLeft || align' == AlignCenter then " " else "") modify $ \s -> s { stInTable = True } contents <- blockListToZimWiki opts item modify $ \s -> s { stInTable = False } return $ mkcell contents -- | Convert list of Pandoc block elements to ZimWiki. blockListToZimWiki :: PandocMonad m => WriterOptions -> [Block] -> ZW m Text blockListToZimWiki opts blocks = vcat <$> mapM (blockToZimWiki opts) blocks -- | Convert list of Pandoc inline elements to ZimWiki. inlineListToZimWiki :: PandocMonad m => WriterOptions -> [Inline] -> ZW m Text inlineListToZimWiki opts lst = T.concat <$> mapM (inlineToZimWiki opts) lst -- | Convert Pandoc inline element to ZimWiki. inlineToZimWiki :: PandocMonad m => WriterOptions -> Inline -> ZW m Text inlineToZimWiki opts (Emph lst) = do contents <- inlineListToZimWiki opts lst return $ "//" <> contents <> "//" inlineToZimWiki opts (Underline lst) = do contents <- inlineListToZimWiki opts lst return $ "__" <> contents <> "__" inlineToZimWiki opts (Strong lst) = do contents <- inlineListToZimWiki opts lst return $ "**" <> contents <> "**" inlineToZimWiki opts (Strikeout lst) = do contents <- inlineListToZimWiki opts lst return $ "~~" <> contents <> "~~" inlineToZimWiki opts (Superscript lst) = do contents <- inlineListToZimWiki opts lst return $ "^{" <> contents <> "}" inlineToZimWiki opts (Subscript lst) = do contents <- inlineListToZimWiki opts lst return $ "_{" <> contents <> "}" inlineToZimWiki opts (Quoted SingleQuote lst) = do contents <- inlineListToZimWiki opts lst return $ "\8216" <> contents <> "\8217" inlineToZimWiki opts (Quoted DoubleQuote lst) = do contents <- inlineListToZimWiki opts lst return $ "\8220" <> contents <> "\8221" inlineToZimWiki opts (Span _attrs ils) = inlineListToZimWiki opts ils inlineToZimWiki opts (SmallCaps lst) = inlineListToZimWiki opts lst inlineToZimWiki opts (Cite _ lst) = inlineListToZimWiki opts lst inlineToZimWiki _ (Code _ str) = return $ "''" <> str <> "''" inlineToZimWiki _ (Str str) = do inTable <- gets stInTable inLink <- gets stInLink if inTable then return $ T.replace "|" "\\|" . escapeText $ str else if inLink then return str else return $ escapeText str inlineToZimWiki _ (Math mathType str) = return $ delim <> str <> delim -- note: str should NOT be escaped where delim = case mathType of DisplayMath -> "$$" InlineMath -> "$" -- f == Format "html" = return $ "<html>" <> str <> "</html>" inlineToZimWiki opts il@(RawInline f str) | f == Format "zimwiki" = return str | f == Format "html" = indentFromHTML opts str | otherwise = do report $ InlineNotRendered il return "" inlineToZimWiki _ LineBreak = do inTable <- gets stInTable if inTable then return "\\n" else return "\n" inlineToZimWiki opts SoftBreak = case writerWrapText opts of WrapNone -> return " " WrapAuto -> return " " WrapPreserve -> return "\n" inlineToZimWiki _ Space = return " " inlineToZimWiki opts (Link _ txt (src, _)) = do inTable <- gets stInTable modify $ \s -> s { stInLink = True } label <- inlineListToZimWiki opts $ removeFormatting txt -- zim does not allow formatting in link text, it takes the text verbatim, no need to escape it modify $ \s -> s { stInLink = False } let label'= if inTable then "" -- no label is allowed in a table else "|"<>label case txt of [Str s] | "mailto:" `T.isPrefixOf` src -> return $ "<" <> s <> ">" | escapeURI s == src -> return src _ -> if isURI src then return $ "[[" <> src <> label' <> "]]" else return $ "[[" <> src' <> label' <> "]]" where -- with leading / it's a link to a help page src' = fromMaybe src $ T.stripPrefix "/" src inlineToZimWiki opts (Image attr alt (source, tit)) = do alt' <- inlineListToZimWiki opts alt inTable <- gets stInTable let txt = case (tit, alt, inTable) of ("",[], _) -> "" ("", _, False ) -> "|" <> alt' (_ , _, False ) -> "|" <> tit (_ , _, True ) -> "" return $ "{{" <> source <> imageDims opts attr <> txt <> "}}" inlineToZimWiki opts (Note contents) = do -- no concept of notes in zim wiki, use a text block contents' <- blockListToZimWiki opts contents return $ " **{Note:** " <> trimr contents' <> "**}**" imageDims :: WriterOptions -> Attr -> Text imageDims opts attr = go (toPx $ dimension Width attr) (toPx $ dimension Height attr) where toPx = fmap (showInPixel opts) . checkPct checkPct (Just (Percent _)) = Nothing checkPct maybeDim = maybeDim go (Just w) Nothing = "?" <> w go (Just w) (Just h) = "?" <> w <> "x" <> h go Nothing (Just h) = "?0x" <> h go Nothing Nothing = "" ================================================ FILE: src/Text/Pandoc/Writers.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- | Module : Text.Pandoc Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable This helper module exports all writers functions. -} module Text.Pandoc.Writers ( -- * Writers: converting /from/ Pandoc format Writer(..) , writers , writeANSI , writeAsciiDoc , writeAsciiDocLegacy , writeAsciiDoctor , writeBeamer , writeBibTeX , writeBibLaTeX , writeChunkedHTML , writeCommonMark , writeConTeXt , writeCslJson , writeDZSlides , writeDjot , writeDocBook4 , writeDocBook5 , writeDocx , writeDokuWiki , writeEPUB2 , writeEPUB3 , writeFB2 , writeIpynb , writeHaddock , writeHtml4 , writeHtml4String , writeHtml5 , writeHtml5String , writeICML , writeJATS , writeJatsArchiving , writeJatsArticleAuthoring , writeJatsPublishing , writeJSON , writeJira , writeLaTeX , writeMan , writeMarkdown , writeMarkua , writeMediaWiki , writeMs , writeMuse , writeNative , writeODT , writeOPML , writeOpenDocument , writeOrg , writePlain , writePowerpoint , writeRST , writeRTF , writeRevealJs , writeS5 , writeSlideous , writeSlidy , writeTEI , writeTexinfo , writeTextile , writeTypst , writeXML , writeXWiki , writeZimWiki , writeVimdoc , writeBBCode , writeBBCodeSteam , writeBBCodeFluxBB , writeBBCodePhpBB , writeBBCodeHubzilla , writeBBCodeXenforo , getWriter ) where import Control.Monad.Except (throwError) import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.Text (Text) import Text.Pandoc.Class import Text.Pandoc.Definition import qualified Text.Pandoc.Format as Format import Text.Pandoc.Options import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Error import Text.Pandoc.Writers.ANSI import Text.Pandoc.Writers.AsciiDoc import Text.Pandoc.Writers.BibTeX import Text.Pandoc.Writers.ChunkedHTML import Text.Pandoc.Writers.CommonMark import Text.Pandoc.Writers.ConTeXt import Text.Pandoc.Writers.CslJson import Text.Pandoc.Writers.Djot import Text.Pandoc.Writers.DocBook import Text.Pandoc.Writers.Docx import Text.Pandoc.Writers.DokuWiki import Text.Pandoc.Writers.EPUB import Text.Pandoc.Writers.FB2 import Text.Pandoc.Writers.Ipynb import Text.Pandoc.Writers.Haddock import Text.Pandoc.Writers.HTML import Text.Pandoc.Writers.ICML import Text.Pandoc.Writers.JATS import Text.Pandoc.Writers.Jira import Text.Pandoc.Writers.LaTeX import Text.Pandoc.Writers.Man import Text.Pandoc.Writers.Markdown import Text.Pandoc.Writers.MediaWiki import Text.Pandoc.Writers.Ms import Text.Pandoc.Writers.Muse import Text.Pandoc.Writers.Native import Text.Pandoc.Writers.ODT import Text.Pandoc.Writers.OpenDocument import Text.Pandoc.Writers.OPML import Text.Pandoc.Writers.Org import Text.Pandoc.Writers.Powerpoint import Text.Pandoc.Writers.RST import Text.Pandoc.Writers.RTF import Text.Pandoc.Writers.TEI import Text.Pandoc.Writers.Texinfo import Text.Pandoc.Writers.Textile import Text.Pandoc.Writers.Typst import Text.Pandoc.Writers.XML import Text.Pandoc.Writers.XWiki import Text.Pandoc.Writers.ZimWiki import Text.Pandoc.Writers.Vimdoc import Text.Pandoc.Writers.BBCode ( writeBBCode, writeBBCodeFluxBB, writeBBCodeHubzilla, writeBBCodePhpBB, writeBBCodeSteam, writeBBCodeXenforo, ) data Writer m = TextWriter (WriterOptions -> Pandoc -> m Text) | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString) -- | Association list of formats and writers. writers :: PandocMonad m => [ (Text, Writer m) ] writers = [ ("native" , TextWriter writeNative) ,("json" , TextWriter writeJSON) ,("docx" , ByteStringWriter writeDocx) ,("odt" , ByteStringWriter writeODT) ,("pptx" , ByteStringWriter writePowerpoint) ,("epub" , ByteStringWriter writeEPUB3) ,("epub2" , ByteStringWriter writeEPUB2) ,("epub3" , ByteStringWriter writeEPUB3) ,("fb2" , TextWriter writeFB2) ,("ipynb" , TextWriter writeIpynb) ,("html" , TextWriter writeHtml5String) ,("html4" , TextWriter writeHtml4String) ,("html5" , TextWriter writeHtml5String) ,("icml" , TextWriter writeICML) ,("s5" , TextWriter writeS5) ,("slidy" , TextWriter writeSlidy) ,("slideous" , TextWriter writeSlideous) ,("dzslides" , TextWriter writeDZSlides) ,("revealjs" , TextWriter writeRevealJs) ,("docbook" , TextWriter writeDocBook5) ,("docbook4" , TextWriter writeDocBook4) ,("docbook5" , TextWriter writeDocBook5) ,("jats" , TextWriter writeJatsArchiving) ,("jats_articleauthoring", TextWriter writeJatsArticleAuthoring) ,("jats_publishing" , TextWriter writeJatsPublishing) ,("jats_archiving" , TextWriter writeJatsArchiving) ,("jira" , TextWriter writeJira) ,("opml" , TextWriter writeOPML) ,("opendocument" , TextWriter writeOpenDocument) ,("latex" , TextWriter writeLaTeX) ,("beamer" , TextWriter writeBeamer) ,("context" , TextWriter writeConTeXt) ,("texinfo" , TextWriter writeTexinfo) ,("man" , TextWriter writeMan) ,("ms" , TextWriter writeMs) ,("markdown" , TextWriter writeMarkdown) ,("markdown_strict" , TextWriter writeMarkdown) ,("markdown_phpextra" , TextWriter writeMarkdown) ,("markdown_github" , TextWriter writeMarkdown) ,("markdown_mmd" , TextWriter writeMarkdown) ,("plain" , TextWriter writePlain) ,("rst" , TextWriter writeRST) ,("mediawiki" , TextWriter writeMediaWiki) ,("dokuwiki" , TextWriter writeDokuWiki) ,("xwiki" , TextWriter writeXWiki) ,("zimwiki" , TextWriter writeZimWiki) ,("textile" , TextWriter writeTextile) ,("typst" , TextWriter writeTypst) ,("rtf" , TextWriter writeRTF) ,("org" , TextWriter writeOrg) ,("asciidoc" , TextWriter writeAsciiDoc) ,("asciidoctor" , TextWriter writeAsciiDoc) ,("asciidoc_legacy" , TextWriter writeAsciiDocLegacy) ,("haddock" , TextWriter writeHaddock) ,("commonmark" , TextWriter writeCommonMark) ,("commonmark_x" , TextWriter writeCommonMark) ,("gfm" , TextWriter writeCommonMark) ,("tei" , TextWriter writeTEI) ,("muse" , TextWriter writeMuse) ,("csljson" , TextWriter writeCslJson) ,("bibtex" , TextWriter writeBibTeX) ,("biblatex" , TextWriter writeBibLaTeX) ,("markua" , TextWriter writeMarkua) ,("chunkedhtml" , ByteStringWriter writeChunkedHTML) ,("djot" , TextWriter writeDjot) ,("ansi" , TextWriter writeANSI) ,("xml" , TextWriter writeXML) ,("vimdoc" , TextWriter writeVimdoc) ,("bbcode" , TextWriter writeBBCode) ,("bbcode_steam" , TextWriter writeBBCodeSteam) ,("bbcode_phpbb" , TextWriter writeBBCodePhpBB) ,("bbcode_fluxbb", TextWriter writeBBCodeFluxBB) ,("bbcode_hubzilla" , TextWriter writeBBCodeHubzilla) ,("bbcode_xenforo" , TextWriter writeBBCodeXenforo) ] -- | Retrieve writer, extensions based on formatSpec (format+extensions). getWriter :: PandocMonad m => Format.FlavoredFormat -> m (Writer m, Extensions) getWriter flvrd = do let writerName = Format.formatName flvrd case lookup writerName writers of Nothing -> throwError $ PandocUnknownWriterError writerName Just w -> (w,) <$> Format.applyExtensionsDiff (Format.getExtensionsConfig writerName) flvrd writeJSON :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJSON _ = return . UTF8.toText . BL.toStrict . encode ================================================ FILE: src/Text/Pandoc/XML.hs ================================================ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.XML Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Functions for escaping and formatting XML. -} module Text.Pandoc.XML ( escapeCharForXML, escapeStringForXML, escapeNCName, inTags, selfClosingTag, inTagsSimple, inTagsIndented, toEntities, toHtml5Entities, fromEntities, lookupEntity, html4Attributes, html5Attributes, rdfaAttributes ) where import Data.Char (isAscii, isSpace, ord, isLetter, isDigit) import Data.Text (Text) import qualified Data.Text as T import Commonmark.Entity (lookupEntity) import Text.HTML.TagSoup.Entity (htmlEntities) import Text.DocLayout ( ($$), char, hcat, nest, text, Doc, HasChars ) import Text.Printf (printf) import qualified Data.Map as M import Data.String ( IsString ) import qualified Data.Set as Set -- | Escape one character as needed for XML. escapeCharForXML :: Char -> Text escapeCharForXML x = case x of '&' -> "&" '<' -> "<" '>' -> ">" '"' -> """ c -> T.singleton c -- | Escape string as needed for XML. Entity references are not preserved. escapeStringForXML :: Text -> Text escapeStringForXML = T.concatMap escapeCharForXML . T.filter isLegalXMLChar where isLegalXMLChar c = c == '\t' || c == '\n' || c == '\r' || (c >= '\x20' && c <= '\xD7FF') || (c >= '\xE000' && c <= '\xFFFD') || (c >= '\x10000' && c <= '\x10FFFF') -- see https://www.w3.org/TR/xml/#charsets -- | Escape newline characters as escapeNls :: Text -> Text escapeNls = T.concatMap $ \case '\n' -> " " c -> T.singleton c -- | Return a text object with a string of formatted XML attributes. attributeList :: (HasChars a, IsString a) => [(Text, Text)] -> Doc a attributeList = hcat . map (\(a, b) -> text (T.unpack $ " " <> escapeStringForXML a <> "=\"" <> escapeNls (escapeStringForXML b) <> "\"")) -- | Put the supplied contents between start and end tags of tagType, -- with specified attributes and (if specified) indentation. inTags :: (HasChars a, IsString a) => Bool -> Text -> [(Text, Text)] -> Doc a -> Doc a inTags isIndented tagType attribs contents = let openTag = char '<' <> text (T.unpack tagType) <> attributeList attribs <> char '>' closeTag = text "</" <> text (T.unpack tagType) <> char '>' in if isIndented then openTag $$ nest 2 contents $$ closeTag else openTag <> contents <> closeTag -- | Return a self-closing tag of tagType with specified attributes selfClosingTag :: (HasChars a, IsString a) => Text -> [(Text, Text)] -> Doc a selfClosingTag tagType attribs = char '<' <> text (T.unpack tagType) <> attributeList attribs <> text " />" -- | Put the supplied contents between start and end tags of tagType. inTagsSimple :: (HasChars a, IsString a) => Text -> Doc a -> Doc a inTagsSimple tagType = inTags False tagType [] -- | Put the supplied contents in indented block btw start and end tags. inTagsIndented :: (HasChars a, IsString a) => Text -> Doc a -> Doc a inTagsIndented tagType = inTags True tagType [] -- | Escape all non-ascii characters using numerical entities. toEntities :: Text -> Text toEntities = T.concatMap go where go c | isAscii c = T.singleton c | otherwise = T.pack (printf "&#x%X;" (ord c)) -- | Escape all non-ascii characters using HTML5 entities, falling -- back to numerical entities. toHtml5Entities :: Text -> Text toHtml5Entities = T.concatMap go where go c | isAscii c = T.singleton c | otherwise = case M.lookup c html5EntityMap of Just t -> T.singleton '&' <> t <> T.singleton ';' Nothing -> T.pack ("&#" ++ show (ord c) ++ ";") html5EntityMap :: M.Map Char Text html5EntityMap = foldr go mempty htmlEntities where go (ent, s) entmap = case s of [c] -> M.insertWith (\new old -> if T.length new > T.length old then old else new) c ent' entmap where ent' = T.takeWhile (/=';') (T.pack ent) _ -> entmap -- | Converts a string into an NCName, i.e., an XML name without colons. -- Disallowed characters are escaped using @ux%x@, where @%x@ is the -- hexadecimal unicode identifier of the escaped character. escapeNCName :: Text -> Text escapeNCName t = case T.uncons t of Nothing -> T.empty Just (c, cs) -> escapeStartChar c <> T.concatMap escapeNCNameChar cs where escapeStartChar :: Char -> Text escapeStartChar c = if isLetter c || c == '_' then T.singleton c else escapeChar c escapeNCNameChar :: Char -> Text escapeNCNameChar c = if isNCNameChar c then T.singleton c else escapeChar c isNCNameChar :: Char -> Bool isNCNameChar c = isLetter c || c `elem` ("_-.·" :: String) || isDigit c || '\x0300' <= c && c <= '\x036f' || '\x203f' <= c && c <= '\x2040' escapeChar :: Char -> Text escapeChar = T.pack . printf "U%04X" . ord -- | Unescapes XML entities fromEntities :: Text -> Text fromEntities t = let (x, y) = T.break (== '&') t in if T.null y then t else x <> let (ent, rest) = T.break (\c -> isSpace c || c == ';') y rest' = case T.uncons rest of Just (';',ys) -> ys _ -> rest ent' = T.drop 1 ent <> ";" in case lookupEntity ent' of Just c -> c <> fromEntities rest' Nothing -> ent <> fromEntities rest html5Attributes :: Set.Set Text html5Attributes = Set.fromList [ "abbr" , "accept" , "accept-charset" , "accesskey" , "action" , "allow" , "allowfullscreen" , "allowpaymentrequest" , "allowusermedia" , "alt" , "as" , "async" , "autocapitalize" , "autocomplete" , "autofocus" , "autoplay" , "charset" , "checked" , "cite" , "class" , "color" , "cols" , "colspan" , "content" , "contenteditable" , "controls" , "coords" , "crossorigin" , "data" , "datetime" , "decoding" , "default" , "defer" , "dir" , "dirname" , "disabled" , "download" , "draggable" , "enctype" , "enterkeyhint" , "fetchpriority" , "for" , "form" , "formaction" , "formenctype" , "formmethod" , "formnovalidate" , "formtarget" , "headers" , "height" , "hidden" , "high" , "href" , "hreflang" , "http-equiv" , "id" , "imagesizes" , "imagesrcset" , "inputmode" , "integrity" , "is" , "ismap" , "itemid" , "itemprop" , "itemref" , "itemscope" , "itemtype" , "kind" , "label" , "lang" , "list" , "loading" , "loop" , "low" , "manifest" , "max" , "maxlength" , "media" , "method" , "min" , "minlength" , "multiple" , "muted" , "name" , "nomodule" , "nonce" , "novalidate" , "onabort" , "onafterprint" , "onauxclick" , "onbeforeprint" , "onbeforeunload" , "onblur" , "oncancel" , "oncanplay" , "oncanplaythrough" , "onchange" , "onclick" , "onclose" , "oncontextmenu" , "oncopy" , "oncuechange" , "oncut" , "ondblclick" , "ondrag" , "ondragend" , "ondragenter" , "ondragexit" , "ondragleave" , "ondragover" , "ondragstart" , "ondrop" , "ondurationchange" , "onemptied" , "onended" , "onerror" , "onfocus" , "onhashchange" , "oninput" , "oninvalid" , "onkeydown" , "onkeypress" , "onkeyup" , "onlanguagechange" , "onload" , "onloadeddata" , "onloadedmetadata" , "onloadend" , "onloadstart" , "onmessage" , "onmessageerror" , "onmousedown" , "onmouseenter" , "onmouseleave" , "onmousemove" , "onmouseout" , "onmouseover" , "onmouseup" , "onoffline" , "ononline" , "onpagehide" , "onpageshow" , "onpaste" , "onpause" , "onplay" , "onplaying" , "onpopstate" , "onprogress" , "onratechange" , "onrejectionhandled" , "onreset" , "onresize" , "onscroll" , "onsecuritypolicyviolation" , "onseeked" , "onseeking" , "onselect" , "onstalled" , "onstorage" , "onsubmit" , "onsuspend" , "ontimeupdate" , "ontoggle" , "onunhandledrejection" , "onunload" , "onvolumechange" , "onwaiting" , "onwheel" , "open" , "optimum" , "pattern" , "ping" , "placeholder" , "playsinline" , "poster" , "preload" , "readonly" , "referrerpolicy" , "rel" , "required" , "reversed" , "role" , "rows" , "rowspan" , "sandbox" , "scope" , "selected" , "shape" , "size" , "sizes" , "slot" , "span" , "spellcheck" , "src" , "srcdoc" , "srclang" , "srcset" , "start" , "step" , "style" , "tabindex" , "target" , "title" , "translate" , "type" , "typemustmatch" , "updateviacache" , "usemap" , "value" , "width" , "workertype" , "wrap" ] -- See https://en.wikipedia.org/wiki/RDFa, https://www.w3.org/TR/rdfa-primer/ rdfaAttributes :: Set.Set Text rdfaAttributes = Set.fromList [ "about" , "rel" , "rev" , "src" , "href" , "resource" , "property" , "content" , "datatype" , "typeof" , "vocab" , "prefix" ] html4Attributes :: Set.Set Text html4Attributes = Set.fromList [ "abbr" , "accept" , "accept-charset" , "accesskey" , "action" , "align" , "alink" , "alt" , "archive" , "axis" , "background" , "bgcolor" , "border" , "cellpadding" , "cellspacing" , "char" , "charoff" , "charset" , "checked" , "cite" , "class" , "classid" , "clear" , "code" , "codebase" , "codetype" , "color" , "cols" , "colspan" , "compact" , "content" , "coords" , "data" , "datetime" , "declare" , "defer" , "dir" , "disabled" , "enctype" , "face" , "for" , "frame" , "frameborder" , "headers" , "height" , "href" , "hreflang" , "hspace" , "http-equiv" , "id" , "ismap" , "label" , "lang" , "language" , "link" , "longdesc" , "marginheight" , "marginwidth" , "maxlength" , "media" , "method" , "multiple" , "name" , "nohref" , "noresize" , "noshade" , "nowrap" , "object" , "onblur" , "onchange" , "onclick" , "ondblclick" , "onfocus" , "onkeydown" , "onkeypress" , "onkeyup" , "onload" , "onmousedown" , "onmousemove" , "onmouseout" , "onmouseover" , "onmouseup" , "onreset" , "onselect" , "onsubmit" , "onunload" , "profile" , "prompt" , "readonly" , "rel" , "rev" , "rows" , "rowspan" , "rules" , "scheme" , "scope" , "scrolling" , "selected" , "shape" , "size" , "span" , "src" , "standby" , "start" , "style" , "summary" , "tabindex" , "target" , "text" , "title" , "usemap" , "valign" , "value" , "valuetype" , "version" , "vlink" , "vspace" , "width" ] ================================================ FILE: src/Text/Pandoc/XMLFormat.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Text.Pandoc.XMLFormat ( atNameAlignment, atNameApiVersion, atNameCitationHash, atNameCitationMode, atNameCitationNoteNum, atNameColspan, atNameColWidth, atNameFormat, atNameImageUrl, atNameLevel, atNameLinkUrl, atNameMathType, atNameMetaBoolValue, atNameMetaMapEntryKey, atNameNumberDelim, atNameNumberStyle, atNameQuoteType, atNameRowHeadColumns, atNameRowspan, atNameSpaceCount, atNameStart, atNameStrContent, atNameTitle, tgNameBodyBody, tgNameBodyHeader, tgNameCitations, tgNameCitationPrefix, tgNameCitationSuffix, tgNameColspecs, tgNameDefListDef, tgNameDefListItem, tgNameDefListTerm, tgNameLineItem, tgNameListItem, tgNameMetaMapEntry, tgNameShortCaption, ) where import Data.Text (Text) -- the attribute carrying the API version of pandoc types in the main Pandoc element atNameApiVersion :: Text atNameApiVersion = "api-version" -- the element of a <meta> or <MetaMap> entry tgNameMetaMapEntry :: Text tgNameMetaMapEntry = "entry" -- the attribute carrying the key name of a <meta> or <MetaMap> entry atNameMetaMapEntryKey :: Text atNameMetaMapEntryKey = "key" -- the attribute carrying the boolean value ("true" or "false") of a MetaBool atNameMetaBoolValue :: Text atNameMetaBoolValue = "value" -- level of a Header atNameLevel :: Text atNameLevel = "level" -- start number of an OrderedList atNameStart :: Text atNameStart = "start" -- number delimiter of an OrderedList atNameNumberDelim :: Text atNameNumberDelim = "number-delim" -- number style of an OrderedList atNameNumberStyle :: Text atNameNumberStyle = "number-style" -- target title in Image and Link atNameTitle :: Text atNameTitle = "title" -- target url in Image atNameImageUrl :: Text atNameImageUrl = "src" -- target url in Link atNameLinkUrl :: Text atNameLinkUrl = "href" -- QuoteType of a Quoted atNameQuoteType :: Text atNameQuoteType = "quote-type" -- MathType of a Math atNameMathType :: Text atNameMathType = "math-type" -- format of a RawInline or a RawBlock atNameFormat :: Text atNameFormat = "format" -- alignment attribute in a ColSpec or in a Cell atNameAlignment :: Text atNameAlignment = "alignment" -- ColWidth attribute in a ColSpec atNameColWidth :: Text atNameColWidth = "col-width" -- RowHeadColumns attribute in a TableBody atNameRowHeadColumns :: Text atNameRowHeadColumns = "row-head-columns" -- RowSpan attribute in a Cell atNameRowspan :: Text atNameRowspan = "row-span" -- ColSpan attribute in a Cell atNameColspan :: Text atNameColspan = "col-span" -- the citationMode of a Citation atNameCitationMode :: Text atNameCitationMode = "mode" -- the citationHash of a Citation atNameCitationHash :: Text atNameCitationHash = "hash" -- the citationNoteNum of a Citation atNameCitationNoteNum :: Text atNameCitationNoteNum = "note-num" -- the number of consecutive spaces of the <Space> element atNameSpaceCount :: Text atNameSpaceCount = "count" -- the content of the <Str> element atNameStrContent :: Text atNameStrContent = "content" -- container of Citation elements in Cite inlines tgNameCitations :: Text tgNameCitations = "citations" -- element around the prefix inlines of a Citation tgNameCitationPrefix :: Text tgNameCitationPrefix = "prefix" -- element around the suffix inlines of a Citation tgNameCitationSuffix :: Text tgNameCitationSuffix = "suffix" -- list item for BulletList and OrderedList tgNameListItem :: Text tgNameListItem = "item" -- list item for DefinitionList tgNameDefListItem :: Text tgNameDefListItem = "item" -- element around the inlines of the term of a DefinitionList item tgNameDefListTerm :: Text tgNameDefListTerm = "term" -- element around the blocks of a definition in a DefinitionList item tgNameDefListDef :: Text tgNameDefListDef = "def" -- optional element of the ShortCaption tgNameShortCaption :: Text tgNameShortCaption = "ShortCaption" -- element around the ColSpec of a Table tgNameColspecs :: Text tgNameColspecs = "colspecs" -- element around the header rows of a TableBody tgNameBodyHeader :: Text tgNameBodyHeader = "header" -- element around the body rows of a TableBody tgNameBodyBody :: Text tgNameBodyBody = "body" -- element around the inlines of a line in a LineBlock tgNameLineItem :: Text tgNameLineItem = "line" ================================================ FILE: src/Text/Pandoc.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Text.Pandoc Copyright : Copyright (C) 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable This helper module exports the main writers, readers, and data structure definitions from the Pandoc libraries. A typical application will chain together a reader and a writer to convert strings from one format to another. For example, the following simple program will act as a filter converting markdown fragments to reStructuredText, using reference-style links instead of inline links: > module Main where > import Text.Pandoc > import Data.Text (Text) > import qualified Data.Text.IO as T > > mdToRST :: Text -> IO Text > mdToRST txt = runIOorExplode $ > readMarkdown def txt > >>= writeRST def{ writerReferenceLinks = True } > > main :: IO () > main = do > T.getContents >>= mdToRST >>= T.putStrLn -} module Text.Pandoc ( -- * Definitions module Text.Pandoc.Definition -- * Generics , module Text.Pandoc.Generic -- * Options , module Text.Pandoc.Options -- * Logging , module Text.Pandoc.Logging -- * Typeclass , module Text.Pandoc.Class -- * Internal data files , module Text.Pandoc.Data -- * Error handling , module Text.Pandoc.Error -- * Readers: converting /to/ Pandoc format , module Text.Pandoc.Readers -- * Writers: converting /from/ Pandoc format , module Text.Pandoc.Writers -- * Rendering templates and default templates , module Text.Pandoc.Templates -- * Localization , setTranslations , translateTerm -- * Version information , pandocVersion , pandocVersionText ) where import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Data import Text.Pandoc.Error import Text.Pandoc.Generic import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Readers import Text.Pandoc.Version (pandocVersion, pandocVersionText) import Text.Pandoc.Templates import Text.Pandoc.Translations (setTranslations, translateTerm) import Text.Pandoc.Writers ================================================ FILE: stack.yaml ================================================ flags: pandoc: embed_data_files: true QuickCheck: old-random: false packages: - '.' - 'pandoc-cli' - 'pandoc-lua-engine' - 'pandoc-server' extra-deps: - hslua-2.5.0 - hslua-marshalling-2.3.2 - hslua-module-doclayout-1.2.1.1 - hslua-module-path-1.2.0 - hslua-module-text-1.2.0 - hslua-module-system-1.3.0 - hslua-module-version-1.2.0.1 - hslua-module-zip-1.2.1 - hslua-objectorientation-2.5.0 - hslua-packaging-2.4.1 - hslua-typing-0.2.0 - pandoc-lua-marshal-0.3.2.1 - typst-symbols-0.1.9.1 - citeproc-0.13 - skylighting-format-blaze-html-0.1.2 - djot-0.1.4 - asciidoc-0.1.0.2 - texmath-0.13.1.1 - typst-0.9.0.1 ghc-options: "$locals": -fhide-source-paths -Wno-missing-home-modules resolver: lts-24.20 nix: packages: - pkg-config - zlib ================================================ FILE: test/Tests/Command.hs ================================================ {-# LANGUAGE BangPatterns #-} {- | Module : Tests.Command Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley@edu> Stability : alpha Portability : portable Run commands, and test results, defined in markdown files. A command test is a code block with the following format: > ``` > % pandoc -f markdown -t latex > *hi* > ^D > \emph{hi} > ``` - The first line, after "%", should contain a command to run. - Then comes zero or more lines of text which will be passed to the command as stdin. - The stdin terminates with a line containing "^D". - The following lines are typically the expected output on stdout. - If any output on stderr is expected, it should come first and each stderr line should be preceded by the string "2> ". - If a nonzero exit status is expected, the last line should contain "=> " followed by the exit status. -} module Tests.Command (tests) where import Data.Maybe (fromMaybe) import Data.Algorithm.Diff import System.Environment (getExecutablePath) import qualified Data.ByteString as BS import qualified Data.Text as T import Data.List (isSuffixOf) import System.Directory import System.Exit import System.FilePath ((</>)) import System.IO.Unsafe (unsafePerformIO) import System.Process import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.Golden.Advanced (goldenTest) import Tests.Helpers import Text.Pandoc import qualified Text.Pandoc.UTF8 as UTF8 -- | Run a test with and return output. execTest :: String -- ^ Path to test executable -> String -- ^ Shell command -> String -- ^ Input text -> IO (ExitCode, String) -- ^ Exit code and actual output execTest testExePath cmd inp = do env' <- setupEnvironment testExePath let pr = (shell (pandocToEmulate True cmd)){ env = Just env' } (!ec, out', err') <- readCreateProcessWithExitCode pr inp let err = unlines . map ("2> " ++) . lines $ err' -- filter \r so the tests will work on Windows machines let out'' = filter (/= '\r') $ err ++ out' let out = out'' ++ case ec of ExitFailure !n -> "=> " ++ show n ++ "\n" ExitSuccess -> "" return (ec, out) pandocToEmulate :: Bool -> String -> String pandocToEmulate True ('p':'a':'n':'d':'o':'c':cs) = "test-pandoc --emulate" ++ pandocToEmulate False cs pandocToEmulate False ('|':' ':'p':'a':'n':'d':'o':'c':cs) = "| " ++ "test-pandoc --emulate" ++ pandocToEmulate False cs pandocToEmulate _ (c:cs) = c : pandocToEmulate False cs pandocToEmulate _ [] = [] tests :: TestTree {-# NOINLINE tests #-} tests = unsafePerformIO $ do files <- filter (".md" `isSuffixOf`) <$> getDirectoryContents "command" testExePath <- getExecutablePath let cmds = map (extractCommandTest testExePath) files return $ testGroup "Command:" cmds isCodeBlock :: Block -> Bool isCodeBlock (CodeBlock _ _) = True isCodeBlock _ = False extractCode :: Block -> String extractCode (CodeBlock _ code) = T.unpack code extractCode _ = "" dropPercent :: String -> Maybe String dropPercent ('%':xs) = Just $ dropWhile (== ' ') xs dropPercent _ = Nothing runCommandTest :: FilePath -> FilePath -> Int -> String -> TestTree runCommandTest testExePath fp num code = do goldenTest testname getExpected getActual compareValues' updateGolden where testname = "#" <> show num codelines = lines code (continuations, r1) = span ("\\" `isSuffixOf`) codelines cmd = fromMaybe (error "Command test line does not begin with %") (dropPercent (unwords (map init continuations ++ take 1 r1))) r2 = drop 1 r1 (inplines, r3) = break (=="^D") r2 normlines = takeWhile (/=".") (drop 1 r3) input = unlines inplines norm = unlines normlines getExpected = return norm getActual = snd <$> execTest testExePath cmd input compareValues' expected actual | actual == expected = return Nothing | otherwise = return $ Just $ "--- test/command/" ++ fp ++ "\n+++ " ++ cmd ++ "\n" ++ showDiff (1,1) (getDiff (lines actual) (lines expected)) updateGolden newnorm = do let fp' = "command" </> fp raw <- UTF8.readFile fp' let cmdline = "% " <> cmd let x = cmdline <> "\n" <> input <> "^D\n" <> norm let y = cmdline <> "\n" <> input <> "^D\n" <> newnorm let updated = T.replace (T.pack x) (T.pack y) raw UTF8.writeFile fp' updated extractCommandTest :: FilePath -> FilePath -> TestTree extractCommandTest testExePath fp = unsafePerformIO $ do contents <- UTF8.toText <$> BS.readFile ("command" </> fp) Pandoc _ blocks <- runIOorExplode (readMarkdown def{ readerExtensions = pandocExtensions } contents) let codeblocks = map extractCode $ filter isCodeBlock blocks let cases = zipWith (runCommandTest testExePath fp) [1..] codeblocks return $ testGroup fp $ if null cases then [testCase "!!" $ assertFailure "No command tests defined"] else cases ================================================ FILE: test/Tests/Helpers.hs ================================================ {-# LANGUAGE CPP #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleInstances #-} {- | Module : Tests.Helpers Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley@edu> Stability : alpha Portability : portable Utility functions for the test suite. -} module Tests.Helpers ( test , TestResult(..) , setupEnvironment , showDiff , nativeDiff , testGolden , (=?>) , purely , ToString(..) , ToPandoc(..) ) where import System.FilePath import Data.Algorithm.Diff import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 import Data.Text (Text, unpack) import qualified Data.Text as T import System.Exit import qualified System.Environment as Env import Test.Tasty import Test.Tasty.Golden.Advanced (goldenTest) import Test.Tasty.HUnit import Text.Pandoc.Builder (Blocks, Inlines, doc, plain) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Shared (trimr) import Text.Pandoc.Writers.Native (writeNative) import Text.Printf test :: (ToString a, ToString b, ToString c, HasCallStack) => (a -> b) -- ^ function to test -> String -- ^ name of test case -> (a, c) -- ^ (input, expected value) -> TestTree test fn name (input, expected) = testCase name' $ assertBool msg (actual' == expected') where msg = nl ++ dashes "input" ++ nl ++ input' ++ nl ++ dashes "result" ++ nl ++ unlines (map vividize diff) ++ dashes "" nl = "\n" name' = if length name > 54 then take 52 name ++ "..." -- avoid wide output else name input' = toString input actual' = lines $ toString $ fn input expected' = lines $ toString expected diff = getDiff expected' actual' dashes "" = replicate 72 '-' dashes x = replicate (72 - length x - 5) '-' ++ " " ++ x ++ " ---" testGolden :: TestName -> FilePath -> FilePath -> (Text -> IO Text) -> TestTree testGolden name expectedPath inputPath fn = goldenTest name (UTF8.readFile expectedPath) (UTF8.readFile inputPath >>= fn) compareVals (UTF8.writeFile expectedPath) where compareVals expected actual | expected == actual = return Nothing | otherwise = return $ Just $ "\n--- " ++ expectedPath ++ "\n+++\n" ++ showDiff (1,1) (getDiff (lines . filter (/='\r') $ T.unpack actual) (lines . filter (/='\r') $ T.unpack expected)) -- | Set up environment for pandoc command tests. setupEnvironment :: FilePath -> IO [(String, String)] setupEnvironment testExePath = do mldpath <- Env.lookupEnv "LD_LIBRARY_PATH" mdyldpath <- Env.lookupEnv "DYLD_LIBRARY_PATH" mpdd <- Env.lookupEnv "pandoc_datadir" mbpath <- Env.lookupEnv "PATH" -- Note that Cabal sets the pandoc_datadir environment variable -- to point to the source directory, since otherwise getDataFilename -- will look in the data directory into which pandoc will be installed -- (but has not yet been). So when we spawn a new process with -- pandoc, we need to make sure this environment variable is set. return $ ("PATH",takeDirectory testExePath <> maybe mempty (searchPathSeparator:) mbpath) : ("TMP",".") : ("LANG","en_US.UTF-8") : ("HOME", "./") : maybe [] ((:[]) . ("pandoc_datadir",)) mpdd ++ maybe [] ((:[]) . ("LD_LIBRARY_PATH",)) mldpath ++ maybe [] ((:[]) . ("DYLD_LIBRARY_PATH",)) mdyldpath data TestResult = TestPassed | TestError ExitCode | TestFailed String FilePath [Diff String] deriving (Eq) instance Show TestResult where show TestPassed = "PASSED" show (TestError ec) = "ERROR " ++ show ec show (TestFailed cmd file d) = '\n' : dash ++ "\n--- " ++ file ++ "\n+++ " ++ cmd ++ "\n" ++ showDiff (1,1) d ++ dash where dash = replicate 72 '-' showDiff :: (Int,Int) -> [Diff String] -> String showDiff _ [] = "" showDiff (l,r) (First ln : ds) = printf "+%4d " l ++ ln ++ "\n" ++ showDiff (l+1,r) ds showDiff (l,r) (Second ln : ds) = printf "-%4d " r ++ ln ++ "\n" ++ showDiff (l,r+1) ds showDiff (l,r) (Both _ _ : ds) = showDiff (l+1,r+1) ds vividize :: Diff String -> String vividize (Both s _) = " " ++ s vividize (First s) = "- " ++ s vividize (Second s) = "+ " ++ s nativeDiff :: FilePath -> Pandoc -> Pandoc -> IO (Maybe String) nativeDiff normPath expectedNative actualNative | expectedNative == actualNative = return Nothing | otherwise = Just <$> do expected <- T.unpack <$> runIOorExplode (writeNative def expectedNative) actual <- T.unpack <$> runIOorExplode (writeNative def actualNative) let dash = replicate 72 '-' let diff = getDiff (lines actual) (lines expected) return $ '\n' : dash ++ "\n--- " ++ normPath ++ "\n+++ " ++ "test" ++ "\n" ++ showDiff (1,1) diff ++ dash purely :: (b -> PandocPure a) -> b -> a purely f = either (error . show) id . runPure . f infix 5 =?> (=?>) :: a -> b -> (a,b) x =?> y = (x, y) class ToString a where toString :: a -> String instance ToString Pandoc where toString d = unpack $ purely (writeNative def{ writerTemplate = s }) $ toPandoc d where s = case d of (Pandoc (Meta m) _) | M.null m -> Nothing | otherwise -> Just mempty -- need this to get meta output instance ToString Blocks where toString = unpack . purely (writeNative def) . toPandoc instance ToString [Block] where toString = toString . B.fromList instance ToString Block where toString = toString . B.singleton instance ToString Inlines where toString = unpack . trimr . purely (writeNative def) . toPandoc instance ToString String where toString = id instance ToString Text where toString = unpack class ToPandoc a where toPandoc :: a -> Pandoc instance ToPandoc Pandoc where toPandoc = id instance ToPandoc Blocks where toPandoc = doc instance ToPandoc Inlines where toPandoc = doc . plain ================================================ FILE: test/Tests/MediaBag.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.MediaBag (tests) where import Test.Tasty import Test.Tasty.HUnit -- import Tests.Helpers import Text.Pandoc.Class.IO (extractMedia) import Text.Pandoc.Class (fillMediaBag, runIOorExplode) import System.IO.Temp (withTempDirectory) import Text.Pandoc.Shared (inDirectory) import System.FilePath import Text.Pandoc.Builder as B import System.Directory (doesFileExist, copyFile) tests :: [TestTree] tests = [ testCase "test fillMediaBag & extractMedia" $ withTempDirectory "." "extractMediaTest" $ \tmpdir -> inDirectory tmpdir $ do copyFile "../../test/lalune.jpg" "moon.jpg" let d = B.doc $ B.para (B.image "../../test/lalune.jpg" "" mempty) <> B.para (B.image "moon.jpg" "" mempty) <> B.para (B.image "data:image/png;base64,cHJpbnQgImhlbGxvIgo=;.lua+%2f%2e%2e%2f%2e%2e%2fa%2elua" "" mempty) <> B.para (B.image "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" "" mempty) runIOorExplode $ do fillMediaBag d extractMedia "foo" d exists1 <- doesFileExist ("foo" </> "moon.jpg") assertBool "file in directory is not extracted with original name" exists1 exists2 <- doesFileExist ("foo" </> "f9d88c3dbe18f6a7f5670e994a947d51216cdf0e.jpg") assertBool "file above directory is not extracted with hashed name" exists2 exists3 <- doesFileExist ("foo" </> "2a0eaa89f43fada3e6c577beea4f2f8f53ab6a1d.png") exists4 <- doesFileExist "a.lua" assertBool "data uri with malicious payload gets written outside of destination dir" (exists3 && not exists4) exists5 <- doesFileExist ("foo" </> "d5fceb6532643d0d84ffe09c40c481ecdf59e15a.gif") assertBool "data uri with gif is not properly decoded" exists5 -- double-encoded version: let e = B.doc $ B.para (B.image "data:image/png;base64,cHJpbnQgInB3bmVkIgo=;.lua+%252f%252e%252e%252f%252e%252e%252fb%252elua" "" mempty) runIOorExplode $ do fillMediaBag e extractMedia "bar" e exists6 <- doesFileExist ("bar" </> "772ceca21a2751863ec46cb23db0e7fc35b9cff8.png") exists7 <- doesFileExist "b.lua" assertBool "data uri with double-encoded malicious payload gets written outside of destination dir" (exists6 && not exists7) ] ================================================ FILE: test/Tests/Old.hs ================================================ {- | Module : Tests.Old Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley@edu> Stability : alpha Portability : portable "Old" style tests (comparing output to golden files). -} module Tests.Old (tests) where import Data.Algorithm.Diff import System.Exit import System.FilePath ((<.>), (</>)) import System.Environment (getExecutablePath) import Text.Pandoc.Process (pipeProcess) import Test.Tasty (TestTree, testGroup) import Test.Tasty.Golden.Advanced (goldenTest) import Tests.Helpers hiding (test) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.Text as T tests :: FilePath -> [TestTree] tests pandocPath = [ testGroup "markdown" [ testGroup "writer" $ writerTests' "markdown" ++ lhsWriterTests' "markdown" ++ extWriterTests' "markdown" , testGroup "reader" [ test' "basic" ["-r", "markdown", "-w", "native", "-s"] "testsuite.txt" "testsuite.native" , test' "tables" ["-r", "markdown", "-w", "native", "--columns=80"] "tables.txt" "tables.native" , test' "pipe tables" ["-r", "markdown", "-w", "native", "--columns=80"] "pipe-tables.txt" "pipe-tables.native" , test' "more" ["-r", "markdown", "-w", "native", "-s"] "markdown-reader-more.txt" "markdown-reader-more.native" , lhsReaderTest' "markdown+lhs" ] , testGroup "citations" [ test' "citations" ["-r", "markdown", "-w", "native"] "markdown-citations.txt" "markdown-citations.native" ] ] , testGroup "rst" [ testGroup "writer" (writerTests' "rst" ++ lhsWriterTests' "rst") , testGroup "reader" [ test' "basic" ["-r", "rst+smart", "-w", "native", "-s", "--columns=80"] "rst-reader.rst" "rst-reader.native" , test' "tables" ["-r", "rst", "-w", "native", "--columns=80"] "tables.rst" "tables-rstsubset.native" , lhsReaderTest' "rst+lhs" ] ] , testGroup "latex" [ testGroup "writer" (extWriterTests' "latex" ++ lhsWriterTests' "latex") , testGroup "reader" [ test' "basic" ["-r", "latex+raw_tex", "-w", "native", "-s"] "latex-reader.latex" "latex-reader.native" , lhsReaderTest' "latex+lhs" ] ] , testGroup "html" [ testGroup "writer" $ mconcat [ extWriterTests' "html4" , extWriterTests' "html5" , lhsWriterTests' "html" ] , test' "reader" ["-r", "html", "-w", "native", "-s"] "html-reader.html" "html-reader.native" ] , testGroup "s5" [ s5WriterTest' "basic" ["-s"] "s5" , s5WriterTest' "fancy" ["-s","--mathjax","-i"] "s5" , s5WriterTest' "fragment" [] "html4" , s5WriterTest' "inserts" ["-s", "-H", "insert", "-B", "insert", "-A", "insert", "-c", "main.css"] "html4" ] , testGroup "asciidoc" [ testGroup "writer" (writerTests' "asciidoc" ++ writerTests' "asciidoc_legacy") , testGroup "reader" [ test' "basic" ["-f", "asciidoc", "-t", "native", "-s"] "asciidoc-reader.adoc" "asciidoc-reader.native" ] ] , testGroup "textile" [ testGroup "writer" $ writerTests' "textile" , test' "reader" ["-r", "textile", "-w", "native", "-s"] "textile-reader.textile" "textile-reader.native" ] , testGroup "djot" [ testGroup "writer" $ writerTests' "djot" , test' "reader" ["-f", "djot", "-t" ,"native", "-s"] "djot-reader.djot" "djot-reader.native" ] , testGroup "docbook" [ testGroup "writer" $ writerTests' "docbook4" , test' "reader" ["-r", "docbook", "-w", "native", "-s"] "docbook-reader.docbook" "docbook-reader.native" , test' "reader" ["-r", "docbook", "-w", "native", "-s"] "docbook-chapter.docbook" "docbook-chapter.native" , test' "reader" ["-r", "docbook", "-w", "native", "-s"] "docbook-xref.docbook" "docbook-xref.native" ] , testGroup "docbook5" [ testGroup "writer" $ writerTests' "docbook5" ] , testGroup "jats" [ testGroup "writer" [ testGroup "jats_archiving" $ extWriterTests' "jats_archiving" , testGroup "jats_articleauthoring" $ writerTests' "jats_articleauthoring" , testGroup "jats_publishing" $ writerTests' "jats_publishing" ] , test' "reader" ["-r", "jats", "-w", "native", "-s"] "jats-reader.xml" "jats-reader.native" ] , testGroup "jira" [ testGroup "writer" $ writerTests' "jira" , test' "reader" ["-r", "jira", "-w", "native", "-s"] "jira-reader.jira" "jira-reader.native" ] , testGroup "native" [ testGroup "writer" $ writerTests' "native" , test' "reader" ["-r", "native", "-w", "native", "-s"] "testsuite.native" "testsuite.native" ] , testGroup "fb2" [ fb2WriterTest' "basic" [] "fb2/basic.markdown" "fb2/basic.fb2" , fb2WriterTest' "titles" [] "fb2/titles.markdown" "fb2/titles.fb2" , fb2WriterTest' "images" [] "fb2/images.markdown" "fb2/images.fb2" , fb2WriterTest' "images-embedded" [] "fb2/images-embedded.html" "fb2/images-embedded.fb2" , fb2WriterTest' "math" [] "fb2/math.markdown" "fb2/math.fb2" , fb2WriterTest' "meta" [] "fb2/meta.markdown" "fb2/meta.fb2" , fb2WriterTest' "tables" [] "tables.native" "tables.fb2" , fb2WriterTest' "testsuite" [] "testsuite.native" "writer.fb2" ] , testGroup "mediawiki" [ testGroup "writer" $ mconcat [ writerTests' "mediawiki" , extWriterTests' "mediawiki" ] , test' "reader" ["-r", "mediawiki", "-w", "native", "-s"] "mediawiki-reader.wiki" "mediawiki-reader.native" ] , testGroup "vimwiki" [ test' "reader" ["-r", "vimwiki", "-w", "native", "-s"] "vimwiki-reader.wiki" "vimwiki-reader.native" ] , testGroup "dokuwiki" [ testGroup "writer" $ writerTests' "dokuwiki" , test' "inline_formatting" ["-r", "native", "-w", "dokuwiki", "-s"] "dokuwiki_inline_formatting.native" "dokuwiki_inline_formatting.dokuwiki" , test' "multiblock table" ["-r", "native", "-w", "dokuwiki", "-s"] "dokuwiki_multiblock_table.native" "dokuwiki_multiblock_table.dokuwiki" , test' "external images" ["-r", "native", "-w", "dokuwiki", "-s"] "dokuwiki_external_images.native" "dokuwiki_external_images.dokuwiki" ] , testGroup "opml" [ test' "basic" ["-r", "native", "-w", "opml", "--columns=80", "-s"] "testsuite.native" "writer.opml" , test' "reader" ["-r", "opml", "-w", "native", "-s"] "opml-reader.opml" "opml-reader.native" ] , testGroup "haddock" [ testGroup "writer" $ writerTests' "haddock" , test' "reader" ["-r", "haddock", "-w", "native", "-s"] "haddock-reader.haddock" "haddock-reader.native" ] , testGroup "txt2tags" [ test' "reader" ["-r", "t2t", "-w", "native", "-s"] "txt2tags.t2t" "txt2tags.native" ] , testGroup "epub" [ test' "features" ["-r", "epub", "-w", "native", "-s"] "epub/features.epub" "epub/features.native" , test' "wasteland" ["-r", "epub", "-w", "native", "-s"] "epub/wasteland.epub" "epub/wasteland.native" , test' "formatting" ["-r", "epub", "-w", "native", "-s"] "epub/formatting.epub" "epub/formatting.native" ] , testGroup "twiki" [ test' "reader" ["-r", "twiki", "-w", "native", "-s"] "twiki-reader.twiki" "twiki-reader.native" ] , testGroup "tikiwiki" [ test' "reader" ["-r", "tikiwiki", "-w", "native", "-s"] "tikiwiki-reader.tikiwiki" "tikiwiki-reader.native" ] , testGroup "other writers" $ map (\f -> testGroup f $ writerTests' f) [ "opendocument" , "context" , "texinfo", "icml", "tei" , "man" , "plain" , "xwiki", "zimwiki" ] , testGroup "writers-lang-and-dir" [ test' "latex" ["-f", "native", "-t", "latex", "-s"] "writers-lang-and-dir.native" "writers-lang-and-dir.latex" , test' "context" ["-f", "native", "-t", "context", "-s"] "writers-lang-and-dir.native" "writers-lang-and-dir.context" ] , testGroup "muse" [ testGroup "writer" $ writerTests' "muse" ] , testGroup "ms" [ test' "basic" ["-f", "native", "-t", "ms", "--columns=80", "--variable", "pandoc-version=", "--pdf-engine", "pdfroff", "-s"] "testsuite.native" "writer.ms" , test' "tables" ["-f", "native", "-t", "ms", "--columns=80", "--variable", "pandoc-version=", "--pdf-engine", "pdfroff"] "tables.native" "tables.ms" ] , testGroup "typst" [ testGroup "writer" $ writerTests' "typst" ++ extWriterTests' "typst" , testGroup "reader" [ test' "typst-reader" ["-r", "typst", "-w", "native", "-s"] "typst-reader.typ" "typst-reader.native" ] ] , testGroup "creole" [ test' "reader" ["-r", "creole", "-w", "native", "-s"] "creole-reader.txt" "creole-reader.native" ] , testGroup "man" [ test' "reader" ["-r", "man", "-w", "native", "-s"] "man-reader.man" "man-reader.native" ] , testGroup "org" [ test' "reader" ["-r", "org", "-w", "native", "-s"] "org-select-tags.org" "org-select-tags.native" , testGroup "writer" $ writerTests' "org" ] , testGroup "rtf" [ testGroup "writer" $ writerTests' "rtf" ] , testGroup "endnotexml" [ test' "reader" ["-r", "endnotexml", "-w", "native", "-s"] "endnotexml-reader.xml" "endnotexml-reader.native" ] , testGroup "ipynb" [ test' "reader" ["-f", "ipynb-raw_html-raw_tex+raw_attribute", "-t", "native", "-s"] "ipynb/simple.ipynb" "ipynb/simple.out.native" , test' "writer" ["-f", "native", "--markdown-headings=setext", "-t", "ipynb-raw_html-raw_tex+raw_attribute", "-s"] "ipynb/simple.in.native" "ipynb/simple.ipynb" , test' "reader" ["-t", "native", "-f", "ipynb", "--ipynb-output=all"] "ipynb/mime.ipynb" "ipynb/mime.native" , test' "writer" ["-f", "native", "-t", "ipynb", "--wrap=preserve"] "ipynb/mime.native" "ipynb/mime.out.ipynb" , test' "reader" ["-f", "ipynb", "-t", "html"] "ipynb/rank.ipynb" "ipynb/rank.out.html" ] , testGroup "markua" [ testGroup "writer" $ writerTests' "markua"] , testGroup "ansi" [ test' "ansi" ["-f", "markdown", "-t", "ansi"] "ansi-test.txt" "ansi-test.ansi" ] , testGroup "pod" [ test' "pod" ["-f", "pod", "-t", "native"] "pod-reader.pod" "pod-reader.native" ] , testGroup "vimdoc" [ testGroup "writer" $ writerTests' "vimdoc" ++ [ test' "vimdoc-specific definition lists" ["-s", "-r", "markdown", "-w", "vimdoc", "--toc", "--columns=78"] "vimdoc/definition-lists.markdown" "vimdoc/definition-lists.vimdoc" , test' "linking to docs" ["-s", "-r", "markdown", "-w", "vimdoc", "--toc", "--columns=78"] "vimdoc/vim-online-doc.markdown" "vimdoc/vim-online-doc.vimdoc" , test' "unnumbered TOC up to level 2 headers" ["-s", "-r", "markdown", "-w", "vimdoc", "--toc", "--columns=78", "--toc-depth=2"] "vimdoc/headers.markdown" "vimdoc/headers.vimdoc" , test' "numbered TOC" ["-s", "-r", "markdown", "-w", "vimdoc", "--toc", "--columns=78", "-N"] "vimdoc/headers.markdown" "vimdoc/headers-numbered.vimdoc" ] ] , testGroup "bbcode" [testGroup "writer" $ writerTests' "bbcode"] ] where test' = test pandocPath writerTests' = writerTests pandocPath s5WriterTest' = s5WriterTest pandocPath fb2WriterTest' = fb2WriterTest pandocPath lhsWriterTests' = lhsWriterTests pandocPath lhsReaderTest' = lhsReaderTest pandocPath extWriterTests' = extendedWriterTests pandocPath -- makes sure file is fully closed after reading readFile' :: FilePath -> IO String readFile' f = do s <- UTF8.readFile f return $! (T.length s `seq` T.unpack s) lhsWriterTests :: FilePath -> String -> [TestTree] lhsWriterTests pandocPath format = [ t "lhs to normal" format , t "lhs to lhs" (format ++ "+lhs") ] where t n f = test pandocPath n ["--wrap=preserve", "-r", "native", "--markdown-headings=setext", "-w", f] "lhs-test.native" ("lhs-test" <.> f) lhsReaderTest :: FilePath -> String -> TestTree lhsReaderTest pandocPath format = test pandocPath "lhs" ["-r", format, "-w", "native"] ("lhs-test" <.> format) norm where norm = if format == "markdown+lhs" then "lhs-test-markdown.native" else "lhs-test.native" writerTests :: FilePath -> String -> [TestTree] writerTests pandocPath format = [ test pandocPath "basic" (opts ++ ["-s"]) "testsuite.native" ("writer" <.> format) , test pandocPath "tables" opts "tables.native" ("tables" <.> format) ] where opts = ["-r", "native", "-w", format, "--columns=80", "--variable", "pandoc-version="] extendedWriterTests :: FilePath -> String -> [TestTree] extendedWriterTests pandocPath format = writerTests pandocPath format ++ let testForTable name = test pandocPath (name ++ " table") opts ("tables" </> name <.> "native") ("tables" </> name <.> format) in map testForTable ["planets", "nordics", "students"] where opts = ["-r", "native", "-w", format, "--columns=80", "--variable", "pandoc-version="] s5WriterTest :: FilePath -> String -> [String] -> String -> TestTree s5WriterTest pandocPath modifier opts format = test pandocPath (format ++ " writer (" ++ modifier ++ ")") (["-r", "native", "-w", format] ++ opts) "s5.native" ("s5-" ++ modifier <.> "html") fb2WriterTest :: FilePath -> String -> [String] -> String -> String -> TestTree fb2WriterTest pandocPath title opts inputfile normfile = testWithNormalize (ignoreBinary . formatXML) pandocPath title (["-t", "fb2"]++opts) inputfile normfile where formatXML xml = splitTags $ zip xml (drop 1 xml) splitTags [] = [] splitTags [end] = [fst end, snd end] splitTags (('>','<'):rest) = ">\n" ++ splitTags rest splitTags ((c,_):rest) = c : splitTags rest ignoreBinary = unlines . filter (not . startsWith "<binary ") . lines startsWith tag str = all (uncurry (==)) $ zip tag str -- | Run a test without normalize function, return True if test passed. test :: FilePath -- ^ Path of pandoc executable -> String -- ^ Title of test -> [String] -- ^ Options to pass to pandoc -> String -- ^ Input filepath -> FilePath -- ^ Norm (for test results) filepath -> TestTree test = testWithNormalize id -- | Run a test with normalize function, return True if test passed. testWithNormalize :: (String -> String) -- ^ Normalize function for output -> FilePath -- ^ Path to pandoc executable -> String -- ^ Title of test -> [String] -- ^ Options to pass to pandoc -> String -- ^ Input filepath -> FilePath -- ^ Norm (for test results) filepath -> TestTree testWithNormalize normalizer pandocPath testname opts inp norm = goldenTest testname getExpected getActual (compareValues norm options) updateGolden where getExpected = normalizer <$> readFile' norm getActual = do env <- setupEnvironment pandocPath (ec, out) <- pipeProcess (Just env) pandocPath ("--emulate":options) mempty if ec == ExitSuccess then return $ filter (/='\r') . normalizer $ UTF8.toStringLazy out -- filter \r so the tests will work on Windows machines else fail $ "Pandoc failed with error code " ++ show ec updateGolden = UTF8.writeFile norm . T.pack options = ["--quiet"] ++ [inp] ++ opts compareValues :: FilePath -> [String] -> String -> String -> IO (Maybe String) compareValues norm options expected actual = do testExePath <- getExecutablePath let cmd = testExePath ++ " --emulate " ++ unwords options let dash = replicate 72 '-' let diff = getDiff (lines actual) (lines expected) if expected == actual then return Nothing else return $ Just $ '\n' : dash ++ "\n--- " ++ norm ++ "\n+++ " ++ cmd ++ "\n" ++ showDiff (1,1) diff ++ dash ================================================ FILE: test/Tests/Readers/Creole.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Creole Copyright : © 2017 Sascha Wilde 2017-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Sascha Wilde <wilde@sha-bang.de> Stability : alpha Portability : portable Tests for the creole reader. -} module Tests.Readers.Creole (tests) where import Data.Text (Text) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder creole :: Text -> Pandoc creole = purely $ readCreole def{ readerStandalone = True } infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test creole tests :: [TestTree] tests = [ testGroup "Basic Text Formatting" [ "bold, single line, fully delimited" =: "only **bold** is bold" =?> para ("only " <> strong "bold" <> " is bold") , "italics, single line, fully delimited" =: "only //this// is in italics" =?> para ("only " <> emph "this" <> " is in italics") , "bold in italics, fully delimited" =: "//**this**// is in bold italics" =?> para (emph (strong "this") <> " is in bold italics") , "italics in bold, fully delimited" =: "**//this//** is in bold italics" =?> para (strong (emph "this") <> " is in bold italics") , "escape bold marker" =: "~**not bold" =?> para "**not bold" , "escape italics marker" =: "~//not in italics" =?> para "//not in italics" , "inline nowiki, simple" =: "this is {{{**not** ~interpreted}}} at all" =?> para ("this is " <> code "**not** ~interpreted" <> " at all") , "inline nowiki, curly braces inside" =: "this is {{{{{{//including// some `}' chars}}}}}}" =?> para ("this is " <> code "{{{//including// some `}' chars}}}") , "placeholder" =: "foo <<<place holder>>> bar" =?> para "foo bar" , "placeholder escaped" =: "foo ~<<<no place holder>>> bar" =?> para "foo <<<no place holder>>> bar" ] , testGroup "Headers" [ "header level 1, no space, no trailing =" =: "= Top-Level Header" =?> header 1 (str "Top-Level Header") , "header level 1, leading space, trailing =" =: " = Top-Level Header = " =?> header 1 (str "Top-Level Header") , "header level 2, no space, no trailing =" =: "== Second Level" =?> header 2 (str "Second Level") , "header level 2, leading space, no trailing =" =: " == Second Level" =?> header 2 (str "Second Level") , "header level 3, no space, no trailing =" =: "=== Third" =?> header 3 (str "Third") , "header level 3, no space, > 3 trailing =" =: "=== Third =======" =?> header 3 (str "Third") , "header level 4, no space, no trailing =" =: "==== Fourth Level Heading" =?> header 4 (str "Fourth Level Heading") , "header level 4, no space, < 4 trailing =" =: "==== Fourth Level Heading ==" =?> header 4 (str "Fourth Level Heading") , "header level 5, no space, no trailing =" =: "===== Fifth" =?> header 5 (str "Fifth") , "header level 6, no space, no trailing =" =: "====== Sixth" =?> header 6 (str "Sixth") ] , testGroup "Paragraphs" [ "paragraphs: multiple, one line" =: "first line\n\nanother line\n" =?> para "first line" <> para "another line" ] , testGroup "Lists" [ "unordered list, two entries, one separating space" =: "* foo\n* bar" =?> bulletList [ plain "foo", plain "bar" ] , "unordered list, three entries, one separating space" =: "* foo\n* bar\n* baz" =?> bulletList [ plain "foo", plain "bar", plain "baz" ] , "para followed by, unordered list, two entries, one separating space" =: "blubber\n* foo\n* bar" =?> para "blubber" <> bulletList [ plain "foo", plain "bar" ] , "nested unordered list, one separating space" =: "* foo\n** bar\n** baz\n* blubb" =?> bulletList [ plain "foo" <> bulletList [ plain "bar", plain "baz" ] , plain "blubb" ] , "nested many unordered lists, one separating space" =: ("* foo\n** bar\n*** third\n*** third two\n** baz\n*** third again\n" <> "**** fourth\n***** fifth\n* blubb") =?> bulletList [ plain "foo" <> bulletList [ plain "bar" <> bulletList [ plain "third" , plain "third two"] , plain "baz" <> bulletList [ plain "third again" <> bulletList [ plain "fourth" <> bulletList [ plain "fifth" ] ] ] ] , plain "blubb" ] , "nested unordered list, mixed separating space" =: "*foo\n ** bar\n **baz\n * blubb" =?> bulletList [ plain "foo" <> bulletList [ plain "bar", plain "baz" ] , plain "blubb" ] , "nested unordered list, one separating space, trailing space" =: "* foo \n** bar \n** baz \n* blubb " =?> bulletList [ plain "foo" <> bulletList [ plain "bar", plain "baz" ] , plain "blubb" ] , "ordered list, two entries, one separating space" =: "# foo\n# bar" =?> orderedList [ plain "foo", plain "bar" ] , "ordered list, three entries, one separating space" =: "# foo\n# bar\n# baz" =?> orderedList [ plain "foo", plain "bar", plain "baz" ] , "para followed by, ordered list, two entries, one separating space" =: "blubber\n# foo\n# bar" =?> para "blubber" <> orderedList [ plain "foo", plain "bar" ] , "nested ordered list, one separating space" =: "# foo\n## bar\n## baz\n# blubb" =?> orderedList [ plain "foo" <> orderedList [ plain "bar", plain "baz" ] , plain "blubb" ] , "nested ordered list, one separating space, trailing space" =: "# foo \n## bar \n## baz \n# blubb " =?> orderedList [ plain "foo" <> orderedList [ plain "bar", plain "baz" ] , plain "blubb" ] , "nested many ordered lists, one separating space" =: ("# foo\n## bar\n### third\n### third two\n## baz\n### third again\n" <> "#### fourth\n##### fifth\n# blubb") =?> orderedList [ plain "foo" <> orderedList [ plain "bar" <> orderedList [ plain "third" , plain "third two"] , plain "baz" <> orderedList [ plain "third again" <> orderedList [ plain "fourth" <> orderedList [ plain "fifth" ] ] ] ] , plain "blubb" ] , "nested ordered list, mixed separating space" =: "#foo\n ## bar\n ##baz\n # blubb" =?> orderedList [ plain "foo" <> orderedList [ plain "bar", plain "baz" ] , plain "blubb" ] , "mixed nested ordered and unordered lists, one separating space" =: ("# foo\n** bar\n### third\n### third two\n** baz\n### third again\n" <> "#### fourth\n***** fifth\n# blubb") =?> orderedList [ plain "foo" <> bulletList [ plain "bar" <> orderedList [ plain "third" , plain "third two"] , plain "baz" <> orderedList [ plain "third again" <> orderedList [ plain "fourth" <> bulletList [ plain "fifth" ] ] ] ] , plain "blubb" ] ] , testGroup "NoWiki" [ "quoted block, simple" =: "{{{\nfoo bar\n //baz//\n}}}" =?> codeBlock "foo bar\n //baz//" , "quoted block, curly bracket exception" =: "{{{\nfoo bar\n }}}\nbaz\n }}}\n}}}" =?> codeBlock "foo bar\n }}}\nbaz\n}}}" , "forced line breaks" =: "{{{no break!\\\\here}}} but a break\\\\here!" =?> para (code "no break!\\\\here" <> " but a break" <> linebreak <> "here!"), "quoted block, after trailing white space" =: "this is a paragraph \n{{{\nfoo bar\n //baz//\n}}}" =?> para "this is a paragraph" <> codeBlock "foo bar\n //baz//" ] , testGroup "Images and Links" [ "image simple" =: "{{foo.png}}" =?> para (image "foo.png" "" (str "")) , "image with alt text" =: "Image of a bar: {{/path/to/bar.png|A Bar}} look at it!" =?> para ("Image of a bar: " <> image "/path/to/bar.png" "" (str "A Bar") <> " look at it!") , "auto link" =: "foo http://foo.example.com/bar/baz.html bar" =?> para ("foo " <> link "http://foo.example.com/bar/baz.html" "" (str "http://foo.example.com/bar/baz.html") <> " bar") , "escaped auto link" =: "foo ~http://foo.example.com/bar/baz.html bar" =?> para "foo http://foo.example.com/bar/baz.html bar" , "wiki link simple" =: "foo [[http://foo.example.com/foo.png]] bar" =?> para ("foo " <> link "http://foo.example.com/foo.png" "" (str "http://foo.example.com/foo.png") <> " bar") , "wiki link with name" =: "foo [[http://foo.example.com/foo.png|my link]] bar" =?> para ("foo " <> link "http://foo.example.com/foo.png" "" (str "my link") <> " bar") , "image link" =: "[[http://foo.example.com/|{{foo.png}}]]" =?> para (link "http://foo.example.com/" "" (image "foo.png" "" (str ""))) ] , testGroup "Table" [ "Table with Header" =: T.unlines [ "|= Foo |= Bar |= Baz |" , "| One | Two | Three |" , "| 1 | 2 | 3 |" , "| A | B | C |" ] =?> simpleTable [plain "Foo", plain "Bar" , plain "Baz"] [[plain "One", plain "Two" , plain "Three"] ,[plain "1", plain "2" , plain "3"] ,[plain "A", plain "B" , plain "C"]] , "Table without Header" =: T.unlines [ "| One | Two | Three |" , "| 1 | 2 | 3 |" , "| A | B | C |" ] =?> simpleTable [mempty] [[plain "One", plain "Two" , plain "Three"] ,[plain "1", plain "2" , plain "3"] ,[plain "A", plain "B" , plain "C"]] , "Table without Header, no markers at line ends" =: T.unlines [ "| One | Two | Three" , "| 1 | 2 | 3" , "| A | B | C " ] =?> simpleTable [mempty] [[plain "One", plain "Two" , plain "Three"] ,[plain "1", plain "2" , plain "3"] ,[plain "A", plain "B" , plain "C"]] , "Table with Header, with formatting" =: T.unlines [ "|= **Foo** |= **Bar** |= **Baz** |" , "|//one// element |//second// elt|Three |" , "| {{{1}}} | {{{{}}}} | [[link]] |" ] =?> simpleTable [plain $ strong "Foo", plain $ strong "Bar" , plain $ strong "Baz"] [[plain (emph "one" <> " element"), plain (emph "second" <> " elt") ,plain "Three"] ,[plain $ code "1", plain $ code "{}" ,plain $ link "link" "" (str "link")]] ] ] ================================================ FILE: test/Tests/Readers/Docx.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Docx Copyright : © 2017-2020 Jesse Rosenthal, John MacFarlane License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> Stability : alpha Portability : portable Tests for the word docx reader. -} module Tests.Readers.Docx (tests) where import Codec.Archive.Zip import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import qualified Data.Text as T import Data.Maybe import System.IO.Unsafe import Test.Tasty import Test.Tasty.Golden.Advanced import Test.Tasty.HUnit import Tests.Helpers import Text.Pandoc import qualified Text.Pandoc.Class as P import qualified Text.Pandoc.MediaBag as MB import Text.Pandoc.UTF8 as UTF8 defopts :: ReaderOptions defopts = def{ readerExtensions = getDefaultExtensions "docx" } testCompare :: String -> FilePath -> FilePath -> TestTree testCompare = testCompareWithOpts defopts testCompareWithOpts :: ReaderOptions -> String -> FilePath -> FilePath -> TestTree testCompareWithOpts opts testName docxFP nativeFP = goldenTest testName (do nf <- UTF8.toText <$> BS.readFile nativeFP runIOorExplode (readNative def nf)) (do df <- B.readFile docxFP runIOorExplode (readDocx opts df)) (nativeDiff nativeFP) (\a -> runIOorExplode (writeNative def{ writerTemplate = Just mempty} a) >>= BS.writeFile nativeFP . UTF8.fromText) testForWarningsWithOptsIO :: ReaderOptions -> String -> FilePath -> [String] -> IO TestTree testForWarningsWithOptsIO opts name docxFile expected = do df <- B.readFile docxFile logs <- runIOorExplode $ setVerbosity ERROR >> readDocx opts df >> P.getLog let warns = [m | DocxParserWarning m <- logs] return $ test id name (T.unlines warns, unlines expected) testForWarningsWithOpts :: ReaderOptions -> String -> FilePath -> [String] -> TestTree testForWarningsWithOpts opts name docxFile expected = unsafePerformIO $ testForWarningsWithOptsIO opts name docxFile expected -- testForWarnings :: String -> FilePath -> [String] -> TestTree -- testForWarnings = testForWarningsWithOpts defopts getMedia :: FilePath -> FilePath -> IO (Maybe B.ByteString) getMedia archivePath mediaPath = fmap fromEntry . findEntryByPath ("word/" ++ mediaPath) . toArchive <$> B.readFile archivePath compareMediaPathIO :: FilePath -> MB.MediaBag -> FilePath -> IO Bool compareMediaPathIO mediaPath mediaBag docxPath = do docxMedia <- getMedia docxPath mediaPath let mbBS = case MB.lookupMedia mediaPath mediaBag of Just item -> MB.mediaContents item Nothing -> error ("couldn't find " ++ mediaPath ++ " in media bag") docxBS = fromMaybe (error ("couldn't find " ++ mediaPath ++ " in media bag")) docxMedia return $ mbBS == docxBS compareMediaBagIO :: FilePath -> IO Bool compareMediaBagIO docxFile = do df <- B.readFile docxFile mb <- runIOorExplode $ readDocx defopts df >> P.getMediaBag bools <- mapM (\(fp, _, _) -> compareMediaPathIO fp mb docxFile) (MB.mediaDirectory mb) return $ and bools testMediaBagIO :: String -> FilePath -> IO TestTree testMediaBagIO name docxFile = do outcome <- compareMediaBagIO docxFile return $ testCase name (assertBool ("Media didn't match media bag in file " ++ docxFile) outcome) testMediaBag :: String -> FilePath -> TestTree testMediaBag name docxFile = unsafePerformIO $ testMediaBagIO name docxFile tests :: [TestTree] tests = [ testGroup "document" [ testCompare "allow different document.xml file as defined in _rels/.rels" "docx/alternate_document_path.docx" "docx/alternate_document_path.native" ] , testGroup "inlines" [ testCompare "font formatting" "docx/inline_formatting.docx" "docx/inline_formatting.native" , testCompare "font formatting with character styles" "docx/char_styles.docx" "docx/char_styles.native" , testCompare "hyperlinks" "docx/links.docx" "docx/links.native" , testCompare "hyperlinks in <w:instrText> tag" "docx/instrText_hyperlink.docx" "docx/instrText_hyperlink.native" , testCompare "nested fields with <w:instrText> tag" "docx/nested_instrText.docx" "docx/nested_instrText.native" , testCompare "empty fields with <w:instrText> tag" "docx/empty_field.docx" "docx/empty_field.native" , testCompare "pageref hyperlinks in <w:instrText> tag" "docx/pageref.docx" "docx/pageref.native" , testCompare "inline image" "docx/image.docx" "docx/image_no_embed.native" , testCompare "VML image" "docx/image_vml.docx" "docx/image_vml.native" , testCompare "VML image as object" "docx/image_vml_as_object.docx" "docx/image_vml_as_object.native" , testCompare "inline image in links" "docx/inline_images.docx" "docx/inline_images.native" , testCompare "handling unicode input" "docx/unicode.docx" "docx/unicode.native" , testCompare "literal tabs" "docx/tabs.docx" "docx/tabs.native" , testCompare "special punctuation" "docx/special_punctuation.docx" "docx/special_punctuation.native" , testCompare "normalizing inlines" "docx/normalize.docx" "docx/normalize.native" , testCompare "normalizing inlines deep inside blocks" "docx/deep_normalize.docx" "docx/deep_normalize.native" , testCompare "move trailing spaces outside of formatting" "docx/trailing_spaces_in_formatting.docx" "docx/trailing_spaces_in_formatting.native" , testCompare "remove trailing spaces from last inline" "docx/trim_last_inline.docx" "docx/trim_last_inline.native" , testCompare "inline code (with VerbatimChar style)" "docx/inline_code.docx" "docx/inline_code.native" , testCompare "inline code in subscript and superscript" "docx/verbatim_subsuper.docx" "docx/verbatim_subsuper.native" , testCompare "inlines inside of Structured Document Tags" "docx/sdt_elements.docx" "docx/sdt_elements.native" , testCompare "Structured Document Tags in footnotes" "docx/sdt_in_footnote.docx" "docx/sdt_in_footnote.native" , testCompare "nested Structured Document Tags" "docx/nested_sdt.docx" "docx/nested_sdt.native" , testCompare "nested Smart Tags" "docx/nested_smart_tags.docx" "docx/nested_smart_tags.native" , testCompare "remove anchor spans with nothing pointing to them" "docx/unused_anchors.docx" "docx/unused_anchors.native" , testCompare "collapse overlapping targets (anchor spans)" "docx/overlapping_targets.docx" "docx/overlapping_targets.native" , testCompare "anchor in header after anchor" "docx/anchor_header_after_anchor.docx" "docx/anchor_header_after_anchor.native" , testCompare "text in shape format" "docx/text_in_shape_format.docx" "docx/text_in_shape_format.native" , testCompare "image inside textbox content" "docx/textbox_image.docx" "docx/textbox_image.native" , testCompare "image inside textbox content with duplicate encoding" "docx/textbox_image_duplicate_encoding.docx" "docx/textbox_image_duplicate_encoding.native" , testCompare "image with textbox caption in same paragraph" "docx/image_with_textbox_caption.docx" "docx/image_with_textbox_caption.native" ] , testGroup "blocks" [ testCompare "headers" "docx/headers.docx" "docx/headers.native" , testCompare "headers already having auto identifiers" "docx/already_auto_ident.docx" "docx/already_auto_ident.native" , testCompare "avoid zero-level headers" "docx/0_level_headers.docx" "docx/0_level_headers.native" , testCompare "nested anchor spans in header" "docx/nested_anchors_in_header.docx" "docx/nested_anchors_in_header.native" , testCompare "single numbered item not made into list" "docx/numbered_header.docx" "docx/numbered_header.native" , testCompare "enumerated headers not made into numbered list" "docx/enumerated_headings.docx" "docx/enumerated_headings.native" , testCompare "i18n blocks (headers and blockquotes)" "docx/i18n_blocks.docx" "docx/i18n_blocks.native" , testCompare "lists" "docx/lists.docx" "docx/lists.native" , testCompare "compact lists" "docx/lists-compact.docx" "docx/lists-compact.native" , testCompare "lists with level overrides" "docx/lists_level_override.docx" "docx/lists_level_override.native" , testCompare "lists continuing after interruption" "docx/lists_continuing.docx" "docx/lists_continuing.native" , testCompare "lists restarting after interruption" "docx/lists_restarting.docx" "docx/lists_restarting.native" , testCompare "sublists reset numbering to 1" "docx/lists_sublist_reset.docx" "docx/lists_sublist_reset.native" , testCompare "definition lists" "docx/definition_list.docx" "docx/definition_list.native" , testCompare "task lists" "docx/task_list.docx" "docx/task_list.native" , testCompare "custom defined lists in styles" "docx/german_styled_lists.docx" "docx/german_styled_lists.native" , testCompare "user deletes bullet after list item (=> part of item par)" "docx/dummy_item_after_list_item.docx" "docx/dummy_item_after_list_item.native" , testCompare "user deletes bullet after par (=> new par)" "docx/dummy_item_after_paragraph.docx" "docx/dummy_item_after_paragraph.native" , testCompare "footnotes and endnotes" "docx/notes.docx" "docx/notes.native" , testCompare "links in footnotes and endnotes" "docx/link_in_notes.docx" "docx/link_in_notes.native" , testCompare "blockquotes (parsing indent as blockquote)" "docx/block_quotes.docx" "docx/block_quotes.native" , testCompare "blockquotes (parsing indent relative to the indent of the parent style as blockquote)" "docx/relative_indentation_blockquotes.docx" "docx/relative_indentation_blockquotes.native" , testCompare "hanging indents" "docx/hanging_indent.docx" "docx/hanging_indent.native" , testCompare "tables" "docx/tables.docx" "docx/tables.native" , testCompare "tables with lists in cells" "docx/table_with_list_cell.docx" "docx/table_with_list_cell.native" , testCompare "a table with gridBefore" "docx/table_gridbefore.docx" "docx/table_gridbefore.native" , testCompare "a table with a header which contains rowspans greater than 1" "docx/table_header_rowspan.docx" "docx/table_header_rowspan.native" , testCompare "tables with one row" "docx/table_one_row.docx" "docx/table_one_row.native" , testCompare "tables with just one row, which is a header" "docx/table_one_header_row.docx" "docx/table_one_header_row.native" , testCompare "tables with variable width" "docx/table_variable_width.docx" "docx/table_variable_width.native" , testCompare "tables with captions which contain a Table field" "docx/table_captions_with_field.docx" "docx/table_captions_with_field.native" , testCompare "tables with captions which don't contain a Table field" "docx/table_captions_no_field.docx" "docx/table_captions_no_field.native" , testCompare "code block" "docx/codeblock.docx" "docx/codeblock.native" , testCompare "combine adjacent code blocks" "docx/adjacent_codeblocks.docx" "docx/adjacent_codeblocks.native" , testCompare "dropcap paragraphs" "docx/drop_cap.docx" "docx/drop_cap.native" ] , testGroup "citations" [ testCompare "zotero with -citations" "docx/zotero_citations.docx" "docx/zotero_citations_minus.native" , testCompareWithOpts def{readerExtensions = extensionsFromList [Ext_citations]} "zotero with +citations" "docx/zotero_citations.docx" "docx/zotero_citations_plus.native" , testCompare "mendeley with -citations" "docx/mendeley_citations.docx" "docx/mendeley_citations_minus.native" , testCompareWithOpts def{readerExtensions = extensionsFromList [Ext_citations]} "mendeley with +citations" "docx/mendeley_citations.docx" "docx/mendeley_citations_plus.native" ] , testGroup "track changes" [ testCompare "insertion (default)" "docx/track_changes_insertion.docx" "docx/track_changes_insertion_accept.native" , testCompareWithOpts def{readerTrackChanges=AcceptChanges} "insert insertion (accept)" "docx/track_changes_insertion.docx" "docx/track_changes_insertion_accept.native" , testCompareWithOpts def{readerTrackChanges=RejectChanges} "remove insertion (reject)" "docx/track_changes_insertion.docx" "docx/track_changes_insertion_reject.native" , testCompare "deletion (default)" "docx/track_changes_deletion.docx" "docx/track_changes_deletion_accept.native" , testCompareWithOpts def{readerTrackChanges=AcceptChanges} "remove deletion (accept)" "docx/track_changes_deletion.docx" "docx/track_changes_deletion_accept.native" , testCompareWithOpts def{readerTrackChanges=RejectChanges} "insert deletion (reject)" "docx/track_changes_deletion.docx" "docx/track_changes_deletion_reject.native" , testCompareWithOpts def{readerTrackChanges=AllChanges} "keep insertion (all)" "docx/track_changes_deletion.docx" "docx/track_changes_deletion_all.native" , testCompareWithOpts def{readerTrackChanges=AllChanges} "keep deletion (all)" "docx/track_changes_deletion.docx" "docx/track_changes_deletion_all.native" , testCompareWithOpts def{readerTrackChanges=AcceptChanges} "move text (accept)" "docx/track_changes_move.docx" "docx/track_changes_move_accept.native" , testCompareWithOpts def{readerTrackChanges=RejectChanges} "move text (reject)" "docx/track_changes_move.docx" "docx/track_changes_move_reject.native" , testCompareWithOpts def{readerTrackChanges=AllChanges} "move text (all)" "docx/track_changes_move.docx" "docx/track_changes_move_all.native" , testCompareWithOpts def{readerTrackChanges=AcceptChanges} "comments (accept -- no comments)" "docx/comments.docx" "docx/comments_no_comments.native" , testCompareWithOpts def{readerTrackChanges=RejectChanges} "comments (reject -- comments)" "docx/comments.docx" "docx/comments_no_comments.native" , testCompareWithOpts def{readerTrackChanges=AllChanges} "comments (all comments)" "docx/comments.docx" "docx/comments.native" , testCompareWithOpts def{readerTrackChanges=AcceptChanges} "paragraph insertion/deletion (accept)" "docx/paragraph_insertion_deletion.docx" "docx/paragraph_insertion_deletion_accept.native" , testCompareWithOpts def{readerTrackChanges=RejectChanges} "paragraph insertion/deletion (reject)" "docx/paragraph_insertion_deletion.docx" "docx/paragraph_insertion_deletion_reject.native" , testCompareWithOpts def{readerTrackChanges=AllChanges} "paragraph insertion/deletion (all)" "docx/paragraph_insertion_deletion.docx" "docx/paragraph_insertion_deletion_all.native" , testCompareWithOpts def{readerTrackChanges=AllChanges} "paragraph insertion/deletion (all)" "docx/track_changes_scrubbed_metadata.docx" "docx/track_changes_scrubbed_metadata.native" , testForWarningsWithOpts def{readerTrackChanges=AcceptChanges} "comment warnings (accept -- no warnings)" "docx/comments_warning.docx" [] , testForWarningsWithOpts def{readerTrackChanges=RejectChanges} "comment warnings (reject -- no warnings)" "docx/comments_warning.docx" [] , testForWarningsWithOpts def{readerTrackChanges=AllChanges} "comment warnings (all)" "docx/comments_warning.docx" ["Docx comment 1 will not retain formatting"] , testForWarningsWithOpts def{readerTrackChanges=AllChanges, readerExtensions=extensionsFromList [Ext_styles]} "comments (with styles extension)" "docx/comments.docx" [] ] , testGroup "media" [ testMediaBag "image extraction" "docx/image.docx" , testMediaBag "image inside textbox content populates media bag" "docx/textbox_image.docx" , testMediaBag "image inside textbox content with duplicate encoding populates media bag" "docx/textbox_image_duplicate_encoding.docx" , testMediaBag "image with textbox caption in same paragraph populates media bag" "docx/image_with_textbox_caption.docx" ] , testGroup "custom styles" [ testCompare "custom styles (`+styles`) not enabled (default)" "docx/custom-style-reference.docx" "docx/custom-style-no-styles.native" , testCompareWithOpts def{readerExtensions=extensionsFromList [Ext_styles]} "custom styles (`+styles`) enabled" "docx/custom-style-reference.docx" "docx/custom-style-with-styles.native" , testCompareWithOpts def{readerExtensions=extensionsFromList [Ext_styles]} "custom styles (`+styles`): Compact style is removed from output" "docx/compact-style-removal.docx" "docx/compact-style-removal.native" ] , testGroup "metadata" [ testCompareWithOpts def{readerStandalone=True} "metadata fields" "docx/metadata.docx" "docx/metadata.native" , testCompareWithOpts def{readerStandalone=True} "stop recording metadata with normal text" "docx/metadata_after_normal.docx" "docx/metadata_after_normal.native" ] ] ================================================ FILE: test/Tests/Readers/DokuWiki.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- | Module : Tests.Readers.DokuWiki Copyright : © 2018-2020 Alexander Krotov License : GNU GPL, version 2 or above Maintainer : Alexander Krotov Stability : alpha Portability : portable Tests for DokuWiki reader. -} module Tests.Readers.DokuWiki (tests) where import Data.Text (Text) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder dokuwiki :: Text -> Pandoc dokuwiki = purely $ readDokuWiki def{ readerStandalone = True } infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test dokuwiki tests :: [TestTree] tests = [ testGroup "inlines" [ "Bold" =: "**bold**" =?> para (strong "bold") , "Italic" =: "//italic//" =?> para (emph "italic") , "Underlined" =: "__underlined__" =?> para (underline "underlined") , "Monospaced" =: "''monospaced''" =?> para (code "monospaced") , "Monospaced with nowiki" =: "''%%monospaced%%''" =?> para (code "monospaced") , "Combined" =: "**__//''combine''//__**" =?> para (strong $ underline $ emph $ code "combine") , "Nowiki" =: T.unlines [ "<nowiki>" , "This is some text which contains addresses like this: http://www.splitbrain.org and **formatting**, but nothing is done with it." , "</nowiki>" ] =?> para "This is some text which contains addresses like this: http://www.splitbrain.org and **formatting**, but nothing is done with it." , "Percent" =: "The same is true for %%//__this__ text// with a smiley ;-)%%." =?> para "The same is true for //__this__ text// with a smiley ;-)." , "Subscript" =: "<sub>subscript</sub>" =?> para (subscript "subscript") , "Superscript" =: "<sup>superscript</sup>" =?> para (superscript "superscript") , "Deleted" =: "<del>deleted</del>" =?> para (strikeout "deleted") , "Inline code" =: "foo <code java>public static void main</code> bar" =?> para (text "foo") <> codeBlockWith ("", ["java"], []) "public static void main" <> para (text "bar") , "Inline file" =: "foo <file></code></file> bar" =?> para (text "foo") <> codeBlock "</code>" <> para (text "bar") , "Inline HTML" =: "<html>\nThis is some <span style=\"color:red;font-size:150%;\">inline HTML</span>\n</html>" =?> para (rawInline "html" "\nThis is some <span style=\"color:red;font-size:150%;\">inline HTML</span>\n") , "Inline PHP" =: "<php>echo '<p>Hello World</p>';</php>" =?> para (rawInline "html" "<?php echo '<p>Hello World</p>'; ?>") , "Linebreak" =: T.unlines [ "This is some text with some linebreaks\\\\ Note that the" , "two backslashes are only recognized at the end of a line\\\\" , "or followed by\\\\ a whitespace \\\\this happens without it." ] =?> para ("This is some text with some linebreaks" <> linebreak <> "Note that the\n" <> "two backslashes are only recognized at the end of a line" <> linebreak <> "or followed by" <> linebreak <> "a whitespace \\\\this happens without it.") , testGroup "External links" [ "Autolink" =: "http://www.google.com" =?> para (link "http://www.google.com" "" (str "http://www.google.com")) , "Link without description" =: "[[https://example.com]]" =?> para (link "https://example.com" "" (str "https://example.com")) , "Link with description" =: "[[http://www.google.com|This Link points to google]]" =?> para (link "http://www.google.com" "" (text "This Link points to google")) , "Trim whitespace around link and description" =: "[[ http://www.google.com | This Link points to google ]]" =?> para (link "http://www.google.com" "" (text "This Link points to google")) , "Email address" =: "<andi@splitbrain.org>" =?> para (link "mailto:andi@splitbrain.org" "" (str "andi@splitbrain.org")) ] , testGroup "Internal links" [ "Current namespace" =: "[[example]]" =?> para (link "example" "" (str "example")) , "Current namespace starting with dot" =: "[[.example]]" =?> para (link "example" "" (str ".example")) , "Current namespace starting with dot and colon" =: "[[.:example]]" =?> para (link "example" "" (str "example")) , "Root namespace" =: "[[:example]]" =?> para (link "/example" "" (str "example")) , "Parent namespace" =: "[[..example]]" =?> para (link "../example" "" (str "..example")) , "Parent namespace with colon" =: "[[..:example]]" =?> para (link "../example" "" (str "example")) , "Beneath the root namespace" =: "[[wiki:example]]" =?> para (link "/wiki/example" "" (str "example")) , "Explicitly beneath the root namespace" =: "[[:wiki:example]]" =?> para (link "/wiki/example" "" (str "example")) ] , testGroup "Interwiki links" [ "Interwiki without description" =: "[[doku>DokuWiki]]" =?> para (link "https://www.dokuwiki.org/DokuWiki" "" (str "DokuWiki")) , "Interwiki link with description" =: "[[doku>toolbar|quickbuttons]]" =?> para (link "https://www.dokuwiki.org/toolbar" "" (str "quickbuttons")) ] , "Footnote" =: "((This is a footnote))" =?> para (note (para "This is a footnote")) , testGroup "Images" [ "Image" =: "{{image.jpg}}" =?> para (image "image.jpg" "" (str "image.jpg")) , "Image with caption" =: "{{image.png|This is the caption}}" =?> para (image "image.png" "" "This is the caption") , "Image with } in caption" =: "{{image.png|There is an } in the caption}}" =?> para (image "image.png" "" "There is an } in the caption") , "Wiki namespace starting with dot" =: "{{.wiki:image.jpg}}" =?> para (image "wiki/image.jpg" "" (str "image.jpg")) , "Left aligned image" =: "{{wiki:dokuwiki-128.png }}" =?> para (imageWith ("", ["align-left"], []) "/wiki/dokuwiki-128.png" "" (str "dokuwiki-128.png")) , "Right aligned image" =: "{{ wiki:dokuwiki-128.png}}" =?> para (imageWith ("", ["align-right"], []) "/wiki/dokuwiki-128.png" "" (str "dokuwiki-128.png")) , "Centered image" =: "{{ wiki:dokuwiki-128.png }}" =?> para (imageWith ("", ["align-center"], []) "/wiki/dokuwiki-128.png" "" (str "dokuwiki-128.png")) , "Image with width" =: "{{wiki:dokuwiki-128.png?50}}" =?> para (imageWith ("", [], [("width", "50"), ("query", "?50")]) "/wiki/dokuwiki-128.png" "" (str "dokuwiki-128.png")) , "Image with width and height" =: "{{wiki:dokuwiki-128.png?nocache&50x100}}" =?> para (imageWith ("", [], [("width", "50"), ("height", "100"), ("query", "?nocache&50x100")]) "/wiki/dokuwiki-128.png" "" (str "dokuwiki-128.png")) , "Linkonly" =: "{{wiki:dokuwiki-128.png?linkonly}}" =?> para (link "/wiki/dokuwiki-128.png" "" (str "dokuwiki-128.png")) ] , "Ignore ~~NOTOC~~" =: "Here is a ~~NOTOC~~ macro" =?> para "Here is a macro" , "Ignore ~~NOCACHE~~" =: "Here is a ~~NOCACHE~~ macro" =?> para "Here is a macro" ] , testGroup "Sectioning" [ "Headline level 1" =: "====== Headline Level 1 ======" =?> header 1 "Headline Level 1" , "Headline level 2" =: "===== Headline Level 2 =====" =?> header 2 "Headline Level 2" , "Headline level 3" =: "==== Headline Level 3 ====" =?> header 3 "Headline Level 3" , "Headline level 4" =: "=== Headline Level 4 ===" =?> header 4 "Headline Level 4" , "Headline level 5" =: "== Headline Level 5 ==" =?> header 5 "Headline Level 5" , "Only two closing = are required" =: "====== Headline Level 1 ==" =?> header 1 "Headline Level 1" , "One closing = is not enough" =: "====== Headline Level 1 =" =?> para "====== Headline Level 1 =" , "One closing = is not enough" =: "== Headline with = sign ==" =?> header 5 "Headline with = sign" ] , "Horizontal line" =: "----" =?> horizontalRule , testGroup "Lists" [ "Unordered list" =: T.unlines [ " * This is a list" , " * The second item" , " * You may have different levels" , " * Another item" ] =?> bulletList [ plain "This is a list" , plain "The second item" <> bulletList [ plain "You may have different levels" ] , plain "Another item" ] , "Ordered list" =: T.unlines [ " - The same list but ordered" , " - Another item" , " - Just use indentation for deeper levels" , " - That's it" ] =?> orderedList [ plain "The same list but ordered" , plain "Another item" <> orderedList [ plain "Just use indentation for deeper levels" ] , plain "That's it" ] , "Multiline list items" =: -- https://www.dokuwiki.org/faq:lists T.unlines [ " - first item" , " - second item with linebreak\\\\ second line" , " - third item with code: <code>" , "some code" , "comes here" , "</code>" , " - fourth item" ] =?> orderedList [ plain "first item" , plain ("second item with linebreak" <> linebreak <> " second line") , plain "third item with code: " <> codeBlock "some code\ncomes here\n" , plain "fourth item" ] ] , "Block HTML" =: T.unlines [ "<HTML>" , "<p style=\"border:2px dashed red;\">And this is some block HTML</p>" , "</HTML>" ] =?> rawBlock "html" "<p style=\"border:2px dashed red;\">And this is some block HTML</p>\n" , "Block PHP" =: T.unlines [ "<PHP>" , "echo '<p>Hello World</p>';" , "</PHP>" ] =?> rawBlock "html" "<?php echo '<p>Hello World</p>';\n ?>" , "Quote" =: T.unlines [ "> foo" , ">no space is required after >" , "> bar" , ">> baz" , "> bat" ] =?> blockQuote (plain (text "foo" <> linebreak <> text "no space is required after >" <> linebreak <> text "bar") <> blockQuote (plain "baz") <> plain "bat") , "Code block" =: T.unlines [ "<code>" , "foo bar baz" , "</code>" ] =?> codeBlock "foo bar baz\n" , "Java code block" =: T.unlines [ "<code java>" , "public static void main" , "</code>" ] =?> codeBlockWith ("", ["java"], []) "public static void main\n" , "File with filename and no language" =: T.unlines [ "<file - foo.bar>" , "file contents" , "</file>" ] =?> codeBlock "file contents\n" , "Table" =: T.unlines [ "| foo | bar |" , "| bat | baz |" ] =?> simpleTable [] [[plain "foo", plain "bar"] ,[plain "bat", plain "baz"]] , "Table with header" =: T.unlines [ "^ foo ^ bar ^" , "| bat | baz |" ] =?> simpleTable [plain "foo", plain "bar"] [[plain "bat", plain "baz"]] , "Table with alignment" =: T.unlines [ "^ 0 ^ 1 ^ 2 ^ 3 ^" , "| a | b | c |d |" ] =?> table emptyCaption (map (, ColWidthDefault) [AlignLeft, AlignCenter, AlignRight, AlignDefault]) (TableHead nullAttr [Row nullAttr . map (simpleCell . plain) $ ["0", "1", "2", "3"]]) [TableBody nullAttr 0 [] [Row nullAttr . map (simpleCell . plain) $ ["a", "b", "c", "d"]]] (TableFoot nullAttr []) , "Table with colspan" =: T.unlines [ "^ 0,0 ^ 0,1 ^ 0,2 ^" , "| 1,0 | 1,1 ||" , "| 2,0 | 2,1 | 2,2 |" ] =?> simpleTable [plain "0,0", plain "0,1", plain "0,2"] [[plain "1,0", plain "1,1", mempty] ,[plain "2,0", plain "2,1", plain "2,2"] ] , "Indented code block" =: T.unlines [ "foo" , " bar" , " bat" , "baz" ] =?> para "foo" <> codeBlock "bar\n bat\n" <> para "baz" ] ================================================ FILE: test/Tests/Readers/EPUB.hs ================================================ {- | Module : Tests.Readers.EPUB Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.eu> Stability : alpha Portability : portable Tests for the EPUB mediabag. -} module Tests.Readers.EPUB (tests) where import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit import qualified Text.Pandoc.Class as P import Text.Pandoc.MediaBag (MediaBag, mediaDirectory) import Text.Pandoc.Options import Text.Pandoc.Readers.EPUB getMediaBag :: FilePath -> IO MediaBag getMediaBag fp = do bs <- BL.readFile fp P.runIOorExplode $ do readEPUB def bs P.getMediaBag testMediaBag :: FilePath -> [(String, String, Int)] -> IO () testMediaBag fp bag = do actBag <- mediaDirectory <$> getMediaBag fp assertBool (show "MediaBag did not match:\nExpected: " ++ show bag ++ "\nActual: " ++ show actBag) (actBag == packBag bag) where packBag = map $ \(x, y, z) -> (x, T.pack y, z) featuresBag :: [(String, String, Int)] featuresBag = [("img/check.gif","image/gif",1340) ,("img/check.jpg","image/jpeg",2661) ,("img/check.png","image/png",2815) ,("img/multiscripts_and_greek_alphabet.png","image/png",10060) ] -- with additional meta tag for cover in EPUB2 format epub3CoverBag :: [(String, String, Int)] epub3CoverBag = [("wasteland-cover.jpg","image/jpeg", 16586)] epub3NoCoverBag :: [(String, String, Int)] epub3NoCoverBag = [("img/check.gif","image/gif",1340) ,("img/check.jpg","image/jpeg",2661) ,("img/check.png","image/png",2815) ] -- content.opf uses the word `picture` to refer to the cover as much as validly possible -- to check if references are resolved correctly epub2PictureBag :: [(String, String, Int)] epub2PictureBag = [("image/image.jpg","image/jpeg",9713)] -- content.opf contains the word `cover` as much as possible, to check if possible multiple matches cause errors epub2CoverBag :: [(String, String, Int)] epub2CoverBag = [("image/cover.jpg","image/jpeg",9713)] epub2NoCoverBag :: [(String, String, Int)] epub2NoCoverBag = [] tests :: [TestTree] tests = [ testGroup "EPUB Mediabag" [ testCase "features bag" (testMediaBag "epub/img.epub" featuresBag), testCase "EPUB3 cover bag" (testMediaBag "epub/wasteland.epub" epub3CoverBag), testCase "EPUB3 no cover bag" (testMediaBag "epub/img_no_cover.epub" epub3NoCoverBag), testCase "EPUB2 picture bag" (testMediaBag "epub/epub2_picture.epub" epub2PictureBag), testCase "EPUB2 cover bag" (testMediaBag "epub/epub2_cover.epub" epub2CoverBag), testCase "EPUB2 no cover bag" (testMediaBag "epub/epub2_no_cover.epub" epub2NoCoverBag) ] ] ================================================ FILE: test/Tests/Readers/FB2.hs ================================================ {- | Module : Tests.Readers.FB2 Copyright : © 2018-2020 Alexander Krotov License : GNU GPL, version 2 or above Maintainer : © 2018-2020 Alexander Krotov <ilabdsf@gmail.com> Stability : alpha Portability : portable Tests for the FB2 reader. -} module Tests.Readers.FB2 (tests) where import Test.Tasty import Tests.Helpers import Test.Tasty.Golden (goldenVsString) import qualified Data.ByteString as BS import Text.Pandoc import Text.Pandoc.UTF8 (toText, fromStringLazy) import Data.Text (Text, unpack) import System.FilePath (replaceExtension) fb2ToNative :: Text -> Text fb2ToNative = purely (writeNative def{ writerTemplate = Just mempty }) . purely (readFB2 def) fb2Test :: TestName -> FilePath -> TestTree fb2Test name path = goldenVsString name native (fromStringLazy . filter (/='\r') . unpack . fb2ToNative . toText <$> BS.readFile path) where native = replaceExtension path ".native" tests :: [TestTree] tests = [ fb2Test "Emphasis" "fb2/reader/emphasis.fb2" , fb2Test "Titles" "fb2/reader/titles.fb2" , fb2Test "Epigraph" "fb2/reader/epigraph.fb2" , fb2Test "Poem" "fb2/reader/poem.fb2" , fb2Test "Meta" "fb2/reader/meta.fb2" , fb2Test "Notes" "fb2/reader/notes.fb2" ] ================================================ FILE: test/Tests/Readers/HTML.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.HTML Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha Portability : portable Tests for the HTML reader. -} module Tests.Readers.HTML (tests) where import Data.Text (Text) import qualified Data.Text as T import Test.Tasty import Test.Tasty.QuickCheck import Test.Tasty.Options (IsOption(defaultValue)) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Shared (isHeaderBlock) import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder import Text.Pandoc.Walk (walk) html :: Text -> Pandoc html = purely $ readHtml def htmlNativeDivs :: Text -> Pandoc htmlNativeDivs = purely $ readHtml def { readerExtensions = enableExtension Ext_native_divs $ readerExtensions def } makeRoundTrip :: Block -> Block makeRoundTrip CodeBlock{} = Para [Str "code block was here"] makeRoundTrip LineBlock{} = Para [Str "line block was here"] makeRoundTrip RawBlock{} = Para [Str "raw block was here"] makeRoundTrip (Div attr bs) = Div attr $ filter (not . isHeaderBlock) bs -- avoids round-trip failures related to makeSections -- e.g. with [Div ("loc",[],[("a","11"),("b_2","a b c")]) [Header 3 ("",[],[]) []]] makeRoundTrip Table{} = Para [Str "table block was here"] makeRoundTrip x = x removeRawInlines :: Inline -> Inline removeRawInlines RawInline{} = Str "raw inline was here" removeRawInlines x = x roundTrip :: Blocks -> Bool roundTrip b = d'' == d''' where d = walk removeRawInlines $ walk makeRoundTrip $ Pandoc nullMeta $ toList b d' = rewrite d d'' = rewrite d' d''' = rewrite d'' rewrite = html . (`T.snoc` '\n') . purely (writeHtml5String def { writerWrapText = WrapPreserve }) tests :: [TestTree] tests = [ testGroup "base tag" [ test html "simple" $ "<head><base href=\"http://www.w3schools.com/images/foo\" ></head><body><img src=\"stickman.gif\" alt=\"Stickman\"></head>" =?> plain (image "http://www.w3schools.com/images/stickman.gif" "" (text "Stickman")) , test html "slash at end of base" $ "<head><base href=\"http://www.w3schools.com/images/\" ></head><body><img src=\"stickman.gif\" alt=\"Stickman\"></head>" =?> plain (image "http://www.w3schools.com/images/stickman.gif" "" (text "Stickman")) , test html "slash at beginning of href" $ "<head><base href=\"http://www.w3schools.com/images/\" ></head><body><img src=\"/stickman.gif\" alt=\"Stickman\"></head>" =?> plain (image "http://www.w3schools.com/stickman.gif" "" (text "Stickman")) , test html "absolute URL" $ "<head><base href=\"http://www.w3schools.com/images/\" ></head><body><img src=\"http://example.com/stickman.gif\" alt=\"Stickman\"></head>" =?> plain (image "http://example.com/stickman.gif" "" (text "Stickman")) ] , testGroup "anchors" [ test html "anchor without href" $ "<a name=\"anchor\"/>" =?> plain (spanWith ("anchor",[],[]) mempty) ] , testGroup "img" [ test html "data-external attribute" $ "<img data-external=\"1\" src=\"http://example.com/stickman.gif\">" =?> plain (imageWith ("", [], [("external", "1")]) "http://example.com/stickman.gif" "" "") , test html "title" $ "<img title=\"The title\" src=\"http://example.com/stickman.gif\">" =?> plain (imageWith ("", [], []) "http://example.com/stickman.gif" "The title" "") ] , testGroup "lang" [ test html "lang on <html>" $ "<html lang=\"es\">hola" =?> setMeta "lang" (text "es") (doc (plain (text "hola"))) , test html "xml:lang on <html>" $ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"es\"><head></head><body>hola</body></html>" =?> setMeta "lang" (text "es") (doc (plain (text "hola"))) ] , testGroup "main" [ test htmlNativeDivs "<main> contents are parsed" $ "<header>ignore me</header><nav><p>ignore me</p><main>hello</main><footer>ignore me</footer>" =?> doc (plain (text "hello")) , test htmlNativeDivs "<main role=X> becomes <div role=X>" $ "<main role=foobar>hello</main>" =?> doc (divWith ("", [], [("role", "foobar")]) (plain (text "hello"))) , test htmlNativeDivs "<main> has attributes preserved" $ "<main id=foo class=bar data-baz=qux>hello</main>" =?> doc (divWith ("foo", ["bar"], [("role", "main"), ("baz", "qux")]) (plain (text "hello"))) , test htmlNativeDivs "<main> closes <p>" $ "<p>hello<main>main content</main>" =?> doc (plain (text "main content")) , test htmlNativeDivs "<main> followed by text" $ "<main>main content</main>non-main content" =?> doc (plain (text "main content")) ] , testGroup "code" [ test html "inline code block" $ "<code>Answer is 42</code>" =?> plain (codeWith ("",[],[]) "Answer is 42") ] , testGroup "tt" [ test html "inline tt block" $ "<tt>Answer is 42</tt>" =?> plain (codeWith ("",[],[]) "Answer is 42") ] , testGroup "samp" [ test html "inline samp block" $ "<samp>Answer is 42</samp>" =?> plain (codeWith ("",["sample"],[]) "Answer is 42") ] , testGroup "var" [ test html "inline var block" $ "<var>result</var>" =?> plain (codeWith ("",["variable"],[]) "result") ] , testGroup "header" [ test htmlNativeDivs "<header> is parsed as a div" $ "<header id=\"title\">Title</header>" =?> divWith ("title", mempty, mempty) (plain "Title") ] , testGroup "code block" [ test html "attributes in pre > code element" $ "<pre><code id=\"a\" class=\"python\">\nprint('hi')\n</code></pre>" =?> codeBlockWith ("a", ["python"], []) "\nprint('hi')" , test html "attributes in pre take precedence" $ "<pre id=\"c\"><code id=\"d\">print('hi mom!')\n</code></pre>" =?> codeBlockWith ("c", [], []) "print('hi mom!')" ] , askOption $ \(QuickCheckTests numtests) -> testProperty "Round trip" $ withMaxSuccess (if QuickCheckTests numtests == defaultValue then 25 else numtests) roundTrip ] ================================================ FILE: test/Tests/Readers/JATS.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.JATS Copyright : © 2017 Hamish Mackenzie License : GNU GPL, version 2 or above Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> Stability : alpha Portability : portable Tests for the JATS reader. -} module Tests.Readers.JATS (tests) where import Data.Text (Text) import Test.Tasty import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder import qualified Data.Text as T jats :: Text -> Pandoc jats = purely $ readJATS def tests :: [TestTree] tests = [ testGroup "inline code" [ test jats "basic" $ "<p>\n <monospace>@&</monospace>\n</p>" =?> para (code "@&") ] , testGroup "block code" [ test jats "basic" $ "<preformat>@&</preformat>" =?> codeBlock "@&" , test jats "lang" $ "<code language=\"c\">@&</code>" =?> codeBlockWith ("", ["c"], []) "@&" ] , testGroup "images" [ test jats "basic" $ "<graphic mimetype=\"image\" mime-subtype=\"\" xlink:href=\"/url\" xlink:title=\"title\" />" =?> para (image "/url" "title" mempty) , test jats "alt-text" $ "<graphic id=\"graphic001\"\n\ \ xlink:href=\"https://lh3.googleusercontent.com/dB7iirJ3ncQaVMBGE2YX-WCeoAVIChb6NAzoFcKCFChMsrixJvD7ZRbvcaC-ceXEzXYaoH4K5vaoRDsUyBHFkpIDPnsn3bnzovbvi0a2Gg=s660\"\n\ \ xlink:title=\"This is the title of the graphic\"\n\ \ xlink:role=\"This is the role of the graphic\">\n\ \ <alt-text>Alternative text of the graphic</alt-text>\n\ \ <caption>\n\ \ <title>This is the title of the caption\n\ \

    Google doodle from 14 March 2003

    \n\ \ " =?> Para [ Image ( "graphic001" , [ "This" , "is" , "the" , "role" , "of" , "the" , "graphic" ] , [] ) [ Str "Alternative" , Space , Str "text" , Space , Str "of" , Space , Str "the" , Space , Str "graphic" ] ( "https://lh3.googleusercontent.com/dB7iirJ3ncQaVMBGE2YX-WCeoAVIChb6NAzoFcKCFChMsrixJvD7ZRbvcaC-ceXEzXYaoH4K5vaoRDsUyBHFkpIDPnsn3bnzovbvi0a2Gg=s660" , "This is the title of the graphic" ) ] ] , test jats "bullet list" $ "\n\ \ \n\ \

    \n\ \ first\n\ \

    \n\ \
    \n\ \ \n\ \

    \n\ \ second\n\ \

    \n\ \
    \n\ \ \n\ \

    \n\ \ third\n\ \

    \n\ \
    \n\ \
    " =?> bulletList [ para $ text "first" , para $ text "second" , para $ text "third" ] , testGroup "definition lists" [ test jats "with internal link" $ "\n\ \ \n\ \ \n\ \ testing\n\ \ \n\ \ \n\ \

    \n\ \ hi there\n\ \

    \n\ \
    \n\ \
    \n\ \
    " =?> definitionList [(link "#go" "" (str "testing"), [para (text "hi there")])] ] , testGroup "math" [ test jats "escape |" $ "

    \n\ \ \n\ \ \n\ \ σ|{x}\n\ \

    " =?> para (math "\\sigma|_{\\{x\\}}") , test jats "tex-math only" $ "

    \n\ \ \n\ \ \n\ \ \n\ \

    " =?> para (math "\\sigma|_{\\{x\\}}") , test jats "math ml only" $ "

    \n\ \ \n\ \ σ|{x}\n\ \

    " =?> para (math "\\sigma|_{\\{ x\\}}") ] , testGroup "headers" -- TODO fix footnotes in headers -- [ test jats "unnumbered header" $ -- "\n\ -- \ Header 1<fn>\n\ -- \ <p>\n\ -- \ note\n\ -- \ </p>\n\ -- \ </fn>\n\ -- \" -- =?> header 1 -- (text "Header 1" <> note (plain $ text "note")) [ test jats "unnumbered sub header" $ "\n\ \ Header\n\ \ \n\ \ Sub-Header\n\ \ \n\ \" =?> headerWith ("foo", [], []) 1 (text "Header") <> headerWith ("foo2", [], []) 2 (text "Sub-Header") , test jats "containing image" $ "\n\ \ <inline-graphic mimetype=\"image\" mime-subtype=\"jpeg\" xlink:href=\"imgs/foo.jpg\" />\n\ \" =?> header 1 (image "imgs/foo.jpg" "" mempty) ] , testGroup "metadata" [ test jats "abstract" $ T.unlines [ "" , "" , "" , "

    Paragraph 1

    " , "

    Paragraph 2

    " , "
    " , "
    " , "
    " ] =?> let abstract = para "Paragraph 1" <> para "Paragraph 2" in setMeta "abstract" abstract $ doc mempty ] ] ================================================ FILE: test/Tests/Readers/Jira.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Tests.Readers.Jira Copyright : © 2019-2024 Albert Krewinel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Tests for the RST reader. -} module Tests.Readers.Jira (tests) where import Prelude hiding (unlines) import Data.Text (Text, unlines) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers (ToString, purely, test, (=?>)) import Text.Pandoc (def) import Text.Pandoc.Readers.Jira (readJira) import Text.Pandoc.Builder jira :: Text -> Pandoc jira = purely $ readJira def infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test jira tests :: [TestTree] tests = [ testGroup "para" [ "Simple sentence" =: "Hello, World!" =?> para "Hello, World!" , "leading blank lines" =: "\n\ntext" =?> para "text" ] , testGroup "header" [ "header" =: "h1. Main\n" =?> header 1 "Main" ] , testGroup "list" [ "simple list" =: "* foo\n* bar\n" =?> bulletList [para "foo", para "bar"] , "list with minus as bullets" =: "- foo\n- bar\n" =?> bulletList [para "foo", para "bar"] , "ordered list / enumeration" =: "# first\n# second\n" =?> orderedList [para "first", para "second"] ] , testGroup "block quote" [ "simple block quote" =: "bq. _Don't_ quote me on this." =?> blockQuote (para $ emph "Don't" <> space <> "quote me on this.") , "block quote between paragraphs" =: unlines [ "Regular text." , "bq.This is a blockquote" , "More text." ] =?> mconcat [ para "Regular text." , blockQuote (para "This is a blockquote") , para "More text." ] ] , testGroup "table" [ "table without header" =: "| one | two |\n| three | four |\n" =?> simpleTable [] [ [para "one", para "two"] , [para "three", para "four"]] , "table with header" =: "|| one || two ||\n| three | four |\n| five | six |\n" =?> simpleTable [para "one", para "two"] [ [para "three", para "four"] , [para "five", para "six"]] , "table with column header" =: "|| language | haskell | lua |\n|| type | static | dynamic |\n" =?> simpleTable [] [ [para "language", para "haskell", para "lua"] , [para "type", para "static", para "dynamic"]] , "table after paragraph" =: "*tabletest*\n||Name|\n|Test|\n" =?> para (strong "tabletest") <> simpleTable [para "Name"] [[para "Test"]] ] , testGroup "panel" [ "simple panel" =: "{panel}\nInterviewer: Jane Doe{panel}\n" =?> divWith ("", ["panel"], []) (para "Interviewer: Jane Doe") ] , testGroup "inlines" [ "emphasis" =: "*quid pro quo*" =?> para (strong "quid pro quo") , "deleted" =: "-old-" =?> para (strikeout "old") , "monospaced" =: "{{this *is* monospace}}" =?> para (code "this is monospace") , "sub- and superscript" =: "HCO ~3~^-^" =?> para ("HCO " <> subscript "3" <> superscript "-") , "citation" =: "Et tu, Brute? ??Caesar??" =?> para ("Et tu, Brute? — " <> emph "Caesar") , "color" =: "This is {color:red}red{color}." =?> para ("This is " <> spanWith ("", [], [("color", "red")]) "red" <> ".") , "hexcolor" =: "{color:#00875A}green{color}" =?> para (spanWith ("", [], [("color", "#00875A")]) "green") , "linebreak" =: "first\nsecond" =?> para ("first" <> linebreak <> "second") , testGroup "links" [ "external" =: "[Example|https://example.org]" =?> para (link "https://example.org" "" "Example") , "URL in alias" =: "[See https://example.com|https://example.com]" =?> para (link "https://example.com" "" "See https://example.com") , "email" =: "[mailto:me@example.org]" =?> para (link "mailto:me@example.org" "" "me@example.org") , "email with description" =: "[email|mailto:me@example.org]" =?> para (link "mailto:me@example.org" "" "email") , "attachment" =: "[^example.txt]" =?> para (linkWith ("", ["attachment"], []) "example.txt" "" "example.txt") , "attachment with description" =: "[an example^example.txt]" =?> para (linkWith ("", ["attachment"], []) "example.txt" "" "an example") , "user" =: "[~johndoe]" =?> para (linkWith ("", ["user-account"], []) "~johndoe" "" "~johndoe") , "user with description" =: "[John Doe|~johndoe]" =?> para (linkWith ("", ["user-account"], []) "~johndoe" "" "John Doe") , "'smart' link" =: "[x|http://example.com|smart-link]" =?> para (linkWith ("", ["smart-link"], []) "http://example.com" "" "x") , "'smart' card" =: "[x|http://example.com|smart-card]" =?> para (linkWith ("", ["smart-card"], []) "http://example.com" "" "x") ] , "image" =: "!https://example.com/image.jpg!" =?> para (image "https://example.com/image.jpg" "" mempty) , "thumbnail image" =: "!image.jpg|thumbnail!" =?> para (imageWith ("", ["thumbnail"], []) "image.jpg" "" mempty) , "image with attributes" =: "!image.gif|align=right, vspace=4, title=Hello!" =?> let attr = ("", [], [("align", "right"), ("vspace", "4")]) in para $ imageWith attr "image.gif" "Hello" mempty , "inserted text" =: "+the new version+" =?> para (underline "the new version") , "HTML entity" =: "me & you" =?> para "me & you" , "non-strikeout dashes" =: "20.09-15 2-678" =?> para "20.09-15 2-678" ] ] ================================================ FILE: test/Tests/Readers/LaTeX.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.LaTeX Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Tests for the LaTeX reader. -} module Tests.Readers.LaTeX (tests) where import Data.Text (Text) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder latex :: Text -> Pandoc latex = purely $ readLaTeX def{ readerExtensions = getDefaultExtensions "latex" } infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test latex table' :: [Alignment] -> [Row] -> Blocks table' aligns rows = table emptyCaption (zip aligns (repeat ColWidthDefault)) (TableHead nullAttr []) [TableBody nullAttr 0 [] rows] (TableFoot nullAttr []) simpleTable' :: [Alignment] -> [[Blocks]] -> Blocks simpleTable' aligns rows = table' aligns (map toRow rows) where toRow = Row nullAttr . map simpleCell tests :: [TestTree] tests = [ testGroup "basic" [ "simple" =: "word" =?> para "word" , "space" =: "some text" =?> para "some text" , "emphasized" =: "\\emph{emphasized}" =?> para (emph "emphasized") ] , testGroup "headers" [ "level 1" =: "\\section{header}" =?> headerWith ("header",[],[]) 1 "header" , "level 2" =: "\\subsection{header}" =?> headerWith ("header",[],[]) 2 "header" , "level 3" =: "\\subsubsection{header}" =?> headerWith ("header",[],[]) 3 "header" , "emph" =: "\\section{text \\emph{emph}}" =?> headerWith ("text-emph",[],[]) 1 ("text" <> space <> emph "emph") , "link" =: "\\section{text \\href{/url}{link}}" =?> headerWith ("text-link",[],[]) 1 ("text" <> space <> link "/url" "" "link") ] , testGroup "math" [ "escaped $" =: "$x=\\$4$" =?> para (math "x=\\$4") ] , testGroup "space and comments" [ "blank lines + space at beginning" =: "\n \n hi" =?> para "hi" , "blank lines + space + comments" =: "% my comment\n\n \n % another\n\nhi" =?> para "hi" , "comment in paragraph" =: "hi % this is a comment\nthere\n" =?> para ("hi" <> softbreak <> "there") ] , testGroup "code blocks" [ "identifier" =: "\\begin{lstlisting}[label=test]\\end{lstlisting}" =?> codeBlockWith ("test", [], [("label","test")]) "" , "no identifier" =: "\\begin{lstlisting}\\end{lstlisting}" =?> codeBlock "" ] , testGroup "tables" [ "Single cell table" =: "\\begin{tabular}{|l|}Test\\\\\\end{tabular}" =?> simpleTable' [AlignLeft] [[plain "Test"]] , "Multi cell table" =: "\\begin{tabular}{|rl|}One & Two\\\\ \\end{tabular}" =?> simpleTable' [AlignRight,AlignLeft] [[plain "One", plain "Two"]] , "Multi line table" =: T.unlines [ "\\begin{tabular}{|c|}" , "One\\\\" , "Two\\\\" , "Three\\\\" , "\\end{tabular}" ] =?> simpleTable' [AlignCenter] [[plain "One"], [plain "Two"], [plain "Three"]] , "Empty table" =: "\\begin{tabular}{}\\end{tabular}" =?> simpleTable' [] [] , "Table with fixed column width" =: "\\begin{tabular}{|p{5cm}r|}One & Two\\\\ \\end{tabular}" =?> simpleTable' [AlignLeft,AlignRight] [[plain "One", plain "Two"]] , "Table with empty column separators" =: "\\begin{tabular}{@{}r@{}l}One & Two\\\\ \\end{tabular}" =?> simpleTable' [AlignRight,AlignLeft] [[plain "One", plain "Two"]] , "Table with custom column separators" =: T.unlines [ "\\begin{tabular}{@{($\\to$)}r@{\\hspace{2cm}}l}" , "One&Two\\\\" , "\\end{tabular}" ] =?> simpleTable' [AlignRight,AlignLeft] [[plain "One", plain "Two"]] , "Table with vertical alignment argument" =: "\\begin{tabular}[t]{r|r}One & Two\\\\ \\end{tabular}" =?> simpleTable' [AlignRight,AlignRight] [[plain "One", plain "Two"]] , "Table with multicolumn item" =: "\\begin{tabular}{l c r}\\multicolumn{2}{c}{One} & Two\\\\ \\end{tabular}" =?> table' [AlignLeft, AlignCenter, AlignRight] [ Row nullAttr [ cell AlignCenter (RowSpan 1) (ColSpan 2) (plain "One") , simpleCell (plain "Two") ] ] , "table with multicolumn item (#6596)" =: "\\begin{tabular}{l c r}One & \\multicolumn{2}{c}{Two} & \\\\ \\end{tabular}" =?> table' [AlignLeft, AlignCenter, AlignRight] [ Row nullAttr [ simpleCell (plain "One") , cell AlignCenter (RowSpan 1) (ColSpan 2) (plain "Two") ] ] , "Table with multirow item" =: T.unlines ["\\begin{tabular}{c}" ,"\\multirow{2}{5em}{One}\\\\Two\\\\" ,"\\end{tabular}" ] =?> table' [AlignCenter] [ Row nullAttr [ cell AlignDefault (RowSpan 2) (ColSpan 1) (plain "One") ] , Row nullAttr [ simpleCell (plain "Two") ] ] , "Table with multirow item using full prototype" =: T.unlines ["\\begin{tabular}{c}" ,"\\multirow[c]{2}[3]{5em}[1in]{One}\\\\Two\\\\" ,"\\end{tabular}" ] =?> table' [AlignCenter] [ Row nullAttr [ cell AlignDefault (RowSpan 2) (ColSpan 1) (plain "One") ] , Row nullAttr [ simpleCell (plain "Two") ] ] , "Table with nested multirow/multicolumn item" =: T.unlines [ "\\begin{tabular}{c c c c}" , "\\multicolumn{3}{c}{\\multirow{2}{5em}{One}}&Two\\\\" , "\\multicolumn{2}{c}{} & & Three\\\\" , "Four&Five&Six&Seven\\\\" , "\\end{tabular}" ] =?> table' [AlignCenter, AlignCenter, AlignCenter, AlignCenter] [ Row nullAttr [ cell AlignCenter (RowSpan 2) (ColSpan 3) (plain "One") , simpleCell (plain "Two") ] , Row nullAttr [ simpleCell (plain "Three") ] , Row nullAttr [ simpleCell (plain "Four") , simpleCell (plain "Five") , simpleCell (plain "Six") , simpleCell (plain "Seven") ] ] , "Table with multicolumn header" =: T.unlines [ "\\begin{tabular}{ |l|l| }" , "\\hline\\multicolumn{2}{|c|}{Header}\\\\" , "\\hline key & val\\\\" , "\\hline\\end{tabular}" ] =?> table emptyCaption (zip [AlignLeft, AlignLeft] (repeat ColWidthDefault)) (TableHead nullAttr [ Row nullAttr [cell AlignCenter (RowSpan 1) (ColSpan 2) (plain "Header")]]) [TableBody nullAttr 0 [] [Row nullAttr [ simpleCell (plain "key") , simpleCell (plain "val") ] ] ] (TableFoot nullAttr []) , "Table with normal empty cells" =: T.unlines [ "\\begin{tabular}{|r|r|r|}" , "A & & B \\\\" , " & C &" , "\\end{tabular}" ] =?> table emptyCaption (replicate 3 (AlignRight, ColWidthDefault)) (TableHead nullAttr []) [TableBody nullAttr 0 [] [Row nullAttr [ simpleCell (plain "A") , emptyCell , simpleCell (plain "B") ] ,Row nullAttr [ emptyCell , simpleCell (plain "C") , emptyCell ]]] (TableFoot nullAttr []) ] , testGroup "citations" [ natbibCitations , biblatexCitations ] , testGroup "images" [ "Basic image" =: "\\includegraphics{foo.png}" =?> para (image "foo.png" "" (text "image")) , "Basic image with blank options" =: "\\includegraphics[]{foo.png}" =?> para (image "foo.png" "" (text "image")) , "Image with both width and height" =: "\\includegraphics[width=17cm,height=5cm]{foo.png}" =?> para (imageWith ("", [], [("width", "17cm"), ("height", "5cm")]) "foo.png" "" "image") , "Image with width and height and a bunch of other options" =: "\\includegraphics[width=17cm,height=5cm,clip,keepaspectratio]{foo.png}" =?> para (imageWith ("", [], [("width", "17cm"), ("height", "5cm")]) "foo.png" "" "image") , "Image with just width" =: "\\includegraphics[width=17cm]{foo.png}" =?> para (imageWith ("", [], [("width", "17cm")]) "foo.png" "" "image") , "Image with just height" =: "\\includegraphics[height=17cm]{foo.png}" =?> para (imageWith ("", [], [("height", "17cm")]) "foo.png" "" "image") , "Image width relative to textsize" =: "\\includegraphics[width=0.6\\textwidth]{foo.png}" =?> para (imageWith ("", [], [("width", "60%")]) "foo.png" "" "image") , "Image with options with spaces" =: "\\includegraphics[width=12cm, height = 5cm]{foo.png}" =?> para (imageWith ("", [], [("width", "12cm"), ("height", "5cm")]) "foo.png" "" "image") ] , let hex = ['0'..'9']++['a'..'f'] in testGroup "Character Escapes" [ "Two-character escapes" =: mconcat ["^^" <> T.pack [i,j] | i <- hex, j <- hex] =?> para (str $ T.pack ['\0'..'\255']) , "One-character escapes" =: mconcat ["^^" <> T.pack [i] | i <- hex] =?> para (str $ T.pack $ ['p'..'y']++['!'..'&']) ] , testGroup "memoir scene breaks" [ "plainbreak" =: "hello\\plainbreak{2}goodbye" =?> para (str "hello") <> horizontalRule <> para (str "goodbye") , "plainbreak*" =: "hello\\plainbreak*{2}goodbye" =?> para (str "hello") <> horizontalRule <> para (str "goodbye") , "fancybreak" =: "hello\\fancybreak{b r e a k}goodbye" =?> para (str "hello") <> horizontalRule <> para (str "goodbye") , "fancybreak*" =: "hello\\fancybreak*{b r e a k}goodbye" =?> para (str "hello") <> horizontalRule <> para (str "goodbye") , "plainfancybreak" =: "hello\\plainfancybreak{4}{2}{b r e a k}goodbye" =?> para (str "hello") <> horizontalRule <> para (str "goodbye") , "plainfancybreak*" =: "hello\\plainfancybreak*{4}{2}{b r e a k}goodbye" =?> para (str "hello") <> horizontalRule <> para (str "goodbye") , "pfbreak" =: "hello\\pfbreak{}goodbye" =?> para (str "hello") <> horizontalRule <> para (str "goodbye") , "pfbreak*" =: "hello\\pfbreak*{}goodbye" =?> para (str "hello") <> horizontalRule <> para (str "goodbye") ] , testGroup "biblatex roman numerals" [ "upper" =: "number \\RN{12}" =?> para (str "number" <> space <> str "XII") , "lower" =: "number \\Rn{29}" =?> para (str "number" <> space <> str "xxix") , "leading zero" =: "\\Rn{014}" =?> para (str "xiv") , "surrounding spaces" =: "number \\Rn{ 41 }" =?> para (str "number" <> space <> str "xli") , "zero" =: "\\RN{0}" =?> para (str "") , "space then unbraced argument" =: "\\RN 7 ok" =?> para (str "VII" <> space <> str "ok") , "space before braced argument" =: "\\Rn {13}ok" =?> para (str "xiiiok") ] , testGroup "polyglossia language spans" [ "french" =: "hello \\textfrench{bonjour}" =?> para (str "hello" <> space <> spanWith ("", [], [("lang", "fr")]) (str "bonjour")) , "nested" =: "\\textfrench{quelle c'est \\textlatin{primus}?}" =?> para (spanWith ("", [], [("lang", "fr")]) $ str "quelle" <> space <> str "c\8217est" <> space <> spanWith ("", [], [("lang", "la")]) (str "primus") <> str "?") , "with formatting" =: "\\textgerman{wie \\emph{spaet} ist es?}" =?> para (spanWith ("", [], [("lang", "de")]) $ str "wie" <> space <> emph (str "spaet") <> space <> str "ist" <> space <> str "es?") , "language options" =: "\\textgerman[variant=swiss]{hoechdeutsche}" =?> para (spanWith ("", [], [("lang", "de-CH")]) $ str "hoechdeutsche") , "unknown option fallback" =: "\\textgerman[variant=moon]{ueberhoechdeutsche}" =?> para (spanWith ("", [], [("lang", "de")]) $ str "ueberhoechdeutsche") ] ] baseCitation :: Citation baseCitation = Citation{ citationId = "item1" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } rt :: String -> Inlines rt = rawInline "latex" . T.pack natbibCitations :: TestTree natbibCitations = testGroup "natbib" [ "citet" =: "\\citet{item1}" =?> para (cite [baseCitation] (rt "\\citet{item1}")) , "suffix" =: "\\citet[p.~30]{item1}" =?> para (cite [baseCitation{ citationSuffix = toList $ text "p.\160\&30" }] (rt "\\citet[p.~30]{item1}")) , "suffix long" =: "\\citet[p.~30, with suffix]{item1}" =?> para (cite [baseCitation{ citationSuffix = toList $ text "p.\160\&30, with suffix" }] (rt "\\citet[p.~30, with suffix]{item1}")) , "multiple" =: "\\citeauthor{item1} \\citetext{\\citeyear{item1}; \\citeyear[p.~30]{item2}; \\citealp[see also][]{item3}}" =?> para (cite [baseCitation{ citationMode = AuthorInText } ,baseCitation{ citationMode = SuppressAuthor , citationSuffix = [Str "p.\160\&30"] , citationId = "item2" } ,baseCitation{ citationId = "item3" , citationPrefix = [Str "see",Space,Str "also"] , citationMode = NormalCitation } ] (rt "\\citetext{\\citeyear{item1}; \\citeyear[p.~30]{item2}; \\citealp[see also][]{item3}}")) , "group" =: "\\citetext{\\citealp[see][p.~34--35]{item1}; \\citealp[also][chap. 3]{item3}}" =?> para (cite [baseCitation{ citationMode = NormalCitation , citationPrefix = [Str "see"] , citationSuffix = [Str "p.\160\&34\8211\&35"] } ,baseCitation{ citationMode = NormalCitation , citationId = "item3" , citationPrefix = [Str "also"] , citationSuffix = [Str "chap.",Space,Str "3"] } ] (rt "\\citetext{\\citealp[see][p.~34--35]{item1}; \\citealp[also][chap. 3]{item3}}")) , "suffix and locator" =: "\\citep[pp.~33, 35--37, and nowhere else]{item1}" =?> para (cite [baseCitation{ citationMode = NormalCitation , citationSuffix = [Str "pp.\160\&33,",Space,Str "35\8211\&37,",Space,Str "and",Space,Str "nowhere",Space, Str "else"] }] (rt "\\citep[pp.~33, 35--37, and nowhere else]{item1}")) , "suffix only" =: "\\citep[and nowhere else]{item1}" =?> para (cite [baseCitation{ citationMode = NormalCitation , citationSuffix = toList $ text "and nowhere else" }] (rt "\\citep[and nowhere else]{item1}")) , "no author" =: "\\citeyearpar{item1}, and now Doe with a locator \\citeyearpar[p.~44]{item2}" =?> para (cite [baseCitation{ citationMode = SuppressAuthor }] (rt "\\citeyearpar{item1}") <> text ", and now Doe with a locator " <> cite [baseCitation{ citationMode = SuppressAuthor , citationSuffix = [Str "p.\160\&44"] , citationId = "item2" }] (rt "\\citeyearpar[p.~44]{item2}")) , "markup" =: "\\citep[\\emph{see}][p. \\textbf{32}]{item1}" =?> para (cite [baseCitation{ citationMode = NormalCitation , citationPrefix = [Emph [Str "see"]] , citationSuffix = [Str "p.",Space, Strong [Str "32"]] }] (rt "\\citep[\\emph{see}][p. \\textbf{32}]{item1}")) ] biblatexCitations :: TestTree biblatexCitations = testGroup "biblatex" [ "textcite" =: "\\textcite{item1}" =?> para (cite [baseCitation] (rt "\\textcite{item1}")) , "suffix" =: "\\textcite[p.~30]{item1}" =?> para (cite [baseCitation{ citationSuffix = toList $ text "p.\160\&30" }] (rt "\\textcite[p.~30]{item1}")) , "suffix long" =: "\\textcite[p.~30, with suffix]{item1}" =?> para (cite [baseCitation{ citationSuffix = toList $ text "p.\160\&30, with suffix" }] (rt "\\textcite[p.~30, with suffix]{item1}")) , "multiple" =: "\\textcites{item1}[p.~30]{item2}[see also][]{item3}" =?> para (cite [baseCitation{ citationMode = AuthorInText } ,baseCitation{ citationMode = NormalCitation , citationSuffix = [Str "p.\160\&30"] , citationId = "item2" } ,baseCitation{ citationId = "item3" , citationPrefix = [Str "see",Space,Str "also"] , citationMode = NormalCitation } ] (rt "\\textcites{item1}[p.~30]{item2}[see also][]{item3}")) , "group" =: "\\autocites[see][p.~34--35]{item1}[also][chap. 3]{item3}" =?> para (cite [baseCitation{ citationMode = NormalCitation , citationPrefix = [Str "see"] , citationSuffix = [Str "p.\160\&34\8211\&35"] } ,baseCitation{ citationMode = NormalCitation , citationId = "item3" , citationPrefix = [Str "also"] , citationSuffix = [Str "chap.",Space,Str "3"] } ] (rt "\\autocites[see][p.~34--35]{item1}[also][chap. 3]{item3}")) , "suffix and locator" =: "\\autocite[pp.~33, 35--37, and nowhere else]{item1}" =?> para (cite [baseCitation{ citationMode = NormalCitation , citationSuffix = [Str "pp.\160\&33,",Space,Str "35\8211\&37,",Space,Str "and",Space,Str "nowhere",Space, Str "else"] }] (rt "\\autocite[pp.~33, 35--37, and nowhere else]{item1}")) , "suffix only" =: "\\autocite[and nowhere else]{item1}" =?> para (cite [baseCitation{ citationMode = NormalCitation , citationSuffix = toList $ text "and nowhere else" }] (rt "\\autocite[and nowhere else]{item1}")) , "no author" =: "\\autocite*{item1}, and now Doe with a locator \\autocite*[p.~44]{item2}" =?> para (cite [baseCitation{ citationMode = SuppressAuthor }] (rt "\\autocite*{item1}") <> text ", and now Doe with a locator " <> cite [baseCitation{ citationMode = SuppressAuthor , citationSuffix = [Str "p.\160\&44"] , citationId = "item2" }] (rt "\\autocite*[p.~44]{item2}")) , "markup" =: "\\autocite[\\emph{see}][p. \\textbf{32}]{item1}" =?> para (cite [baseCitation{ citationMode = NormalCitation , citationPrefix = [Emph [Str "see"]] , citationSuffix = [Str "p.",Space, Strong [Str "32"]] }] (rt "\\autocite[\\emph{see}][p. \\textbf{32}]{item1}")) , "parencite" =: "\\parencite{item1}" =?> para (cite [baseCitation{ citationMode = NormalCitation }] (rt "\\parencite{item1}")) ] ================================================ FILE: test/Tests/Readers/Man.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Man Copyright : © 2018-2019 Yan Pas , 2018-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Tests for the Man reader. -} module Tests.Readers.Man (tests) where import Data.Text (Text) import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder man :: Text -> Pandoc man = purely $ readMan def infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test man toRow :: [Blocks] -> Row toRow = Row nullAttr . map simpleCell tests :: [TestTree] tests = [ -- .SH "HEllo bbb" "aaa"" as" testGroup "Macros" [ "Bold" =: ".B foo" =?> para (strong "foo") , "Italic" =: ".I bar\n" =?> para (emph "bar") , "BoldItalic" =: ".BI foo bar" =?> para (strong (str "foo") <> emph (str "bar")) , "H1" =: ".SH The header\n" =?> header 1 (text "The header") , "H2" =: ".SS \"The header 2\"" =?> header 2 (text "The header 2") , "Macro args" =: ".B \"single arg with \"\"Q\"\"\"" =?>para (strong $ text "single arg with \"Q\"") , "Argument from next line" =: ".B\nsingle arg with \"Q\"" =?>para (strong $ text "single arg with \"Q\"") , "comment" =: ".\\\"bla\naaa" =?>para (str "aaa") , "link" =: ".BR aa (1)" =?> para (strong (str "aa") <> str "(1)") ], testGroup "Escapes" [ "fonts" =: "aa\\fIbb\\fRcc" =?>para (str "aa" <> emph (str "bb") <> str "cc") , "nested fonts" =: "\\f[BI]hi\\f[I] there\\f[R]" =?> para (emph (strong (text "hi") <> text " there")) , "nested fonts 2" =: "\\f[R]hi \\f[I]there \\f[BI]bold\\f[R] ok" =?> para (text "hi " <> emph (text "there " <> strong (text "bold")) <> text " ok") , "skip" =: "a\\%\\\n\\:b\\0" =?>para (str "ab\8199") , "replace" =: "\\-\\ \\\\\\[lq]\\[rq]\\[em]\\[en]\\*(lq\\*(rq" =?>para (text "- \\“”—–“”") , "replace2" =: "\\t\\e\\`\\^\\|\\'" =?>para (text "\\`\8202\8198'") , "comment with \\\"" =: "Foo \\\" bar\n" =?>para (text "Foo") , "comment with \\#" =: "Foo\\#\nbar\n" =?>para (text "Foobar") , "two letter escapes" =: "\\(oA\\(~O" =?>para (text "ÅÕ") , "bracketed escapes" =: "\\[oA]\\[~O]\\[Do]\\[Ye]\\[product]\\[ul]" =?>para (text "ÅÕ$¥∏_") , "unicode escapes" =: "\\[u2020]" =?>para (text "†") , "unicode escapes (combined)" =: "\\[u0075_u0301]" =?>para (text "\250") , "unknown escape (#5034)" =: "\\9" =?>para (text "9") ], testGroup "Lists" [ "bullet" =: ".IP \"\\[bu]\"\nfirst\n.IP \"\\[bu]\"\nsecond" =?> bulletList [para $ str "first", para $ str "second"] , "ordered" =: ".IP 2 a\nfirst\n.IP 3 a\nsecond" =?> orderedListWith (2,Decimal,DefaultDelim) [para $ str "first", para $ str "second"] , "upper" =: ".IP A) a\nfirst\n.IP B) a\nsecond" =?> orderedListWith (1,UpperAlpha,OneParen) [para $ str "first", para $ str "second"] , "nested" =: ".IP \"\\[bu]\"\nfirst\n.RS\n.IP \"\\[bu]\"\n1a\n.IP \"\\[bu]\"\n1b\n.RE" =?> bulletList [para (str "first") <> bulletList [para $ str "1a", para $ str "1b"]] , "change in list style" =: ".IP \\[bu]\nfirst\n.IP 1\nsecond" =?> bulletList [para (str "first")] <> orderedListWith (1,Decimal,DefaultDelim) [para (str "second")] ], testGroup "CodeBlocks" [ "cb1"=: ".nf\naa\n\tbb\n.fi" =?> codeBlock "aa\n\tbb" ], testGroup "Tables" [ "t1" =: ".TS\nallbox;\nl l l.\na\tb\tc\nd\te\tf\n.TE" =?> table emptyCaption (replicate 3 (AlignLeft, ColWidthDefault)) (TableHead nullAttr []) [TableBody nullAttr 0 [] $ map toRow [map (plain . str ) ["a", "b", "c"], map (plain . str ) ["d", "e", "f"]]] (TableFoot nullAttr []), "longcell" =: ".TS\n;\nr.\nT{\na\nb\nc d\nT}\nf\n.TE" =?> table emptyCaption [(AlignRight, ColWidthDefault)] (TableHead nullAttr []) [TableBody nullAttr 0 [] $ map toRow [[plain $ text "a b c d"], [plain $ str "f"]]] (TableFoot nullAttr []) ] ] ================================================ FILE: test/Tests/Readers/Markdown.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Markdown Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Tests for the Markdown reader. -} module Tests.Readers.Markdown (tests) where import Data.Text (Text, unpack) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder markdown :: Text -> Pandoc markdown = purely $ readMarkdown def { readerExtensions = disableExtension Ext_smart pandocExtensions } markdownSmart :: Text -> Pandoc markdownSmart = purely $ readMarkdown def { readerExtensions = enableExtension Ext_smart pandocExtensions } markdownGH :: Text -> Pandoc markdownGH = purely $ readMarkdown def {readerExtensions = enableExtension Ext_wikilinks_title_before_pipe githubMarkdownExtensions } markdownMMD :: Text -> Pandoc markdownMMD = purely $ readMarkdown def { readerExtensions = multimarkdownExtensions } infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test markdown testBareLink :: (Text, Inlines) -> TestTree testBareLink (inp, ils) = test (purely $ readMarkdown def{ readerExtensions = extensionsFromList [Ext_autolink_bare_uris, Ext_raw_html] }) (unpack inp) (inp, doc $ para ils) autolink :: String -> Inlines autolink = autolinkWith ("",["uri"],[]) autolinkWith :: Attr -> String -> Inlines autolinkWith attr s = linkWith attr s' "" (str s') where s' = T.pack s wikilink :: Attr wikilink = (mempty, ["wikilink"], mempty) bareLinkTests :: [(Text, Inlines)] bareLinkTests = [ ("http://google.com is a search engine.", autolink "http://google.com" <> " is a search engine.") , ("
    http://foo.bar.baz", rawInline "html" "" <> "http://foo.bar.baz" <> rawInline "html" "") , ("Try this query: http://google.com?search=fish&time=hour.", "Try this query: " <> autolink "http://google.com?search=fish&time=hour" <> ".") , ("HTTPS://GOOGLE.COM,", autolink "HTTPS://GOOGLE.COM" <> ",") , ("http://el.wikipedia.org/wiki/Τεχνολογία,", autolink "http://el.wikipedia.org/wiki/Τεχνολογία" <> ",") , ("doi:10.1000/182,", autolink "doi:10.1000/182" <> ",") , ("git://github.com/foo/bar.git,", autolink "git://github.com/foo/bar.git" <> ",") , ("file:///Users/joe/joe.txt, and", autolink "file:///Users/joe/joe.txt" <> ", and") , ("mailto:someone@somedomain.com.", autolink "mailto:someone@somedomain.com" <> ".") , ("Use http: this is not a link!", "Use http: this is not a link!") , ("(http://google.com).", "(" <> autolink "http://google.com" <> ").") , ("http://en.wikipedia.org/wiki/Sprite_(computer_graphics)", autolink "http://en.wikipedia.org/wiki/Sprite_(computer_graphics)") , ("http://en.wikipedia.org/wiki/Sprite_[computer_graphics]", linkWith ("",["uri"],[]) "http://en.wikipedia.org/wiki/Sprite_%5Bcomputer_graphics%5D" "" (str "http://en.wikipedia.org/wiki/Sprite_[computer_graphics]")) , ("http://en.wikipedia.org/wiki/Sprite_{computer_graphics}", linkWith ("",["uri"],[]) "http://en.wikipedia.org/wiki/Sprite_%7Bcomputer_graphics%7D" "" (str "http://en.wikipedia.org/wiki/Sprite_{computer_graphics}")) , ("http://example.com/Notification_Center-GitHub-20101108-140050.jpg", autolink "http://example.com/Notification_Center-GitHub-20101108-140050.jpg") , ("https://github.com/github/hubot/blob/master/scripts/cream.js#L20-20", autolink "https://github.com/github/hubot/blob/master/scripts/cream.js#L20-20") , ("http://www.rubyonrails.com", autolink "http://www.rubyonrails.com") , ("http://www.rubyonrails.com:80", autolink "http://www.rubyonrails.com:80") , ("http://www.rubyonrails.com/~minam", autolink "http://www.rubyonrails.com/~minam") , ("https://www.rubyonrails.com/~minam", autolink "https://www.rubyonrails.com/~minam") , ("http://www.rubyonrails.com/~minam/url%20with%20spaces", autolink "http://www.rubyonrails.com/~minam/url%20with%20spaces") , ("http://www.rubyonrails.com/foo.cgi?something=here", autolink "http://www.rubyonrails.com/foo.cgi?something=here") , ("http://www.rubyonrails.com/foo.cgi?something=here&and=here", autolink "http://www.rubyonrails.com/foo.cgi?something=here&and=here") , ("http://www.rubyonrails.com/contact;new", autolink "http://www.rubyonrails.com/contact;new") , ("http://www.rubyonrails.com/contact;new%20with%20spaces", autolink "http://www.rubyonrails.com/contact;new%20with%20spaces") , ("http://www.rubyonrails.com/contact;new?with=query&string=params", autolink "http://www.rubyonrails.com/contact;new?with=query&string=params") , ("http://www.rubyonrails.com/~minam/contact;new?with=query&string=params", autolink "http://www.rubyonrails.com/~minam/contact;new?with=query&string=params") , ("http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007", autolink "http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007") , ("http://www.mail-archive.com/rails@lists.rubyonrails.org/", autolink "http://www.mail-archive.com/rails@lists.rubyonrails.org/") , ("http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1", autolink "http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1") , ("http://en.wikipedia.org/wiki/Texas_hold%27em", autolink "http://en.wikipedia.org/wiki/Texas_hold%27em") , ("https://www.google.com/doku.php?id=gps:resource:scs:start", autolink "https://www.google.com/doku.php?id=gps:resource:scs:start") , ("http://www.rubyonrails.com", autolink "http://www.rubyonrails.com") , ("http://manuals.ruby-on-rails.com/read/chapter.need_a-period/103#page281", autolink "http://manuals.ruby-on-rails.com/read/chapter.need_a-period/103#page281") , ("http://foo.example.com/controller/action?parm=value&p2=v2#anchor123", autolink "http://foo.example.com/controller/action?parm=value&p2=v2#anchor123") , ("http://foo.example.com:3000/controller/action", autolink "http://foo.example.com:3000/controller/action") , ("http://foo.example.com:3000/controller/action+pack", autolink "http://foo.example.com:3000/controller/action+pack") , ("http://business.timesonline.co.uk/article/0,,9065-2473189,00.html", autolink "http://business.timesonline.co.uk/article/0,,9065-2473189,00.html") , ("http://www.mail-archive.com/ruby-talk@ruby-lang.org/", autolink "http://www.mail-archive.com/ruby-talk@ruby-lang.org/") , ("https://example.org/?anchor=lala-", autolink "https://example.org/?anchor=lala-") , ("https://example.org/?anchor=-lala", autolink "https://example.org/?anchor=-lala") ] {- p_markdown_round_trip :: Block -> Bool p_markdown_round_trip b = matches d' d'' where d' = normalize $ Pandoc (Meta [] [] []) [b] d'' = normalize $ readMarkdown def { readerSmart = True } $ writeMarkdown def d' matches (Pandoc _ [Plain []]) (Pandoc _ []) = True matches (Pandoc _ [Para []]) (Pandoc _ []) = True matches (Pandoc _ [Plain xs]) (Pandoc _ [Para xs']) = xs == xs' matches x y = x == y -} tests :: [TestTree] tests = [ testGroup "inline code" [ "with attribute" =: "`document.write(\"Hello\");`{.javascript}" =?> para (codeWith ("",["javascript"],[]) "document.write(\"Hello\");") , "with attribute space" =: "`*` {.haskell .special x=\"7\"}" =?> para (code "*" <> space <> str "{.haskell" <> space <> str ".special" <> space <> str "x=\"7\"}") ] , testGroup "inline code in lists (regression tests for #6284)" $ let lists = [("ordered", "1. ", ol), ("bullet", "- ", ul)] ol = orderedListWith (1, Decimal, Period) ul = bulletList items = [ ("in text" , ["If `(1) x`, then `2`"], [text "If " <> code "(1) x" <> text ", then " <> code "2"]) , ("at start" , ["`#. x`" ], [code "#. x" ]) , ("at start" , ["`- x`" ], [code "- x" ]) , ("after literal backticks", ["`x``#. x`" ], [code "x``#. x" ]) , ("after literal backticks", ["`x``- x`" ], [code "x``- x" ]) ] lis = ["`text","y","x`"] bldLsts w lsts txts = let (res, res', f) = foldr (\((_, _, lt), lc) (acc, tacc, t) -> if lt [] == t [] then (acc, lc : tacc, lt) else (join t tacc acc, [lc], lt)) (mempty, [], mconcat) (zip lsts (map text txts)) join t tacc acc = case tacc of [] -> acc [x] -> t [plain x] <> acc xs -> t (map w xs) <> acc in join f res' res in ["code with list marker "<>mp<>" in " <> ln <> " list" =: T.intercalate "\n" (map (lstr <>) istrs) =?> lbld (map plain iblds) | (ln, lstr, lbld) <- lists, (mp, istrs, iblds) <- items] <> [ "lists with newlines in backticks" =: T.intercalate "\n" (zipWith (\i (_, lt, _) -> lt <> i) lis lsts) =?> bldLsts plain lsts lis | lsts <- [ [i, j, k] | i <- lists, j <- lists, k <- lists] ] <> [ "lists with blank lines and indent in backticks" =: T.intercalate ("\n\n" <> T.replicate 4 " ") (zipWith (\i (_, lt, _) -> lt <> i) lis (l:ls)) <> "\n" =?> let (_, _, f) = l in f . pure $ (para . text $ "`text") <> bldLsts para ls (drop 1 lis) | (l:ls) <- [ [i, j, k] | i <- lists, j <- lists, k <- lists] ] , testGroup "emph and strong" [ "two strongs in emph" =: "***a**b **c**d*" =?> para (emph (strong (str "a") <> str "b" <> space <> strong (str "c") <> str "d")) , "emph and strong emph alternating" =: "*xxx* ***xxx*** xxx\n*xxx* ***xxx*** xxx" =?> para (emph "xxx" <> space <> strong (emph "xxx") <> space <> "xxx" <> softbreak <> emph "xxx" <> space <> strong (emph "xxx") <> space <> "xxx") , "emph with spaced strong" =: "*x **xx** x*" =?> para (emph ("x" <> space <> strong "xx" <> space <> "x")) , "intraword underscore with opening underscore (#1121)" =: "_foot_ball_" =?> para (emph (text "foot_ball")) ] , testGroup "raw LaTeX" [ "in URL" =: "\\begin\n" =?> para (text "\\begin") ] , testGroup "raw HTML" [ "nesting (issue #1330)" =: "test" =?> rawBlock "html" "" <> plain (str "test") <> rawBlock "html" "" , "invalid tag (issue #1820" =: "" =?> para (text "") , "technically invalid comment" =: "" =?> rawBlock "html" "" , test markdownGH "issue 2469" $ "<\n\na>" =?> para (text "<") <> para (text "a>") ] , testGroup "raw email addresses" [ test markdownGH "issue 2940" $ "**@user**" =?> para (strong (text "@user")) ] , testGroup "emoji" [ test markdownGH "emoji symbols" $ ":smile: and :+1:" =?> para (spanWith ("", ["emoji"], [("data-emoji", "smile")]) "😄" <> space <> str "and" <> space <> spanWith ("", ["emoji"], [("data-emoji", "+1")]) "👍") ] , "unbalanced brackets" =: "[[[[[[[[[[[[hi" =?> para (text "[[[[[[[[[[[[hi") , testGroup "backslash escapes" [ "in URL" =: "[hi](/there\\))" =?> para (link "/there)" "" "hi") , "in title" =: "[hi](/there \"a\\\"a\")" =?> para (link "/there" "a\"a" "hi") , "in reference link title" =: "[hi]\n\n[hi]: /there (a\\)a)" =?> para (link "/there" "a)a" "hi") , "in reference link URL" =: "[hi]\n\n[hi]: /there\\.0" =?> para (link "/there.0" "" "hi") ] , testGroup "bare URIs" (map testBareLink bareLinkTests) , testGroup "autolinks" [ "with unicode dash following" =: "\8212" =?> para (autolink "http://foo.bar" <> str "\8212") , "a partial URL (#2277)" =: "" =?> para (text "") , "with some attributes" =: "{#i .j .z k=v}" =?> para (autolinkWith ("i", ["j", "z"], [("k", "v")]) "http://foo.bar") , "with some attributes and spaces" =: " {#i .j .z k=v}" =?> para (autolink "http://foo.bar" <> space <> text "{#i .j .z k=v}") ] , testGroup "links" [ "no autolink inside link" =: "[](url)" =?> para (link "url" "" (text "")) , "no inline link inside link" =: "[[a](url2)](url)" =?> para (link "url" "" (text "[a](url2)")) , "no bare URI inside link" =: "[https://example.org(](url)" =?> para (link "url" "" (text "https://example.org(")) ] , testGroup "Github wiki links" [ test markdownGH "autolink" $ "[[https://example.org]]" =?> para (linkWith wikilink "https://example.org" "" (str "https://example.org")) , test markdownGH "link with title" $ "[[title|https://example.org]]" =?> para (linkWith wikilink "https://example.org" "" (str "title")) , test markdownGH "bad link with title" $ "[[title|random string]]" =?> para (linkWith wikilink "random string" "" (str "title")) , test markdownGH "autolink not being a link" $ "[[Name of page]]" =?> para (linkWith wikilink "Name of page" "" (text "Name of page")) , test markdownGH "autolink not being a link with a square bracket" $ "[[Name of ]page]]" =?> para (linkWith wikilink "Name of ]page" "" (text "Name of ]page")) , test markdownGH "link with inline start should be a link" $ "[[t`i*t_le|https://example.org]]" =?> para (linkWith wikilink "https://example.org" "" (str "t`i*t_le")) ] , testGroup "Headers" [ "blank line before header" =: "\n# Header\n" =?> headerWith ("header",[],[]) 1 "Header" , "bracketed text (#2062)" =: "# [hi]\n" =?> headerWith ("hi",[],[]) 1 "[hi]" , "ATX header without trailing #s" =: "# Foo bar\n\n" =?> headerWith ("foo-bar",[],[]) 1 "Foo bar" , "ATX header without trailing #s" =: "# Foo bar with # #" =?> headerWith ("foo-bar-with",[],[]) 1 "Foo bar with #" , "setext header" =: "Foo bar\n=\n\n Foo bar 2 \n=" =?> headerWith ("foo-bar",[],[]) 1 "Foo bar" <> headerWith ("foo-bar-2",[],[]) 1 "Foo bar 2" ] , testGroup "Implicit header references" [ "ATX header without trailing #s" =: "# Header\n[header]\n\n[header ]\n\n[ header]" =?> headerWith ("header",[],[]) 1 "Header" <> para (link "#header" "" (text "header")) <> para (link "#header" "" (text "header")) <> para (link "#header" "" (text "header")) , "ATX header with trailing #s" =: "# Foo bar #\n[foo bar]\n\n[foo bar ]\n\n[ foo bar]" =?> headerWith ("foo-bar",[],[]) 1 "Foo bar" <> para (link "#foo-bar" "" (text "foo bar")) <> para (link "#foo-bar" "" (text "foo bar")) <> para (link "#foo-bar" "" (text "foo bar")) , "setext header" =: " Header \n=\n\n[header]\n\n[header ]\n\n[ header]" =?> headerWith ("header",[],[]) 1 "Header" <> para (link "#header" "" (text "header")) <> para (link "#header" "" (text "header")) <> para (link "#header" "" (text "header")) ] , testGroup "smart punctuation" [ test markdownSmart "quote before ellipses" ("'...hi'" =?> para (singleQuoted "…hi")) , test markdownSmart "apostrophe before emph" ("D'oh! A l'*aide*!" =?> para ("D’oh! A l’" <> emph "aide" <> "!")) , test markdownSmart "apostrophe in French" ("À l'arrivée de la guerre, le thème de l'«impossibilité du socialisme»" =?> para "À l’arrivée de la guerre, le thème de l’«impossibilité du socialisme»") , test markdownSmart "apostrophe after math" $ -- issue #1909 "The value of the $x$'s and the systems' condition." =?> para (text "The value of the " <> math "x" <> text "\8217s and the systems\8217 condition.") , test markdownSmart "unclosed double quote" ("**this should \"be bold**" =?> para (strong "this should \8220be bold")) ] , testGroup "sub- and superscripts" [ test markdownMMD "normal subscript" ("H~2~" =?> para ("H" <> subscript "2")) , test markdownMMD "normal superscript" ("x^3^" =?> para ("x" <> superscript "3")) , test markdownMMD "short subscript delimited by space" ("O~2 is dangerous" =?> para ("O" <> subscript "2" <> space <> "is dangerous")) , test markdownMMD "short subscript delimited by newline" ("O~2\n" =?> para ("O" <> subscript "2")) , test markdownMMD "short subscript delimited by EOF" ("O~2" =?> para ("O" <> subscript "2")) , test markdownMMD "short subscript delimited by punctuation" ("O~2." =?> para ("O" <> subscript "2" <> ".")) , test markdownMMD "short subscript delimited by emph" ("O~2*combustible!*" =?> para ("O" <> subscript "2" <> emph "combustible!")) , test markdownMMD "no nesting in short subscripts" ("y~*2*" =?> para ("y~" <> emph "2")) , test markdownMMD "short superscript delimited by space" ("x^2 = y" =?> para ("x" <> superscript "2" <> space <> "= y")) , test markdownMMD "short superscript delimited by newline" ("x^2\n" =?> para ("x" <> superscript "2")) , test markdownMMD "short superscript delimited by ExF" ("x^2" =?> para ("x" <> superscript "2")) , test markdownMMD "short superscript delimited by punctuation" ("x^2." =?> para ("x" <> superscript "2" <> ".")) , test markdownMMD "short superscript delimited by emph" ("x^2*combustible!*" =?> para ("x" <> superscript "2" <> emph "combustible!")) , test markdownMMD "no nesting in short superscripts" ("y^*2*" =?> para ("y^" <> emph "2")) ] , testGroup "footnotes" [ "indent followed by newline and flush-left text" =: "[^1]\n\n[^1]: my note\n\n \nnot in note\n" =?> para (note (para "my note")) <> para "not in note" , "indent followed by newline and indented text" =: "[^1]\n\n[^1]: my note\n \n in note\n" =?> para (note (para "my note" <> para "in note")) , "recursive note" =: "[^1]\n\n[^1]: See [^1]\n" =?> para (note (para "See [^1]")) ] , testGroup "lhs" [ test (purely $ readMarkdown def{ readerExtensions = enableExtension Ext_literate_haskell pandocExtensions }) "inverse bird tracks and html" $ ("> a\n\n< b\n\n
    \n" :: Text) =?> codeBlockWith ("",["haskell","literate"],[]) "a" <> codeBlockWith ("",["haskell"],[]) "b" <> divWith ("",[],[]) mempty ] -- the round-trip properties frequently fail -- , testGroup "round trip" -- [ property "p_markdown_round_trip" p_markdown_round_trip -- ] , testGroup "definition lists" [ "no blank space" =: "foo1\n : bar\n\nfoo2\n : bar2\n : bar3\n" =?> definitionList [ (text "foo1", [plain (text "bar")]) , (text "foo2", [plain (text "bar2"), plain (text "bar3")]) ] , "blank space before first def" =: "foo1\n\n : bar\n\nfoo2\n\n : bar2\n : bar3\n" =?> definitionList [ (text "foo1", [para (text "bar")]) , (text "foo2", [plain (text "bar2"), para (text "bar3")]) ] , "blank space before second def" =: "foo1\n : bar\n\nfoo2\n : bar2\n\n : bar3\n" =?> definitionList [ (text "foo1", [plain (text "bar")]) , (text "foo2", [plain (text "bar2"), plain (text "bar3")]) ] , "laziness" =: "foo1\n : bar\nbaz\n : bar2\n" =?> definitionList [ (text "foo1", [plain (text "bar" <> softbreak <> text "baz"), plain (text "bar2")]) ] , "no blank space before first of two paragraphs" =: "foo1\n : bar\n\n baz\n" =?> definitionList [ (text "foo1", [plain (text "bar") <> plain (text "baz")]) ] , "first line not indented" =: "foo\n: bar\n" =?> definitionList [ (text "foo", [plain (text "bar")]) ] , "list in definition" =: "foo\n: - bar\n" =?> definitionList [ (text "foo", [bulletList [plain (text "bar")]]) ] , "in div" =: "
    foo\n: - bar\n
    " =?> divWith nullAttr (definitionList [ (text "foo", [bulletList [plain (text "bar")]]) ]) ] , testGroup "lists" [ "issue #1154" =: " -
    \n first div breaks\n
    \n\n \n\n
    \n with this div too.\n
    \n" =?> bulletList [divWith nullAttr (para $ text "first div breaks") <> rawBlock "html" "" <> divWith nullAttr (para $ text "with this div too.")] , test markdownGH "issue #1636" $ T.unlines [ "* a" , "* b" , "* c" , " * d" ] =?> bulletList [ plain "a" , plain "b" , plain "c" <> bulletList [plain "d"] ] ] , testGroup "entities" [ "character references" =: "⟨ ö" =?> para (text "\10216 ö") , "numeric" =: ",DD" =?> para (text ",DD") , "in link title" =: "[link](/url \"title ⟨ ö ,\")" =?> para (link "/url" "title \10216 ö ," (text "link")) ] , testGroup "citations" [ "simple" =: "@item1" =?> para (cite [ Citation{ citationId = "item1" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 1 , citationHash = 0 } ] "@item1") , "key starts with digit" =: "@1657:huyghens" =?> para (cite [ Citation{ citationId = "1657:huyghens" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 1 , citationHash = 0 } ] "@1657:huyghens") ] , let citation = cite [Citation "cita" [] [] AuthorInText 1 0] (str "@cita") in testGroup "footnote/link following citation" -- issue #2083 [ "footnote" =: T.unlines [ "@cita[^note]" , "" , "[^note]: note" ] =?> para ( citation <> note (para $ str "note") ) , "normal link" =: "@cita [link](http://www.com)" =?> para ( citation <> space <> link "http://www.com" "" (str "link") ) , "reference link" =: T.unlines [ "@cita [link][link]" , "" , "[link]: http://www.com" ] =?> para ( citation <> space <> link "http://www.com" "" (str "link") ) , "short reference link" =: T.unlines [ "@cita [link]" , "" , "[link]: http://www.com" ] =?> para ( citation <> space <> link "http://www.com" "" (str "link") ) , "implicit header link" =: T.unlines [ "# Header" , "@cita [Header]" ] =?> headerWith ("header",[],[]) 1 (str "Header") <> para ( citation <> space <> link "#header" "" (str "Header") ) , "regular citation" =: "@cita [foo]" =?> para ( cite [Citation "cita" [] [Str "foo"] AuthorInText 1 0] (str "@cita" <> space <> str "[foo]") ) ] , testGroup "figures" [ "latex placement" =: "![caption](img.jpg){latex-placement=\"htbp\" alt=\"alt text\"}" =?> figureWith ("", [], [("latex-placement", "htbp")]) (simpleCaption $ plain "caption") (plain $ image (T.pack "img.jpg") (T.pack "") (text "alt text")) ] ] ================================================ FILE: test/Tests/Readers/Mdoc.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Mdoc Copyright : © 2024 Evan Silberman License : GNU GPL, version 2 or above Maintainer : Stability : alpha Portability : portable Tests for the Mdoc reader. -} module Tests.Readers.Mdoc (tests) where import Data.Text (Text) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder mdoc :: Text -> Pandoc mdoc = purely $ readMdoc def infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test mdoc cls :: Text -> Attr cls x = (mempty, [x], mempty) tests :: [TestTree] tests = [ testGroup "one-line enclosures" [ "Dq" =: ".Dq hello world" =?> para (doubleQuoted "hello world") , "Sq" =: ".Sq hello world" =?> para (singleQuoted "hello world") , "empty" =: ".Dq" =?> para (doubleQuoted mempty) , "nested" =: ".Dq Pq hello world" =?> para (doubleQuoted "(hello world)") , "nested with closing delimiters" =: ".Dq Pq hi mom !" =?> para (doubleQuoted "(hi mom)" <> "!") , "nested multiline enclosure" =: ".Dq Po a \\&; b \\&; c Pc ." =?> para (doubleQuoted "(a ; b ; c)" <> ".") , "with inlines" =: ".Dq hello Sy world ." =?> para (doubleQuoted ("hello" <> space <> strong "world" <> ".")) , "with text production" =: ".Dq I love the St -iso8601 standard!" =?> para (doubleQuoted "I love the ISO 8601 standard!") , "with Ns" =: ".Op , Ns Ar value ..." =?> para ("[," <> (codeWith (cls "variable") "value ...") <> "]") , "ending with open delimiter" =: ".Dq hi (" =?> para (doubleQuoted "hi (") ] , testGroup "multiline enclosures" [ "nested multiline" =: T.unlines [".Bo", ".Po", "hi", ".Pc", ".Bc"] =?> para ("[(hi)]") , "nested on one line" =: ".Bo Po hi Pc Bc" =?> para ("[(hi)]") , "with wacky delimiters" =: ".Bo ( | hi ! Bc ?" =?> para ("([| hi!]?") ] , testGroup "simple inlines" [ "Sy" =: ".Sy hello world" =?> para (strong "hello world") , "Em" =: ".Em hello world" =?> para (emph "hello world") , "Ev" =: ".Ev HELLO_WORLD ," =?> para (codeWith (cls "Ev") "HELLO_WORLD" <> ",") , "Ar" =: ".Ar ) z" =?> para (codeWith (cls "variable") "file ..." <> ") " <> codeWith (cls "variable") "z") , "In" =: ".In ( math.h ) b c" =?> para ("(" <> codeWith (cls "In") "" <> ") b c") , "Mt" =: ".Mt a@example.org , b@example.org" =?> para ((link "mailto:a@example.org" "" "a@example.org") <> "," <> space <> (link "mailto:b@example.org" "" "b@example.org")) , "No" =: ".No ( hello , world ! )" =?> para "(hello, world!)" , "empty Pa with closing punctuation" =: ".Pa ) z" =?> para (spanWith (cls "Pa") "~" <> ")" <> space <> spanWith (mempty, ["Pa"], mempty) "z") , "delimiters" =: ".Sy ( hello world )" =?> para (mconcat ["(", strong "hello world", ")"]) , "multiple" =: ".Sy hello Em world" =?> para (strong "hello" <> space <> emph "world") ] , testGroup "Fl" [ "simple" =: ".Fl w" =?> para (codeWith (cls "Fl") "-w") , "multiple" =: ".Fl W all" =?> para (codeWith (cls "Fl") "-W" <> space <> codeWith (cls "Fl") "-all") , "empty with following macro" =: ".Fl Cm x" =?> para (codeWith (cls "Fl") "-" <> codeWith (cls "Cm") "x") , "enclosed with following macro" =: ".Pq Fl Cm x" =?> para ("(" <> codeWith (cls "Fl") "-" <> codeWith (cls "Cm") "x" <> ")") -- XXX this is a mandoc delta, the period is meant to land outside -- the enclosure. parseInline has learned how to defer eols but not -- closing delimiters , "enclosed with following delimiters" =: ".Pq Fl x ." =?> para ("(" <> codeWith (cls "Fl") "-x" <> ".)") , "following Ns" =: ".Fl W Ns Cm all" =?> para (codeWith (cls "Fl") "-W" <> codeWith (cls "Cm") "all") , "GNU" =: ".Fl -help" =?> para (codeWith (cls "Fl") "--help") , "GNU escaped" =: ".Fl \\-help" =?> para (codeWith (cls "Fl") "--help") , "GNU Fl Fl" =: ".Fl Fl help" =?> para (codeWith (cls "Fl") "--help") , "punctuation" =: ".Op Fl a | b" =?> para ("[" <> codeWith (cls "Fl") "-a" <> " | " <> codeWith (cls "Fl") "-b" <> "]") , "middle close paren" =: ".Fl a ) z" =?> para (codeWith (cls "Fl") "-a" <> ") " <> codeWith (cls "Fl") "-z") , "empty with close paren" =: ".Fl ) z" =?> para (codeWith (cls "Fl") "-" <> ") " <> codeWith (cls "Fl") "-z") , "empty with pipe" =: ".Fl | z" =?> para (codeWith (cls "Fl") "-" <> " | " <> codeWith (cls "Fl") "-z") , "empty with parens" =: ".Fl ( )" =?> para ("(" <> codeWith (cls "Fl") "-" <> ")") ] , testGroup "links" [ "basic" =: ".Lk href name" =?> para (link "href" "" "name") , "complicated" =: ".Lk , ( href name )" =?> para ("," <> space <> "(" <> link "href" "" "name" <> ")") , "unnamed" =: ".Lk href" =?> para (link "href" "" "href") ] , testGroup "Ns macro" [ "at the beginning of a macro line (mandoc delta)" =: T.unlines [".Op before", ".Ns Op after"] =?> para "[before] [after]" -- mandoc: warning , "after a block closing macro" =: T.unlines [".Oo before", ".Oc Ns Op after"] =?> para "[before][after]" , "in the middle of a macro line" =: ".Oo before Oc Ns Op after" =?> para "[before][after]" , "before closing punctuation" =: ".Oo before Oc Ns : Op after" =?> para "[before]: [after]" -- mandoc: warning , "after closing punctuation" =: ".Oo before Oc : Ns Op after" =?> para "[before]:[after]" , "at the end of a macro line" =: T.unlines [".Oo before Oc Ns", ".Op after"] =?> para "[before][after]" , "at the end of a partial-implicit line" =: T.unlines [".Op before Ns", ".Op after"] =?> para "[before][after]" , "normal words" =: ".No no Ns ns No no" =?> para ("nons" <> space <> "no") , "opening punctuation" =: ".No no Ns \"(\" ns No no" =?> para ("no(ns" <> space <> "no") , "closing punctuation" =: ".No no \"Ns\" ns \")\" No no" =?> para ("nons)" <> space <> "no") ] , testGroup "spacing mode" [ "all text" =: T.unlines ["a", ".Sm off", "b c", "d", ".Sm on", "e"] =?> para ("a b c d e") , "text around macro" =: T.unlines ["a", ".Sm off", ".Sy b c", ".Sm on", "d"] =?> para ("a" <> space <> strong "bc" <> space <> "d") , "mulitple macros" =: T.unlines ["a", ".Sm off", ".Sy b Em c", ".Sm on", "d"] =?> para ("a" <> space <> strong "b" <> emph "c" <> space <> "d") , "mulitple control lines" =: T.unlines ["a", ".Sm off", ".Sy b", ".Em c", ".Sm on", "d"] =?> para ("a" <> space <> strong "b" <> emph "c" <> space <> "d") , "mixed control and text lines" =: T.unlines ["a", ".Sm off", ".Sy b", "c", ".Em d", ".Sm on", "d"] =?> para ("a" <> space <> strong "b" <> "c" <> emph "d" <> space <> "d") , "delimiters" =: T.unlines [".Sm off", ".Em a ", ".Em [ b | c ]", ".Sm on"] =?> para (emph "a" <> "[" <> emph "b" <> "|" <> emph "c" <> "]") ] , testGroup "Ap macro" [ "in the middle of a macro line" =: ".Xr mandoc 1 Ap s" =?> para (spanWith (cls "Xr") "mandoc(1)" <> "'s") -- mandoc difference: the edge case of "Ap (" tested in this mandoc regress -- isn't present in any actual OpenBSD base system manuals, where Ap is -- only ever followed by a letter. Furthermore, "Ap" is generally uncommon -- compared to "Ns '" (e.g. ".Xr mandoc 1 Ns 's"). I'm accepting a -- difference from mandoc here because correctly suppressing space after -- the "(" here would require more refactoring than I feel like doing at -- time of writing. -- per mandoc, should be: para (strong "bold" <> "'(" <> strong "bold") , "with punctuation and called macro" =: ".Sy bold Ap ( \"Sy\" bold" =?> para (strong "bold" <> "'( " <> strong "bold") ] , testGroup "Pf macro" [ "closing punctuation" =: ".Pf . right ." =?> para ".right." , "double punctuation" =: ".Pf . . double" =?> para ".. double" , "opening punctuation" =: ".Pf ( left ." =?> para "(left." , "unparsed argument" =: ".Pf Ar Sy gument " =?> para ("Ar" <> strong "gument") ] , testGroup "text production" [ "early NetBSD versions" =: T.unlines [".Nx 0.9a", "and", ".Nx 9.4b"] =?> para "NetBSD 0.9A and NetBSD 9.4b" , "with Ns" =: ".Ox Ns -specific" =?> para "OpenBSD-specific" , "with punctuation" =: ".Fx ," =?> para "FreeBSD," , "with argument and punctuation" =: ".Fx 12.0 ." =?> para "FreeBSD 12.0." , "BSD alone" =: ".Bx ." =?> para "BSD." , "BSD with macro" =: ".Bx No rocks" =?> para "BSD rocks" , "BSD with version" =: ".Bx 4.4 ," =?> para "4.4BSD," , "BSD with variant" =: ".Bx 4.3 tahoe !" =?> para "4.3BSD-Tahoe!" ] , testGroup "inline punctuation" [ testGroup "leading punctuation" [ "open paren" =: ".Em ( b" =?> para ("(" <> emph "b") , "open square bracket" =: ".Em \"[\" b" =?> para ("[" <> emph "b") , "pipe" =: ".Em | b" =?> para ("|" <> space <> emph "b") , "period" =: ".Em . b" =?> para ("." <> space <> emph "b") , "comma" =: ".Em , b" =?> para ("," <> space <> emph "b") , "semicolon" =: ".Em ; b" =?> para (";" <> space <> emph "b") , "colon" =: ".Em : b" =?> para (":" <> space <> emph "b") , "question mark" =: ".Em ? b" =?> para ("?" <> space <> emph "b") , "exclamation" =: ".Em ! b" =?> para ("!" <> space <> emph "b") , "close paren" =: ".Em ) b" =?> para (")" <> space <> emph "b") , "close square bracket" =: ".Em \"]\" b" =?> para ("]" <> space <> emph "b") ] , testGroup "trailing punctuation" [ "open paren" =: ".Em a (" =?> para (emph "a" <> space <> "(") , "open square bracket" =: ".Em a [" =?> para (emph "a" <> space <> "[") , "pipe" =: ".Em a |" =?> para (emph "a" <> space <> "|") , "period" =: ".Em a ." =?> para (emph "a" <> ".") , "comma" =: ".Em a ," =?> para (emph "a" <> ",") , "semicolon" =: ".Em a ;" =?> para (emph "a" <> ";") , "colon" =: ".Em a :" =?> para (emph "a" <> ":") , "question mark" =: ".Em a ?" =?> para (emph "a" <> "?") , "exclamation" =: ".Em a !" =?> para (emph "a" <> "!") , "close parens" =: ".Em a \")\"" =?> para (emph "a" <> ")") , "close square bracket" =: ".Em a ]" =?> para (emph "a" <> "]") ] , testGroup "middle punctuation" [ "open paren" =: ".Em a ( b" =?> para (mconcat [emph "a", space, "(", emph "b"]) , "open square bracket" =: ".Em a [ b" =?> para (mconcat [emph "a", space, "[", emph "b"]) , "pipe" =: ".Em a \"|\" b" =?> para (mconcat [emph "a", space, "|", space, emph "b"]) , "period" =: ".Em a . b" =?> para (mconcat [emph "a", ".", space, emph "b"]) , "comma" =: ".Em a , b" =?> para (mconcat [emph "a", ",", space, emph "b"]) , "semicolon" =: ".Em a ; b" =?> para (mconcat [emph "a", ";", space, emph "b"]) , "colon" =: ".Em a \":\" b" =?> para (mconcat [emph "a", ":", space, emph "b"]) , "question mark" =: ".Em a ? b" =?> para (mconcat [emph "a", "?", space, emph "b"]) , "exclamation" =: ".Em a ! b" =?> para (mconcat [emph "a", "!", space, emph "b"]) , "close paren" =: ".Em a ) b" =?> para (mconcat [emph "a", ")", space, emph "b"]) , "close square bracket" =: ".Em a ] b" =?> para (mconcat [emph "a", "]", space, emph "b"]) ] ] ] ================================================ FILE: test/Tests/Readers/Muse.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Muse Copyright : © 2017-2020 Alexander Krotov License : GNU GPL, version 2 or above Maintainer : Alexander Krotov Stability : alpha Portability : portable Tests for the Muse reader. -} module Tests.Readers.Muse (tests) where import Data.List (intersperse) import Data.Text (Text) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder amuse :: Text -> Pandoc amuse = purely $ readMuse def { readerExtensions = extensionsFromList [Ext_amuse]} emacsMuse :: Text -> Pandoc emacsMuse = purely $ readMuse def { readerExtensions = emptyExtensions } infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test amuse spcSep :: [Inlines] -> Inlines spcSep = mconcat . intersperse space simpleTable' :: Int -> Caption -> [Blocks] -> [[Blocks]] -> Blocks simpleTable' n capt headers rows = table capt (replicate n (AlignDefault, ColWidthDefault)) (TableHead nullAttr $ toHeaderRow headers) [TableBody nullAttr 0 [] $ map toRow rows] (TableFoot nullAttr []) where toRow = Row nullAttr . map simpleCell toHeaderRow l = [toRow l | not (null l)] tests :: [TestTree] tests = [ testGroup "Inlines" [ "Plain String" =: "Hello, World" =?> para "Hello, World" , "Muse is not XML" =: "<" =?> para "<" , "Emphasis" =: "*Foo bar*" =?> para (emph . spcSep $ ["Foo", "bar"]) , "Newline in the beginning of emphasis" =: "*\nFoo bar*" =?> para (text "*\nFoo bar*") , "Newline in the end of emphasis" =: "*Foo bar\n*" =?> para (text "*Foo bar\n*") , "Comma after closing *" =: "Foo *bar*, baz" =?> para ("Foo " <> emph "bar" <> ", baz") , "Letter after closing *" =: "Foo *bar*x baz" =?> para "Foo *bar*x baz" , "Letter before opening *" =: "Foo x*bar* baz" =?> para "Foo x*bar* baz" , "Digit after closing *" =: "Foo *bar*0 baz" =?> para "Foo *bar*0 baz" , "Emphasis tag" =: "Foo bar" =?> para (emph . spcSep $ ["Foo", "bar"]) , "Strong" =: "**Cider**" =?> para (strong "Cider") , "Strong tag" =: "Strong" =?> para (strong "Strong") , "Strong Emphasis" =: "***strength***" =?> para (strong . emph $ "strength") , "Strong inside emphasis" =: "*foo **bar** baz*" =?> para (emph (text "foo " <> strong (text "bar") <> text " baz")) , "Emphasis inside strong" =: "**foo *bar* baz**" =?> para (strong (text "foo " <> emph (text "bar") <> text " baz")) , "Opening asterisk can't be preceded by another one" =: "**foo*" =?> para "**foo*" , "Asterisk between words does not terminate emphasis" =: "*foo*bar*" =?> para (emph "foo*bar") , "Two asterisks between words do not terminate emphasis" =: "*foo**bar*" =?> para (emph "foo**bar") , "Three asterisks between words do not terminate emphasis" =: "*foo***bar*" =?> para (emph "foo***bar") , "Two asterisks between words do not terminate strong" =: "**foo**bar**" =?> para (strong "foo**bar") , "Three asterisks between words do not terminate strong" =: "**foo***bar**" =?> para (strong "foo***bar") , "Three asterisks between words do not terminate strong emphasis" =: "***foo***bar***" =?> para (strong . emph $ "foo***bar") , "Six asterisks between words do not terminate strong emphasis" =: "***foo******bar***" =?> para (strong . emph $ "foo******bar") , test emacsMuse "Underline" ("_Underline_" =?> para (underline "Underline")) , "Superscript tag" =: "Superscript" =?> para (superscript "Superscript") , "Subscript tag" =: "Subscript" =?> para (subscript "Subscript") , "Strikeout tag" =: "Strikeout" =?> para (strikeout "Strikeout") , "Opening inline tags" =: "foo bar baz" =?> para "foo bar baz" , "Closing inline tags" =: "foo bar baz" =?> para "foo bar baz" , "Tag soup" =: "foo bar
    baz" =?> para "foo bar baz" -- Both inline tags must be within the same paragraph , "No multiparagraph inline tags" =: T.unlines [ "First line" , "Second line" , "" , "Fourth line" ] =?> para "First line\nSecond line" <> para "Fourth line" , "Linebreak" =: "Line
    break" =?> para ("Line" <> linebreak <> "break") , "Trailing whitespace inside paragraph" =: T.unlines [ "First line " -- trailing whitespace here , "second line" ] =?> para "First line\nsecond line" , "Non-breaking space" =: "Foo~~bar" =?> para "Foo\160bar" , "Single ~" =: "Foo~bar" =?> para "Foo~bar" , testGroup "Code markup" [ "Code" =: "=foo(bar)=" =?> para (code "foo(bar)") , "Not code" =: "a=b= =c=d" =?> para (text "a=b= =c=d") -- Emacs Muse 3.20 parses this as code, we follow Amusewiki , "Not code if closing = is detached" =: "=this is not a code =" =?> para "=this is not a code =" , "Not code if opening = is detached" =: "= this is not a code=" =?> para "= this is not a code=" , "Code if followed by comma" =: "Foo =bar=, baz" =?> para (text "Foo " <> code "bar" <> text ", baz") , "Not code if followed by digit" =: "Foo =bar=0 baz" =?> para (text "Foo =bar=0 baz") , "One character code" =: "=c=" =?> para (code "c") , "Code with equal sign" =: "=foo = bar=" =?> para (code "foo = bar") , "Three = characters is not a code" =: "===" =?> para "===" , "Multiline code markup" =: "foo =bar\nbaz= end of code" =?> para (text "foo " <> code "bar\nbaz" <> text " end of code") {- Emacs Muse 3.20 has a bug: it publishes -

    foo bar - - baz foo

    - which is displayed as one paragraph by browsers. - We follow Amusewiki here and avoid joining paragraphs. -} , "No multiparagraph code" =: T.unlines [ "foo =bar" , "" , "baz= foo" ] =?> para "foo =bar" <> para "baz= foo" , "Code at the beginning of paragraph but not first column" =: " - =foo=" =?> bulletList [ para $ code "foo" ] ] , "Code tag" =: "foo(bar)" =?> para (code "foo(bar)") , "Math tag" =: "\\sum_{i=0}^n i^2" =?> para (math "\\sum_{i=0}^n i^2") , "Verbatim tag" =: "***" =?> para (emph "*") , "Verbatim inside code" =: "foo" =?> para (code "foo") , "Verbatim tag after text" =: "Foo bar" =?> para "Foo bar" , "Verbatim tag escapes block level markup" =: T.unlines [ "Foo bar" , "* Not a heading" , "baz" ] =?> para "Foo bar\n* Not a heading\nbaz" , "Class tag" =: "bar" =?> para (spanWith ("", ["foo"], []) "bar") , "Class tag without name" =: "foobar" =?> para (spanWith ("", [], []) "foobar") , "RTL" =: "<<>>" =?> para (spanWith ("", [], [("dir", "rtl")]) "foo bar") , "LTR" =: ">>>foo bar<<<" =?> para (spanWith ("", [], [("dir", "ltr")]) "foo bar") -- tag should match with the last tag, not verbatim one , "Nested \"
    \" inside em tag" =: "foobar
    " =?> para (emph "foobar") , testGroup "Links" [ "Link without description" =: "[[https://amusewiki.org/]]" =?> para (link "https://amusewiki.org/" "" (str "https://amusewiki.org/")) , "Link with description" =: "[[https://amusewiki.org/][A Muse Wiki]]" =?> para (link "https://amusewiki.org/" "" (text "A Muse Wiki")) , "Link with empty description" =: "[[https://amusewiki.org/][]]" =?> para (link "https://amusewiki.org/" "" (text "")) , "Image" =: "[[image.jpg]]" =?> para (image "image.jpg" "" mempty) , "Closing bracket is not allowed in image filename" =: "[[foo]bar.jpg]]" =?> para (text "[[foo]bar.jpg]]") , "Image with description" =: "[[image.jpg][Image]]" =?> para (image "image.jpg" "" (text "Image")) , "Image with space in filename" =: "[[image name.jpg]]" =?> para (image "image name.jpg" "" mempty) , "Image with width" =: "[[image.jpg 60]]" =?> para (imageWith ("", [], [("width", "60%")]) "image.jpg" mempty mempty) , "At least one space is required between image filename and width" =: "[[image.jpg60]]" =?> para (link "image.jpg60" mempty (str "image.jpg60")) , "Left-aligned image with width" =: "[[image.png 60 l][Image]]" =?> para (imageWith ("", ["align-left"], [("width", "60%")]) "image.png" "" (str "Image")) , "Right-aligned image with width" =: "[[image.png 60 r][Image]]" =?> para (imageWith ("", ["align-right"], [("width", "60%")]) "image.png" "" (str "Image")) , "Image link" =: "[[URL:image.jpg]]" =?> para (link "image.jpg" "" (str "image.jpg")) , "Image link with description" =: "[[URL:image.jpg][Image]]" =?> para (link "image.jpg" "" (text "Image")) -- Implicit links are supported in Emacs Muse, but not in Amusewiki: -- https://github.com/melmothx/text-amuse/issues/18 -- -- This test also makes sure '=' without whitespace is not treated as code markup , "No implicit links" =: "http://example.org/index.php?action=view&id=1" =?> para "http://example.org/index.php?action=view&id=1" , "Link with empty URL" =: "[[][empty URL]]" =?> para (link "" "" (text "empty URL")) , "No footnotes inside links" =: "[[https://amusewiki.org/][foo[1]]" =?> para (link "https://amusewiki.org/" "" (text "foo[1")) , "Image inside link" =: "[[https://amusewiki.org/][Image [[image.png][with it's own description]] inside link description]]" =?> para (link "https://amusewiki.org/" "" (text "Image " <> image "image.png" "" (text "with it's own description") <> text " inside link description")) , "Link inside image description" =: "[[image.jpg][Image from [[https://amusewiki.org/]]]]" =?> para (image "image.jpg" "" (text "Image from " <> link "https://amusewiki.org/" "" (str "https://amusewiki.org/"))) ] , testGroup "Literal" [ test emacsMuse "Inline literal" ("Foolitbar" =?> para (text "Foo" <> rawInline "html" "lit" <> text "bar")) , test emacsMuse "Single inline literal in paragraph" ("lit" =?> para (rawInline "html" "lit")) ] ] , testGroup "Blocks" [ "Block elements end paragraphs" =: T.unlines [ "First paragraph" , "----" , "Second paragraph" ] =?> para (text "First paragraph") <> horizontalRule <> para (text "Second paragraph") , testGroup "Horizontal rule" [ "Less than 4 dashes is not a horizontal rule" =: "---" =?> para (text "---") , "4 dashes is a horizontal rule" =: "----" =?> horizontalRule , "5 dashes is a horizontal rule" =: "-----" =?> horizontalRule , "4 dashes with spaces is a horizontal rule" =: "---- " =?> horizontalRule ] , testGroup "Page breaks" [ "Page break" =: " * * * * *" =?> divWith ("", [], [("style", "page-break-before: always;")]) mempty , "Page break with trailing space" =: " * * * * * " =?> divWith ("", [], [("style", "page-break-before: always;")]) mempty ] , testGroup "Paragraphs" [ "Simple paragraph" =: T.unlines [ "First line" , "second line." ] =?> para "First line\nsecond line." , "Indented paragraph" =: T.unlines [ " First line" , "second line." ] =?> para "First line\nsecond line." -- Emacs Muse starts a blockquote on the second line. -- We copy Amusewiki behavior and require a blank line to start a blockquote. , "Indentation in the middle of paragraph" =: T.unlines [ "First line" , " second line" , "third line" ] =?> para "First line\nsecond line\nthird line" , "Quote" =: " This is a quotation\n" =?> blockQuote (para "This is a quotation") , "Indentation does not indicate quote inside quote tag" =: T.unlines [ "" , " Not a nested quote" , "" ] =?> blockQuote (para "Not a nested quote") , "Multiline quote" =: T.unlines [ " This is a quotation" , " with a continuation" ] =?> blockQuote (para "This is a quotation\nwith a continuation") , testGroup "Div" [ "Div without id" =: T.unlines [ "
    " , "Foo bar" , "
    " ] =?> divWith nullAttr (para "Foo bar") , "Div with id" =: T.unlines [ "
    " , "Foo bar" , "
    " ] =?> divWith ("foo", [], []) (para "Foo bar") ] , "Biblio" =: T.unlines [ "" , "" , "Author, *Title*, description" , "" , "Another author, *Another title*, another description" , "" , "" ] =?> divWith ("", ["biblio"], []) (para (text "Author, " <> emph "Title" <> ", description") <> para (text "Another author, " <> emph "Another title" <> text ", another description")) , "Play" =: T.unlines [ "" , "Foo bar" , "" ] =?> divWith ("", ["play"], []) (para "Foo bar") , "Verse" =: T.unlines [ "> This is" , "> First stanza" , ">" -- Emacs produces verbatim ">" here, we follow Amusewiki , "> And this is" , "> Second stanza" , ">" , "" , ">" , "" , "> Another verse" , "> is here" ] =?> lineBlock [ "This is" , "First stanza" , "" , "And this is" , "\160\160Second stanza" , "" ] <> lineBlock [ "" ] <> lineBlock [ "Another verse" , "\160\160\160is here" ] ] , "Verse in list" =: " - > foo" =?> bulletList [ lineBlock [ "foo" ] ] , "Verse line starting with emphasis" =: "> *foo* bar" =?> lineBlock [ emph "foo" <> text " bar" ] , "Multiline verse in list" =: T.unlines [ " - > foo" , " > bar" ] =?> bulletList [ lineBlock [ "foo", "bar" ] ] , "Paragraph after verse in list" =: T.unlines [ " - > foo" , " bar" ] =?> bulletList [ lineBlock [ "foo" ] <> para "bar" ] , "Empty quote tag" =: T.unlines [ "" , "" ] =?> blockQuote mempty , "Quote tag" =: T.unlines [ "" , "Hello, world" , "" ] =?> blockQuote (para $ text "Hello, world") , "Nested quote tag" =: T.unlines [ "" , "foo" , "" , "bar" , "" , "baz" , "" ] =?> blockQuote (para "foo" <> blockQuote (para "bar") <> para "baz") , "Indented quote inside list" =: T.unlines [ " - " , " foo" , " " ] =?> bulletList [ blockQuote (para "foo") ] , "Verse tag" =: T.unlines [ "" , "" , "Foo bar baz" , " One two three" , "" , "" ] =?> lineBlock [ "" , text "Foo bar baz" , text "\160\160One two three" , "" ] , "Verse tag with empty line inside" =: T.unlines [ "" , "" , "" ] =?> lineBlock [ "" ] , "Verse tag with verbatim close tag inside" =: T.unlines [ "" , "" , "" ] =?> lineBlock [ "" ] , testGroup "Example" [ "Braces on separate lines" =: T.unlines [ "{{{" , "Example line" , "}}}" ] =?> codeBlock "Example line" , "Spaces after opening braces" =: T.unlines [ "{{{ " , "Example line" , "}}}" ] =?> codeBlock "Example line" , "One blank line in the beginning" =: T.unlines [ "{{{" , "" , "Example line" , "}}}" ] =?> codeBlock "\nExample line" , "One blank line in the end" =: T.unlines [ "{{{" , "Example line" , "" , "}}}" ] =?> codeBlock "Example line\n" , "Indented braces" =: T.unlines [ " - {{{" , " Example line" , " }}}" ] =?> bulletList [ codeBlock "Example line" ] , "Tabs" =: T.unlines [ "{{{" , "\t foo" , "\t\t" , "\t bar" , "}}}" ] =?> codeBlock " foo\n\t\n bar" -- Amusewiki requires braces to be on separate line, -- this is an extension. , "One line" =: "{{{Example line}}}" =?> codeBlock "Example line" ] , testGroup "Example tag" [ "Tags on separate lines" =: T.unlines [ "" , "Example line" , "" ] =?> codeBlock "Example line" , "One line" =: "Example line" =?> codeBlock "Example line" , "One blank line in the beginning" =: T.unlines [ "" , "" , "Example line" , "" ] =?> codeBlock "\nExample line" , "One blank line in the end" =: T.unlines [ "" , "Example line" , "" , "" ] =?> codeBlock "Example line\n" , "Example inside list" =: T.unlines [ " - " , " foo" , " " ] =?> bulletList [ codeBlock "foo" ] , "Empty example inside list" =: T.unlines [ " - " , " " ] =?> bulletList [ codeBlock "" ] , "Example inside list with empty lines" =: T.unlines [ " - " , " foo" , " " , "" , " bar" , "" , " " , " baz" , " " ] =?> bulletList [ codeBlock "foo" <> para "bar" <> codeBlock "baz" ] , "Indented example inside list" =: T.unlines [ " - " , " foo" , " " ] =?> bulletList [ codeBlock "foo" ] , "Example inside definition list" =: T.unlines [ " foo :: " , " bar" , " " ] =?> definitionList [ ("foo", [codeBlock "bar"]) ] , "Example inside list definition with empty lines" =: T.unlines [ " term :: " , " foo" , " " , "" , " bar" , "" , " " , " baz" , " " ] =?> definitionList [ ("term", [codeBlock "foo" <> para "bar" <> codeBlock "baz"]) ] , "Example inside note" =: T.unlines [ "Foo[1]" , "" , "[1] " , " bar" , " " ] =?> para ("Foo" <> note (codeBlock "bar")) ] , testGroup "Literal blocks" [ test emacsMuse "Literal block" (T.unlines [ "" , "\\newpage" , "" ] =?> rawBlock "latex" "\\newpage") ] , "Center" =: T.unlines [ "
    " , "Hello, world" , "
    " ] =?> para (text "Hello, world") , "Right" =: T.unlines [ "" , "Hello, world" , "" ] =?> para (text "Hello, world") , testGroup "Comments" [ "Comment tag" =: "\nThis is a comment\n" =?> (mempty::Blocks) , "Line comment" =: "; Comment" =?> (mempty::Blocks) , "Empty comment" =: ";" =?> (mempty::Blocks) , "Text after empty comment" =: ";\nfoo" =?> para "foo" -- Make sure we don't consume newline while looking for whitespace , "Not a comment (does not start with a semicolon)" =: " ; Not a comment" =?> para (text "; Not a comment") , "Not a comment (has no space after semicolon)" =: ";Not a comment" =?> para (text ";Not a comment") , "Not a comment (semicolon not in the first column)" =: " - ; foo" =?> bulletList [para "; foo"] ] , testGroup "Headers" [ "Part" =: "* First level" =?> header 1 "First level" , "Chapter" =: "** Second level" =?> header 2 "Second level" , "Section" =: "*** Third level" =?> header 3 "Third level" , "Subsection" =: "**** Fourth level" =?> header 4 "Fourth level" , "Subsubsection" =: "***** Fifth level" =?> header 5 "Fifth level" , "Whitespace is required after *" =: "**Not a header" =?> para "**Not a header" , "No headers in footnotes" =: T.unlines [ "Foo[1]" , "[1] * Bar" ] =?> para (text "Foo" <> note (para "* Bar")) , "No headers in quotes" =: T.unlines [ "" , "* Hi" , "" ] =?> blockQuote (para "* Hi") , "Headers consume anchors" =: T.unlines [ "; A comment to make sure anchor is not parsed as a directive" , "#bar" , "** Foo" ] =?> headerWith ("bar",[],[]) 2 "Foo" , "Headers don't consume anchors separated with a blankline" =: T.unlines [ "; A comment to make sure anchor is not parsed as a directive" , "#bar" , "" , "** Foo" ] =?> para (spanWith ("bar", [], []) mempty) <> header 2 "Foo" , "Headers terminate paragraph" =: T.unlines [ "foo" , "* bar" ] =?> para "foo" <> header 1 "bar" , "Headers terminate lists" =: T.unlines [ " - foo" , "* bar" ] =?> bulletList [ para "foo" ] <> header 1 "bar" , test emacsMuse "Paragraphs terminate Emacs Muse headers" (T.unlines [ "* Foo" , "bar" ] =?> header 1 "Foo" <> para "bar") , "Paragraphs don't terminate Text::Amuse headers" =: T.unlines [ "* Foo" , "bar" ] =?> header 1 "Foo\nbar" , "Empty header" =: T.unlines [ "Foo" , "" , "* " , "" , "bar" ] =?> para (text "Foo") <> header 1 "" <> para (text "bar") , test (purely $ readMuse def { readerExtensions = extensionsFromList [Ext_amuse, Ext_auto_identifiers]}) "Auto identifiers" (T.unlines [ "* foo" , "** Foo" , "* bar" , "** foo" , "* foo" ] =?> headerWith ("foo",[],[]) 1 "foo" <> headerWith ("foo-1",[],[]) 2 "Foo" <> headerWith ("bar",[],[]) 1 "bar" <> headerWith ("foo-2",[],[]) 2 "foo" <> headerWith ("foo-3",[],[]) 1 "foo") ] , testGroup "Directives" [ "Title" =: "#title Document title" =?> let titleInline = toList "Document title" meta = setMeta "title" (MetaInlines titleInline) nullMeta in Pandoc meta mempty -- Emacs Muse documentation says that "You can use any combination -- of uppercase and lowercase letters for directives", -- but also allows '-', which is not documented, but used for disable-tables. , test emacsMuse "Disable tables" ("#disable-tables t" =?> Pandoc (setMeta "disable-tables" (MetaInlines $ toList "t") nullMeta) mempty) , "Multiple directives" =: T.unlines [ "#title Document title" , "#subtitle Document subtitle" ] =?> Pandoc (setMeta "title" (MetaInlines $ toList "Document title") $ setMeta "subtitle" (MetaInlines $ toList "Document subtitle") nullMeta) mempty , "Multiline directive" =: T.unlines [ "#title Document title" , "#notes First line" , "and second line" , "#author Name" ] =?> Pandoc (setMeta "title" (MetaInlines $ toList "Document title") $ setMeta "notes" (MetaInlines $ toList "First line\nand second line") $ setMeta "author" (MetaInlines $ toList "Name") nullMeta) mempty , "Amusewiki's #cover is translated to pandoc's #cover-image" =: "#cover cover.png" =?> let titleInline = toList "cover.png" meta = setMeta "cover-image" (MetaInlines titleInline) nullMeta in Pandoc meta mempty ] , testGroup "Anchors" [ "Anchor" =: T.unlines [ "; A comment to make sure anchor is not parsed as a directive" , "#anchor Target" ] =?> para (spanWith ("anchor", [], []) mempty <> "Target") , "Anchor cannot start with a number" =: T.unlines [ "; A comment to make sure anchor is not parsed as a directive" , "#0notanchor Target" ] =?> para "#0notanchor Target" , "Not anchor if starts with a space" =: " #notanchor Target" =?> para "#notanchor Target" , "Anchor inside a paragraph" =: T.unlines [ "Paragraph starts here" , "#anchor and ends here." ] =?> para ("Paragraph starts here\n" <> spanWith ("anchor", [], []) mempty <> "and ends here.") , "Anchor with \"-\"" =: T.unlines [ "; A comment to make sure anchor is not parsed as a directive" , "#anchor-id Target" ] =?> para (spanWith ("anchor-id", [], []) mempty <> "Target") ] , testGroup "Footnotes" [ "Simple footnote" =: T.unlines [ "Here is a footnote[1]." , "" , "[1] Footnote contents" ] =?> para (text "Here is a footnote" <> note (para "Footnote contents") <> str ".") , "Simple secondary footnote" =: T.unlines [ "Here is a secondary note{1}." , "" , "{1} Secondary note contents" ] =?> para (text "Here is a secondary note" <> note (para "Secondary note contents") <> str ".") , "Missing footnote" =: "Foo[1]" =?> para "Foo[1]" , "Missing secondary note" =: "Foo{1}" =?> para "Foo{1}" , "Wrong note type" =: T.unlines [ "Here is a secondary note{1}" , "" , "Footnote contents[1]" ] =?> para "Here is a secondary note{1}" <> para "Footnote contents[1]" , "Recursive footnote" =: T.unlines [ "Start recursion here[1]" , "" , "[1] Recursion continues here[1]" ] =?> para (text "Start recursion here" <> note (para "Recursion continues here[1]")) , "Nested footnotes" =: T.unlines [ "Footnote: [1]" , "" , "[1] Nested: [2]" , "" , "[2] No recursion: [1]" ] =?> para (text "Footnote: " <> note (para (text "Nested: " <> note (para $ text "No recursion: [1]")))) , "No zero footnotes" =: T.unlines [ "Here is a footnote[0]." , "" , "[0] Footnote contents" ] =?> para "Here is a footnote[0]." <> para "[0] Footnote contents" , "Footnotes can't start with zero" =: T.unlines [ "Here is a footnote[01]." , "" , "[01] Footnote contents" ] =?> para "Here is a footnote[01]." <> para "[01] Footnote contents" , testGroup "Multiparagraph footnotes" [ "Amusewiki multiparagraph footnotes" =: T.unlines [ "Multiparagraph[1] footnotes[2]" , "" , "[1] First footnote paragraph" , "" , " Second footnote paragraph" , "with continuation" , "" , "Not a note" , "[2] Second footnote" ] =?> para (text "Multiparagraph" <> note (para "First footnote paragraph" <> para "Second footnote paragraph\nwith continuation") <> text " footnotes" <> note (para "Second footnote")) <> para (text "Not a note") -- Verse requires precise indentation, so it is good to test indentation requirements , "Note continuation with verse" =: T.unlines [ "Foo[1]" , "" , "[1] Bar" , "" , " > Baz" ] =?> para ("Foo" <> note (para "Bar" <> lineBlock ["Baz"])) , "Footnote ending in self-terminating element and followed by paragraph" =: T.unlines [ "Foo[1]" , "" , "[1] > bar" , "baz" ] =?> para (str "Foo" <> note (lineBlock ["bar"])) <> para (str "baz") , "Footnote starting with empty line" =: T.unlines [ "Foo[1]" , "" , "[1]" -- No space character after note marker , "" , " Bar" ] =?> para (str "Foo" <> note (para $ text "Bar")) , "Indentation in footnote starting with empty line" =: T.unlines [ "Foo[1]" , "" , "[1]" -- No space character after note marker , "" , " Bar" ] =?> para (str "Foo" <> note mempty) <> blockQuote (para $ text "Bar") , test emacsMuse "Emacs multiparagraph footnotes" (T.unlines [ "First footnote reference[1] and second footnote reference[2]." , "" , "[1] First footnote paragraph" , "" , "Second footnote" , "paragraph" , "" , "[2] Third footnote paragraph" , "" , "Fourth footnote paragraph" ] =?> para (text "First footnote reference" <> note (para "First footnote paragraph" <> para "Second footnote\nparagraph") <> text " and second footnote reference" <> note (para "Third footnote paragraph" <> para "Fourth footnote paragraph") <> text ".")) ] ] ] , testGroup "Tables" [ "Two cell table" =: "One | Two" =?> simpleTable [] [[plain "One", plain "Two"]] , "Table with multiple words" =: "One two | three four" =?> simpleTable [] [[plain "One two", plain "three four"]] , "Not a table" =: "One| Two" =?> para (text "One| Two") , "Not a table again" =: "One |Two" =?> para (text "One |Two") , "Two line table" =: T.unlines [ "One | Two" , "Three | Four" ] =?> simpleTable [] [[plain "One", plain "Two"], [plain "Three", plain "Four"]] , "Table with one header" =: T.unlines [ "First || Second" , "Third | Fourth" ] =?> simpleTable [plain "First", plain "Second"] [[plain "Third", plain "Fourth"]] , "Table with two headers" =: T.unlines [ "First || header" , "Second || header" , "Foo | bar" ] =?> simpleTable [plain "First", plain "header"] [[plain "Second", plain "header"], [plain "Foo", plain "bar"]] , "Header and footer reordering" =: T.unlines [ "Foo ||| bar" , "Baz || foo" , "Bar | baz" ] =?> simpleTable [plain "Baz", plain "foo"] [[plain "Bar", plain "baz"], [plain "Foo", plain "bar"]] , "Table with caption" =: T.unlines [ "Foo || bar || baz" , "First | row | here" , "Second | row | there" , "|+ Table caption +|" ] =?> simpleTable' 3 (simpleCaption $ plain $ text "Table caption") [plain "Foo", plain "bar", plain "baz"] [[plain "First", plain "row", plain "here"], [plain "Second", plain "row", plain "there"]] , "Table caption with +" =: T.unlines [ "Foo | bar" , "|+ Table + caption +|" ] =?> simpleTable' 2 (simpleCaption $ plain $ text "Table + caption") [] [[plain "Foo", plain "bar"]] , "Caption without table" =: "|+ Foo bar baz +|" =?> simpleTable' 0 (simpleCaption $ plain $ text "Foo bar baz") [] [] , "Table indented with space" =: T.unlines [ " Foo | bar" , " Baz | foo" , " Bar | baz" ] =?> simpleTable [] [[plain "Foo", plain "bar"], [plain "Baz", plain "foo"], [plain "Bar", plain "baz"]] , "Empty cells" =: T.unlines [ " | Foo" , " |" , " bar |" , " || baz" ] =?> simpleTable [plain "", plain "baz"] [[plain "", plain "Foo"], [plain "", plain ""], [plain "bar", plain ""]] , "Empty cell in the middle" =: T.unlines [ " 1 | 2 | 3" , " 4 | | 6" , " 7 | 8 | 9" ] =?> simpleTable [] [[plain "1", plain "2", plain "3"], [plain "4", mempty, plain "6"], [plain "7", plain "8", plain "9"]] , "Grid table" =: T.unlines [ "+-----+-----+" , "| foo | bar |" , "+-----+-----+" ] =?> simpleTable [] [[para "foo", para "bar"]] , "Grid table inside list" =: T.unlines [ " - +-----+-----+" , " | foo | bar |" , " +-----+-----+" ] =?> bulletList [simpleTable [] [[para "foo", para "bar"]]] , "Grid table with two rows" =: T.unlines [ "+-----+-----+" , "| foo | bar |" , "+-----+-----+" , "| bat | baz |" , "+-----+-----+" ] =?> simpleTable [] [[para "foo", para "bar"] ,[para "bat", para "baz"]] , "Grid table inside grid table" =: T.unlines [ "+-----+" , "|+---+|" , "||foo||" , "|+---+|" , "+-----+" ] =?> simpleTable [] [[simpleTable [] [[para "foo"]]]] , "Grid table with example" =: T.unlines [ "+------------+" , "| |" , "| foo |" , "| |" , "+------------+" ] =?> simpleTable [] [[codeBlock "foo"]] ] , testGroup "Lists" [ "Bullet list" =: T.unlines [ " - Item1" , "" , " - Item2" ] =?> bulletList [ para "Item1" , para "Item2" ] , "Ordered list" =: T.unlines [ " 1. Item1" , "" , " 2. Item2" ] =?> orderedListWith (1, Decimal, Period) [ para "Item1" , para "Item2" ] , "Ordered list with implicit numbers" =: T.unlines [ " 1. Item1" , "" , " 1. Item2" , "" , " 1. Item3" ] =?> orderedListWith (1, Decimal, Period) [ para "Item1" , para "Item2" , para "Item3" ] , "Ordered list with roman numerals" =: T.unlines [ " i. First" , " ii. Second" , " iii. Third" , " iv. Fourth" ] =?> orderedListWith (1, LowerRoman, Period) [ para "First" , para "Second" , para "Third" , para "Fourth" ] , "Bullet list with empty items" =: T.unlines [ " -" , "" , " - Item2" ] =?> bulletList [ mempty , para "Item2" ] , "Ordered list with empty items" =: T.unlines [ " 1." , "" , " 2." , "" , " 3. Item3" ] =?> orderedListWith (1, Decimal, Period) [ mempty , mempty , para "Item3" ] , "Bullet list with last item empty" =: T.unlines [ " -" , "" , "foo" ] =?> bulletList [ mempty ] <> para "foo" , testGroup "Nested lists" [ "Nested bullet list" =: T.unlines [ " - Item1" , " - Item2" , " - Item3" , " - Item4" , " - Item5" , " - Item6" ] =?> bulletList [ para "Item1" <> bulletList [ para "Item2" <> bulletList [ para "Item3" ] , para "Item4" <> bulletList [ para "Item5" ] ] , para "Item6" ] , "Nested ordered list" =: T.unlines [ " 1. Item1" , " 1. Item2" , " 1. Item3" , " 2. Item4" , " 1. Item5" , " 2. Item6" ] =?> orderedListWith (1, Decimal, Period) [ para "Item1" <> orderedListWith (1, Decimal, Period) [ para "Item2" <> orderedListWith (1, Decimal, Period) [ para "Item3" ] , para "Item4" <> orderedListWith (1, Decimal, Period) [ para "Item5" ] ] , para "Item6" ] , "Mixed nested list" =: T.unlines [ " - Item1" , " - Item2" , " - Item3" , " - Item4" , " 1. Nested" , " 2. Ordered" , " 3. List" ] =?> bulletList [ mconcat [ para "Item1" , bulletList [ para "Item2" , para "Item3" ] ] , mconcat [ para "Item4" , orderedListWith (1, Decimal, Period) [ para "Nested" , para "Ordered" , para "List" ] ] ] , "Text::Amuse includes only one space in list marker" =: T.unlines [ " - First item" , " - Nested item" ] =?> bulletList [ para "First item" <> bulletList [ para "Nested item"]] ] , "List continuation" =: T.unlines [ " - a" , "" , " b" , "" , " c" ] =?> bulletList [ mconcat [ para "a" , para "b" , para "c" ] ] , "List continuation after nested list" =: T.unlines [ " - - foo" , "" , " bar" ] =?> bulletList [ bulletList [ para "foo" ] <> para "bar" ] -- Emacs Muse allows to separate lists with two or more blank lines. -- Text::Amuse (Amusewiki engine) always creates a single list as of version 0.82. -- pandoc follows Emacs Muse behavior , testGroup "Blank lines" [ "Blank lines between list items are not required" =: T.unlines [ " - Foo" , " - Bar" ] =?> bulletList [ para "Foo" , para "Bar" ] , "One blank line between list items is allowed" =: T.unlines [ " - Foo" , "" , " - Bar" ] =?> bulletList [ para "Foo" , para "Bar" ] , "Two blank lines separate lists" =: T.unlines [ " - Foo" , "" , "" , " - Bar" ] =?> bulletList [ para "Foo" ] <> bulletList [ para "Bar" ] , "No blank line after multiline first item" =: T.unlines [ " - Foo" , " bar" , " - Baz" ] =?> bulletList [ para "Foo\nbar" , para "Baz" ] , "One blank line after multiline first item" =: T.unlines [ " - Foo" , " bar" , "" , " - Baz" ] =?> bulletList [ para "Foo\nbar" , para "Baz" ] , "Two blank lines after multiline first item" =: T.unlines [ " - Foo" , " bar" , "" , "" , " - Baz" ] =?> bulletList [ para "Foo\nbar" ] <> bulletList [ para "Baz" ] , "No blank line after list continuation" =: T.unlines [ " - Foo" , "" , " bar" , " - Baz" ] =?> bulletList [ para "Foo" <> para "bar" , para "Baz" ] , "One blank line after list continuation" =: T.unlines [ " - Foo" , "" , " bar" , "" , " - Baz" ] =?> bulletList [ para "Foo" <> para "bar" , para "Baz" ] , "Two blank lines after list continuation" =: T.unlines [ " - Foo" , "" , " bar" , "" , "" , " - Baz" ] =?> bulletList [ para "Foo" <> para "bar" ] <> bulletList [ para "Baz" ] , "No blank line after blockquote" =: T.unlines [ " - " , " foo" , " " , " - bar" ] =?> bulletList [ blockQuote $ para "foo", para "bar" ] , "One blank line after blockquote" =: T.unlines [ " - " , " foo" , " " , "" , " - bar" ] =?> bulletList [ blockQuote $ para "foo", para "bar" ] , "Two blank lines after blockquote" =: T.unlines [ " - " , " foo" , " " , "" , "" , " - bar" ] =?> bulletList [ blockQuote $ para "foo" ] <> bulletList [ para "bar" ] , "No blank line after verse" =: T.unlines [ " - > foo" , " - bar" ] =?> bulletList [ lineBlock [ "foo" ], para "bar" ] , "One blank line after verse" =: T.unlines [ " - > foo" , "" , " - bar" ] =?> bulletList [ lineBlock [ "foo" ], para "bar" ] , "Two blank lines after verse" =: T.unlines [ " - > foo" , "" , "" , " - bar" ] =?> bulletList [ lineBlock [ "foo" ] ] <> bulletList [ para "bar" ] ] , "List ending in self-terminating element and followed by paragraph" =: T.unlines [ " - > Foo" , "bar" ] =?> bulletList [lineBlock ["Foo"]] <> para (str "bar") -- Test that definition list requires a leading space. -- Emacs Muse does not require a space, we follow Amusewiki here. , "Not a definition list" =: T.unlines [ "First :: second" , "Foo :: bar" ] =?> para "First :: second\nFoo :: bar" , test emacsMuse "Emacs Muse definition list" (T.unlines [ "First :: second" , "Foo :: bar" ] =?> definitionList [ ("First", [ para "second" ]) , ("Foo", [ para "bar" ]) ]) , "Definition list" =: T.unlines [ " First :: second" , " Foo :: bar" ] =?> definitionList [ ("First", [ para "second" ]) , ("Foo", [ para "bar" ]) ] , "Definition list term cannot include newline" =: T.unlines [ " Foo" -- "Foo" is not a part of the definition list term , " Bar :: baz" ] =?> para "Foo" <> definitionList [ ("Bar", [ para "baz" ]) ] , "One-line definition list" =: " foo :: bar" =?> definitionList [ ("foo", [ para "bar" ]) ] , "Definition list term may include single colon" =: " foo:bar :: baz" =?> definitionList [ ("foo:bar", [ para "baz" ]) ] , "Definition list term with emphasis" =: " *Foo* :: bar\n" =?> definitionList [ (emph "Foo", [ para "bar" ]) ] , "Definition list term with :: inside code" =: " foo :: :: bar :: baz\n" =?> definitionList [ ("foo " <> code " :: ", [ para $ "bar " <> code " :: " <> " baz" ]) ] , "Multi-line definition lists" =: T.unlines [ " First term :: Definition of first term" , "and its continuation." , " Second term :: Definition of second term." ] =?> definitionList [ ("First term", [ para "Definition of first term\nand its continuation." ]) , ("Second term", [ para "Definition of second term." ]) ] , "Definition list with verse" =: T.unlines [ " First term :: Definition of first term" , " > First verse" , " > Second line of first verse" , "" , " > Second verse" , " > Second line of second verse" ] =?> definitionList [ ("First term", [ para "Definition of first term" <> lineBlock [ text "First verse" , text "Second line of first verse" ] <> lineBlock [ text "Second verse" , text "Second line of second verse" ] ]) ] , "Definition list with table" =: " foo :: bar | baz" =?> definitionList [ ("foo", [ simpleTable [] [[plain "bar", plain "baz"]] ])] , "Definition list with table inside bullet list" =: " - foo :: bar | baz" =?> bulletList [definitionList [ ("foo", [ simpleTable [] [[plain "bar", plain "baz"]] ])]] , test emacsMuse "Multi-line definition lists from Emacs Muse manual" (T.unlines [ "Term1 ::" , " This is a first definition" , " And it has two lines;" , "no, make that three." , "" , "Term2 :: This is a second definition" ] =?> definitionList [ ("Term1", [ para "This is a first definition\nAnd it has two lines;\nno, make that three."]) , ("Term2", [ para "This is a second definition"]) ]) -- Text::Amuse requires indentation with one space , "Multi-line definition lists from Emacs Muse manual with initial space" =: (T.unlines [ " Term1 ::" , " This is a first definition" , " And it has two lines;" , "no, make that three." , "" , " Term2 :: This is a second definition" ] =?> definitionList [ ("Term1", [ para "This is a first definition\nAnd it has two lines;\nno, make that three."]) , ("Term2", [ para "This is a second definition"]) ]) , "One-line nested definition list" =: " Foo :: bar :: baz" =?> definitionList [ ("Foo", [ definitionList [ ("bar", [ para "baz" ])]])] , "Nested definition list" =: T.unlines [ " First :: Second :: Third" , " Fourth :: Fifth :: Sixth" , " Seventh :: Eighth" ] =?> definitionList [ ("First", [ definitionList [ ("Second", [ para "Third" ]), ("Fourth", [ definitionList [ ("Fifth", [ para "Sixth"] ) ] ] ) ] ] ) , ("Seventh", [ para "Eighth" ]) ] , testGroup "Definition lists with multiple descriptions" [ "Correctly indented second description" =: T.unlines [ " First term :: first description" , " :: second description" ] =?> definitionList [ ("First term", [ para "first description" , para "second description" ]) ] , "Incorrectly indented second description" =: T.unlines [ " First term :: first description" , " :: second description" ] =?> definitionList [ ("First term", [ para "first description" ]) , ("", [ para "second description" ]) ] ] , "Two blank lines separate definition lists" =: T.unlines [ " First :: list" , "" , "" , " Second :: list" ] =?> definitionList [ ("First", [ para "list" ]) ] <> definitionList [ ("Second", [ para "list" ]) ] -- Headers in first column of list continuation are not allowed , "No headers in list continuation" =: T.unlines [ " - Foo" , "" , " * Bar" ] =?> bulletList [ mconcat [ para "Foo" , para "* Bar" ] ] , "Bullet list inside a tag" =: T.unlines [ "" , " - First" , "" , " - Second" , "" , " - Third" , "" ] =?> blockQuote (bulletList [ para "First" , para "Second" , para "Third" ]) , "Ordered list inside a tag" =: T.unlines [ "" , " 1. First" , "" , " 2. Second" , "" , " 3. Third" , "" ] =?> blockQuote (orderedListWith (1, Decimal, Period) [ para "First" , para "Second" , para "Third" ]) -- Regression test for a bug caught by round-trip test , "Do not consume whitespace while looking for end tag" =: T.unlines [ "" , " - " , " foo" , " " , " bar" -- Do not consume whitespace while looking for arbitrarily indented , "" ] =?> blockQuote (bulletList [ blockQuote $ para "foo" ] <> para "bar") , "Unclosed quote tag" =: T.unlines [ "" , "" , "" , "" ] =?> para "" <> lineBlock [ "" ] , "Unclosed quote tag inside list" =: T.unlines [ " - " , " " , " " , " " ] =?> bulletList [ para "" <> lineBlock [ "" ] ] -- Allowing indented closing tags is dangerous, -- as they may terminate lists , "No indented closing tags" =: T.unlines [ "" , "" , " - Foo" , "" , " " , "" , " bar" , "" , " " , " " , " " , "" ] =?> blockQuote (bulletList [ para "Foo" <> para "" <> para "bar" <> lineBlock [ "" ] ]) ] ] ================================================ FILE: test/Tests/Readers/ODT.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.ODT Copyright : © 2015-2024 John MacFarlane 2015 Martin Linnemann License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Tests for the ODT reader. -} module Tests.Readers.ODT (tests) where import Control.Monad (liftM) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import qualified Data.Map as M import Data.Text (unpack) import System.IO.Unsafe (unsafePerformIO) import Test.Tasty import Tests.Helpers import Text.Pandoc import qualified Text.Pandoc.UTF8 as UTF8 defopts :: ReaderOptions defopts = def{ readerExtensions = getDefaultExtensions "odt" } tests :: [TestTree] tests = testsComparingToMarkdown ++ testsComparingToNative testsComparingToMarkdown :: [TestTree] testsComparingToMarkdown = map nameToTest namesOfTestsComparingToMarkdown where nameToTest name = createTest compareODTToMarkdown name (toODTPath name) (toMarkdownPath name) toODTPath name = "odt/odt/" ++ name ++ ".odt" toMarkdownPath name = "odt/markdown/" ++ name ++ ".md" testsComparingToNative :: [TestTree] testsComparingToNative = map nameToTest namesOfTestsComparingToNative where nameToTest name = createTest compareODTToNative name (toODTPath name) (toNativePath name) toODTPath name = "odt/odt/" ++ name ++ ".odt" toNativePath name = "odt/native/" ++ name ++ ".native" newtype NoNormPandoc = NoNormPandoc {unNoNorm :: Pandoc} deriving ( Show ) instance ToString NoNormPandoc where toString d = unpack $ purely (writeNative def{ writerTemplate = s }) $ toPandoc d where s = case d of NoNormPandoc (Pandoc (Meta m) _) | M.null m -> Nothing | otherwise -> Just mempty -- need this for Meta output instance ToPandoc NoNormPandoc where toPandoc = unNoNorm getNoNormVia :: (a -> Pandoc) -> String -> Either PandocError a -> NoNormPandoc getNoNormVia _ readerName (Left _) = error (readerName ++ " reader failed") getNoNormVia f _ (Right a) = NoNormPandoc (f a) type TestCreator = ReaderOptions -> FilePath -> FilePath -> IO (NoNormPandoc, NoNormPandoc) compareODTToNative :: TestCreator compareODTToNative opts odtPath nativePath = do nativeFile <- UTF8.toText <$> BS.readFile nativePath odtFile <- B.readFile odtPath native <- getNoNormVia id "native" <$> runIO (readNative def nativeFile) odt <- getNoNormVia id "odt" <$> runIO (readODT opts odtFile) return (odt,native) compareODTToMarkdown :: TestCreator compareODTToMarkdown opts odtPath markdownPath = do markdownFile <- UTF8.toText <$> BS.readFile markdownPath odtFile <- B.readFile odtPath markdown <- getNoNormVia id "markdown" <$> runIO (readMarkdown def{ readerExtensions = pandocExtensions } markdownFile) odt <- getNoNormVia id "odt" <$> runIO (readODT opts odtFile) return (odt,markdown) createTest :: TestCreator -> TestName -> FilePath -> FilePath -> TestTree createTest creator name path1 path2 = unsafePerformIO $ liftM (test id name) (creator defopts path1 path2) {- -- getMedia :: FilePath -> FilePath -> IO (Maybe B.ByteString) getMedia archivePath mediaPath = do zf <- B.readFile archivePath >>= return . toArchive return $ findEntryByPath ("Pictures/" ++ mediaPath) zf >>= (Just . fromEntry) compareMediaPathIO :: FilePath -> MediaBag -> FilePath -> IO Bool compareMediaPathIO mediaPath mediaBag odtPath = do odtMedia <- getMedia odtPath mediaPath let mbBS = case lookupMedia mediaPath mediaBag of Just (_, bs) -> bs Nothing -> error ("couldn't find " ++ mediaPath ++ " in media bag") odtBS = case odtMedia of Just bs -> bs Nothing -> error ("couldn't find " ++ mediaPath ++ " in media bag") return $ mbBS == odtBS compareMediaBagIO :: FilePath -> IO Bool compareMediaBagIO odtFile = do df <- B.readFile odtFile let (_, mb) = readODT def df bools <- mapM (\(fp, _, _) -> compareMediaPathIO fp mb odtFile) (mediaDirectory mb) return $ and bools testMediaBagIO :: String -> FilePath -> IO TestTree testMediaBagIO name odtFile = do outcome <- compareMediaBagIO odtFile return $ testCase name (assertBool ("Media didn't match media bag in file " ++ odtFile) outcome) testMediaBag :: String -> FilePath -> TestTree testMediaBag name odtFile = buildTest $ testMediaBagIO name odtFile -} -- namesOfTestsComparingToMarkdown :: [ String ] namesOfTestsComparingToMarkdown = [ "blockquote2" , "bold" -- , "citation" , "endnote" , "externalLink" , "footnote" , "formula" , "headers" -- , "horizontalRule" , "italic" -- , "listBlocks" , "paragraph" , "strikeout" -- , "trackedChanges" , "underlined" ] namesOfTestsComparingToNative :: [ String ] namesOfTestsComparingToNative = [ "blockquote" , "image" , "imageIndex" , "imageRelative" , "imageWithCaption" , "inlinedCode" , "listContinueNumbering" , "listContinueNumbering2" , "orderedListMixed" , "orderedListRoman" , "orderedListSimple" , "orderedListHeader" , "preformattedText" , "preformattedTextParentStyle" , "referenceToChapter" , "referenceToListItem" , "referenceToText" , "simpleTable" , "simpleTableWithCaption" , "simpleTableWithHeader" , "simpleTableWithMultipleHeaderRows" , "tab" -- , "table" , "textMixedStyles" , "tableWithContents" , "tableWithSpans" , "unicode" , "unorderedList" , "unorderedListHeader" ] ================================================ FILE: test/Tests/Readers/Org/Block/CodeBlock.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.CodeBlock Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Test parsing of org code blocks. -} module Tests.Readers.Org.Block.CodeBlock (tests) where import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) import Text.Pandoc.Builder import qualified Data.Text as T tests :: [TestTree] tests = [ "Source block" =: T.unlines [ " #+begin_src haskell" , " main = putStrLn greeting" , " where greeting = \"moin\"" , " #+end_src" ] =?> let attr' = ("", ["haskell"], []) code' = "main = putStrLn greeting\n" <> " where greeting = \"moin\"\n" in codeBlockWith attr' code' , "Source block with indented code" =: T.unlines [ " #+begin_src haskell" , " main = putStrLn greeting" , " where greeting = \"moin\"" , " #+end_src" ] =?> let attr' = ("", ["haskell"], []) code' = "main = putStrLn greeting\n" <> " where greeting = \"moin\"\n" in codeBlockWith attr' code' , "Source block with tab-indented code" =: T.unlines [ "\t#+begin_src haskell" , "\tmain = putStrLn greeting" , "\t where greeting = \"moin\"" , "\t#+end_src" ] =?> let attr' = ("", ["haskell"], []) code' = "main = putStrLn greeting\n" <> " where greeting = \"moin\"\n" in codeBlockWith attr' code' , "Empty source block" =: T.unlines [ " #+begin_src haskell" , " #+end_src" ] =?> let attr' = ("", ["haskell"], []) code' = "" in codeBlockWith attr' code' , "Source block between paragraphs" =: T.unlines [ "Low German greeting" , " #+begin_src haskell" , " main = putStrLn greeting" , " where greeting = \"Moin!\"" , " #+end_src" ] =?> let attr' = ("", ["haskell"], []) code' = "main = putStrLn greeting\n" <> " where greeting = \"Moin!\"\n" in mconcat [ para $ spcSep [ "Low", "German", "greeting" ] , codeBlockWith attr' code' ] , "Source block with babel arguments" =: T.unlines [ "#+begin_src emacs-lisp :exports both" , "(progn (message \"Hello, World!\")" , " (+ 23 42))" , "#+end_src" ] =?> let classes = [ "commonlisp" ] -- as kate doesn't know emacs-lisp syntax params = [ ("org-language", "emacs-lisp") , ("exports", "both") ] code' = T.unlines [ "(progn (message \"Hello, World!\")" , " (+ 23 42))" ] in codeBlockWith ("", classes, params) code' , "Source block with results and :exports both" =: T.unlines [ "#+begin_src emacs-lisp :exports both" , "(progn (message \"Hello, World!\")" , " (+ 23 42))" , "#+end_src" , "" , "#+RESULTS:" , ": 65"] =?> let classes = [ "commonlisp" ] params = [ ("org-language", "emacs-lisp") , ("exports", "both") ] code' = T.unlines [ "(progn (message \"Hello, World!\")" , " (+ 23 42))" ] results' = "65\n" in codeBlockWith ("", classes, params) code' <> codeBlockWith ("", [], []) results' , "Source block with results and :exports code" =: T.unlines [ "#+begin_src emacs-lisp :exports code" , "(progn (message \"Hello, World!\")" , " (+ 23 42))" , "#+end_src" , "" , "#+RESULTS:" , ": 65" ] =?> let classes = [ "commonlisp" ] params = [ ("org-language", "emacs-lisp") , ("exports", "code") ] code' = T.unlines [ "(progn (message \"Hello, World!\")" , " (+ 23 42))" ] in codeBlockWith ("", classes, params) code' , "Source block with results and :exports results" =: T.unlines [ "#+begin_src emacs-lisp :exports results" , "(progn (message \"Hello, World!\")" , " (+ 23 42))" , "#+end_src" , "" , "#+RESULTS:" , ": 65" ] =?> let results' = "65\n" in codeBlockWith ("", [], []) results' , "Source block with results and :exports none" =: T.unlines [ "#+begin_src emacs-lisp :exports none" , "(progn (message \"Hello, World!\")" , " (+ 23 42))" , "#+end_src" , "" , "#+RESULTS:" , ": 65" ] =?> (mempty :: Blocks) , "Source block with toggling header arguments" =: T.unlines [ "#+begin_src sh :noeval" , "echo $HOME" , "#+end_src" ] =?> let classes = [ "bash" ] params = [ ("org-language", "sh"), ("noeval", "yes") ] in codeBlockWith ("", classes, params) "echo $HOME\n" , "Source block with line number switch" =: T.unlines [ "#+begin_src sh -n 10" , ":() { :|:& };:" , "#+end_src" ] =?> let classes = [ "bash", "numberLines" ] params = [ ("org-language", "sh"), ("startFrom", "10") ] in codeBlockWith ("", classes, params) ":() { :|:& };:\n" , "Source block with multi-word parameter values" =: T.unlines [ "#+begin_src dot :cmdline -Kdot -Tpng " , "digraph { id [label=\"ID\"] }" , "#+end_src" ] =?> let classes = [ "dot" ] params = [ ("cmdline", "-Kdot -Tpng") ] in codeBlockWith ("", classes, params) "digraph { id [label=\"ID\"] }\n" , "Example block" =: T.unlines [ "#+begin_example" , "A chosen representation of" , "a rule." , "#+eND_exAMPle" ] =?> codeBlockWith ("", [], []) "A chosen representation of\na rule.\n" , "Code block with caption" =: T.unlines [ "#+caption: Functor laws in Haskell" , "#+name: functor-laws" , "#+begin_src haskell" , "fmap id = id" , "fmap (p . q) = (fmap p) . (fmap q)" , "#+end_src" ] =?> divWith ("", ["captioned-content"], [] ) (mappend (divWith ("", ["caption"], []) $ plain (spcSep [ "Functor", "laws", "in", "Haskell" ])) (codeBlockWith ("functor-laws", ["haskell"], []) (T.unlines [ "fmap id = id" , "fmap (p . q) = (fmap p) . (fmap q)" ]))) , "Non-letter chars in source block parameters" =: T.unlines [ "#+begin_src C :tangle xxxx.c :city Zürich" , "code body" , "#+end_src" ] =?> let params = [ ("org-language", "C") , ("tangle", "xxxx.c") , ("city", "Zürich") ] in codeBlockWith ( "", ["c"], params) "code body\n" ] ================================================ FILE: test/Tests/Readers/Org/Block/Figure.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.Figure Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Test parsing of org figures. -} module Tests.Readers.Org.Block.Figure (tests) where import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:)) import Text.Pandoc.Builder ( emptyCaption, figure, figureWith, image , plain, simpleCaption, simpleFigure ) import qualified Data.Text as T tests :: [TestTree] tests = [ "Figure" =: T.unlines [ "#+caption: A courageous man." , "#+name: ed" , "[[file:edward.jpg]]" ] =?> figureWith ("ed", mempty, mempty) (plainCaption "A courageous man.") (plain $ image "edward.jpg" mempty "") , "Figure with no name" =: T.unlines [ "#+caption: I've been through the desert on this" , "[[file:horse.png]]" ] =?> figure (plainCaption "I've been through the desert on this") (plain $ image "horse.png" "" "") , "Figure with `fig:` prefix in name" =: T.unlines [ "#+caption: Used as a metapher in evolutionary biology." , "#+name: fig:redqueen" , "[[./the-red-queen.jpg]]" ] =?> figureWith ("fig:redqueen", mempty, mempty) (plainCaption "Used as a metapher in evolutionary biology.") (plain $ image "./the-red-queen.jpg" mempty "") , "Figure with HTML attributes" =: T.unlines [ "#+caption: mah brain just explodid" , "#+name: lambdacat" , "#+attr_html: :style color: blue :role button" , "[[file:lambdacat.jpg]]" ] =?> let kv = [("style", "color: blue"), ("role", "button")] name = "lambdacat" capt = plain "mah brain just explodid" in figureWith (name, mempty, kv) (simpleCaption capt) (plain $ image "lambdacat.jpg" mempty "") , "LaTeX attributes are ignored" =: T.unlines [ "#+caption: Attribute after caption" , "#+attr_latex: :float nil" , "[[file:test.png]]" ] =?> simpleFigure "Attribute after caption" "test.png" "" , "Labelled figure" =: T.unlines [ "#+caption: My figure" , "#+label: fig:myfig" , "[[file:blub.png]]" ] =?> figureWith ("fig:myfig", mempty, mempty) (simpleCaption $ plain "My figure") (plain (image "blub.png" "" "")) , "Figure with empty caption" =: T.unlines [ "#+caption:" , "[[file:guess.jpg]]" ] =?> figure emptyCaption (plain (image "guess.jpg" "" "")) ] where plainCaption = simpleCaption . plain ================================================ FILE: test/Tests/Readers/Org/Block/Header.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.Header Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Test parsing of org header blocks. -} module Tests.Readers.Org.Block.Header (tests) where import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep, tagSpan) import Text.Pandoc.Builder import qualified Data.Text as T tests :: [TestTree] tests = [ "First Level Header" =: "* Headline\n" =?> headerWith ("headline", [], []) 1 "Headline" , "Third Level Header" =: "*** Third Level Headline\n" =?> headerWith ("third-level-headline", [], []) 3 ("Third" <> space <> "Level" <> space <> "Headline") , "Compact Headers with Paragraph" =: T.unlines [ "* First Level" , "** Second Level" , " Text" ] =?> mconcat [ headerWith ("first-level", [], []) 1 ("First" <> space <> "Level") , headerWith ("second-level", [], []) 2 ("Second" <> space <> "Level") , para "Text" ] , "Separated Headers with Paragraph" =: T.unlines [ "* First Level" , "" , "** Second Level" , "" , " Text" ] =?> mconcat [ headerWith ("first-level", [], []) 1 ("First" <> space <> "Level") , headerWith ("second-level", [], []) 2 ("Second" <> space <> "Level") , para "Text" ] , "Headers not preceded by a blank line" =: T.unlines [ "** eat dinner" , "Spaghetti and meatballs tonight." , "** walk dog" ] =?> mconcat [ headerWith ("eat-dinner", [], []) 2 ("eat" <> space <> "dinner") , para $ spcSep [ "Spaghetti", "and", "meatballs", "tonight." ] , headerWith ("walk-dog", [], []) 2 ("walk" <> space <> "dog") ] , testGroup "Todo keywords" [ "Header with known todo keyword" =: "* TODO header" =?> let todoSpan = spanWith ("", ["todo", "TODO"], []) "TODO" in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") , "Header marked as done" =: "* DONE header" =?> let todoSpan = spanWith ("", ["done", "DONE"], []) "DONE" in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") , "emphasis in first word" =: "** TODO /fix/ this" =?> let todoSpan = spanWith ("", ["todo", "TODO"], []) "TODO" in headerWith ("fix-this", [], []) 2 (todoSpan <> space <> emph "fix" <> space <> "this") , "Header with unknown todo keyword" =: "* WAITING header" =?> headerWith ("waiting-header", [], []) 1 "WAITING header" , "Custom todo keywords" =: T.unlines [ "#+todo: WAITING CANCELLED" , "* WAITING compile" , "* CANCELLED lunch" ] =?> let todoSpan = spanWith ("", ["todo", "WAITING"], []) "WAITING" doneSpan = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" in headerWith ("compile", [], []) 1 (todoSpan <> space <> "compile") <> headerWith ("lunch", [], []) 1 (doneSpan <> space <> "lunch") , "Custom todo keywords with multiple done-states" =: T.unlines [ "#+todo: WAITING | DONE CANCELLED " , "* WAITING compile" , "* CANCELLED lunch" , "* DONE todo-feature" ] =?> let waiting = spanWith ("", ["todo", "WAITING"], []) "WAITING" cancelled = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" done = spanWith ("", ["done", "DONE"], []) "DONE" in headerWith ("compile", [], []) 1 (waiting <> space <> "compile") <> headerWith ("lunch", [], []) 1 (cancelled <> space <> "lunch") <> headerWith ("todo-feature", [], []) 1 (done <> space <> "todo-feature") , "Fast access TODO states" =: T.unlines [ "#+TODO: TODO(t) | DONE(d)" , "* TODO test" ] =?> let todoSpan = spanWith ("", ["todo", "TODO"], []) "TODO" in headerWith ("test", [], []) 1 (todoSpan <> space <> "test") ] , "Tagged headers" =: T.unlines [ "* Personal :PERSONAL:" , "** Call Mom :@PHONE:" , "** Call John :@PHONE:JOHN: " ] =?> mconcat [ headerWith ("personal", [], []) 1 ("Personal " <> tagSpan "PERSONAL") , headerWith ("call-mom", [], []) 2 ("Call Mom " <> tagSpan "@PHONE") , headerWith ("call-john", [], []) 2 ("Call John " <> tagSpan "@PHONE" <> "\160" <> tagSpan "JOHN") ] , "Untagged header containing colons" =: "* This: is not: tagged" =?> headerWith ("this-is-not-tagged", [], []) 1 "This: is not: tagged" , "Untagged header time followed by colon" =: "** Meeting at 5:23: free food" =?> let attr = ("meeting-at-523-free-food", [], []) in headerWith attr 2 "Meeting at 5:23: free food" , "tag followed by text" =: "*** Looks like a :tag: but isn't" =?> let attr = ("looks-like-a-tag-but-isnt", [], []) in headerWith attr 3 "Looks like a :tag: but isn't" , "Header starting with strokeout text" =: T.unlines [ "foo" , "" , "* +thing+ other thing" ] =?> mconcat [ para "foo" , headerWith ("thing-other-thing", [], []) 1 (strikeout "thing" <> " other thing") ] , "Comment Trees" =: T.unlines [ "* COMMENT A comment tree" , " Not much going on here" , "** This will be dropped" , "* Comment tree above" ] =?> headerWith ("comment-tree-above", [], []) 1 "Comment tree above" , "Nothing but a COMMENT header" =: "* COMMENT Test" =?> (mempty::Blocks) , "Tree with :noexport:" =: T.unlines [ "* Should be ignored :archive:noexport:old:" , "** Old stuff" , " This is not going to be exported" ] =?> (mempty::Blocks) , "Subtree with :noexport:" =: T.unlines [ "* Exported" , "** This isn't exported :noexport:" , "*** This neither" , "** But this is" ] =?> mconcat [ headerWith ("exported", [], []) 1 "Exported" , headerWith ("but-this-is", [], []) 2 "But this is" ] , "Preferences are treated as header attributes" =: T.unlines [ "* foo" , " :PROPERTIES:" , " :custom_id: fubar" , " :bar: baz" , " :END:" ] =?> headerWith ("fubar", [], [("bar", "baz")]) 1 "foo" , "Headers marked with a unnumbered property get a class of the same name" =: T.unlines [ "* Not numbered" , " :PROPERTIES:" , " :UNNUMBERED: t" , " :END:" ] =?> headerWith ("not-numbered", ["unnumbered"], []) 1 "Not numbered" , testGroup "planning information" [ "Planning info is not included in output" =: T.unlines [ "* important" , T.unwords [ "CLOSED: [2018-09-05 Wed 13:58]" , "DEADLINE: <2018-09-17 Mon>" , "SCHEDULED: <2018-09-10 Mon>" ] ] =?> headerWith ("important", [], []) 1 "important" , "Properties after planning info are recognized" =: T.unlines [ "* important " , " " <> T.unwords [ "CLOSED: [2018-09-05 Wed 13:58]" , "DEADLINE: <2018-09-17 Mon>" , "SCHEDULED: <2018-09-10 Mon>" ] , " :PROPERTIES:" , " :custom_id: look" , " :END:" ] =?> headerWith ("look", [], []) 1 "important" , "Planning info followed by test" =: T.unlines [ "* important " , " " <> T.unwords [ "CLOSED: [2018-09-05 Wed 13:58]" , "DEADLINE: <2018-09-17 Mon>" , "SCHEDULED: <2018-09-10 Mon>" ] , " :PROPERTIES:" , " :custom_id: look" , " :END:" ] =?> headerWith ("look", [], []) 1 "important" , "third and forth level headers" =: T.unlines [ "#+options: p:t h:3" , "*** Third" , " CLOSED: [2018-09-05 Wed 13:58]" , " Text 3" , "**** Fourth" , "SCHEDULED: <2019-05-13 Mon 22:42>" , "Text 4" ] =?> mconcat [ headerWith ("third", [], mempty) 3 "Third" , plain $ strong "CLOSED:" <> space <> emph (str "[2018-09-05 Wed 13:58]") , para "Text 3" , orderedList [ mconcat [ para "Fourth" , plain $ strong "SCHEDULED:" <> space <> emph (str "<2019-05-13 Mon 22:42>") , para "Text 4" ]] ] ] ] ================================================ FILE: test/Tests/Readers/Org/Block/List.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.Header Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Test parsing of org lists. -} module Tests.Readers.Org.Block.List (tests) where import Data.Text (Text) import Test.Tasty (TestTree) import Tests.Helpers ((=?>), purely, test) import Tests.Readers.Org.Shared ((=:), spcSep) import Text.Pandoc (ReaderOptions (readerExtensions), Extension (Ext_fancy_lists), def, enableExtension, getDefaultExtensions, readOrg) import Text.Pandoc.Builder import qualified Data.Text as T orgFancyLists :: Text -> Pandoc orgFancyLists = purely $ let extensionsFancy = enableExtension Ext_fancy_lists (getDefaultExtensions "org") in readOrg def{ readerExtensions = extensionsFancy } tests :: [TestTree] tests = [ "Simple Bullet Lists" =: ("- Item1\n" <> "- Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" ] , "Simple Bullet List with Ignored Counter Cookie" =: ("- [@4] Item1\n" <> "- Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" ] , "Indented Bullet Lists" =: (" - Item1\n" <> " - Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" ] , "Unindented *" =: ("- Item1\n" <> "* Item2\n") =?> bulletList [ plain "Item1" ] <> headerWith ("item2", [], []) 1 "Item2" , "Multi-line Bullet Lists" =: ("- *Fat\n" <> " Tony*\n" <> "- /Sideshow\n" <> " Bob/") =?> bulletList [ plain $ strong ("Fat" <> softbreak <> "Tony") , plain $ emph ("Sideshow" <> softbreak <> "Bob") ] , "Nested Bullet Lists" =: ("- Discovery\n" <> " + One More Time\n" <> " + Harder, Better, Faster, Stronger\n" <> "- Homework\n" <> " + Around the World\n"<> "- Human After All\n" <> " + Technologic\n" <> " + Robot Rock\n") =?> bulletList [ mconcat [ plain "Discovery" , bulletList [ plain ("One" <> space <> "More" <> space <> "Time") , plain ("Harder," <> space <> "Better," <> space <> "Faster," <> space <> "Stronger") ] ] , mconcat [ plain "Homework" , bulletList [ plain ("Around" <> space <> "the" <> space <> "World") ] ] , mconcat [ plain ("Human" <> space <> "After" <> space <> "All") , bulletList [ plain "Technologic" , plain ("Robot" <> space <> "Rock") ] ] ] , "Bullet List with Decreasing Indent" =: " - Discovery\n\ \ - Human After All\n" =?> mconcat [ bulletList [ plain "Discovery" ] , bulletList [ plain ("Human" <> space <> "After" <> space <> "All")] ] , "Header follows Bullet List" =: " - Discovery\n\ \ - Human After All\n\ \* Homework" =?> mconcat [ bulletList [ plain "Discovery" , plain ("Human" <> space <> "After" <> space <> "All") ] , headerWith ("homework", [], []) 1 "Homework" ] , "Bullet List Unindented with trailing Header" =: "- Discovery\n\ \- Homework\n\ \* NotValidListItem" =?> mconcat [ bulletList [ plain "Discovery" , plain "Homework" ] , headerWith ("notvalidlistitem", [], []) 1 "NotValidListItem" ] , "Empty bullet points" =: T.unlines [ "-" , "- " ] =?> bulletList [ plain "", plain "" ] , "Task list" =: T.unlines [ "- [ ] nope" , "- [X] yup" , "- [-] started" , " 1. [X] sure" , " 2. [ ] nuh-uh" ] =?> bulletList [ plain "☐ nope", plain "☒ yup" , mconcat [ plain "☐ started" , orderedList [plain "☒ sure", plain "☐ nuh-uh"] ] ] , "Task List with Counter Cookies" =: T.unlines [ "- [ ] nope" , "- [@9] [X] yup" , "- [@a][-] started" , " 1. [@3][X] sure" , " 2. [@b] [ ] nuh-uh" ] =?> bulletList [ plain "☐ nope", plain "☒ yup" , mconcat [ plain "☐ started" , orderedListWith (3, DefaultStyle, DefaultDelim) [plain "☒ sure", plain "☐ nuh-uh"] ] ] , test orgFancyLists "Task with alphabetical markers and counter cookie" $ T.unlines [ "- [ ] nope" , "- [@9] [X] yup" , "- [@a][-] started" , " a) [@D][X] sure" , " b) [@8] [ ] nuh-uh" ] =?> bulletList [ plain "☐ nope", plain "☒ yup" , mconcat [ plain "☐ started" , orderedListWith (4, LowerAlpha, OneParen) [plain "☒ sure", plain "☐ nuh-uh"] ] ] , "Simple Ordered List" =: ("1. Item1\n" <> "2. Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , test orgFancyLists "Simple Ordered List with fancy lists extension" $ ("1. Item1\n" <> "2. Item2\n") =?> let listStyle = (1, Decimal, Period) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , test orgFancyLists "Simple Ordered List with lower alpha marker" $ ("a) Item1\n" <> "b) Item2\n") =?> let listStyle = (1, LowerAlpha, OneParen) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , test orgFancyLists "Simple Ordered List with upper and lower alpha markers" $ ("A. Item1\n" <> "b) Item2\n") =?> let listStyle = (1, UpperAlpha, Period) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , "Simple Ordered List with Counter Cookie" =: ("1. [@1234] Item1\n" <> "2. Item2\n") =?> let listStyle = (1234, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , "Simple Ordered List with Alphabetical Counter Cookie" =: ("1. [@c] Item1\n" <> "2. Item2\n") =?> let listStyle = (3, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , "Simple Ordered List with Ignored Counter Cookie" =: ("1. Item1\n" <> "2. [@4] Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , "Simple Ordered List with Parens" =: ("1) Item1\n" <> "2) Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , "Indented Ordered List" =: (" 1. Item1\n" <> " 2. Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , "Empty ordered list item" =: T.unlines [ "1." , "3. " ] =?> orderedList [ plain "", plain "" ] , test orgFancyLists "Empty ordered list item with fancy lists extension" $ T.unlines [ "a." , "2. " ] =?> orderedListWith (1, LowerAlpha, Period) [ plain "", plain "" ] , "Empty ordered list item with counter cookie" =: T.unlines [ "1. [@5]" , "3. [@e] " ] =?> let listStyle = (5, DefaultStyle, DefaultDelim) in orderedListWith listStyle [ plain "", plain "" ] , "Nested Ordered Lists" =: ("1. One\n" <> " 1. One-One\n" <> " 2. One-Two\n" <> "2. Two\n" <> " 1. Two-One\n"<> " 2. Two-Two\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ mconcat [ plain "One" , orderedList [ plain "One-One" , plain "One-Two" ] ] , mconcat [ plain "Two" , orderedList [ plain "Two-One" , plain "Two-Two" ] ] ] in orderedListWith listStyle listStructure , "Ordered List in Bullet List" =: ("- Emacs\n" <> " 1. Org\n") =?> bulletList [ plain "Emacs" <> orderedList [ plain "Org"] ] , "Bullet List in Ordered List" =: ("1. GNU\n" <> " - Freedom\n") =?> orderedList [ plain "GNU" <> bulletList [ plain "Freedom" ] ] , "Definition List" =: T.unlines [ "- PLL :: phase-locked loop" , "- TTL ::" , " transistor-transistor logic" , "- PSK :: phase-shift keying" , "" , " a digital modulation scheme" ] =?> definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ]) , ("TTL", [ plain $ "transistor-transistor" <> space <> "logic" ]) , ("PSK", [ mconcat [ para $ "phase-shift" <> space <> "keying" , para $ spcSep [ "a", "digital" , "modulation", "scheme" ] ] ]) ] , "Definition list with multi-word term" =: " - Elijah Wood :: He plays Frodo" =?> definitionList [ ("Elijah" <> space <> "Wood", [plain $ "He" <> space <> "plays" <> space <> "Frodo"])] , "Compact definition list" =: T.unlines [ "- ATP :: adenosine 5' triphosphate" , "- DNA :: deoxyribonucleic acid" , "- PCR :: polymerase chain reaction" , "" ] =?> definitionList [ ("ATP", [ plain $ spcSep [ "adenosine", "5'", "triphosphate" ] ]) , ("DNA", [ plain $ spcSep [ "deoxyribonucleic", "acid" ] ]) , ("PCR", [ plain $ spcSep [ "polymerase", "chain", "reaction" ] ]) ] , "Definition List With Trailing Header" =: "- definition :: list\n\ \- cool :: defs\n\ \* header" =?> mconcat [ definitionList [ ("definition", [plain "list"]) , ("cool", [plain "defs"]) ] , headerWith ("header", [], []) 1 "header" ] , "Definition lists double-colon markers must be surrounded by whitespace" =: "- std::cout" =?> bulletList [ plain "std::cout" ] , "Loose bullet list" =: T.unlines [ "- apple" , "" , "- orange" , "" , "- peach" ] =?> bulletList [ para "apple" , para "orange" , para "peach" ] , "Recognize preceding paragraphs in non-list contexts" =: T.unlines [ "CLOSED: [2015-10-19 Mon 15:03]" , "- Note taken on [2015-10-19 Mon 13:24]" ] =?> mconcat [ para "CLOSED: [2015-10-19 Mon 15:03]" , bulletList [ plain "Note taken on [2015-10-19 Mon 13:24]" ] ] , "Markup after header and list" =: T.unlines [ "* headline" , "- list" , "" , "~variable name~" ] =?> mconcat [ headerWith ("headline", [], []) 1 "headline" , bulletList [ plain "list" ] , para (code "variable name") ] ] ================================================ FILE: test/Tests/Readers/Org/Block/Table.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block.Table Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Test parsing of org tables. -} module Tests.Readers.Org.Block.Table (tests) where import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) import Text.Pandoc.Builder import qualified Data.Text as T simpleTable' :: Int -> [Blocks] -> [[Blocks]] -> Blocks simpleTable' n = simpleTable'' emptyCaption $ replicate n (AlignDefault, ColWidthDefault) simpleTable'' :: Caption -> [ColSpec] -> [Blocks] -> [[Blocks]] -> Blocks simpleTable'' = simpleTableWith'' (mempty, [], []) simpleTableWith'' :: Attr -> Caption -> [ColSpec] -> [Blocks] -> [[Blocks]] -> Blocks simpleTableWith'' attr capt spec headers rows = tableWith attr capt spec (TableHead nullAttr $ toHeaderRow headers) [TableBody nullAttr 0 [] $ map toRow rows] (TableFoot nullAttr []) where toRow = Row nullAttr . map simpleCell toHeaderRow l = [toRow l | not (null l)] tests :: [TestTree] tests = [ "Single cell table" =: "|Test|" =?> simpleTable' 1 mempty [[plain "Test"]] , "Multi cell table" =: "| One | Two |" =?> simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] , "Multi line table" =: T.unlines [ "| One |" , "| Two |" , "| Three |" ] =?> simpleTable' 1 mempty [ [ plain "One" ] , [ plain "Two" ] , [ plain "Three" ] ] , "Empty table" =: "||" =?> simpleTable' 1 mempty [[mempty]] , "Glider Table" =: T.unlines [ "| 1 | 0 | 0 |" , "| 0 | 1 | 1 |" , "| 1 | 1 | 0 |" ] =?> simpleTable' 3 mempty [ [ plain "1", plain "0", plain "0" ] , [ plain "0", plain "1", plain "1" ] , [ plain "1", plain "1", plain "0" ] ] , "Table between Paragraphs" =: T.unlines [ "Before" , "| One | Two |" , "After" ] =?> mconcat [ para "Before" , simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] , para "After" ] , "Table with Header" =: T.unlines [ "| Species | Status |" , "|--------------+--------------|" , "| cervisiae | domesticated |" , "| paradoxus | wild |" ] =?> simpleTable [ plain "Species", plain "Status" ] [ [ plain "cervisiae", plain "domesticated" ] , [ plain "paradoxus", plain "wild" ] ] , "Table with final hline" =: T.unlines [ "| cervisiae | domesticated |" , "| paradoxus | wild |" , "|--------------+--------------|" ] =?> simpleTable' 2 mempty [ [ plain "cervisiae", plain "domesticated" ] , [ plain "paradoxus", plain "wild" ] ] , "Table in a box" =: T.unlines [ "|---------|---------|" , "| static | Haskell |" , "| dynamic | Lisp |" , "|---------+---------|" ] =?> simpleTable' 2 mempty [ [ plain "static", plain "Haskell" ] , [ plain "dynamic", plain "Lisp" ] ] , "Table with empty cells" =: "|||c|" =?> simpleTable' 3 mempty [[mempty, mempty, plain "c"]] , "Table with empty rows" =: T.unlines [ "| first |" , "| |" , "| third |" ] =?> simpleTable' 1 mempty [[plain "first"], [mempty], [plain "third"]] , "Table with alignment row" =: T.unlines [ "| Numbers | Text | More |" , "| | | |" , "| 1 | One | foo |" , "| 2 | Two | bar |" ] =?> simpleTable'' emptyCaption (zip [AlignCenter, AlignRight, AlignDefault] [ColWidthDefault, ColWidthDefault, ColWidthDefault]) [] [ [ plain "Numbers", plain "Text", plain "More" ] , [ plain "1" , plain "One" , plain "foo" ] , [ plain "2" , plain "Two" , plain "bar" ] ] , "Pipe within text doesn't start a table" =: "Ceci n'est pas une | pipe " =?> para (spcSep [ "Ceci", "n'est", "pas", "une", "|", "pipe" ]) , "Missing pipe at end of row" =: "|incomplete-but-valid" =?> simpleTable' 1 mempty [ [ plain "incomplete-but-valid" ] ] , "Table with differing row lengths" =: T.unlines [ "| Numbers | Text " , "|-" , "| | |" , "| 1 | One | foo |" , "| 2" ] =?> simpleTable'' emptyCaption (zip [AlignCenter, AlignRight] [ColWidthDefault, ColWidthDefault]) [ plain "Numbers", plain "Text" ] [ [ plain "1" , plain "One" , plain "foo" ] , [ plain "2" ] ] , "Table with caption" =: T.unlines [ "#+caption: Hitchhiker's Multiplication Table" , "| x | 6 |" , "| 9 | 42 |" ] =?> simpleTable'' (simpleCaption $ plain "Hitchhiker's Multiplication Table") [(AlignDefault, ColWidthDefault), (AlignDefault, ColWidthDefault)] [] [ [ plain "x", plain "6" ] , [ plain "9", plain "42" ] ] , "named table" =: T.unlines [ "#+name: x-marks-the-spot" , "| x |" ] =?> simpleTableWith'' ("x-marks-the-spot", mempty, mempty) emptyCaption (replicate 1 (AlignDefault, ColWidthDefault)) mempty [ [ plain "x" ] ] ] ================================================ FILE: test/Tests/Readers/Org/Block.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Block Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Tests parsing of org blocks. -} module Tests.Readers.Org.Block (tests) where import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) import Text.Pandoc.Builder import qualified Data.Text as T import qualified Tests.Readers.Org.Block.CodeBlock as CodeBlock import qualified Tests.Readers.Org.Block.Figure as Figure import qualified Tests.Readers.Org.Block.Header as Header import qualified Tests.Readers.Org.Block.List as List import qualified Tests.Readers.Org.Block.Table as Table tests :: [TestTree] tests = [ "Paragraph" =: "Paragraph\n" =?> para "Paragraph" , "Paragraph starting with an asterisk" =: "*five" =?> para "*five" , "Paragraph containing asterisk at beginning of line" =: T.unlines [ "lucky" , "*star" ] =?> para ("lucky" <> softbreak <> "*star") , "Example block" =: T.unlines [ ": echo hello" , ": echo dear tester" ] =?> codeBlockWith ("", [], []) "echo hello\necho dear tester\n" , "Example block surrounded by text" =: T.unlines [ "Greetings" , ": echo hello" , ": echo dear tester" , "Bye" ] =?> mconcat [ para "Greetings" , codeBlockWith ("", [], []) "echo hello\necho dear tester\n" , para "Bye" ] , "Horizontal Rule" =: T.unlines [ "before" , "-----" , "after" ] =?> mconcat [ para "before" , horizontalRule , para "after" ] , "Not a Horizontal Rule" =: "----- em and en dash" =?> para "\8212\8211 em and en dash" , testGroup "Comments" [ "Comment Block" =: T.unlines [ "#+begin_comment" , "stuff" , "bla" , "#+end_comment"] =?> (mempty::Blocks) , "Comment line" =: T.unlines [ "# this is a comment" ] =?> (mempty :: Blocks) , "Empty comment line" =: T.unlines [ " #" ] =?> (mempty :: Blocks) ] , testGroup "Blocks and fragments" [ "HTML block" =: T.unlines [ "#+begin_html" , "" , "#+end_html" ] =?> rawBlock "html" "\n" , "Quote block" =: T.unlines [ "#+begin_quote" , "/Niemand/ hat die Absicht, eine Mauer zu errichten!" , "#+end_quote" ] =?> blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht," , "eine", "Mauer", "zu", "errichten!" ])) , "Verse block" =: T.unlines [ "The first lines of Goethe's /Faust/:" , "#+begin_verse" , "Habe nun, ach! Philosophie," , "Juristerei und Medizin," , "Und leider auch Theologie!" , "Durchaus studiert, mit heißem Bemühn." , "#+end_verse" ] =?> mconcat [ para $ spcSep [ "The", "first", "lines", "of" , "Goethe's", emph "Faust" <> ":"] , lineBlock [ "Habe nun, ach! Philosophie," , "Juristerei und Medizin," , "Und leider auch Theologie!" , "Durchaus studiert, mit heißem Bemühn." ] ] , "Verse block with blank lines" =: T.unlines [ "#+begin_verse" , "foo" , "" , "bar" , "#+end_verse" ] =?> lineBlock [ "foo", mempty, "bar" ] , "Verse block with varying indentation" =: T.unlines [ "#+begin_verse" , " hello darkness" , "my old friend" , "#+end_verse" ] =?> lineBlock [ "\160\160hello darkness", "my old friend" ] , "Raw block LaTeX" =: T.unlines [ "#+begin_latex" , "The category $\\cat{Set}$ is adhesive." , "#+end_latex" ] =?> rawBlock "latex" "The category $\\cat{Set}$ is adhesive.\n" , "Raw LaTeX line" =: "#+latex: \\let\\foo\\bar" =?> rawBlock "latex" "\\let\\foo\\bar" , "Raw Beamer line" =: "#+beamer: \\pause" =?> rawBlock "beamer" "\\pause" , "Raw HTML line" =: "#+html: " =?> rawBlock "html" "" , "Export block HTML" =: T.unlines [ "#+begin_export html" , "Hello, World!" , "#+end_export" ] =?> rawBlock "html" "Hello, World!\n" , "LaTeX fragment" =: "\\begin{equation}\n\ \X_i = \\begin{cases}\n\ \ G_{\\alpha(i)} & \\text{if }\\alpha(i-1) = \\alpha(i)\\\\\n\ \ C_{\\alpha(i)} & \\text{otherwise}\n\ \ \\end{cases}\n\ \\\end{equation}" =?> para (rawInline "latex" "\\begin{equation}\n\ \X_i = \\begin{cases}\n\ \ G_{\\alpha(i)} & \\text{if }\\alpha(i-1) = \\alpha(i)\\\\\n\ \ C_{\\alpha(i)} & \\text{otherwise}\n\ \ \\end{cases}\n\ \\\end{equation}") , "One-line LaTeX fragment" =: "\\begin{equation} 2 + 3 \\end{equation}" =?> para (rawInline "latex" "\\begin{equation} 2 + 3 \\end{equation}") , "LaTeX fragment with more arguments" =: T.unlines [ "\\begin{tikzcd}[ampersand replacement=\\&]" , " A \\& B \\\\" , " C \\& D" , " \\arrow[from=1-1, to=1-2]" , " \\arrow[\"f\", from=2-1, to=2-2]" , "\\end{tikzcd}" ] =?> rawBlock "latex" (T.unlines [ "\\begin{tikzcd}[ampersand replacement=\\&]" , " A \\& B \\\\" , " C \\& D" , " \\arrow[from=1-1, to=1-2]" , " \\arrow[\"f\", from=2-1, to=2-2]" , "\\end{tikzcd}" ]) , "Convert blank lines in blocks to single newlines" =: T.unlines [ "#+begin_html" , "" , "boring" , "" , "#+end_html" ] =?> rawBlock "html" "\nboring\n\n" , "Accept `attr_html` attributes for generic block" =: T.unlines [ "#+attr_html: :title hello, world :id test :class fun code" , "#+begin_test" , "nonsense" , "#+end_test" ] =?> let attr = ("test", ["fun", "code", "test"], [("title", "hello, world")]) in divWith attr (para "nonsense") ] , testGroup "Headers" Header.tests , testGroup "Figures" Figure.tests , testGroup "Lists" List.tests , testGroup "CodeBlocks" CodeBlock.tests , testGroup "Tables" Table.tests ] ================================================ FILE: test/Tests/Readers/Org/Directive.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Directive Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Tests parsing of org directives (like @#+OPTIONS@). -} module Tests.Readers.Org.Directive (tests) where import Data.Time (UTCTime (UTCTime), secondsToDiffTime) import Data.Time.Calendar (Day (ModifiedJulianDay)) import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>), ToString, purely, test) import Tests.Readers.Org.Shared ((=:), tagSpan) import Text.Pandoc import Text.Pandoc.Builder import qualified Data.ByteString as BS import qualified Data.Text as T testWithFiles :: (ToString c) => [(FilePath, BS.ByteString)] -> String -- ^ name of test case -> (T.Text, c) -- ^ (input, expected value) -> TestTree testWithFiles fileDefs = test (orgWithFiles fileDefs) orgWithFiles :: [(FilePath, BS.ByteString)] -> T.Text -> Pandoc orgWithFiles fileDefs input = let readOrg' = readOrg def{ readerExtensions = getDefaultExtensions "org" } in flip purely input $ \inp -> do modifyPureState (\st -> st { stFiles = files fileDefs }) readOrg' inp files :: [(FilePath, BS.ByteString)] -> FileTree files fileDefs = let dummyTime = UTCTime (ModifiedJulianDay 125) (secondsToDiffTime 0) in foldr (\(fp, bs) -> insertInFileTree fp (FileInfo dummyTime bs)) mempty fileDefs tests :: [TestTree] tests = [ testGroup "export options" [ "disable simple sub/superscript syntax" =: T.unlines [ "#+OPTIONS: ^:nil" , "a^b" ] =?> para "a^b" , "directly select drawers to be exported" =: T.unlines [ "#+OPTIONS: d:(\"IMPORTANT\")" , ":IMPORTANT:" , "23" , ":END:" , ":BORING:" , "very boring" , ":END:" ] =?> divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "23") , "exclude drawers from being exported" =: T.unlines [ "#+OPTIONS: d:(not \"BORING\")" , ":IMPORTANT:" , "5" , ":END:" , ":BORING:" , "very boring" , ":END:" ] =?> divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "5") , "don't include archive trees" =: T.unlines [ "#+OPTIONS: arch:nil" , "* old :ARCHIVE:" ] =?> (mempty ::Blocks) , "include complete archive trees" =: T.unlines [ "#+OPTIONS: arch:t" , "* old :ARCHIVE:" , " boring" ] =?> mconcat [ headerWith ("old", [], mempty) 1 ("old" <> space <> tagSpan "ARCHIVE") , para "boring" ] , "include archive tree header only" =: T.unlines [ "#+OPTIONS: arch:headline" , "* old :ARCHIVE:" , " boring" ] =?> headerWith ("old", [], mempty) 1 ("old" <> space <> tagSpan "ARCHIVE") , "limit headline depth" =: T.unlines [ "#+OPTIONS: H:2" , "* top-level section" , "** subsection" , "*** list item 1" , "*** list item 2" ] =?> mconcat [ headerWith ("top-level-section", [], []) 1 "top-level section" , headerWith ("subsection", [], []) 2 "subsection" , orderedList [ para "list item 1", para "list item 2" ] ] , "turn all headlines into lists" =: T.unlines [ "#+OPTIONS: H:0" , "first block" , "* top-level section 1" , "** subsection" , "* top-level section 2" ] =?> mconcat [ para "first block" , orderedList [ para "top-level section 1" <> orderedList [ para "subsection" ] , para "top-level section 2" ] ] , "preserve linebreaks as hard breaks" =: T.unlines [ "#+OPTIONS: \\n:t" , "first" , "second" ] =?> para ("first" <> linebreak <> "second") , "disable author export" =: T.unlines [ "#+OPTIONS: author:nil" , "#+AUTHOR: ShyGuy" ] =?> Pandoc nullMeta mempty , "disable creator export" =: T.unlines [ "#+OPTIONS: creator:nil" , "#+creator: The Architect" ] =?> Pandoc nullMeta mempty , "disable email export" =: T.unlines [ "#+OPTIONS: email:nil" , "#+email: no-mail-please@example.com" ] =?> Pandoc nullMeta mempty , "disable MathML-like entities" =: T.unlines [ "#+OPTIONS: e:nil" , "Icelandic letter: \\thorn" ] =?> para "Icelandic letter: \\thorn" , testGroup "Option f" [ "disable inline footnotes" =: T.unlines [ "#+OPTIONS: f:nil" , "Funny![fn:funny:or not]" ] =?> para "Funny!" , "disable reference footnotes" =: T.unlines [ "#+OPTIONS: f:nil" , "Burn everything[fn:1] down!" , "" , "[fn:2] Not quite everything." ] =?> para "Burn everything down!" ] , "disable inclusion of todo keywords" =: T.unlines [ "#+OPTIONS: todo:nil" , "** DONE todo export" ] =?> headerWith ("todo-export", [], []) 2 "todo export" , "remove tags from headlines" =: T.unlines [ "#+OPTIONS: tags:nil" , "* Headline :hello:world:" ] =?> headerWith ("headline", [], mempty) 1 "Headline" , testGroup "LaTeX" [ testGroup "Include LaTeX fragments" [ "Inline command" =: T.unlines [ "#+OPTIONS: tex:t" , "Hello \\emph{Name}" ] =?> para ("Hello" <> space <> rawInline "latex" "\\emph{Name}") , "Alpha" =: T.unlines [ "#+OPTIONS: tex:t" , "\\alpha" ] =?> para "α" , "equation environment" =: "#+OPTIONS: tex:t\n\ \\\begin{equation}\n\ \f(x) = x^2\n\ \\\end{equation}" =?> para (rawInline "latex" "\\begin{equation}\n\ \f(x) = x^2\n\ \\\end{equation}") ] , testGroup "Ignore LaTeX fragments" [ "Inline command" =: T.unlines [ "#+OPTIONS: tex:nil" , "Hello \\emph{Emphasised}" ] =?> para "Hello" , "MathML symbol (alpha)" =: T.unlines [ "#+OPTIONS: tex:nil" , "\\alpha" ] =?> para "α" , "equation environment" =: T.unlines [ "#+OPTIONS: tex:nil" , "\\begin{equation}" , "f(x) = x^2" , "\\end{equation}" ] =?> (para mempty) ] , testGroup "Verbatim LaTeX" [ "Inline command" =: T.unlines [ "#+OPTIONS: tex:verbatim" , "Hello \\emph{Emphasised}" ] =?> para "Hello \\emph{Emphasised}" , "MathML symbol (alpha)" =: T.unlines [ "#+OPTIONS: tex:verbatim" , "\\alpha" ] =?> para "α" , "equation environment" =: T.unlines [ "#+OPTIONS: tex:verbatim" , "\\begin{equation}" , "f(x) = x^2" , "\\end{equation}" ] =?> para (str "\\begin{equation}" <> softbreak <> text "f(x) = x^2" <> softbreak <> str "\\end{equation}") ] ] , testGroup "planning information" [ "include planning info after headlines" =: T.unlines [ "#+OPTIONS: p:t" , "* important" , " DEADLINE: <2018-10-01 Mon> SCHEDULED: <2018-09-15 Sat>" ] =?> mconcat [ headerWith ("important", mempty, mempty) 1 "important" , plain $ strong "DEADLINE:" <> space <> emph (str "<2018-10-01 Mon>") <> space <> strong "SCHEDULED:" <> space <> emph (str "<2018-09-15 Sat>") ] , "empty planning info is not included" =: T.unlines [ "#+OPTIONS: p:t" , "* Wichtig" ] =?> headerWith ("wichtig", mempty, mempty) 1 "Wichtig" ] , testGroup "Option |" [ "disable export of tables" =: T.unlines [ "#+OPTIONS: |:nil" , "| chair |" ] =?> (mempty :: Blocks) ] , testGroup "unknown options" [ "unknown options are ignored" =: T.unlines [ "#+OPTIONS: does-not-exist:t "] =?> (mempty :: Pandoc) , "highlighting after unknown option" =: T.unlines [ "#+OPTIONS: nope" , "/yup/" ] =?> para (emph "yup") , "unknown option interleaved with known" =: T.unlines [ "#+OPTIONS: tags:nil foo:bar todo:nil" , "* DONE ignore things :easy:" ] =?> headerWith ("ignore-things", [], mempty) 1 "ignore things" ] ] , testGroup "Include" [ testWithFiles [("./other.org", "content of other file\n")] "file inclusion" (T.unlines [ "#+include: \"other.org\"" ] =?> plain "content of other file") , testWithFiles [("./world.org", "World\n\n")] "Included file belongs to item" (T.unlines [ "- Hello,\n #+include: \"world.org\"" ] =?> bulletList [para "Hello," <> para "World"]) , testWithFiles [("./level3.org", "*** Level3\n\n")] "Default include preserves level" (T.unlines [ "#+include: \"level3.org\"" ] =?> headerWith ("level3", [], []) 3 "Level3") , testWithFiles [("./level3.org", "*** Level3\n\n")] "Minlevel shifts level leftward" (T.unlines [ "#+include: \"level3.org\" :minlevel 1" ] =?> headerWith ("level3", [], []) 1 "Level3") , testWithFiles [("./level1.org", "* Level1\n\n")] "Minlevel shifts level rightward" (T.unlines [ "#+include: \"level1.org\" :minlevel 3" ] =?> headerWith ("level1", [], []) 3 "Level1") , testWithFiles [("./src.hs", "putStrLn outString\n")] "Include file as source code snippet" (T.unlines [ "#+include: \"src.hs\" src haskell" ] =?> codeBlockWith ("", ["haskell"], []) "putStrLn outString\n") , testWithFiles [("./export-latex.org", "\\emph{Hello}\n")] "Include file as export snippet" (T.unlines [ "#+include: \"export-latex.org\" export latex" ] =?> rawBlock "latex" "\\emph{Hello}\n") , testWithFiles [("./subdir/foo-bar.latex", "foo\n"), ("./hello.lisp", "(print \"Hello!\")\n") ] "include directive is limited to one line" (T.unlines [ "#+INCLUDE: \"hello.lisp\" src lisp" , "#+include: \"subdir/foo-bar.latex\" export latex" , "bar" ] =?> mconcat [ codeBlockWith ("", ["lisp"], []) "(print \"Hello!\")\n" , rawBlock "latex" "foo\n" , para "bar" ] ) ] ] ================================================ FILE: test/Tests/Readers/Org/Inline/Citation.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Inline.Citation Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Test parsing of citations in org input. -} module Tests.Readers.Org.Inline.Citation (tests) where import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:)) import Text.Pandoc.Builder tests :: [TestTree] tests = [ testGroup "Org-cite citations" [ "Citation" =: "[cite:@nonexistent]" =?> let citation = Citation { citationId = "nonexistent" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0} in (para $ cite [citation] "[cite:@nonexistent]") , "Citation containing text" =: "[cite:see @item1 p. 34-35]" =?> let citation = Citation { citationId = "item1" , citationPrefix = [Str "see"] , citationSuffix = [Space ,Str "p.",Space,Str "34-35"] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0} in (para $ cite [citation] "[cite:see @item1 p. 34-35]") , "Author-in-text citation with locator and suffix" =: "[cite/t:see @item1 p. 34-35 and *passim*; @item2]" =?> let citations = [ Citation { citationId = "item1" , citationPrefix = [ Str "see" ] , citationSuffix = [ Str "p." , Space , Str "34-35" , Space , Str "and" , Space , Strong [ Str "passim" ] ] , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "item2" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] in (para $ cite citations "[cite/t:see @item1 p. 34-35 and *passim*; @item2]") ] , testGroup "org-ref citations" [ "simple citation" =: "cite:pandoc" =?> let citation = Citation { citationId = "pandoc" , citationPrefix = mempty , citationSuffix = mempty , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [citation] "cite:pandoc") , "simple citation with underscores" =: "cite:pandoc_org_ref" =?> let citation = Citation { citationId = "pandoc_org_ref" , citationPrefix = mempty , citationSuffix = mempty , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [citation] "cite:pandoc_org_ref") , "simple citation succeeded by comma" =: "cite:pandoc," =?> let citation = Citation { citationId = "pandoc" , citationPrefix = mempty , citationSuffix = mempty , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [citation] "cite:pandoc" <> str ",") , "simple citation succeeded by dot" =: "cite:pandoc." =?> let citation = Citation { citationId = "pandoc" , citationPrefix = mempty , citationSuffix = mempty , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [citation] "cite:pandoc" <> str ".") , "simple citation succeeded by colon" =: "cite:pandoc:" =?> let citation = Citation { citationId = "pandoc" , citationPrefix = mempty , citationSuffix = mempty , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [citation] "cite:pandoc" <> str ":") , "simple citep citation" =: "citep:pandoc" =?> let citation = Citation { citationId = "pandoc" , citationPrefix = mempty , citationSuffix = mempty , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [citation] "citep:pandoc") , "multiple simple citations" =: "citep:picard,riker" =?> let picard = Citation { citationId = "picard" , citationPrefix = mempty , citationSuffix = mempty , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } riker = Citation { citationId = "riker" , citationPrefix = mempty , citationSuffix = mempty , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [picard,riker] "citep:picard,riker") , "multiple simple citations succeeded by comma" =: "citep:picard,riker," =?> let picard = Citation { citationId = "picard" , citationPrefix = mempty , citationSuffix = mempty , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } riker = Citation { citationId = "riker" , citationPrefix = mempty , citationSuffix = mempty , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [picard,riker] "citep:picard,riker" <> str ",") , "extended citation" =: "[[citep:Dominik201408][See page 20::, for example]]" =?> let citation = Citation { citationId = "Dominik201408" , citationPrefix = toList "See page 20" , citationSuffix = toList ", for example" , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } in (para $ cite [citation] "[[citep:Dominik201408][See page 20::, for example]]") ] , "LaTeX citation" =: "\\cite{Coffee}" =?> para (rawInline "latex" "\\cite{Coffee}") ] ================================================ FILE: test/Tests/Readers/Org/Inline/Note.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Inline.Note Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Test parsing of footnotes in org input. -} module Tests.Readers.Org.Inline.Note (tests) where import Test.Tasty (TestTree) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:)) import Text.Pandoc.Builder import qualified Data.Text as T tests :: [TestTree] tests = [ "Footnote" =: T.unlines [ "A footnote[1]" , "" , "[1] First paragraph" , "" , "second paragraph" ] =?> para (mconcat [ "A", space, "footnote" , note $ mconcat [ para ("First" <> space <> "paragraph") , para ("second" <> space <> "paragraph") ] ]) , "Two footnotes" =: T.unlines [ "Footnotes[fn:1][fn:2]" , "" , "[fn:1] First note." , "" , "[fn:2] Second note." ] =?> para (mconcat [ "Footnotes" , note $ para ("First" <> space <> "note.") , note $ para ("Second" <> space <> "note.") ]) , "Emphasized text before footnote" =: T.unlines [ "/text/[fn:1]" , "" , "[fn:1] unicorn" ] =?> para (mconcat [ emph "text" , note . para $ "unicorn" ]) , "Footnote that starts with emphasized text" =: T.unlines [ "text[fn:1]" , "" , "[fn:1] /emphasized/" ] =?> para (mconcat [ "text" , note . para $ emph "emphasized" ]) , "Footnote followed by header" =: T.unlines [ "Another note[fn:yay]" , "" , "[fn:yay] This is great!" , "" , "** Headline" ] =?> mconcat [ para (mconcat [ "Another", space, "note" , note $ para ("This" <> space <> "is" <> space <> "great!") ]) , headerWith ("headline", [], []) 2 "Headline" ] , "Footnote followed by two blank lines" =: T.unlines [ "footnote[fn:blanklines]" , "" , "[fn:blanklines] followed by blank lines" , "" , "" , "next" ] =?> mconcat [ para ("footnote" <> note (para "followed by blank lines")) , para "next" ] ] ================================================ FILE: test/Tests/Readers/Org/Inline/Smart.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Inline.Smart Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Test smart parsing of quotes, apostrophe, etc. -} module Tests.Readers.Org.Inline.Smart (tests) where import Data.Text (Text) import Test.Tasty (TestTree) import Tests.Helpers ((=?>), purely, test) import Text.Pandoc (ReaderOptions (readerExtensions), Extension (Ext_smart), def, enableExtension, getDefaultExtensions, readOrg) import Text.Pandoc.Builder orgSmart :: Text -> Pandoc orgSmart = purely $ let extensionsSmart = enableExtension Ext_smart (getDefaultExtensions "org") in readOrg def{ readerExtensions = extensionsSmart } tests :: [TestTree] tests = [ test orgSmart "quote before ellipses" ("'...hi'" =?> para (singleQuoted "…hi")) , test orgSmart "apostrophe before emph" ("D'oh! A l'/aide/!" =?> para ("D’oh! A l’" <> emph "aide" <> "!")) , test orgSmart "apostrophe in French" ("À l'arrivée de la guerre, le thème de l'«impossibilité du socialisme»" =?> para "À l’arrivée de la guerre, le thème de l’«impossibilité du socialisme»") , test orgSmart "Quotes cannot occur at the end of emphasized text" ("/say \"yes\"/" =?> para ("/say" <> space <> doubleQuoted "yes" <> "/")) , test orgSmart "Dashes are allowed at the borders of emphasis'" ("/foo---/" =?> para (emph "foo—")) , test orgSmart "Support for shy (soft) hyphen" ("Ur\\-instinkt" =?> para "Ur\173instinkt") , test orgSmart "Single quotes can be followed by emphasized text" ("Singles on the '/meat market/'" =?> para ("Singles on the " <> singleQuoted (emph "meat market"))) , test orgSmart "Double quotes can be followed by emphasized text" ("Double income, no kids: \"/DINK/\"" =?> para ("Double income, no kids: " <> doubleQuoted (emph "DINK"))) ] ================================================ FILE: test/Tests/Readers/Org/Inline.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Inline Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Tests parsing of org inlines. -} module Tests.Readers.Org.Inline (tests) where import Data.List (intersperse) import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) import Text.Pandoc.Builder import qualified Data.Text as T import qualified Tests.Readers.Org.Inline.Citation as Citation import qualified Tests.Readers.Org.Inline.Note as Note import qualified Tests.Readers.Org.Inline.Smart as Smart tests :: [TestTree] tests = [ "Plain String" =: "Hello, World" =?> para (spcSep [ "Hello,", "World" ]) , "Emphasis" =: "/Planet Punk/" =?> para (emph . spcSep $ ["Planet", "Punk"]) , "Strong" =: "*Cider*" =?> para (strong "Cider") , "Strong Emphasis" =: "/*strength*/" =?> para (emph . strong $ "strength") , "Emphasized Strong preceded by space" =: " */super/*" =?> para (strong . emph $ "super") , "Underline" =: "_underline_" =?> para (underline "underline") , "Strikeout" =: "+Kill Bill+" =?> para (strikeout . spcSep $ [ "Kill", "Bill" ]) , "Verbatim" =: "=Robot.rock()=" =?> para (codeWith ("", ["verbatim"], []) "Robot.rock()") , "Code" =: "~word for word~" =?> para (code "word for word") , "Math $..$" =: "$E=mc^2$" =?> para (math "E=mc^2") , "Math $$..$$" =: "$$E=mc^2$$" =?> para (displayMath "E=mc^2") , "Math \\[..\\]" =: "\\[E=ℎν\\]" =?> para (displayMath "E=ℎν") , "Math \\(..\\)" =: "\\(σ_x σ_p ≥ \\frac{ℏ}{2}\\)" =?> para (math "σ_x σ_p ≥ \\frac{ℏ}{2}") , "Symbol" =: "A * symbol" =?> para (str "A" <> space <> str "*" <> space <> "symbol") , "Superscript simple expression" =: "2^-λ" =?> para (str "2" <> superscript "-λ") , "Superscript multi char" =: "2^{n-1}" =?> para (str "2" <> superscript "n-1") , "Superscript-like, but not after string" =: "a ^caret" =?> para "a ^caret" , "Subscript simple expression" =: "a_n" =?> para (str "a" <> subscript "n") , "Subscript multi char" =: "a_{n+1}" =?> para (str "a" <> subscript "n+1") , "Subscript-like, but not after string" =: "_underscore" =?> para "_underscore" , "Subscript takes precedence before underline" =: "text_subscript_" =?> para (str "text" <> subscript "subscript" <> str "_") , "Linebreak" =: "line \\\\ \nbreak" =?> para ("line" <> linebreak <> "break") , "Inline note" =: "[fn::Schreib mir eine E-Mail]" =?> para (note $ para "Schreib mir eine E-Mail") , "By default, markup-chars not occurring on word break are symbols" =: T.unlines [ "#+pandoc-emphasis-pre:" , "#+pandoc-emphasis-post:" , "this+that+ +so+on" , "seven*eight* nine*" , "+not+funny+" ] =?> para ("this+that+ +so+on" <> softbreak <> "seven*eight* nine*" <> softbreak <> strikeout "not+funny") , "No empty markup" =: "// ** __ <> == ~~ $$" =?> para (spcSep [ "//", "**", "__", "<>", "==", "~~", "$$" ]) , "Adherence to Org's rules for markup borders" =: "/t/& a/ / ./r/ (*l*) /e/! ze\x200b/r/\x200bo /b/." =?> para (spcSep [ emph $ "t/&" <> space <> "a" , "/" , "./r/" , "(" <> strong "l" <> ")" , emph "e" <> "!" , "ze\x200b" <> emph "r" <> "\x200bo" , emph "b" <> "." ]) , "Quotes are allowed border chars" =: "/'yep/ *sure\"*" =?> para (emph "'yep" <> space <> strong "sure\"") , "Spaces are forbidden border chars" =: "/nada /" =?> para "/nada /" , "Zero width spaces are forbidden border chars" =: "/emph\x200b/asis" =?> para "/emph\x200b/asis" , "Markup should work properly after a blank line" =: T.unlines ["foo", "", "/bar/"] =?> para (text "foo") <> para (emph $ text "bar") , "Inline math must stay within three lines" =: T.unlines [ "$a", "b", "c$", "$d", "e", "f", "g$" ] =?> para (math "a\nb\nc" <> softbreak <> "$d" <> softbreak <> "e" <> softbreak <> "f" <> softbreak <> "g$") , "Single-character math" =: "$a$ $b$! $c$?" =?> para (spcSep [ math "a" , "$b$!" , math "c" <> "?" ]) , "Markup may not span more than two lines" =: "/this *is +totally\nnice+ not*\nemph/" =?> para ("/this" <> space <> strong ("is" <> space <> strikeout ("totally" <> softbreak <> "nice") <> space <> "not") <> softbreak <> "emph/") , "Sub- and superscript expressions" =: T.unlines [ "a_(a(b)(c)d)" , "e^(f(g)h)" , "i_(jk)l)" , "m^()n" , "o_{p{q{}r}}" , "s^{t{u}v}" , "w_{xy}z}" , "1^{}2" , "3_{{}}" , "4^(a(*b(c*)d))" ] =?> para (mconcat $ intersperse softbreak [ "a" <> subscript "(a(b)(c)d)" , "e" <> superscript "(f(g)h)" , "i" <> subscript "(jk)" <> "l)" , "m" <> superscript "()" <> "n" , "o" <> subscript "p{q{}r}" , "s" <> superscript "t{u}v" , "w" <> subscript "xy" <> "z}" , "1" <> superscript "" <> "2" , "3" <> subscript "{}" , "4" <> superscript ("(a(" <> strong "b(c" <> ")d))") ]) , "Verbatim text can contain equal signs (=)" =: "=is_subst = True=" =?> para (codeWith ("", ["verbatim"], []) "is_subst = True") , testGroup "Images" [ "Image" =: "[[./sunset.jpg]]" =?> para (image "./sunset.jpg" "" "") , "Image with explicit file: prefix" =: "[[file:sunrise.jpg]]" =?> para (image "sunrise.jpg" "" "") , "Multiple images within a paragraph" =: T.unlines [ "[[file:sunrise.jpg]]" , "[[file:sunset.jpg]]" ] =?> para (image "sunrise.jpg" "" "" <> softbreak <> image "sunset.jpg" "" "") , "Image with html attributes" =: T.unlines [ "#+attr_html: :width 50%" , "[[file:guinea-pig.gif]]" ] =?> para (imageWith ("", [], [("width", "50%")]) "guinea-pig.gif" "" "") , "HTML attributes can have trailing spaces" =: T.unlines [ "#+attr_html: :width 100% :height 360px " , "[[file:fireworks.jpg]]" ] =?> let kv = [("width", "100%"), ("height", "360px")] in para (imageWith (mempty, mempty, kv) "fireworks.jpg" mempty mempty) , "Uppercase extension" =: "[[file:test.PNG]]" =?> para (image "test.PNG" "" "") ] , "Explicit link" =: "[[http://zeitlens.com/][pseudo-random /nonsense/]]" =?> para (link "http://zeitlens.com/" "" ("pseudo-random" <> space <> emph "nonsense")) , "Self-link" =: "[[http://zeitlens.com/]]" =?> para (link "http://zeitlens.com/" "" "http://zeitlens.com/") , "Internal self-link (reference)" =: "[[#rabbit]]" =?> para (link "#rabbit" "" "#rabbit") , "Absolute file link" =: "[[/url][hi]]" =?> para (link "file:///url" "" "hi") , "Link to file in parent directory" =: "[[../file.txt][moin]]" =?> para (link "../file.txt" "" "moin") , "Empty link (for gitit interop)" =: "[[][New Link]]" =?> para (link "" "" "New Link") , "Image link" =: "[[sunset.png][file:dusk.svg]]" =?> para (link "sunset.png" "" (image "dusk.svg" "" "")) , "Image link with non-image target" =: "[[http://example.com][./logo.png]]" =?> para (link "http://example.com" "" (image "./logo.png" "" "")) , "Link to image" =: "[[https://example.com/image.jpg][Look!]]" =?> para (link "https://example.com/image.jpg" "" (str "Look!")) , "Plain link" =: "Posts on http://zeitlens.com/ can be funny at times." =?> para (spcSep [ "Posts", "on" , link "http://zeitlens.com/" "" "http://zeitlens.com/" , "can", "be", "funny", "at", "times." ]) , "Angle link" =: "Look at for fnords." =?> para (spcSep [ "Look", "at" , link "http://moltkeplatz.de" "" "http://moltkeplatz.de" , "for", "fnords." ]) , "Absolute file link" =: "[[file:///etc/passwd][passwd]]" =?> para (link "file:///etc/passwd" "" "passwd") , "File link" =: "[[file:target][title]]" =?> para (link "target" "" "title") , "Anchor" =: "<> Link here later." =?> para (spanWith ("anchor", [], []) mempty <> "Link" <> space <> "here" <> space <> "later.") , "Inline code block" =: "src_emacs-lisp{(message \"Hello\")}" =?> para (codeWith ( "" , [ "commonlisp" ] , [ ("org-language", "emacs-lisp") ]) "(message \"Hello\")") , "Inline code block with arguments" =: "src_sh[:export both :results output]{echo 'Hello, World'}" =?> para (codeWith ( "" , [ "bash" ] , [ ("org-language", "sh") , ("export", "both") , ("results", "output") ] ) "echo 'Hello, World'") , "Inline code block with a blank argument array" =: "src_sh[]{echo 'Hello, World'}" =?> para (codeWith ( "" , [ "bash" ] , [ ("org-language", "sh") ]) "echo 'Hello, World'") , "Inline code block with toggle" =: "src_sh[:toggle]{echo $HOME}" =?> para (codeWith ( "" , [ "bash" ] , [ ("org-language", "sh") , ("toggle", "yes") ] ) "echo $HOME") , "Inline LaTeX symbol" =: "\\dots" =?> para "…" , "Inline LaTeX command" =: "\\textit{Emphasised}" =?> para (emph "Emphasised") , "Inline LaTeX command with spaces" =: "\\emph{Emphasis mine}" =?> para (rawInline "latex" "\\emph{Emphasis mine}") , "Inline math symbols" =: "\\tau \\oplus \\alpha" =?> para "τ ⊕ α" , "Inline LaTeX math command" =: "\\crarr" =?> para "↵" , "Unknown inline LaTeX command" =: "\\notacommand{foo}" =?> para (rawInline "latex" "\\notacommand{foo}") , "Export snippet" =: "@@html:M-x org-agenda@@" =?> para (rawInline "html" "M-x org-agenda") , "MathML symbol in LaTeX-style" =: "There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: '\\nbsp')." =?> para "There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: ' ')." , "MathML symbol in LaTeX-style, including braces" =: "\\Aacute{}stor" =?> para "Ástor" , "MathML copy sign" =: "\\copy" =?> para "©" , "MathML symbols, space separated" =: "\\ForAll \\Auml" =?> para "∀ Ä" , "Macro" =: T.unlines [ "#+MACRO: HELLO /Hello, $1/" , "{{{HELLO(World)}}}" ] =?> para (emph "Hello, World") , "Macro duplicating its argument" =: T.unlines [ "#+MACRO: HELLO $1$1" , "{{{HELLO(moin)}}}" ] =?> para "moinmoin" , "Macro called with too few arguments" =: T.unlines [ "#+MACRO: HELLO Foo $1 $2 Bar" , "{{{HELLO()}}}" ] =?> para "Foo Bar" , "Macro called with an escaped comma" =: T.unlines [ "#+MACRO: HELLO Foo $1" , "{{{HELLO(moin\\, niom)}}}" ] =?> para "Foo moin, niom" , testGroup "Citations" Citation.tests , testGroup "Footnotes" Note.tests , testGroup "Smart punctuation" Smart.tests ] ================================================ FILE: test/Tests/Readers/Org/Meta.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Meta Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Tests parsing of org meta data (mostly lines starting with @#+@). -} module Tests.Readers.Org.Meta (tests) where import Test.Tasty (TestTree, testGroup) import Tests.Helpers ((=?>)) import Tests.Readers.Org.Shared ((=:), spcSep) import Text.Pandoc import Text.Pandoc.Builder import qualified Data.Text as T tests :: [TestTree] tests = [ testGroup "Comments" [ "Comment" =: "# Nothing to see here" =?> (mempty::Blocks) , "Hash not followed by space is text" =: "#-tag" =?> para "#-tag" , "Comment surrounded by Text" =: T.unlines [ "Before" , "# Comment" , "After" ] =?> mconcat [ para "Before" , para "After" ] ] , testGroup "Export settings" [ "Title" =: "#+title: Hello, World" =?> let titleInline = toList $ "Hello," <> space <> "World" meta = setMeta "title" (MetaInlines titleInline) nullMeta in Pandoc meta mempty , testGroup "Author" [ "sets 'author' field" =: "#+author: John /Emacs-Fanboy/ Doe" =?> let author = toList . spcSep $ [ "John", emph "Emacs-Fanboy", "Doe" ] meta = setMeta "author" (MetaInlines author) nullMeta in Pandoc meta mempty , "Multiple author lines" =: T.unlines [ "#+author: James Dewey Watson," , "#+author: Francis Harry Compton Crick" ] =?> let watson = toList "James Dewey Watson," crick = toList "Francis Harry Compton Crick" meta = setMeta "author" (MetaInlines (watson ++ SoftBreak : crick)) nullMeta in Pandoc meta mempty ] , "Date" =: "#+date: Feb. *28*, 2014" =?> let date = toList . spcSep $ [ "Feb.", strong "28" <> ",", "2014" ] meta = setMeta "date" (MetaInlines date) nullMeta in Pandoc meta mempty , testGroup "Description" [ "Single line" =: "#+description: Explanatory text" =?> let description = [Str "Explanatory", Space, Str "text"] meta = setMeta "description" (MetaInlines description) nullMeta in Pandoc meta mempty , "Multiline" =: T.unlines [ "#+description: /Short/ introduction" , "#+description: to Org-mode" ] =?> let description = [ Emph [Str "Short"], Space, Str "introduction" , SoftBreak , Str "to", Space, Str "Org-mode" ] meta = setMeta "description" (MetaInlines description) nullMeta in Pandoc meta mempty ] , "Subtitle" =: T.unlines [ "#+subtitle: Your Life in" , "#+subtitle: /Plain/ Text" ] =?> let subtitle = "Your Life in" <> softbreak <> emph "Plain" <> " Text" in Pandoc (setMeta "subtitle" (toMetaValue subtitle) nullMeta) mempty , "Keywords" =: T.unlines [ "#+keywords: pandoc, testing," , "#+keywords: Org" ] =?> let keywords = toList $ "pandoc, testing," <> softbreak <> "Org" meta = setMeta "keywords" (MetaInlines keywords) nullMeta in Pandoc meta mempty , "Institute" =: "#+institute: ACME Inc." =?> Pandoc (setMeta "institute" ("ACME Inc." :: Inlines) nullMeta) mempty , "Document language" =: "#+LANGUAGE: de-DE" =?> Pandoc (setMeta "lang" (MetaString "de-DE") nullMeta) mempty , testGroup "Todo sequences" [ "not included in document" =: "#+todo: WAITING | FINISHED" =?> Pandoc mempty mempty , "can contain multiple pipe characters" =: "#+todo: UNFINISHED | RESEARCH | NOTES | CHART\n" =?> Pandoc mempty mempty ] , testGroup "LaTeX" [ "LATEX_HEADER" =: "#+latex_header: \\usepackage{tikz}" =?> let latexInlines = rawInline "latex" "\\usepackage{tikz}" inclList = MetaList [MetaInlines (toList latexInlines)] meta = setMeta "header-includes" inclList nullMeta in Pandoc meta mempty , "LATEX_HEADER_EXTRA" =: "#+latex_header_extra: \\usepackage{calc}" =?> let latexInlines = rawInline "latex" "\\usepackage{calc}" inclList = toMetaValue [latexInlines] in Pandoc (setMeta "header-includes" inclList nullMeta) mempty , testGroup "LaTeX_CLASS" [ "stored as documentclass" =: "#+latex_class: article" =?> let meta = setMeta "documentclass" (MetaString "article") nullMeta in Pandoc meta mempty , "last definition takes precedence" =: T.unlines [ "#+latex_class: this will not be used" , "#+latex_class: report" ] =?> let meta = setMeta "documentclass" (MetaString "report") nullMeta in Pandoc meta mempty ] , "LATEX_CLASS_OPTIONS as classoption" =: "#+latex_class_options: [a4paper]" =?> let meta = setMeta "classoption" (MetaString "a4paper") nullMeta in Pandoc meta mempty ] , testGroup "HTML" [ "HTML_HEAD values are added to header-includes" =: "#+html_head: " =?> let html = rawInline "html" "" inclList = MetaList [MetaInlines (toList html)] meta = setMeta "header-includes" inclList nullMeta in Pandoc meta mempty , "HTML_HEAD_EXTRA behaves like HTML_HEAD" =: T.unlines [ "#+html_head: " , "#+html_head_extra: " ] =?> let generator = rawInline "html" "" charset = rawInline "html" "" inclList = toMetaValue [generator, charset] in Pandoc (setMeta "header-includes" inclList nullMeta) mempty ] ] , testGroup "Non-export keywords" [ testGroup "#+link" [ "Link abbreviation" =: T.unlines [ "#+link: wp https://en.wikipedia.org/wiki/%s" , "[[wp:Org_mode][Wikipedia on Org-mode]]" ] =?> para (link "https://en.wikipedia.org/wiki/Org_mode" "" ("Wikipedia" <> space <> "on" <> space <> "Org-mode")) , "Link abbreviation, defined after first use" =: T.unlines [ "[[zl:non-sense][Non-sense articles]]" , "#+link: zl http://zeitlens.com/tags/%s.html" ] =?> para (link "http://zeitlens.com/tags/non-sense.html" "" ("Non-sense" <> space <> "articles")) , "Link abbreviation, URL encoded arguments" =: T.unlines [ "#+link: expl http://example.com/%h/foo" , "[[expl:Hello, World!][Moin!]]" ] =?> para (link "http://example.com/Hello%2C%20World%21/foo" "" "Moin!") , "Link abbreviation, append arguments" =: T.unlines [ "#+link: expl http://example.com/" , "[[expl:foo][bar]]" ] =?> para (link "http://example.com/foo" "" "bar") ] , testGroup "emphasis config" [ "Changing pre chars for emphasis" =: T.unlines [ "#+pandoc-emphasis-pre: \"[)$a1%\"" , "[/emph/.)*strong*.a~code~" ] =?> para ("[" <> emph "emph" <> ".)" <> strong "strong" <> ".a" <> code "code") , "Changing post chars for emphasis" =: T.unlines [ "#+pandoc-emphasis-post: \"(]$a1%\"" , "/emph/('*strong*]'~code~a" ] =?> para (emph "emph" <> "('" <> strong "strong" <> "]'" <> code "code" <> "a") , "setting an invalid value restores the default" =: T.unlines [ "#+pandoc-emphasis-pre: \"[\"" , "#+pandoc-emphasis-post: \"]\"" , "#+pandoc-emphasis-pre:" , "#+pandoc-emphasis-post:" , "[/noemph/]" ] =?> para "[/noemph/]" ] , "Unknown keyword" =: T.unlines [ "#+unknown_keyword: Chumbawamba" , "#+another_unknown: Blur" ] =?> rawBlock "org" "#+unknown_keyword: Chumbawamba" <> rawBlock "org" "#+another_unknown: Blur" ] , "Properties drawer" =: T.unlines [ " :PROPERTIES:" , " :setting: foo" , " :END:" ] =?> (setMeta "setting" ("foo" :: T.Text) (doc mempty)) , "Logbook drawer" =: T.unlines [ " :LogBook:" , " - State \"DONE\" from \"TODO\" [2014-03-03 Mon 11:00]" , " :END:" ] =?> (mempty::Blocks) , "Drawer surrounded by text" =: T.unlines [ "Before" , ":PROPERTIES:" , ":END:" , "After" ] =?> para "Before" <> para "After" , "Drawer markers must be the only text in the line" =: T.unlines [ " :LOGBOOK: foo" , " :END: bar" ] =?> para (":LOGBOOK: foo" <> softbreak <> ":END: bar") , "Drawers can be arbitrary" =: T.unlines [ ":FOO:" , "/bar/" , ":END:" ] =?> divWith (mempty, ["FOO", "drawer"], mempty) (para $ emph "bar") , "Anchor reference" =: T.unlines [ "<> Target." , "" , "[[link-here][See here!]]" ] =?> (para (spanWith ("link-here", [], []) mempty <> "Target.") <> para (link "#link-here" "" ("See" <> space <> "here!"))) , "Search links are read as emph" =: "[[Wally][Where's Wally?]]" =?> para (spanWith ("", ["spurious-link"], [("target", "Wally")]) (emph $ "Where's" <> space <> "Wally?")) , "Link to nonexistent anchor" =: T.unlines [ "<> Target." , "" , "[[link$here][See here!]]" ] =?> (para (spanWith ("link-here", [], []) mempty <> "Target.") <> para (spanWith ("", ["spurious-link"], [("target", "link$here")]) (emph ("See" <> space <> "here!")))) ] ================================================ FILE: test/Tests/Readers/Org/Shared.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Org.Shared Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Helper functions used by other org tests. -} module Tests.Readers.Org.Shared ( (=:) , org , spcSep , tagSpan ) where import Data.List (intersperse) import Data.Text (Text) import Tests.Helpers (ToString, purely, test) import Test.Tasty (TestTree) import Test.Tasty.HUnit (HasCallStack) import Text.Pandoc (Pandoc, ReaderOptions (readerExtensions), def, getDefaultExtensions, readOrg) import Text.Pandoc.Builder (Inlines, smallcaps, space, spanWith, str) org :: Text -> Pandoc org = purely $ readOrg def{ readerExtensions = getDefaultExtensions "org" } infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test org spcSep :: [Inlines] -> Inlines spcSep = mconcat . intersperse space -- | Create a span for the given tag. tagSpan :: Text -> Inlines tagSpan t = spanWith ("", ["tag"], [("tag-name", t)]) . smallcaps $ str t ================================================ FILE: test/Tests/Readers/Org.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Shared Copyright : © 2014-2024 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel Stability : alpha Portability : portable Tests of the org reader. -} module Tests.Readers.Org (tests) where import Test.Tasty (TestTree, testGroup) import qualified Tests.Readers.Org.Block as Block import qualified Tests.Readers.Org.Directive as Directive import qualified Tests.Readers.Org.Inline as Inline import qualified Tests.Readers.Org.Meta as Meta tests :: [TestTree] tests = [ testGroup "Inlines" Inline.tests , testGroup "Basic Blocks" Block.tests , testGroup "Meta Information" Meta.tests , testGroup "Directives" Directive.tests ] ================================================ FILE: test/Tests/Readers/Pod.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Pod Copyright : © 2024 Evan Silberman License : GNU GPL, version 2 or above Maintainer : Stability : alpha Portability : portable Tests for the Pod reader. -} module Tests.Readers.Pod (tests) where import Data.Text (Text, pack) import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder pod :: Text -> Pandoc pod t = (purely $ readPod def) ("=pod\n\n" <> t <> "\n\n=cut\n") manLink :: Text -> Maybe Text -> Inlines -> Inlines manLink nm Nothing = linkWith (mempty, mempty, [("manual", nm)]) "" "" manLink nm (Just sc) = linkWith (mempty, mempty, [("manual", nm), ("section", sc)]) "" "" bogusEntity :: String -> TestTree bogusEntity t = t =: "E<" <> pack t <> ">" =?> para ("E<" <> str (pack t) <> ">") infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test pod tests :: [TestTree] tests = [ testGroup "inlines" [ "code with nested inlines" =: "C (*PRUNE) I/>" =?> para (code "/A (*PRUNE) B/") , "compact in compact" =: "I emphasis>" =?> para (emph $ (strong "strong") <> " emphasis") , "expanded in compact" =: "I> emphasis>" =?> para (emph $ (strong "strong") <> " emphasis") , "compact in expanded" =: "I<<< B emphasis >>>" =?> para (emph $ (strong "strong") <> " emphasis") , "expanded in expanded" =: "I<<< B<<< strong >>> emphasis >>>" =?> para (emph $ (strong "strong") <> " emphasis") ] , testGroup "links" [ testGroup "compact" [ "URL" =: "L" =?> para (link "https://example.org" "" "https://example.org") , "URL with link text" =: "L" =?> para (link "https://example.org/index.html" "" "link") , "perl manual" =: "L" =?> para (manLink "Foo::Bar" Nothing "Foo::Bar") , "manual with quoted section" =: "L" =?> para (manLink "crontab(5)" (Just "DESCRIPTION") (doubleQuoted "DESCRIPTION" <> " in crontab(5)")) , "manual with section and formatted link text" =: "L> link|HTTP::Simple/is_info>" =?> para (manLink "HTTP::Simple" (Just "is_info") (strong "extravagant" <> " link")) , "internal link" =: "L" =?> para (link "#section-name" "" (doubleQuoted "section name")) , "internal link with formatting" =: "L command>" =?> para (link "#the-pod2html-command" "" (doubleQuoted ("The " <> code "pod2html" <> " command"))) , "link with angle bracket" =: "L" =?> para (manLink "m<" Nothing "m<") , "empty name" =: "L<|https://example.org>" =?> para (link "https://example.org" "" mempty) ] , testGroup "expanded" [ "URL" =: "L<< https://example.org >>" =?> para (link "https://example.org" "" "https://example.org") , "URL with link text" =: "L<< link|https://example.org/index.html >>" =?> para (link "https://example.org/index.html" "" "link") , "perl manual" =: "L<<< Foo::Bar >>>" =?> para (manLink "Foo::Bar" Nothing "Foo::Bar") , "manual with quoted section" =: "L<< crontab(5)/\"DESCRIPTION\" >>" =?> para (manLink "crontab(5)" (Just "DESCRIPTION") (doubleQuoted "DESCRIPTION" <> " in crontab(5)")) , "manual with section and formatted link text" =: "L<< B<< extravagant >> link|HTTP::Simple/is_info >>" =?> para (manLink "HTTP::Simple" (Just "is_info") (strong "extravagant" <> " link")) , "internal link" =: "L<< /section name >>" =?> para (link "#section-name" "" (doubleQuoted "section name")) , "internal link with formatting" =: "L<<<<< /The C command >>>>>" =?> para (link "#the-pod2html-command" "" (doubleQuoted ("The " <> code "pod2html" <> " command"))) , "link with angle bracket" =: "L<< m< >>" =?> para (manLink "m<" Nothing "m<") , "empty name" =: "L<< |https://example.org >>" =?> para (link "https://example.org" "" mempty) ] ] , testGroup "entities" [ testGroup "required" [ "quot" =: "E" =?> para "\"" , "amp" =: "E" =?> para "&" , "apos" =: "E" =?> para "'" , "lt" =: "E" =?> para "<" , "gt" =: "E" =?> para ">" , "sol" =: "E" =?> para "/" , "verbar" =: "E" =?> para "|" , "lchevron" =: "E" =?> para "«" , "rchevron" =: "E" =?> para "»" ] , testGroup "html" [ "trade" =: "E" =?> para "™" , "ccaron" =: "E" =?> para "č" , "cent" =: "E" =?> para "¢" ] , testGroup "numeric" [ "decimal" =: "E<162>" =?> para "¢" , "octal" =: "E<0242>" =?> para "¢" , "hexadecimal" =: "E<0xA2>" =?> para "¢" , "hexadecimal variant" =: "E<0x00A2>" =?> para "¢" , "actually decimal" =: "E<099>" =?> para "c" ] , testGroup "bogus" [ bogusEntity "0XA2" , bogusEntity "not a real entity" , bogusEntity "162 1" , bogusEntity "99 bottles of beer" , bogusEntity "0xhh" , bogusEntity "077x" , bogusEntity "0x63 skidoo" , bogusEntity "trade;" ] ] ] ================================================ FILE: test/Tests/Readers/Pptx.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Pptx Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Tests for the PPTX reader. -} module Tests.Readers.Pptx (tests) where import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Test.Tasty import Test.Tasty.Golden.Advanced import Tests.Helpers import Text.Pandoc import Text.Pandoc.UTF8 as UTF8 defopts :: ReaderOptions defopts = def{ readerExtensions = getDefaultExtensions "pptx" } testCompare :: String -> FilePath -> FilePath -> TestTree testCompare = testCompareWithOpts defopts testCompareWithOpts :: ReaderOptions -> String -> FilePath -> FilePath -> TestTree testCompareWithOpts opts testName pptxFP nativeFP = goldenTest testName (do nf <- UTF8.toText <$> BS.readFile nativeFP runIOorExplode (readNative def nf)) (do df <- B.readFile pptxFP runIOorExplode (readPptx opts df)) (nativeDiff nativeFP) (\a -> runIOorExplode (writeNative def{ writerTemplate = Just mempty} a) >>= BS.writeFile nativeFP . UTF8.fromText) tests :: [TestTree] tests = [ testGroup "basic" [ testCompare "text extraction" "pptx-reader/basic.pptx" "pptx-reader/basic.native" ] ] ================================================ FILE: test/Tests/Readers/RST.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- | Module : Tests.Readers.RST Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Tests for the RST reader. -} module Tests.Readers.RST (tests) where import Data.Text (Text) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder rst :: Text -> Pandoc rst = purely $ readRST def{ readerStandalone = True } infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test rst tests :: [TestTree] tests = [ "line block with blank line" =: "| a\n|\n| b" =?> lineBlock [ "a", mempty, "\160b" ] , testGroup "field list" [ "general" =: T.unlines [ "para" , "" , ":Hostname: media08" , ":IP address: 10.0.0.19" , ":Size: 3ru" , ":Version: 1" , ":Indentation: Since the field marker may be quite long, the second" , " and subsequent lines of the field body do not have to line up" , " with the first line, but they must be indented relative to the" , " field name marker, and they must line up with each other." , ":Parameter i: integer" , ":Final: item" , " on two lines" ] =?> doc (para "para" <> definitionList [ (str "Hostname", [plain "media08"]) , (text "IP address", [plain "10.0.0.19"]) , (str "Size", [plain "3ru"]) , (str "Version", [plain "1"]) , (str "Indentation", [plain "Since the field marker may be quite long, the second\nand subsequent lines of the field body do not have to line up\nwith the first line, but they must be indented relative to the\nfield name marker, and they must line up with each other."]) , (text "Parameter i", [plain "integer"]) , (str "Final", [plain "item\non two lines"]) ]) , "metadata" =: T.unlines [ "=====" , "Title" , "=====" , "--------" , "Subtitle" , "--------" , "" , ":Version: 1" ] =?> setMeta "version" (str "1") (setMeta "title" ("Title" :: Inlines) $ setMeta "subtitle" ("Subtitle" :: Inlines) $ doc mempty) , "with inline markup" =: T.unlines [ ":*Date*: today" , "" , ".." , "" , ":*one*: emphasis" , ":two_: reference" , ":`three`_: another one" , ":``four``: literal" , "" , ".. _two: http://example.com" , ".. _three: http://example.org" ] =?> setMeta "date" (str "today") (doc $ definitionList [ (emph "one", [plain "emphasis"]) , (link "http://example.com" "" "two", [plain "reference"]) , (link "http://example.org" "" "three", [plain "another one"]) , (code "four", [plain "literal"]) ]) ] , "URLs with following punctuation" =: ("http://google.com, http://yahoo.com; http://foo.bar.baz.\n" <> "http://foo.bar/baz_(bam) (http://foo.bar)") =?> para (link "http://google.com" "" "http://google.com" <> ", " <> link "http://yahoo.com" "" "http://yahoo.com" <> "; " <> link "http://foo.bar.baz" "" "http://foo.bar.baz" <> ". " <> softbreak <> link "http://foo.bar/baz_(bam)" "" "http://foo.bar/baz_(bam)" <> " (" <> link "http://foo.bar" "" "http://foo.bar" <> ")") , "Reference names with special characters" =: ("A-1-B_2_C:3:D+4+E.5.F_\n\n" <> ".. _A-1-B_2_C:3:D+4+E.5.F: https://example.com\n") =?> para (link "https://example.com" "" "A-1-B_2_C:3:D+4+E.5.F") , "Code directive with class and number-lines" =: T.unlines [ ".. code::python" , " :number-lines: 34" , " :class: class1 class2 class3" , "" , " def func(x):" , " return y" ] =?> doc (codeBlockWith ( "" , ["python", "numberLines", "class1", "class2", "class3"] , [ ("startFrom", "34") ] ) "def func(x):\n return y") , "Code directive with number-lines, no line specified" =: T.unlines [ ".. code::python" , " :number-lines:" , "" , " def func(x):" , " return y" ] =?> doc (codeBlockWith ( "" , ["python", "numberLines"] , [] ) "def func(x):\n return y") , testGroup "literal / line / code blocks" [ "indented literal block" =: T.unlines [ "::" , "" , " block quotes" , "" , " can go on for many lines" , "but must stop here"] =?> doc ( codeBlock "block quotes\n\ncan go on for many lines" <> para "but must stop here") , "line block with 3 lines" =: "| a\n| b\n| c" =?> lineBlock ["a", "b", "c"] , "line blocks with blank lines" =: T.unlines [ "|" , "" , "|" , "| a" , "| b" , "|" , "" , "|" ] =?> lineBlock [""] <> lineBlock ["", "a", "b", ""] <> lineBlock [""] , "quoted literal block using >" =: "::\n\n> quoted\n> block\n\nOrdinary paragraph" =?> codeBlock "> quoted\n> block" <> para "Ordinary paragraph" , "quoted literal block using | (not a line block)" =: "::\n\n| quoted\n| block\n\nOrdinary paragraph" =?> codeBlock "| quoted\n| block" <> para "Ordinary paragraph" , "class directive with single paragraph" =: ".. class:: special\n\nThis is a \"special\" paragraph." =?> divWith ("", ["special"], []) (para "This is a \"special\" paragraph.") , "class directive with two paragraphs" =: ".. class:: exceptional remarkable\n\n First paragraph.\n\n Second paragraph." =?> divWith ("", ["exceptional", "remarkable"], []) (para "First paragraph." <> para "Second paragraph.") , "class directive around literal block" =: ".. class:: classy\n\n::\n\n a\n b" =?> divWith ("", ["classy"], []) (codeBlock "a\nb")] , testGroup "interpreted text roles" [ "literal role prefix" =: ":literal:`a`" =?> para (code "a") , "literal role postfix" =: "`a`:literal:" =?> para (code "a") , "literal text" =: "``text``" =?> para (code "text") , "code role" =: ":code:`a`" =?> para (codeWith ("", [], []) "a") , "inherited code role" =: ".. role:: codeLike(code)\n\n:codeLike:`a`" =?> para (codeWith ("", ["codeLike"], []) "a") , "custom code role with language field" =: ".. role:: lhs(code)\n :language: haskell\n\n:lhs:`a`" =?> para (codeWith ("", ["lhs", "haskell"], []) "a") , "custom role with class field" =: ".. role:: classy\n :class: myclass\n\n:classy:`a`" =?> para (spanWith ("", ["myclass"], []) "a") , "custom role with class field containing multiple whitespace-separated classes" =: ".. role:: classy\n :class: myclass1 myclass2\n myclass3\n\n:classy:`a`" =?> para (spanWith ("", ["myclass1", "myclass2", "myclass3"], []) "a") , "custom role with inherited class field" =: ".. role:: classy\n :class: myclass1\n.. role:: classier(classy)\n :class: myclass2\n\n:classier:`a`" =?> para (spanWith ("", ["myclass2", "myclass1"], []) "a") , "custom role with unspecified parent role" =: ".. role:: classy\n\n:classy:`text`" =?> para (spanWith ("", ["classy"], []) "text") , "role with recursive inheritance" =: ".. role:: haskell(code)\n.. role:: lhs(haskell)\n\n:lhs:`text`" =?> para (codeWith ("", ["lhs", "haskell"], []) "text") , "unknown role" =: ":unknown:`text`" =?> para (codeWith ("",["interpreted-text"],[("role","unknown")]) "text") ] , testGroup "footnotes" [ "remove space before note" =: T.unlines [ "foo [1]_" , "" , ".. [1]" , " bar" ] =?> para ("foo" <> note (para "bar")) ] , testGroup "inlines" [ "links can contain an URI without being parsed twice (#4581)" =: "`http://loc `__" =?> para (link "http://loc" "" "http://loc") , "inline markup cannot be nested" =: "**a*b*c**" =?> para (strong "a*b*c") , "bare URI parsing disabled inside emphasis (#4561)" =: "*http://location*" =?> para (emph (text "http://location")) , "include newlines" =: "**before\nafter**" =?> para (strong (text "before\nafter")) ] ] ================================================ FILE: test/Tests/Readers/RTF.hs ================================================ {- | Module : Tests.Readers.RTF Copyright : © 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : jgm@berkeley.edu Stability : alpha Portability : portable Tests for the RTF reader. -} module Tests.Readers.RTF (tests) where import Test.Tasty import Tests.Helpers import Text.Pandoc import System.FilePath (replaceExtension, (), (<.>)) rtfTest :: TestName -> TestTree rtfTest name = testGolden name native path (\t -> runIOorExplode (readRTF def t >>= writeNative def{ writerTemplate = Just mempty })) where native = replaceExtension path ".native" path = "rtf" name <.> "rtf" tests :: [TestTree] tests = map rtfTest [ "footnote" , "accent" , "unicode" , "image" , "link" , "heading" , "formatting" , "list_simple" , "list_complex" , "bookmark" , "table_simple" , "table_error_codes" ] ================================================ FILE: test/Tests/Readers/Txt2Tags.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Txt2Tags Copyright : © 2014-2024 John MacFarlane, © 2014 Matthew Pickering License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Tests for the Txt2Tags reader. -} module Tests.Readers.Txt2Tags (tests) where import Data.List (intersperse) import Data.Text (Text) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder t2t :: Text -> Pandoc -- t2t = handleError . readTxt2Tags (T2TMeta "date" "mtime" "in" "out") def t2t = purely $ \s -> do setInputFiles ["in"] setOutputFile (Just "out") readTxt2Tags def s infix 4 =: (=:) :: (ToString c, HasCallStack) => String -> (Text, c) -> TestTree (=:) = test t2t spcSep :: [Inlines] -> Inlines spcSep = mconcat . intersperse space simpleTable' :: Int -> [Blocks] -> [[Blocks]] -> Blocks simpleTable' n = simpleTable'' $ replicate n (AlignCenter, ColWidthDefault) simpleTable'' :: [ColSpec] -> [Blocks] -> [[Blocks]] -> Blocks simpleTable'' spec headers rows = table emptyCaption spec (TableHead nullAttr $ toHeaderRow headers) [TableBody nullAttr 0 [] $ map toRow rows] (TableFoot nullAttr []) where toRow = Row nullAttr . map simpleCell toHeaderRow l = [toRow l | not (null l)] tests :: [TestTree] tests = [ testGroup "Inlines" [ "Plain String" =: "Hello, World" =?> para (spcSep [ "Hello,", "World" ]) , "Emphasis" =: "//Planet Punk//" =?> para (emph . spcSep $ ["Planet", "Punk"]) , "Strong" =: "**Cider**" =?> para (strong "Cider") , "Strong Emphasis" =: "//**strength**//" =?> para (emph . strong $ "strength") , "Strikeout" =: "--Kill Bill--" =?> para (strikeout . spcSep $ [ "Kill", "Bill" ]) , "Verbatim" =: "``Robot.rock()``" =?> para (code "Robot.rock()") , "Symbol" =: "A * symbol" =?> para (str "A" <> space <> str "*" <> space <> "symbol") , "No empty markup" =: "//// **** ____ ---- ```` \"\"\"\" ''''" =?> para (spcSep [ "////", "****", "____", "----", "````", "\"\"\"\"", "''''" ]) , "Inline markup is greedy" =: "***** ///// _____ ----- ````` \"\"\"\"\" '''''" =?> para (spcSep [strong "*", emph "/", underline "_" , strikeout "-", code "`", text "\"" , rawInline "html" "'"]) , "Markup must be greedy" =: "********** ////////// __________ ---------- `````````` \"\"\"\"\"\"\"\"\"\" ''''''''''" =?> para (spcSep [strong "******", emph "//////", underline "______" , strikeout "------", code "``````", text "\"\"\"\"\"\"" , rawInline "html" "''''''"]) , "Inlines must be glued" =: "** a** **a ** ** a **" =?> para (text "** a** **a ** ** a **") , "Macros: Date" =: "%%date" =?> para "1970-01-01" , "Macros: Mod Time" =: "%%mtime" =?> para (str "") , "Macros: Infile" =: "%%infile" =?> para "in" , "Macros: Outfile" =: "%%outfile" =?> para "out" , "Autolink" =: "http://www.google.com" =?> para (link "http://www.google.com" "" (str "http://www.google.com")) , "JPEG Image" =: "[image.jpg]" =?> para (image "image.jpg" "" mempty) , "PNG Image" =: "[image.png]" =?> para (image "image.png" "" mempty) , "Link" =: "[title http://google.com]" =?> para (link "http://google.com" "" (str "title")) , "Image link" =: "[[image.jpg] abc]" =?> para (link "abc" "" (image "image.jpg" "" mempty)) , "Invalid link: No trailing space" =: "[title invalid ]" =?> para (text "[title invalid ]") ] , testGroup "Basic Blocks" ["Paragraph, lines grouped together" =: "A paragraph\n A blank line ends the \n current paragraph\n" =?> para "A paragraph\n A blank line ends the\n current paragraph" , "Paragraph, ignore leading and trailing spaces" =: " Leading and trailing spaces are ignored. \n" =?> para "Leading and trailing spaces are ignored." , "Comment line in paragraph" =: "A comment line can be placed inside a paragraph.\n% this comment will be ignored \nIt will not affect it.\n" =?> para "A comment line can be placed inside a paragraph.\nIt will not affect it." , "Paragraph" =: "Paragraph\n" =?> para "Paragraph" , "First Level Header" =: "+ Headline +\n" =?> header 1 "Headline" , "Third Level Header" =: "=== Third Level Headline ===\n" =?> header 3 ("Third" <> space <> "Level" <> space <> "Headline") , "Header with label" =: "= header =[label]" =?> headerWith ("label", [], []) 1 "header" , "Invalid header, mismatched delimiters" =: "== header =" =?> para (text "== header =") , "Invalid header, spaces in label" =: "== header ==[ haha ]" =?> para (text "== header ==[ haha ]") , "Invalid header, invalid label character" =: "== header ==[lab/el]" =?> para (text "== header ==[lab/el]") , "Headers not preceded by a blank line" =: T.unlines [ "++ eat dinner ++" , "Spaghetti and meatballs tonight." , "== walk dog ==" ] =?> mconcat [ header 2 ("eat" <> space <> "dinner") , para $ spcSep [ "Spaghetti", "and", "meatballs", "tonight." ] , header 2 ("walk" <> space <> "dog") ] , "Paragraph starting with an equals" =: "=five" =?> para "=five" , "Paragraph containing asterisk at beginning of line" =: T.unlines [ "lucky" , "*star" ] =?> para ("lucky" <> softbreak <> "*star") , "Horizontal Rule" =: T.unlines [ "before" , T.replicate 20 "-" , T.replicate 20 "=" , T.replicate 20 "_" , "after" ] =?> mconcat [ para "before" , horizontalRule , horizontalRule , horizontalRule , para "after" ] , "Comment Block" =: T.unlines [ "%%%" , "stuff" , "bla" , "%%%"] =?> (mempty::Blocks) ] , testGroup "Lists" [ "Simple Bullet Lists" =: ("- Item1\n" <> "- Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" ] , "Indented Bullet Lists" =: (" - Item1\n" <> " - Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" ] , "Nested Bullet Lists" =: ("- Discovery\n" <> " + One More Time\n" <> " + Harder, Better, Faster, Stronger\n" <> "- Homework\n" <> " + Around the World\n"<> "- Human After All\n" <> " + Technologic\n" <> " + Robot Rock\n") =?> bulletList [ mconcat [ plain "Discovery" , orderedList [ plain ("One" <> space <> "More" <> space <> "Time") , plain ("Harder," <> space <> "Better," <> space <> "Faster," <> space <> "Stronger") ] ] , mconcat [ plain "Homework" , orderedList [ plain ("Around" <> space <> "the" <> space <> "World") ] ] , mconcat [ plain ("Human" <> space <> "After" <> space <> "All") , orderedList [ plain "Technologic" , plain ("Robot" <> space <> "Rock") ] ] ] , "Simple Ordered List" =: ("+ Item1\n" <> "+ Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , "Indented Ordered List" =: (" + Item1\n" <> " + Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" , plain "Item2" ] in orderedListWith listStyle listStructure , "Nested Ordered Lists" =: ("+ One\n" <> " + One-One\n" <> " + One-Two\n" <> "+ Two\n" <> " + Two-One\n"<> " + Two-Two\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ mconcat [ plain "One" , orderedList [ plain "One-One" , plain "One-Two" ] ] , mconcat [ plain "Two" , orderedList [ plain "Two-One" , plain "Two-Two" ] ] ] in orderedListWith listStyle listStructure , "Ordered List in Bullet List" =: ("- Emacs\n" <> " + Org\n") =?> bulletList [ plain "Emacs" <> orderedList [ plain "Org"] ] , "Bullet List in Ordered List" =: ("+ GNU\n" <> " - Freedom\n") =?> orderedList [ plain "GNU" <> bulletList [ plain "Freedom" ] ] , "Definition List" =: T.unlines [ ": PLL" , " phase-locked loop" , ": TTL" , " transistor-transistor logic" , ": PSK" , " a digital" ] =?> definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ]) , ("TTL", [ plain $ "transistor-transistor" <> space <> "logic" ]) , ("PSK", [ plain $ "a" <> space <> "digital" ]) ] , "Loose bullet list" =: T.unlines [ "- apple" , "" , "- orange" , "" , "- peach" ] =?> bulletList [ para "apple" , para "orange" , para "peach" ] ] , testGroup "Tables" [ "Single cell table" =: "| Test " =?> simpleTable' 1 mempty [[plain "Test"]] , "Multi cell table" =: "| One | Two |" =?> simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] , "Multi line table" =: T.unlines [ "| One |" , "| Two |" , "| Three |" ] =?> simpleTable' 1 mempty [ [ plain "One" ] , [ plain "Two" ] , [ plain "Three" ] ] , "Empty table" =: "| |" =?> simpleTable' 1 mempty [[mempty]] , "Glider Table" =: T.unlines [ "| 1 | 0 | 0 |" , "| 0 | 1 | 1 |" , "| 1 | 1 | 0 |" ] =?> simpleTable' 3 mempty [ [ plain "1", plain "0", plain "0" ] , [ plain "0", plain "1", plain "1" ] , [ plain "1", plain "1", plain "0" ] ] , "Table with Header" =: T.unlines [ "|| Species | Status |" , "| cervisiae | domesticated |" , "| paradoxus | wild |" ] =?> simpleTable [ plain "Species", plain "Status" ] [ [ plain "cervisiae", plain "domesticated" ] , [ plain "paradoxus", plain "wild" ] ] , "Table alignment determined by spacing" =: T.unlines [ "| Numbers | Text | More |" , "| 1 | One | foo |" , "| 2 | Two | bar |" ] =?> simpleTable'' (zip [AlignCenter, AlignRight, AlignDefault] [ColWidthDefault, ColWidthDefault, ColWidthDefault]) [] [ [ plain "Numbers", plain "Text", plain "More" ] , [ plain "1" , plain "One" , plain "foo" ] , [ plain "2" , plain "Two" , plain "bar" ] ] , "Pipe within text doesn't start a table" =: "Ceci n'est pas une | pipe " =?> para (spcSep [ "Ceci", "n'est", "pas", "une", "|", "pipe" ]) , "Table with differing row lengths" =: T.unlines [ "|| Numbers | Text " , "| 1 | One | foo |" , "| 2 " ] =?> simpleTable'' (zip [AlignCenter, AlignLeft, AlignLeft] [ColWidthDefault, ColWidthDefault, ColWidthDefault]) [ plain "Numbers", plain "Text" , plain mempty ] [ [ plain "1" , plain "One" , plain "foo" ] , [ plain "2" , plain mempty , plain mempty ] ] ] , testGroup "Blocks and fragments" [ "Source block" =: T.unlines [ "```" , "main = putStrLn greeting" , " where greeting = \"moin\"" , "```" ] =?> let code' = "main = putStrLn greeting\n" <> " where greeting = \"moin\"\n" in codeBlock code' , "tagged block" =: T.unlines [ "'''" , "" , "'''" ] =?> rawBlock "html" "\n" , "Quote block" =: T.unlines ["\t//Niemand// hat die Absicht, eine Mauer zu errichten!" ] =?> blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht," , "eine", "Mauer", "zu", "errichten!" ])) ] ] ================================================ FILE: test/Tests/Readers/Xlsx.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Readers.Xlsx Copyright : © 2025 Anton Antic License : GNU GPL, version 2 or above Maintainer : Anton Antic Stability : alpha Portability : portable Tests for the XLSX reader. -} module Tests.Readers.Xlsx (tests) where import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Test.Tasty import Test.Tasty.Golden.Advanced import Tests.Helpers import Text.Pandoc import Text.Pandoc.UTF8 as UTF8 defopts :: ReaderOptions defopts = def{ readerExtensions = getDefaultExtensions "xlsx" } testCompare :: String -> FilePath -> FilePath -> TestTree testCompare = testCompareWithOpts defopts testCompareWithOpts :: ReaderOptions -> String -> FilePath -> FilePath -> TestTree testCompareWithOpts opts testName xlsxFP nativeFP = goldenTest testName (do nf <- UTF8.toText <$> BS.readFile nativeFP runIOorExplode (readNative def nf)) (do df <- B.readFile xlsxFP runIOorExplode (readXlsx opts df)) (nativeDiff nativeFP) (\a -> runIOorExplode (writeNative def{ writerTemplate = Just mempty} a) >>= BS.writeFile nativeFP . UTF8.fromText) tests :: [TestTree] tests = [ testGroup "basic" [ testCompare "sheet extraction" "xlsx-reader/basic.xlsx" "xlsx-reader/basic.native" ] ] ================================================ FILE: test/Tests/Shared.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Shared Copyright : © 2006-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Tests for functions used in many parts of the library. -} module Tests.Shared (tests) where import System.FilePath.Posix (joinPath) import Test.Tasty import Text.Pandoc.Arbitrary () import Test.Tasty.QuickCheck (testProperty) import Text.Pandoc.Builder import Text.Pandoc.Shared import Test.Tasty.HUnit import Tests.Helpers import Text.Pandoc import Text.Pandoc.Writers.Shared import qualified Data.Text as T tests :: [TestTree] tests = [ testGroup "compactifyDL" [ testCase "compactifyDL with empty def" $ assertBool "compactifyDL" (let x = [(str "word", [para (str "def"), mempty])] in compactifyDL x == x) ] , testGroup "collapseFilePath" testCollapse , testGroup "toLegacyTable" testLegacyTable , testGroup "table of contents" testTOC , testGroup "makeSections" [ testProperty "makeSections is idempotent" makeSectionsIsIdempotent , testCase "makeSections is idempotent for test case" $ let d = header 1 "H1" <> header 2 "H2" <> header 3 "H3" <> header 2 "H2a" <> header 4 "H4" <> header 1 "H1a" d' = makeSections False Nothing $ toList d in assertBool "makeSections is idempotent for test case" (makeSections False Nothing d' == d') ] ] makeSectionsIsIdempotent :: [Block] -> Bool makeSectionsIsIdempotent d = let d' = makeSections False Nothing d in d' == makeSections False Nothing d' givesTOC :: String -> (Blocks, Blocks) -> TestTree givesTOC desc (blocks, toc) = test (singleton . toTableOfContents def) desc (toList blocks, toc) linkId :: T.Text -> T.Text -> T.Text -> Inlines -> Inlines linkId lId = linkWith (lId,[],[]) headerId :: T.Text -> Int -> Inlines -> Blocks headerId hId = headerWith (hId,[],[]) testTOC :: [TestTree] testTOC = [ givesTOC "empty case" $ mempty =?> bulletList [] , givesTOC "no headers" $ horizontalRule =?> bulletList [] , givesTOC "unlinked header" $ header 1 "H1" =?> bulletList [plain "H1"] , givesTOC "linked header" $ headerId "h1" 1 "H1" =?> bulletList [plain $ linkId "toc-h1" "#h1" "" "H1"] , givesTOC "nested headlines" $ header 1 "H1a" <> header 2 "H2" =?> bulletList [plain "H1a" <> bulletList [plain "H2"]] , givesTOC "only referenced headers" $ header 1 "H1a" <> headerId "h2" 2 "H2" =?> bulletList [plain "H1a" <> bulletList [plain $ linkId "toc-h2" "#h2" "" "H2"]] , givesTOC "section id used as backup" $ divWith ("sec",["section"],[]) (header 1 "H1") =?> bulletList [plain $ linkId "toc-sec" "#sec" "" "H1"] ] testCollapse :: [TestTree] testCollapse = map (testCase "collapse") [ collapseFilePath (joinPath [ ""]) @?= joinPath [ ""] , collapseFilePath (joinPath [ ".","foo"]) @?= joinPath [ "foo"] , collapseFilePath (joinPath [ ".",".","..","foo"]) @?= joinPath [ joinPath ["..", "foo"]] , collapseFilePath (joinPath [ "..","foo"]) @?= joinPath [ "..","foo"] , collapseFilePath (joinPath [ "","bar","..","baz"]) @?= joinPath [ "","baz"] , collapseFilePath (joinPath [ "","..","baz"]) @?= joinPath [ "","..","baz"] , collapseFilePath (joinPath [ ".","foo","..",".","bar","..",".",".","baz"]) @?= joinPath [ "baz"] , collapseFilePath (joinPath [ ".",""]) @?= joinPath [ ""] , collapseFilePath (joinPath [ ".",".",""]) @?= joinPath [ ""] , collapseFilePath (joinPath [ "..",""]) @?= joinPath [ ".."] , collapseFilePath (joinPath [ "..",".",""]) @?= joinPath [ ".."] , collapseFilePath (joinPath [ ".","..",""]) @?= joinPath [ ".."] , collapseFilePath (joinPath [ "..","..",""]) @?= joinPath [ "..",".."] , collapseFilePath (joinPath [ "parent","foo","baz","..","bar"]) @?= joinPath [ "parent","foo","bar"] , collapseFilePath (joinPath [ "parent","foo","baz","..","..","bar"]) @?= joinPath [ "parent","bar"] , collapseFilePath (joinPath [ "parent","foo",".."]) @?= joinPath [ "parent"] , collapseFilePath (joinPath [ "","parent","foo","..","..","bar"]) @?= joinPath [ "","bar"] , collapseFilePath (joinPath [ "",".","parent","foo"]) @?= joinPath [ "","parent","foo"]] testLegacyTable :: [TestTree] testLegacyTable = [ testCase "decomposes a table with head" $ gen1 @?= expect1 , testCase "decomposes a table without head" $ gen2 @?= expect2 , testCase "decomposes the table from issue 7683" $ gen3 @?= expect3 ] where pln = toList . plain . str cl a h w = Cell ("", [], []) AlignDefault h w $ pln a rws = map $ Row nullAttr th = TableHead nullAttr . rws tb n x y = TableBody nullAttr n (rws x) (rws y) tf = TableFoot nullAttr . rws headRows1 = [[cl "a" 1 1, cl "b" 2 2] ,[cl "c" 1 1] ] body1 = tb 1 [[cl "e" 3 1,cl "f" 3 2] ,[] ,[] ] [[emptyCell,emptyCell,emptyCell] ,[cl "g" 1 1,emptyCell,emptyCell] ] footRows1 = [[cl "h" 1 2,cl "i" 2 1] ,[cl "j" 1 2]] caption1 = simpleCaption $ plain "caption" spec1 = replicate 2 (AlignDefault, ColWidth 0.5) ++ [(AlignRight, ColWidthDefault)] expect1 = ( [Str "caption"] , replicate 2 AlignDefault ++ [AlignRight] , replicate 2 0.5 ++ [0] , [pln "a", pln "b", mempty] , [[pln "c", mempty, mempty] ,[pln "e", pln "f", mempty] ,[mempty, mempty, mempty] ,[mempty, mempty, mempty] ,[mempty, mempty, mempty] ,[pln "g", mempty, mempty] ,[pln "h", mempty, pln "i"] ,[pln "j", mempty, mempty]] ) gen1 = toLegacyTable caption1 spec1 (th headRows1) [body1] (tf footRows1) expect2 = ( [] , replicate 2 AlignDefault ++ [AlignRight] , replicate 2 0.5 ++ [0] , [] , [[pln "e", pln "f", mempty] ,[mempty, mempty, mempty] ,[mempty, mempty, mempty] ,[mempty, mempty, mempty] ,[pln "g", mempty, mempty] ,[pln "h", mempty, pln "i"] ,[pln "j", mempty, mempty]] ) gen2 = toLegacyTable emptyCaption spec1 (th []) [body1] (tf footRows1) spec3 = replicate 4 (AlignDefault, ColWidthDefault) body3 = tb 0 [] [[cl "a" 2 1, cl "b" 1 2, cl "c" 2 1] ,[cl "d" 1 1, cl "e" 1 1] ] expect3 = ( [] , replicate 4 AlignDefault , replicate 4 0 , [] , [[pln "a", pln "b", mempty, pln "c"] ,[mempty, pln "d", pln "e", mempty]] ) gen3 = toLegacyTable emptyCaption spec3 (th []) [body3] (tf []) ================================================ FILE: test/Tests/Writers/AnnotatedTable.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {- | Module : Tests.Writers.AnnotatedTable Copyright : 2020 Christian Despres License : GNU GPL, version 2 or above Maintainer : Christian Despres Stability : alpha Portability : portable Tests for the table helper functions. -} module Tests.Writers.AnnotatedTable ( tests ) where import Prelude import qualified Data.Foldable as F import qualified Data.List.NonEmpty as NonEmpty import Test.Tasty import Test.Tasty.HUnit ( testCase , (@?=) ) import Test.Tasty.QuickCheck ( QuickCheckTests(..) , Property , Testable , conjoin , forAll , testProperty , (===) , vectorOf , choose , arbitrary , elements ) import Text.Pandoc.Arbitrary ( ) import Text.Pandoc.Builder import qualified Text.Pandoc.Writers.AnnotatedTable as Ann tests :: [TestTree] tests = [testGroup "toTable" $ testAnnTable <> annTableProps] getSpec :: Ann.Cell -> [ColSpec] getSpec (Ann.Cell colspec _ _) = F.toList colspec catHeaderSpec :: Ann.HeaderRow -> [ColSpec] catHeaderSpec (Ann.HeaderRow _ _ x) = concatMap getSpec x catBodySpec :: Ann.BodyRow -> [ColSpec] catBodySpec (Ann.BodyRow _ _ x y) = concatMap getSpec x <> concatMap getSpec y -- Test if the first list can be obtained from the second by deleting -- elements from it. isSubsetOf :: Eq a => [a] -> [a] -> Bool isSubsetOf (x : xs) (y : ys) | x == y = isSubsetOf xs ys | otherwise = isSubsetOf (x : xs) ys isSubsetOf [] _ = True isSubsetOf _ [] = False testAnnTable :: [TestTree] testAnnTable = [testCase "annotates a sample table properly" $ generated @?= expected] where spec1 = (AlignRight, ColWidthDefault) spec2 = (AlignLeft, ColWidthDefault) spec3 = (AlignCenter, ColWidthDefault) spec = [spec1, spec2, spec3] cl a h w = Cell (a, [], []) AlignDefault h w [] rws = map $ Row nullAttr th = TableHead nullAttr . rws tb n x y = TableBody nullAttr n (rws x) (rws y) tf = TableFoot nullAttr . rws initialHeads = [[cl "a" 1 1, cl "b" 3 2], [cl "c" 2 2, cl "d" 1 1]] initialTB1 = tb 1 [[], [cl "e" 5 1, cl "f" (-7) 0]] [[cl "g" 4 3, cl "h" 4 3], [], [emptyCell]] initialTB2 = tb 2 [] [[cl "i" 4 3, cl "j" 4 3]] generated = Ann.toTable nullAttr emptyCaption spec (th initialHeads) [initialTB1, initialTB2] (tf initialHeads) acl al n a h w = Ann.Cell (NonEmpty.fromList al) n $ Cell (a, [], []) AlignDefault h w [] emptyAnnCell al n = acl al n "" 1 1 ahrw = Ann.HeaderRow nullAttr abrw = Ann.BodyRow nullAttr ath = Ann.TableHead nullAttr atb = Ann.TableBody nullAttr atf = Ann.TableFoot nullAttr finalTH = ath [ ahrw 0 [acl [spec1] 0 "a" 1 1, acl [spec2, spec3] 1 "b" 2 2] , ahrw 1 [acl [spec1] 0 "c" 1 1] ] finalTB1 = atb 1 [ ahrw 2 [emptyAnnCell [spec1] 0, emptyAnnCell [spec2] 1, emptyAnnCell [spec3] 2] , ahrw 3 [acl [spec1] 0 "e" 1 1, acl [spec2] 1 "f" 1 1, emptyAnnCell [spec3] 2] ] [ abrw 4 [acl [spec1] 0 "g" 3 1] [acl [spec2, spec3] 1 "h" 3 2] , abrw 5 [] [] , abrw 6 [] [] ] finalTB2 = atb 2 [] [abrw 7 [acl [spec1, spec2] 0 "i" 1 2] [acl [spec3] 2 "j" 1 1]] finalTF = atf [ ahrw 8 [acl [spec1] 0 "a" 1 1, acl [spec2, spec3] 1 "b" 2 2] , ahrw 9 [acl [spec1] 0 "c" 1 1] ] expected = Ann.Table nullAttr emptyCaption spec finalTH [finalTB1, finalTB2] finalTF withColSpec :: Testable prop => ([ColSpec] -> prop) -> Property withColSpec = forAll arbColSpec where arbColSpec = do cs <- choose (1 :: Int, 6) vectorOf cs ((,) <$> arbitrary <*> elements [ColWidthDefault, ColWidth (1 / 3), ColWidth 0.25] ) annTableProps :: [TestTree] annTableProps = localOption (QuickCheckTests 50) <$> [ testProperty "normalizes like the table builder" propBuilderAnnTable , testProperty "has valid final cell columns" propColNumber , testProperty "has valid first row column data" propFirstRowCols , testProperty "has valid all row column data" propColSubsets , testProperty "has valid cell column data lengths" propCellColLengths ] -- The property that Ann.toTable will normalize a table identically to -- the table builder. This should mean that Ann.toTable is at least as -- rigorous as Builder.table in that respect without repeating those -- tests here (see the pandoc-types Table tests for examples). propBuilderAnnTable :: TableHead -> [TableBody] -> TableFoot -> Property propBuilderAnnTable th tbs tf = withColSpec $ \cs -> convertTable (table emptyCaption cs th tbs tf) === convertAnnTable (Ann.toTable nullAttr emptyCaption cs th tbs tf) where convertTable blks = case toList blks of [Table _ _ colspec a b c] -> Right (colspec, a, b, c) x -> Left x convertAnnTable x = case Ann.fromTable x of (_, _, colspec, a, b, c) -> Right (colspec, a, b, c) -- The property of Ann.toTable that if the last cell in the first row -- of a table section has ColSpan w and ColNumber n, then w + n is the -- width of the table. propColNumber :: TableHead -> [TableBody] -> TableFoot -> Property propColNumber th tbs tf = withColSpec $ \cs -> let twidth = length cs Ann.Table _ _ _ ath atbs atf = Ann.toTable nullAttr emptyCaption cs th tbs tf in conjoin $ [colNumTH twidth ath] <> (colNumTB twidth <$> atbs) <> [colNumTF twidth atf] where colNumTH n (Ann.TableHead _ rs) = firstly (isHeaderValid n) rs colNumTB n (Ann.TableBody _ _ rs ts) = firstly (isHeaderValid n) rs && firstly (isBodyValid n) ts colNumTF n (Ann.TableFoot _ rs) = firstly (isHeaderValid n) rs isHeaderValid n (Ann.HeaderRow _ _ x) = isSegmentValid n x isBodyValid n (Ann.BodyRow _ _ _ x) = isSegmentValid n x firstly f (x : _) = f x firstly _ [] = True lastly f [x ] = f x lastly f (_ : xs) = lastly f xs lastly _ [] = True isSegmentValid twidth cs = flip lastly cs $ \(Ann.Cell _ (Ann.ColNumber n) (Cell _ _ _ (ColSpan w) _)) -> n + w == twidth -- The property of an Ann.Table from Ann.toTable that if the NonEmpty -- ColSpec data of the cells in the first row of a table section are -- concatenated, the result should equal the [ColSpec] of the entire -- table. propFirstRowCols :: TableHead -> [TableBody] -> TableFoot -> Property propFirstRowCols th tbs tf = withColSpec $ \cs -> let Ann.Table _ _ _ ath atbs atf = Ann.toTable nullAttr emptyCaption cs th tbs tf in conjoin $ [firstRowTH cs ath] <> (firstRowTB cs <$> atbs) <> [firstRowTF cs atf] where firstly f (x : _) = f x firstly _ [] = True firstHeaderValid cs = firstly $ \r -> cs == catHeaderSpec r firstBodyValid cs = firstly $ \r -> cs == catBodySpec r firstRowTH cs (Ann.TableHead _ rs) = firstHeaderValid cs rs firstRowTB cs (Ann.TableBody _ _ rs ts) = firstHeaderValid cs rs && firstBodyValid cs ts firstRowTF cs (Ann.TableFoot _ rs) = firstHeaderValid cs rs -- The property that in any row in an Ann.Table from Ann.toTable, the -- NonEmpty ColSpec annotations on cells, when concatenated, form a -- subset (really sublist) of the [ColSpec] of the entire table. propColSubsets :: TableHead -> [TableBody] -> TableFoot -> Property propColSubsets th tbs tf = withColSpec $ \cs -> let Ann.Table _ _ _ ath atbs atf = Ann.toTable nullAttr emptyCaption cs th tbs tf in conjoin $ subsetTH cs ath <> concatMap (subsetTB cs) atbs <> subsetTF cs atf where subsetTH cs (Ann.TableHead _ rs) = map (subsetHeader cs) rs subsetTB cs (Ann.TableBody _ _ rs ts) = map (subsetHeader cs) rs <> map (subsetBody cs) ts subsetTF cs (Ann.TableFoot _ rs) = map (subsetHeader cs) rs subsetHeader cs r = catHeaderSpec r `isSubsetOf` cs subsetBody cs r = catBodySpec r `isSubsetOf` cs -- The property that in any cell in an Ann.Table from Ann.toTable, the -- NonEmpty ColSpec annotation on a cell is equal in length to its -- ColSpan. propCellColLengths :: TableHead -> [TableBody] -> TableFoot -> Property propCellColLengths th tbs tf = withColSpec $ \cs -> let Ann.Table _ _ _ ath atbs atf = Ann.toTable nullAttr emptyCaption cs th tbs tf in conjoin $ cellColTH ath <> concatMap cellColTB atbs <> cellColTF atf where cellColTH (Ann.TableHead _ rs) = concatMap cellColHeader rs cellColTB (Ann.TableBody _ _ rs ts) = concatMap cellColHeader rs <> concatMap cellColBody ts cellColTF (Ann.TableFoot _ rs) = concatMap cellColHeader rs cellColHeader (Ann.HeaderRow _ _ x) = fmap validLength x cellColBody (Ann.BodyRow _ _ x y) = fmap validLength x <> fmap validLength y validLength (Ann.Cell colspec _ (Cell _ _ _ (ColSpan w) _)) = length colspec == w ================================================ FILE: test/Tests/Writers/AsciiDoc.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.AsciiDoc (tests) where import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder asciidoc :: (ToPandoc a) => a -> String asciidoc = unpack . purely (writeAsciiDocLegacy def) . toPandoc asciidoctor :: (ToPandoc a) => a -> String asciidoctor = unpack . purely (writeAsciiDoc def) . toPandoc testAsciidoc :: (ToString a, ToPandoc a) => String -> (a, String) -> TestTree testAsciidoc = test asciidoc testAsciidoctor :: (ToString a, ToPandoc a) => String -> (a, String) -> TestTree testAsciidoctor = test asciidoctor tests :: [TestTree] tests = [ testGroup "emphasis" [ testAsciidoc "emph word before" $ para (text "foo" <> emph (text "bar")) =?> "foo__bar__" , testAsciidoc "emph word after" $ para (emph (text "foo") <> text "bar") =?> "__foo__bar" , testAsciidoc "emph quoted" $ para (doubleQuoted (emph (text "foo"))) =?> "``_foo_''" , testAsciidoc "strong word before" $ para (text "foo" <> strong (text "bar")) =?> "foo**bar**" , testAsciidoc "strong word after" $ para (strong (text "foo") <> text "bar") =?> "**foo**bar" , testAsciidoc "strong quoted" $ para (singleQuoted (strong (text "foo"))) =?> "`*foo*'" ] , testGroup "blocks" [ testAsciidoc "code block without line numbers" $ codeBlockWith ("", [ "haskell" ], []) "foo" =?> unlines [ "[source,haskell]" , "----" , "foo" , "----" ] , testAsciidoc "code block with line numbers" $ codeBlockWith ("", [ "haskell", "numberLines" ], []) "foo" =?> unlines [ "[source%linesnum,haskell]" , "----" , "foo" , "----" ] , testAsciidoc "sidebar block" $ divWith ("sidebar_id", ["sidebar"], []) (divWith ("", ["title"], []) (plain "Sidebar Title") <> para "Sidebar paragraph" ) =?> unlines [ "[[sidebar_id]]" , "[SIDEBAR]" , ".Sidebar Title" , "****" , "Sidebar paragraph" , "****" ] ] , testGroup "tables" [ testAsciidoc "empty cells" $ simpleTable [] [[mempty],[mempty]] =?> unlines [ "[cols=\"\",]" , "|===" , "|" , "|" , "|===" ] , test asciidoc "multiblock cells" $ simpleTable [] [[para (text "Para 1") <> para (text "Para 2")]] =?> unlines [ "[cols=\"\",]" , "|===" , "a|" , "Para 1" , "" , "Para 2" , "" , "|===" ] ] , testGroup "lists" [ testAsciidoctor "bullet task list" $ bulletList [plain "☐ a", plain "☒ b"] =?> unlines [ "* [ ] a" , "* [x] b" ] ] ] ================================================ FILE: test/Tests/Writers/BBCode.hs ================================================ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} module Tests.Writers.BBCode (tests) where import Data.Maybe (isNothing) import Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Test.Tasty.QuickCheck import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder import Text.Pandoc.Shared (tshow) import Text.Read (readMaybe) bbcodeDefault , bbcodeSteam , bbcodePhpBB , bbcodeFluxBB , bbcodeHubzilla , bbcodeXenforo :: (ToPandoc a) => a -> Text bbcodeDefault = purely (writeBBCode def) . toPandoc bbcodeSteam = purely (writeBBCodeSteam def) . toPandoc bbcodePhpBB = purely (writeBBCodePhpBB def) . toPandoc bbcodeFluxBB = purely (writeBBCodeFluxBB def) . toPandoc bbcodeHubzilla = purely (writeBBCodeHubzilla def) . toPandoc bbcodeXenforo = purely (writeBBCodeXenforo def) . toPandoc infix 4 =:, `steam`, `phpbb`, `fluxbb`, `hubzilla`, `xenforo` (=:) , steam , phpbb , fluxbb , hubzilla , xenforo :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, Text) -> TestTree (=:) = test bbcodeDefault steam = test bbcodeSteam phpbb = test bbcodePhpBB fluxbb = test bbcodeFluxBB hubzilla = test bbcodeHubzilla xenforo = test bbcodeXenforo spanClasses :: [Text] -> Inlines -> Inlines spanClasses cls = spanWith ("", cls, []) spanAttrs :: [(Text, Text)] -> Inlines -> Inlines spanAttrs kvList = spanWith ("", [], kvList) divClasses :: [Text] -> Blocks -> Blocks divClasses cls = divWith ("", cls, []) divAttrs :: [(Text, Text)] -> Blocks -> Blocks divAttrs kvList = divWith ("", [], kvList) tests :: [TestTree] tests = [ testGroup "spans classes" [ "left" =: spanClasses ["left"] "foo" =?> "foo" , "center" =: spanClasses ["center"] "foo" =?> "foo" , "right" =: spanClasses ["right"] "foo" =?> "foo" , "spoiler" =: spanClasses ["spoiler"] "foo" =?> "foo" ] , testGroup "spans attributes" [ testProperty "incorrect size ignored" . property $ do n <- arbitrary @String let nInt = readMaybe @Int n let actual = bbcodeDefault (spanAttrs [("size", T.pack n)] "foo") pure $ isNothing nInt ==> actual === "foo" , testProperty "size<=0 ignored" . property $ do NonPositive n <- arbitrary @(NonPositive Int) let actual = bbcodeDefault (spanAttrs [("size", tshow n)] "foo") pure $ actual === "foo" , testProperty "size>0" . property $ do Positive n <- arbitrary @(Positive Int) let actual = bbcodeDefault (spanAttrs [("size", tshow n)] "foo") let expected = "[size=" <> tshow n <> "]" <> "foo[/size]" pure $ actual === expected , "size=20" =: spanAttrs [("size", "20")] "foo" =?> "[size=20]foo[/size]" , "color=#AAAAAA" =: spanAttrs [("color", "#AAAAAA")] "foo" =?> "[color=#AAAAAA]foo[/color]" , "spoiler ignored" =: spanAttrs [("spoiler", "name with spaces and ]brackets[]")] "foo" =?> "foo" ] , testGroup "divs classes" [ "left" =: divClasses ["left"] (para "foo") =?> "[left]foo[/left]" , "center" =: divClasses ["center"] (para "foo") =?> "[center]foo[/center]" , "right" =: divClasses ["right"] (para "foo") =?> "[right]foo[/right]" , "spoiler" =: divClasses ["spoiler"] (para "foo") =?> "[spoiler]foo[/spoiler]" ] , testGroup "divs attributes" [ testProperty "incorrect size ignored" . property $ do n <- arbitrary @String let nInt = readMaybe @Int n let actual = bbcodeDefault (divAttrs [("size", T.pack n)] $ para "foo") pure $ isNothing nInt ==> actual === "foo" , testProperty "size<=0 ignored" . property $ do NonPositive n <- arbitrary @(NonPositive Int) let actual = bbcodeDefault (divAttrs [("size", tshow n)] $ para "foo") pure $ actual === "foo" , testProperty "size>0" . property $ do Positive n <- arbitrary @(Positive Int) let actual = bbcodeDefault (divAttrs [("size", tshow n)] $ para "foo") let expected = "[size=" <> tshow n <> "]" <> "foo[/size]" pure $ actual === expected , "size=20" =: divAttrs [("size", "20")] (para "foo") =?> "[size=20]foo[/size]" , "color=#AAAAAA" =: divAttrs [("color", "#AAAAAA")] (para "foo") =?> "[color=#AAAAAA]foo[/color]" , "spoiler" =: divAttrs [("spoiler", "name with spaces and ]brackets[]")] (para "foo") =?> "[spoiler=name with spaces and brackets]foo[/spoiler]" ] , testGroup "default flavor" [ "link" =: link "https://example.com" "title" "label" =?> "[url=https://example.com]label[/url]" , "autolink" =: link "https://example.com" "title" "https://example.com" =?> "[url]https://example.com[/url]" , "email autolink" =: link "mailto:example@example.com" "title" "example@example.com" =?> "[email]example@example.com[/email]" , "named email" =: link "mailto:example@example.com" "title" "example email" =?> "[email=example@example.com]example email[/email]" , "h0" =: header 0 "heading 0" =?> "[u][b]heading 0[/b][/u]" , "h1" =: header 1 "heading 1" =?> "[u][b]heading 1[/b][/u]" , "h2" =: header 2 "heading 2" =?> "[b]heading 2[/b]" , "h3" =: header 3 "heading 3" =?> "[u]heading 3[/u]" , "h4" =: header 4 "heading 4" =?> "heading 4" , "h5" =: header 5 "heading 5" =?> "heading 5" ] , testGroup "steam" [ test bbcodeSteam "dename spoiler" $ divAttrs [("spoiler", "bar")] (para "foo") =?> ("[spoiler]foo[/spoiler]" :: Text) , testProperty "ordered list styleless" . property $ do let listItems = [para "foo", para "bar", para "baz"] attrsRand <- (,,) <$> arbitrary <*> arbitrary <*> arbitrary let actual = bbcodeSteam $ orderedListWith attrsRand listItems let expected = "[olist]\n[*]foo\n[*]bar\n[*]baz\n[/olist]" pure $ actual === expected , "h0" `steam` header 0 "heading 0" =?> "[h1]heading 0[/h1]" , "h1" `steam` header 1 "heading 1" =?> "[h1]heading 1[/h1]" , "h2" `steam` header 2 "heading 2" =?> "[h2]heading 2[/h2]" , "h3" `steam` header 3 "heading 3" =?> "[h3]heading 3[/h3]" , "h4" `steam` header 4 "heading 4" =?> "[h3]heading 4[/h3]" , "code" `steam` codeWith ("id", ["haskell"], []) "map (2^) [1..5]" =?> "[noparse]map (2^) [1..5][/noparse]" ] , testGroup "phpBB" [ "image" `phpbb` imageWith ("id", [], [("width", "100")]) "https://example.com" "title" "alt text" =?> "[img]https://example.com[/img]" ] , testGroup "FluxBB" [ "image" `fluxbb` imageWith ("id", [], [("width", "100")]) "https://example.com" "title" "alt text" =?> "[img=alt text]https://example.com[/img]" , testProperty "ordered list" . property $ do let listItems = [para "foo", para "bar", para "baz"] attrsRand <- (,,) <$> arbitrary <*> arbitrary <*> arbitrary let actual = bbcodeFluxBB $ orderedListWith attrsRand listItems let opening = case attrsRand of (_, LowerAlpha, _) -> "[list=a]" (_, UpperAlpha, _) -> "[list=a]" _ -> "[list=1]" let expected = opening <> "\n[*]foo\n[*]bar\n[*]baz\n[/list]" pure $ actual === expected , "ulist > BlockQuote not rendered" `fluxbb` bulletList [blockQuote (para "foo") <> para "bar"] =?> "[list]\n[*]bar\n[/list]" , "code block" `fluxbb` codeBlockWith ("id", ["haskell"], []) ( T.intercalate "\n" $ [ "vals =" , " take 10" , " . filter (\\x -> (x - 5) `mod` 3 == 0)" , " $ map (2 ^) [1 ..]" ] ) =?> T.intercalate "\n" [ "[code]vals =" , " take 10" , " . filter (\\x -> (x - 5) `mod` 3 == 0)" , " $ map (2 ^) [1 ..]" , "[/code]" ] ] , testGroup "Hubzilla" [ "unordered list" `hubzilla` bulletList [para "foo", para "bar", para "baz"] =?> "[ul]\n[*]foo\n[*]bar\n[*]baz\n[/ul]" , testProperty "ordered list" . property $ do let listItems = [para "foo", para "bar", para "baz"] attrsRand <- (,,) <$> arbitrary <*> arbitrary <*> arbitrary let actual = bbcodeHubzilla $ orderedListWith attrsRand listItems let (opening, closing) = case attrsRand of (_, Decimal, _) -> ("[list=1]", "[/list]") (_, DefaultStyle, _) -> ("[ol]", "[/ol]") (_, Example, _) -> ("[ol]", "[/ol]") (_, LowerAlpha, _) -> ("[list=a]", "[/list]") (_, UpperAlpha, _) -> ("[list=A]", "[/list]") (_, LowerRoman, _) -> ("[list=i]", "[/list]") (_, UpperRoman, _) -> ("[list=I]", "[/list]") let expected = opening <> "\n[*]foo\n[*]bar\n[*]baz\n" <> closing pure $ actual === expected , "definition list" `hubzilla` definitionList [ ("term_foo", [para "def_foo1", para "def_foo2"]) , ("term_bar", [para "def_bar1", para "def_bar2"]) , ("term_baz", [para "def_baz1", para "def_baz2"]) ] =?> mconcat [ "[dl terms=\"b\"]\n" , "[*= term_foo]\ndef_foo1\ndef_foo2\n" , "[*= term_bar]\ndef_bar1\ndef_bar2\n" , "[*= term_baz]\ndef_baz1\ndef_baz2\n" , "[/dl]" ] , "h0" `hubzilla` header 0 "heading 0" =?> "[h1]heading 0[/h1]" , "h1" `hubzilla` header 1 "heading 1" =?> "[h1]heading 1[/h1]" , "h2" `hubzilla` header 2 "heading 2" =?> "[h2]heading 2[/h2]" , "h3" `hubzilla` header 3 "heading 3" =?> "[h3]heading 3[/h3]" , "h4" `hubzilla` header 4 "heading 4" =?> "[h4]heading 4[/h4]" , "h5" `hubzilla` header 5 "heading 5" =?> "[h5]heading 5[/h5]" , "h6" `hubzilla` header 6 "heading 6" =?> "[h6]heading 6[/h6]" , "h7" `hubzilla` header 7 "heading 7" =?> "[h6]heading 7[/h6]" , "link" `hubzilla` link "https://example.com" "title" "label" =?> "[url=https://example.com]label[/url]" , "autolink" `hubzilla` link "https://example.com" "title" "https://example.com" =?> "[url]https://example.com[/url]" , "email autolink" `hubzilla` link "mailto:example@example.com" "title" "example@example.com" =?> "[url=mailto:example@example.com]example@example.com[/url]" , "named email" `hubzilla` link "mailto:example@example.com" "title" "example email" =?> "[url=mailto:example@example.com]example email[/url]" , "inline code" `hubzilla` ( "inline code: " <> codeWith ("id", ["haskell"], []) "map (2^) [1..5]" ) =?> "inline code: [code]map (2^) [1..5][/code]" , "font" `hubzilla` divAttrs [("font", "serif")] (para "foo") =?> "[font=serif]foo[/font]" ] , testGroup "xenForo" [ "unordered list" `xenforo` bulletList [para "foo", para "bar", para "baz"] =?> "[list]\n[*]foo\n[*]bar\n[*]baz\n[/list]" , testProperty "ordered list styleless" . property $ do let listItems = [para "foo", para "bar", para "baz"] attrsRand <- (,,) <$> arbitrary <*> arbitrary <*> arbitrary let actual = bbcodeXenforo $ orderedListWith attrsRand listItems let expected = "[list=1]\n[*]foo\n[*]bar\n[*]baz\n[/list]" pure $ actual === expected , "h0" `xenforo` header 0 "heading 0" =?> "[heading=1]heading 0[/heading]" , "h1" `xenforo` header 1 "heading 1" =?> "[heading=1]heading 1[/heading]" , "h2" `xenforo` header 2 "heading 2" =?> "[heading=2]heading 2[/heading]" , "h3" `xenforo` header 3 "heading 3" =?> "[heading=3]heading 3[/heading]" , "h4" `xenforo` header 4 "heading 4" =?> "[heading=4]heading 4[/heading]" , "link" `xenforo` link "https://example.com" "title" "label" =?> "[url=https://example.com]label[/url]" , "autolink" `xenforo` link "https://example.com" "title" "https://example.com" =?> "[url]https://example.com[/url]" , "email autolink" `xenforo` link "mailto:example@example.com" "title" "example@example.com" =?> "[email]example@example.com[/email]" , "named email" `xenforo` link "mailto:example@example.com" "title" "example email" =?> "[email=example@example.com]example email[/email]" , "inline code" `xenforo` ( "inline code: " <> codeWith ("id", ["haskell"], []) "map (2^) [1..5]" ) =?> "inline code: [icode]map (2^) [1..5][/icode]" , "font" `xenforo` divAttrs [("font", "serif")] (para "foo") =?> "[font=serif]foo[/font]" , "inline spoiler" `xenforo` ("It was " <> spanClasses ["spoiler"] ("DNS") <> "!") =?> "It was [ispoiler]DNS[/ispoiler]!" , "image w=50% h=50%" `xenforo` imageWith ("", [], [("width", "50%"), ("height", "50%")]) "https://example.com" "title text" "alt text" =?> "[img alt=\"alt text\" title=\"title text\" width=50%]https://example.com[/img]" , "image w=50 h=50" `xenforo` imageWith ("", [], [("width", "50"), ("height", "50")]) "https://example.com" "" "" =?> "[img]https://example.com[/img]" ] ] ================================================ FILE: test/Tests/Writers/ConTeXt.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.ConTeXt (tests) where import Data.Text (unpack, pack) import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Test.Tasty.QuickCheck import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder import qualified Data.Text as T context :: (ToPandoc a) => a -> String context = unpack . purely (writeConTeXt def) . toPandoc context' :: (ToPandoc a) => a -> String context' = unpack . purely (writeConTeXt def{ writerWrapText = WrapNone }) . toPandoc contextNtb :: (ToPandoc a) => a -> String contextNtb = unpack . purely (writeConTeXt def{ writerExtensions = enableExtension Ext_ntb pandocExtensions }) . toPandoc contextSection :: (ToPandoc a) => a -> String contextSection = unpack . purely (writeConTeXt def{ writerTopLevelDivision = TopLevelSection }) . toPandoc {- "my test" =: X =?> Y is shorthand for test context "my test" $ X =?> Y which is in turn shorthand for test context "my test" (X,Y) -} infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test context tests :: [TestTree] tests = [ testGroup "inline code" [ "with '}'" =: code "}" =?> "\\type\"}\"" , "without '}'" =: code "]" =?> "\\type{]}" , "span with ID" =: spanWith ("city", [], []) "Berlin" =?> "\\reference[city]{}Berlin" , testProperty "code property" $ \s -> null s || '\n' `elem` s || case T.stripPrefix "\\type" (pack $ context' (code $ pack s)) >>= T.uncons of Just (c, _) -> c `notElem` s Nothing -> False ] , testGroup "headers" [ "level 1" =: headerWith ("my-header",[],[]) 1 "My header" =?> "\\startsectionlevel[title={My header},reference={my-header}]\n" <> "\n" <> "\\stopsectionlevel" , test contextSection "Section as top-level" $ ( headerWith ("header1", [], []) 1 (text "Header1") <> headerWith ("header2", [], []) 2 (text "Header2") <> headerWith ("header3", [], []) 3 (text "Header3") <> headerWith ("header4", [], []) 4 (text "Header4") <> headerWith ("header5", [], []) 5 (text "Header5") <> headerWith ("header6", [], []) 6 (text "Header6")) =?> unlines [ "\\startsection[title={Header1},reference={header1}]\n" , "\\startsubsection[title={Header2},reference={header2}]\n" , "\\startsubsubsection[title={Header3},reference={header3}]\n" , "\\startsubsubsubsection[title={Header4},reference={header4}]\n" , "\\startsubsubsubsubsection[title={Header5},reference={header5}]\n" , "\\startsubsubsubsubsubsection[title={Header6},reference={header6}]\n" , "\\stopsubsubsubsubsubsection\n" , "\\stopsubsubsubsubsection\n" , "\\stopsubsubsubsection\n" , "\\stopsubsubsection\n" , "\\stopsubsection\n" , "\\stopsection" ] ] , testGroup "bullet lists" [ "nested" =: bulletList [ plain (text "top") <> bulletList [ plain (text "next") <> bulletList [plain (text "bot")] ] ] =?> unlines [ "\\startitemize[packed]" , "\\item" , " top" , " \\startitemize[packed]" , " \\item" , " next" , " \\startitemize[packed]" , " \\item" , " bot" , " \\stopitemize" , " \\stopitemize" , "\\stopitemize" ] ] , testGroup "natural tables" [ test contextNtb "table with header and caption" $ let capt = text "Table 1" aligns = [ (AlignRight, ColWidthDefault) , (AlignLeft, ColWidthDefault) , (AlignCenter, ColWidthDefault) , (AlignDefault, ColWidthDefault) ] headers = [plain $ text "Right", plain $ text "Left", plain $ text "Center", plain $ text "Default"] rows = [[plain $ text "1.1", plain $ text "1.2", plain $ text "1.3", plain $ text "1.4"] ,[plain $ text "2.1", plain $ text "2.2", plain $ text "2.3", plain $ text "2.4"] ,[plain $ text "3.1", plain $ text "3.2", plain $ text "3.3", plain $ text "3.4"]] toRow = Row nullAttr . map simpleCell in table (simpleCaption $ plain capt) aligns (TableHead nullAttr [toRow headers]) [TableBody nullAttr 0 [] $ map toRow rows] (TableFoot nullAttr []) =?> unlines [ "\\startplacetable[title={Table 1}]" , "\\setupTABLE[column][1][align=left]" , "\\setupTABLE[column][2][align=right]" , "\\setupTABLE[column][3][align=middle]" , "\\setupTABLE[column][4][align=left]" , "\\bTABLE" , "\\bTABLEhead" , "\\bTR" , "\\bTH Right\\eTH" , "\\bTH Left\\eTH" , "\\bTH Center\\eTH" , "\\bTH Default\\eTH" , "\\eTR" , "\\eTABLEhead" , "\\bTABLEbody" , "\\bTR" , "\\bTD 1.1\\eTD" , "\\bTD 1.2\\eTD" , "\\bTD 1.3\\eTD" , "\\bTD 1.4\\eTD" , "\\eTR" , "\\bTR" , "\\bTD 2.1\\eTD" , "\\bTD 2.2\\eTD" , "\\bTD 2.3\\eTD" , "\\bTD 2.4\\eTD" , "\\eTR" , "\\bTR" , "\\bTD 3.1\\eTD" , "\\bTD 3.2\\eTD" , "\\bTD 3.3\\eTD" , "\\bTD 3.4\\eTD" , "\\eTR" , "\\eTABLEbody" , "\\bTABLEfoot" , "\\eTABLEfoot" , "\\eTABLE" , "\\stopplacetable" ] ] ] ================================================ FILE: test/Tests/Writers/DocBook.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.DocBook (tests) where import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder docbook :: (ToPandoc a) => a -> String docbook = docbookWithOpts def{ writerWrapText = WrapNone } docbook5 :: (ToPandoc a) => a -> String docbook5 = docbook5WithOpts def{ writerWrapText = WrapNone } docbookWithOpts :: ToPandoc a => WriterOptions -> a -> String docbookWithOpts opts = unpack . purely (writeDocBook4 opts) . toPandoc docbook5WithOpts :: ToPandoc a => WriterOptions -> a -> String docbook5WithOpts opts = unpack . purely (writeDocBook5 opts) . toPandoc {- "my test" =: X =?> Y is shorthand for test docbook "my test" $ X =?> Y which is in turn shorthand for test docbook "my test" (X,Y) -} infix 4 =: (=:), testDb4, testDb5 :: (ToString a, ToPandoc a) => String -> (a, String) -> TestTree (=:) = test docbook testDb4 = test docbook testDb5 = test docbook5 lineblock :: Blocks lineblock = para ("some text" <> linebreak <> "and more lines" <> linebreak <> "and again") lineblock_out :: [String] lineblock_out = [ "some text" , "and more lines" , "and again" ] tests :: [TestTree] tests = [ testGroup "inline elements" [ testGroup "links" [ testDb4 "db4 external link" $ link "https://example.com" "" "Hello" =?> "Hello" , testDb5 "db5 external link" $ link "https://example.com" "" "Hello" =?> "Hello" , testDb5 "anchor" $ link "#foo" "" "Hello" =?> "Hello" , testDb5 "automatic anchor" $ link "#foo" "" "" =?> "" ] ] , testGroup "line blocks" [ "none" =: para "This is a test" =?> unlines [ "" , " This is a test" , "" ] , "basic" =: lineblock =?> unlines lineblock_out , "blockquote" =: blockQuote lineblock =?> unlines ( [ "
    " ] ++ lineblock_out ++ [ "
    " ] ) , "footnote" =: para ("This is a test" <> note lineblock <> " of footnotes") =?> unlines ( [ "" , " This is a test" ] ++ lineblock_out ++ [ " of footnotes" , "" ] ) ] , testGroup "divs" [ "admonition" =: divWith ("foo", ["warning"], []) (para "This is a test") =?> unlines [ "" , " " , " This is a test" , " " , "" ] , "admonition-with-title" =: divWith ("foo", ["note"], []) ( divWith ("foo", ["title"], []) (plain (text "This is title")) <> para "This is a test" ) =?> unlines [ "" , " This is title" , " " , " This is a test" , " " , "" ] , "admonition-with-title-in-para" =: divWith ("foo", ["note"], []) ( divWith ("foo", ["title"], []) (para "This is title") <> para "This is a test" ) =?> unlines [ "" , " This is title" , " " , " This is a test" , " " , "" ] , "single-child" =: divWith ("foo", [], []) (para "This is a test") =?> unlines [ "" , " This is a test" , "" ] , "single-literal-child" =: divWith ("foo", [], []) lineblock =?> unlines [ "some text" , "and more lines" , "and again" ] , "multiple-children" =: divWith ("foo", [], []) ( para "This is a test" <> para "This is an another test" ) =?> unlines [ "" , "" , " This is a test" , "" , "" , " This is an another test" , "" ] ] , testGroup "compact lists" [ testGroup "bullet" [ "compact" =: bulletList [plain "a", plain "b", plain "c"] =?> unlines [ "" , " " , " " , " a" , " " , " " , " " , " " , " b" , " " , " " , " " , " " , " c" , " " , " " , "" ] , "loose" =: bulletList [para "a", para "b", para "c"] =?> unlines [ "" , " " , " " , " a" , " " , " " , " " , " " , " b" , " " , " " , " " , " " , " c" , " " , " " , "" ] ] , testGroup "ordered" [ "compact" =: orderedList [plain "a", plain "b", plain "c"] =?> unlines [ "" , " " , " " , " a" , " " , " " , " " , " " , " b" , " " , " " , " " , " " , " c" , " " , " " , "" ] , "loose" =: orderedList [para "a", para "b", para "c"] =?> unlines [ "" , " " , " " , " a" , " " , " " , " " , " " , " b" , " " , " " , " " , " " , " c" , " " , " " , "" ] ] , testGroup "definition" [ "compact" =: definitionList [ ("an", [plain "apple" ]) , ("a", [plain "banana"]) , ("an", [plain "orange"])] =?> unlines [ "" , " " , " " , " an" , " " , " " , " " , " apple" , " " , " " , " " , " " , " " , " a" , " " , " " , " " , " banana" , " " , " " , " " , " " , " " , " an" , " " , " " , " " , " orange" , " " , " " , " " , "" ] , "loose" =: definitionList [ ("an", [para "apple" ]) , ("a", [para "banana"]) , ("an", [para "orange"])] =?> unlines [ "" , " " , " " , " an" , " " , " " , " " , " apple" , " " , " " , " " , " " , " " , " a" , " " , " " , " " , " banana" , " " , " " , " " , " " , " " , " an" , " " , " " , " " , " orange" , " " , " " , " " , "" ] ] ] , testGroup "writer options" [ testGroup "top-level division" $ let headers = header 1 (text "header1") <> header 2 (text "header2") <> header 3 (text "header3") docbookTopLevelDiv :: (ToPandoc a) => TopLevelDivision -> a -> String docbookTopLevelDiv division = docbookWithOpts def{ writerTopLevelDivision = division } in [ test (docbookTopLevelDiv TopLevelSection) "sections as top-level" $ headers =?> unlines [ "" , " header1" , " " , " header2" , " " , " header3" , " " , " " , " " , " " , "" ] , test (docbookTopLevelDiv TopLevelChapter) "chapters as top-level" $ headers =?> unlines [ "" , " header1" , " " , " header2" , " " , " header3" , " " , " " , " " , " " , "" ] , test (docbookTopLevelDiv TopLevelPart) "parts as top-level" $ headers =?> unlines [ "" , " header1" , " " , " header2" , " " , " header3" , " " , " " , " " , " " , "" ] , test (docbookTopLevelDiv TopLevelDefault) "default top-level" $ headers =?> unlines [ "" , " header1" , " " , " header2" , " " , " header3" , " " , " " , " " , " " , "" ] ] ] , testGroup "section attributes" $ let headers = headerWith ("myid1",["unnumbered","ignored"],[("role","internal"),("xml:id","anotherid"),("dir","rtl")]) 1 "header1" <> headerWith ("myid2",["unnumbered"],[("invalidname","value"),("arch","linux"),("dir","invaliddir")]) 1 "header2" <> headerWith ("myid3",["ignored"],[]) 1 "header3" in [ test docbook5 "sections with attributes (db5)" $ headers =?> unlines [ "
    " , " header1" , " " , " " , "
    " , "
    " , " header2" , " " , " " , "
    " , "
    " , " header3" , " " , " " , "
    " ] , test docbook "sections with attributes (db4)" $ headers =?> unlines [ "" , " header1" , " " , " " , "" , "" , " header2" , " " , " " , "" , "" , " header3" , " " , " " , "" ] ] ] ================================================ FILE: test/Tests/Writers/Docx.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Docx (tests) where 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 Test.Tasty.HUnit 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 -- Word-generated docx file used to test the reader. docxTest :: String -> WriterOptions -> FilePath -> FilePath -> TestTree docxTest testName opts nativeFP goldenFP = if "docx/golden/" `isPrefixOf` goldenFP then ooxmlTest writeDocx testName opts nativeFP goldenFP else testCase testName $ assertFailure $ goldenFP ++ " is not in `test/docx/golden`" tests :: [TestTree] tests = [ testGroup "inlines" [ docxTest "font formatting" def "docx/inline_formatting.native" "docx/golden/inline_formatting.docx" , docxTest "hyperlinks" def "docx/links.native" "docx/golden/links.docx" , docxTest "inline image" def{ writerExtensions = enableExtension Ext_native_numbering (writerExtensions def) } "docx/image_writer_test.native" "docx/golden/image.docx" , docxTest "inline images" def "docx/inline_images_writer_test.native" "docx/golden/inline_images.docx" , docxTest "handling unicode input" def "docx/unicode.native" "docx/golden/unicode.docx" , docxTest "inline code" def "docx/inline_code.native" "docx/golden/inline_code.docx" , docxTest "inline code in subscript and superscript" def "docx/verbatim_subsuper.native" "docx/golden/verbatim_subsuper.docx" ] , testGroup "blocks" [ docxTest "headers" def "docx/headers.native" "docx/golden/headers.docx" , docxTest "nested anchor spans in header" def "docx/nested_anchors_in_header.native" "docx/golden/nested_anchors_in_header.docx" , docxTest "lists" def "docx/lists.native" "docx/golden/lists.docx" , docxTest "lists continuing after interruption" def "docx/lists_continuing.native" "docx/golden/lists_continuing.docx" , docxTest "lists restarting after interruption" def "docx/lists_restarting.native" "docx/golden/lists_restarting.docx" , docxTest "lists with multiple initial list levels" def "docx/lists_multiple_initial.native" "docx/golden/lists_multiple_initial.docx" , docxTest "lists with div bullets" def "docx/lists_div_bullets.native" "docx/golden/lists_div_bullets.docx" , docxTest "definition lists" def "docx/definition_list.native" "docx/golden/definition_list.docx" , docxTest "task lists" def "docx/task_list.native" "docx/golden/task_list.docx" , docxTest "issue 9994" def "docx/lists_9994.native" "docx/golden/lists_9994.docx" , docxTest "footnotes and endnotes" def "docx/notes.native" "docx/golden/notes.docx" , docxTest "links in footnotes and endnotes" def "docx/link_in_notes.native" "docx/golden/link_in_notes.docx" , docxTest "blockquotes" def "docx/block_quotes.native" "docx/golden/block_quotes.docx" , docxTest "tables" def "docx/tables.native" "docx/golden/tables.docx" , docxTest "tables without explicit column widths" def "docx/tables-default-widths.native" "docx/golden/tables-default-widths.docx" , docxTest "tables with lists in cells" def "docx/table_with_list_cell.native" "docx/golden/table_with_list_cell.docx" , docxTest "tables with one row" def "docx/table_one_row.native" "docx/golden/table_one_row.docx" , docxTest "tables separated with RawBlock" def "docx/tables_separated_with_rawblock.native" "docx/golden/tables_separated_with_rawblock.docx" , docxTest "code block" def "docx/codeblock.native" "docx/golden/codeblock.docx" , docxTest "raw OOXML blocks" def "docx/raw-blocks.native" "docx/golden/raw-blocks.docx" , docxTest "raw bookmark markers" def "docx/raw-bookmarks.native" "docx/golden/raw-bookmarks.docx" ] , testGroup "track changes" [ docxTest "insertion" def "docx/track_changes_insertion_all.native" "docx/golden/track_changes_insertion.docx" , docxTest "deletion" def "docx/track_changes_deletion_all.native" "docx/golden/track_changes_deletion.docx" , docxTest "move text" def "docx/track_changes_move_all.native" "docx/golden/track_changes_move.docx" , docxTest "comments" def "docx/comments.native" "docx/golden/comments.docx" , docxTest "scrubbed metadata" def "docx/track_changes_scrubbed_metadata.native" "docx/golden/track_changes_scrubbed_metadata.docx" ] , testGroup "custom styles" [ docxTest "custom styles without reference.docx" def "docx/custom_style.native" "docx/golden/custom_style_no_reference.docx" , docxTest "custom styles with reference.docx" def{writerReferenceDoc = Just "docx/custom-style-reference.docx"} "docx/custom_style.native" "docx/golden/custom_style_reference.docx" , docxTest "suppress custom style for headers and blockquotes" def "docx/custom-style-preserve.native" "docx/golden/custom_style_preserve.docx" ] , testGroup "metadata" [ docxTest "document properties (core, custom)" def "docx/document-properties.native" "docx/golden/document-properties.docx" , docxTest "document properties (short description)" def "docx/document-properties-short-desc.native" "docx/golden/document-properties-short-desc.docx" ] , testGroup "top-level-division" -- Helper to count occurrences of a substring -- Note: counts by splitting on " assertFailure "Missing word/document.xml in output docx" Just e -> return e let docXml = show (fromEntry entry) -- Count occurrences of " assertFailure "Missing word/document.xml in output docx" Just e -> return e let docXml = show (fromEntry entry) -- Count occurrences of ">= 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)) ] ] ================================================ FILE: test/Tests/Writers/FB2.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.FB2 (tests) where import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder fb2 :: String -> String fb2 x = "\n" ++ "unrecognisedpandoc<p />
    " ++ x ++ "
    " infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test (purely (writeFB2 def) . toPandoc) tests :: [TestTree] tests = [ testGroup "block elements" ["para" =: para "Lorem ipsum cetera." =?> fb2 "

    Lorem ipsum cetera.

    " ] , testGroup "inlines" [ "Emphasis" =: para (emph "emphasized") =?> fb2 "

    emphasized

    " ] , "bullet list" =: bulletList [ plain $ text "first" , plain $ text "second" , plain $ text "third" ] =?> fb2 "

    \x2022 first

    \x2022 second

    \x2022 third

    " ] ================================================ FILE: test/Tests/Writers/HTML.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.HTML (tests) where import Data.Text (unpack) import qualified Data.Text as T import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder htmlWithOpts :: (ToPandoc a) => WriterOptions -> a -> String htmlWithOpts opts = unpack . purely (writeHtml4String opts{ writerWrapText = WrapNone }) . toPandoc html :: (ToPandoc a) => a -> String html = htmlWithOpts def htmlQTags :: (ToPandoc a) => a -> String htmlQTags = unpack . purely (writeHtml4String def{ writerWrapText = WrapNone, writerHtmlQTags = True }) . toPandoc {- "my test" =: X =?> Y is shorthand for test html "my test" $ X =?> Y which is in turn shorthand for test html "my test" (X,Y) -} infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, String) -> TestTree (=:) = test html noteTestDoc :: Blocks noteTestDoc = header 1 "Page title" <> header 2 "First section" <> para ("This is a footnote." <> note (para "Down here.") <> " And this is a " <> link "https://www.google.com" "" "link" <> ".") <> blockQuote (para ("A note inside a block quote." <> note (para "The second note.")) <> para "A second paragraph.") <> header 2 "Second section" <> para "Some more text." tests :: [TestTree] tests = [ testGroup "inline code" [ "basic" =: code "@&" =?> "@&" , "haskell" =: codeWith ("",["haskell"],[]) ">>=" =?> ">>=" , "nolanguage" =: codeWith ("",["nolanguage"],[]) ">>=" =?> ">>=" ] , testGroup "images" [ "alt with formatting" =: image "/url" "title" ("my " <> emph "image") =?> "\"my" ] , testGroup "blocks" [ "definition list with empty
    " =: definitionList [(mempty, [para $ text "foo bar"])] =?> "
    \n
    \n
    \n

    foo bar

    \n
    \n
    " , "heading with disallowed attributes" =: headerWith ("", [], [("invalid","1"), ("lang", "en")]) 1 "test" =?> "

    test

    " ] , testGroup "quotes" [ "quote with cite attribute (without q-tags)" =: doubleQuoted (spanWith ("", [], [("cite", "http://example.org")]) (str "examples")) =?> "“examples”" , tQ "quote with cite attribute (with q-tags)" $ doubleQuoted (spanWith ("", [], [("cite", "http://example.org")]) (str "examples")) =?> "examples" ] , testGroup "code" [ "code should be rendered correctly" =: plain (codeWith ("",[],[]) "Answer is 42") =?> "Answer is 42" ] , testGroup "sample" [ "sample should be rendered correctly" =: plain (codeWith ("",["sample"],[]) "Answer is 42") =?> "Answer is 42" ] , testGroup "variable" [ "variable should be rendered correctly" =: plain (codeWith ("",["variable"],[]) "result") =?> "result" ] , testGroup "sample with style" [ "samp should wrap highlighted code" =: codeWith ("",["sample","haskell"],[]) ">>=" =?> ("" ++ ">>=") ] , testGroup "variable with style" [ "var should wrap highlighted code" =: codeWith ("",["haskell","variable"],[]) ">>=" =?> ("" ++ ">>=") ] , testGroup "footnotes" [ test (htmlWithOpts def{writerReferenceLocation=EndOfDocument}) "at the end of a document" $ noteTestDoc =?> T.unlines [ "

    Page title

    " , "

    First section

    " , "

    This is a footnote.1 And this is a link.

    " , "
    " , "

    A note inside a block quote.2

    " , "

    A second paragraph.

    " , "
    " , "

    Second section

    " , "

    Some more text.

    " , "
    " , "
    " , "
      " , "
    1. Down here.↩︎

    2. " , "
    3. The second note.↩︎

    4. " , "
    " , "
    " ] , test (htmlWithOpts def{writerReferenceLocation=EndOfBlock}) "at the end of a block" $ noteTestDoc =?> T.unlines [ "

    Page title

    " , "

    First section

    " , "

    This is a footnote.1 And this is a link.

    " , "
    " , "
      " , "
    1. Down here.↩︎

    2. " , "
    " , "
    " , "
    " , "

    A note inside a block quote.2

    " , "

    A second paragraph.

    " , "
    " , "
    " , "
      " , "
    1. The second note.↩︎

    2. " , "
    " , "
    " , "

    Second section

    " , "

    Some more text.

    " ] , test (htmlWithOpts def{writerReferenceLocation=EndOfSection}) "at the end of a section" $ noteTestDoc =?> T.unlines [ "

    Page title

    " , "

    First section

    " , "

    This is a footnote.1 And this is a link.

    " , "
    " , "

    A note inside a block quote.2

    " , "

    A second paragraph.

    " , "
    " , "
    " , "
    " , "
      " , "
    1. Down here.↩︎

    2. " , "
    3. The second note.↩︎

    4. " , "
    " , "
    " , "

    Second section

    " , "

    Some more text.

    " ] , test (htmlWithOpts def{writerReferenceLocation=EndOfSection, writerSectionDivs=True}) "at the end of a section, with section divs" $ noteTestDoc =?> -- Footnotes are rendered _after_ their section (in this case after the level2 section -- that contains it). T.unlines [ "
    " , "

    Page title

    " , "
    " , "

    First section

    " , "

    This is a footnote.1 And this is a link.

    " , "
    " , "

    A note inside a block quote.2

    " , "

    A second paragraph.

    " , "
    " , "
    " , "
    " , "
      " , "
    1. Down here.↩︎

    2. " , "
    3. The second note.↩︎

    4. " , "
    " , "
    " , "
    " , "
    " , "

    Second section

    " , "

    Some more text.

    " , "
    " , "
    " ] ] ] where tQ :: (ToString a, ToPandoc a) => String -> (a, String) -> TestTree tQ = test htmlQTags ================================================ FILE: test/Tests/Writers/JATS.hs ================================================ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.JATS (tests) where import Data.Text (Text) import Test.Tasty import Test.Tasty.HUnit (HasCallStack) import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder import qualified Data.Text as T jats :: (ToPandoc a) => a -> Text jats = purely (writeJatsArchiving def{ writerWrapText = WrapNone }) . toPandoc jatsArticleAuthoring :: (ToPandoc a) => a -> Text jatsArticleAuthoring = purely (writeJatsArticleAuthoring def{ writerWrapText = WrapNone }) . toPandoc {- "my test" =: X =?> Y is shorthand for test jats "my test" $ X =?> Y which is in turn shorthand for test jats "my test" (X,Y) -} infix 4 =: (=:) :: (ToString a, ToPandoc a, HasCallStack) => String -> (a, Text) -> TestTree (=:) = test jats tests :: [TestTree] tests = [ testGroup "inline code" [ "basic" =: code "@&" =?> "

    @&

    " ] , testGroup "block code" [ "basic" =: codeBlock "@&" =?> "@&" , "lang" =: codeBlockWith ("", ["c"], []) "@&" =?> "@&" ] , testGroup "images" [ "basic" =: image "/url" "title" mempty =?> "" ] , testGroup "inlines" [ "Emphasis" =: emph "emphasized" =?> "

    emphasized

    " , test jatsArticleAuthoring "footnote in articleauthoring tag set" ("test" <> note (para "footnote") =?> unlines [ "

    test" , "

    footnote

    " , "

    " ]) ] , testGroup "bullet list" [ "plain items" =: bulletList [ plain $ text "first" , plain $ text "second" , plain $ text "third" ] =?> "\n\ \ \n\ \

    first

    \n\ \
    \n\ \ \n\ \

    second

    \n\ \
    \n\ \ \n\ \

    third

    \n\ \
    \n\ \
    " , "item with implicit figure" =: bulletList [ simpleFigure (text "caption") "a.png" "" ] =?> T.unlines [ "" , " " , "

    " , " " , "

    caption

    Function 1
    a yes
    b
    ``` ``` % pandoc -f rst ========== ==== Function 1 ========== ==== a yes & no ========== ==== ^D
    Function 1
    a yes & no
    ``` ================================================ FILE: test/command/10094.md ================================================ ``` % pandoc -f rst -t markdown_strict .. table:: Test Table ================================== ==== Functions for tests 1 ================================== ==== a yes b c d yes e ================================== ==== ^D
    Test Table
    Functions for tests 1
    a yes
    b
    c
    d yes
    e
    ``` ================================================ FILE: test/command/10105.md ================================================ ``` % pandoc -t asciidoc --wrap=preserve [link](https://example.com) [link](ftps://example.com) ^D https://example.com[link] link:ftps://example.com[link] ``` ================================================ FILE: test/command/10127.md ================================================ RST Reader ``` % pandoc -f rst -t native ===== ===== ====== Inputs Output ------------ ------ A B A or B ===== ===== ====== False False False True False True False True True True True True ===== ===== ====== Table with trailing whitespaces to ensure they don't interfere with col span parsing. =============== ============ ===================== Full column header ---------------------------------------------------- Header One Header Two Header 3 =============== ============ ===================== Row With col span 2 on the right --------------- ----------------------------------- Full column row ---------------------------------------------------- Row with Col spans For visual separation --------------- ------------ --------------------- Row with col span 2 On the left ----------------------------- --------------------- Final row =============== ============ ===================== ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Inputs" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Output" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" , Space , Str "or" , Space , Str "B" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "with" , Space , Str "trailing" , Space , Str "whitespaces" , Space , Str "to" , Space , Str "ensure" , Space , Str "they" , Space , Str "don't" , Space , Str "interfere" , Space , Str "with" , Space , Str "col" , Space , Str "span" , Space , Str "parsing." ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Full" , Space , Str "column" , Space , Str "header" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "One" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "Two" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "With" , Space , Str "col" , Space , Str "span" , Space , Str "2" , Space , Str "on" , Space , Str "the" , Space , Str "right" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Full" , Space , Str "column" , Space , Str "row" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" , Space , Str "with" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Col" , Space , Str "spans" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "For" , Space , Str "visual" , Space , Str "separation" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Row" , Space , Str "with" , Space , Str "col" , Space , Str "span" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "On" , Space , Str "the" , Space , Str "left" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Final" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` RST Writer ``` % pandoc -f native -t rst [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Inputs" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Output" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" , Space , Str "or" , Space , Str "B" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "False" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "True" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Full" , Space , Str "column" , Space , Str "header" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "One" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "Two" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "With" , Space , Str "col" , Space , Str "span" , Space , Str "2" , Space , Str "on" , Space , Str "the" , Space , Str "right" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Full" , Space , Str "column" , Space , Str "row" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Row" , Space , Str "with" , Space , Str "col" , Space , Str "span" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "On" , Space , Str "the" , Space , Str "left" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Final" , Space , Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Footer" , Space , Str "Row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "at" , Space , Str "the" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "bottom" ] ] ] ]) ] ^D ====== ===== ====== Inputs Output ------------ ------ A B A or B ====== ===== ====== False False False True False True False True True True True True ====== ===== ====== =================== ============================ =========== Full column header ------------------------------------------------------------ Header One Header Two Header 3 =================== ============================ =========== Row With col span 2 on the right ------------------- ---------------------------------------- Full column row ------------------------------------------------------------ Row with col span 2 On the left ------------------------------------------------ ----------- Final row Footer Row at the bottom =================== ============================ =========== ``` ================================================ FILE: test/command/10145.md ================================================ ``` % pandoc -f rtf -t native {\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 \fmodern Courier;}} {\colortbl;\red255\green0\blue0;\red0\green0\blue255;} \widowctrl\hyphauto {\pard \ql \f0 \sa0 \li0 \fi0 {\shp{\*\shpinst\shpwr2\shpwrk3\shpbypara\shpbyignore\shptop1320\shpbottom7745\shpbxcolumn\shpbxignore\shpleft0\shpright9638{\sp{\sn shapeType}{\sv 75}}{\sp{\sn wzDescription}{\sv }}{\sp{\sn wzName}{\sv }}{\sp{\sn pib}{\sv {\pict\jpegblip\picw20\pich22\picwgoal400\pichgoal440 ffd8ffe000104a46494600010101004800480000fffe0050546869732061727420697320696e20746865207075626c696320646f6d61696e2e204b6576696e204875676865732c206b6576696e68406569742e636f6d2c2053657074656d6265722031393935ffdb00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffdb00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffc00011080016001403012200021101031101ffc4001a000100020301000000000000000000000000080905060a07ffc400231000010501000300010500000000000000060304050708020001090a11153976b7ffc400160101010100000000000000000000000000060800ffc400261101000102050109000000000000000000010200030405061121b33134365154717475b4ffda000c03010002110311003f00a90cf388f366a62aa720ed6ae07f96901f3831d973452b8cf36fe3570fc908e46d466433e5dd954f2e96992d9e498c7753faa44916e016ca91cc7d88b38fe60a5b97737defcbcc539c98d336a57f4fc2ca9a486bf07ab575ad9a3af4df221d8215e36df86c4504ff0024574551b3d687ee0575757b3ad64e311ee62bd94158d37e24198c43973099f1fc0c41614d950246513a081abf76cfe7061f6863281e6352fd1670949c148dd6dfb0d25f5b3689b1d5c965b0eacbf4e0932ad28e22ab9ae945633f4744bd3c8cee0a7fdf085b9000f449c5f7afa30b83e0b6fd7b0c8429c9467ff9715347c891e25fa24a205861aa715e6a09bd0488237dc2723414d9891381524e8ca7c0894664f835653631ab55ee7e3de433e4ff001b30949124e4c10c8b6ad0a479b3f9c937b2cf5bc0095ad600a0a41a0e9faee174a1c605e161c6c7a313539650b0113190f1a8368e60d5b24f30ff008ea7f0bf867fa6595feeb6978f1fe0f9c26177f4d63a51a9235184750e7d18811339cd000000c75f000e00380380ae390c350def826ed42ad051fa6f501c50f9b699c3b69cbeb76476d202bf3ac985b6e0e968be66572893e6a744540bd9722e5c87956848629bc2559306bd113e8653d3b6aff651dfad7a3ac8b02958cba02a93ccf525757039bae6cff090e1d90688e8aa233ee86a4c4a3e0586d6b2340522e47dcb7d0046d8a5acb05a123ee25d2b230b2ada6e2e2f9ede3c05202520ec2487b0d56562529d8b3393bca76adca4ec1bca508abb001babc007915d84fe3dd14e207e3c62f8379da2a3b861fb6629d28dba53b6ea388ebfed866bf6dfb553455e91ed547ae92e9445253a4fdf3efb4f8ebdfbe7d3c78f1ee0bb9e13e358e942a4ed49e22cff00eeb35fdd7ebfffd9}}}}}\par} {\pard \ql \f0 \sa0 \li0 \fi0 moon\par} } ^D [ Para [ Image ( "" , [] , [ ( "width" , "0.2777777777777778in" ) , ( "height" , "0.3055555555555556in" ) ] ) [ Str "image" ] ( "9ea9761885249909bcd8a610706eef51b54dc351.jpg" , "" ) ] , Para [ Str "moon" ] ] ``` ================================================ FILE: test/command/10148.md ================================================ ``` % pandoc -t typst See [@MyPaper]. See [@MyPaper, Equation 1] for details. See [@MyPaper, Equation 1]. See [@MyPaper, Equation 1; @Other, Figure 1]. ^D See @MyPaper. See @MyPaper[Equation 1] for details. See @MyPaper[Equation 1]. See @MyPaper[Equation 1]@Other[Figure 1]. ``` ================================================ FILE: test/command/10149.md ================================================ Row spans ``` % pandoc -f html --columns 24 -t ansi
    1 2 3 4 5 6
    2 3 6
    2 6
    1 3 4 6
    a c d e f
    a b d
    a b c
    A C D E
    C D
    C D
    C D E F
    ^D 1 2 3 4 5 6 2 3 6 2 6 1 3 4 6 ─── ─── ─── ─── ─── ─── a c d e f a b d a b c ─── ─── ─── ─── ─── ─── A C D E C D C D C D E F]8;;\ ``` Empty rows ``` % pandoc -f native --columns 12 -t ansi [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 2) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 4) (ColSpan 1) [ Plain [ Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] ] , Row ( "" , [] , [] ) [] ]) ] ^D 1 3 3 1 2 3 ─── ─── ─── 1 2 3 1 2 1 3 1 3 ─── ─── ─── 1 2 3 1 2 3 1 2]8;;\ ``` ================================================ FILE: test/command/10152.md ================================================ # CRediT Roles This document contains tests and examples for enabling export of roles to JATS. It was added for [Issue #10152](https://github.com/jgm/pandoc/issues/10152) and corresponding [Pull Request #10153](https://github.com/jgm/pandoc/pull/10153). In the first example, we show a fully qualified CRediT role. An explicit name isn't given, so the CRediT name is used. ``` % pandoc -s -t jats --- title: CRediT Test author: - name: Max Mustermann affiliation: [ 1 ] roles: - credit: software credit-name: Software degree: Lead affiliation: - id: 1 name: Silverlight University --- ^D
    CRediT Test Max Mustermann Software Silverlight University
    ``` In the second example, we show a fully qualified CRediT role. An explicit name is given in a different language. ``` % pandoc -s -t jats --- title: CRediT Test author: - name: Max Mustermann affiliation: [ 1 ] roles: - credit: software credit-name: Software degree: Lead name: Programas affiliation: - id: 1 name: Silverlight University --- ^D
    CRediT Test Max Mustermann Programas Silverlight University
    ``` In this example, we show a partially qualified CRediT role that does not have a `degree`: ``` % pandoc -s -t jats --- title: CRediT Test author: - name: Max Mustermann affiliation: [ 1 ] roles: - credit: software credit-name: Software affiliation: - id: 1 name: Silverlight University --- ^D
    CRediT Test Max Mustermann Software Silverlight University
    ``` In this example, we show a more stripped-down data that requires automatic lookup of the `credit-name`. ``` % pandoc -s -t jats --- title: CRediT Test author: - name: Max Mustermann affiliation: [ 1 ] roles: - credit: software affiliation: - id: 1 name: Silverlight University --- ^D
    CRediT Test Max Mustermann Software Silverlight University
    ``` In this example, we test the correct XML encoding of the CRediT role [Writing – review & editing](https://credit.niso.org/contributor-roles/writing-review-editing/), which annoyingly contains an ampersand in its label. ``` % pandoc -s -t jats --- title: CRediT Test author: - name: Max Mustermann affiliation: [ 1 ] roles: - credit: writing-review-editing credit-name: Writing – review & editing degree: Lead affiliation: - id: 1 name: Silverlight University --- ^D
    CRediT Test Max Mustermann Writing – review & editing Silverlight University
    ``` In this example, we show a role that isn't qualified with CRediT. ``` % pandoc -s -t jats --- title: CRediT Test author: - name: Max Mustermann affiliation: [ 1 ] roles: - name: Dolphin Catcher affiliation: - id: 1 name: Silverlight University --- ^D
    CRediT Test Max Mustermann Dolphin Catcher Silverlight University
    ``` In this example, we show a role that neither has a CRediT identifer, nor a name, so it's ignored. ``` % pandoc -s -t jats --- title: CRediT Test author: - name: Max Mustermann affiliation: [ 1 ] roles: - irrelevant-key: Dolphin Catcher affiliation: - id: 1 name: Silverlight University --- ^D
    CRediT Test Max Mustermann Silverlight University
    ``` ================================================ FILE: test/command/10160.md ================================================ ``` % pandoc -f latex -t native \begin{equation} [0,1) \end{equation} \begin{table}[h] \end{table} ^D [ Para [ Math DisplayMath "\\begin{equation}\n [0,1)\n\\end{equation}" ] ] ``` ================================================ FILE: test/command/10185.md ================================================ ``` % pandoc -t latex --citeproc Lorem ipsum # References ::: {#refs} ::: ^D Lorem ipsum \section{References}\label{references} \protect\phantomsection\label{refs} ``` ``` % pandoc -t latex --citeproc --- references: - id: foo type: book title: The Title author: John Doe ... Lorem ipsum [@foo]. # References ::: {#refs} ::: ^D Lorem ipsum (John Doe, n.d.). \section{References}\label{references} \protect\phantomsection\label{refs} \begin{CSLReferences}{1}{1} \bibitem[\citeproctext]{ref-foo} John Doe. n.d. \emph{The Title}. \end{CSLReferences} ``` ================================================ FILE: test/command/10236.md ================================================ ``` % pandoc -t rst - > test > > para 2 ^D - .. test para 2 ``` ``` % pandoc -t rst 1. > test > > para 2 ^D 1. .. test para 2 ``` ``` % pandoc -t rst ::: caution > test ::: ^D .. caution:: .. test ``` ``` % pandoc -f native -t rst --list-tables [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.26 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ BlockQuote [ Para [ Emph [ Str "Level" , Space , Str "0:" ] ] , Para [ Emph [ Str "Incomplete" , Space , Str "process" ] ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ^D .. list-table:: :widths: 19 * - .. *Level 0:* *Incomplete process* ``` ================================================ FILE: test/command/10271.md ================================================ ``` % pandoc -f markdown+smart -t typst+smart "don't do it---" ^D "don't do it---" ``` ``` % pandoc -f markdown+smart -t typst-smart "don't do it---" ^D “don’t do it—” ``` ``` % pandoc -f markdown-smart -t typst+smart "don't do it---" ^D \"don\'t do it-\-\-\" ``` ``` % pandoc -f markdown-smart -t typst-smart "don't do it---" ^D "don't do it---" ``` ================================================ FILE: test/command/10279.md ================================================ ``` % pandoc -f rst See `the full compatibility guidelines `_ for more information. ^D

    See the full compatibility guidelines for more information.

    ``` ================================================ FILE: test/command/10281.md ================================================ ``` % pandoc -f rst -t native `Want Speed? Pass by Value`_ .. note:: For more information about the pass-by-value idiom, read: `Want Speed? Pass by Value`_. .. _Want Speed? Pass by Value: https://web.archive.org/web/20140205194657/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ ^D [ Para [ Link ( "" , [] , [] ) [ Str "Want" , Space , Str "Speed?" , Space , Str "Pass" , Space , Str "by" , Space , Str "Value" ] ( "https://web.archive.org/web/20140205194657/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/" , "" ) ] , Div ( "" , [ "note" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Note" ] ] , Para [ Str "For" , Space , Str "more" , Space , Str "information" , Space , Str "about" , Space , Str "the" , Space , Str "pass-by-value" , Space , Str "idiom," , Space , Str "read:" , Space , Link ( "" , [] , [] ) [ Str "Want" , Space , Str "Speed?" , Space , Str "Pass" , Space , Str "by" , Space , Str "Value" ] ( "https://web.archive.org/web/20140205194657/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/" , "" ) , Str "." ] ] ] ``` ================================================ FILE: test/command/10318.md ================================================ ``` % pandoc -f rst -t html5 -a Output all. -c arg Output just arg. --long Output all day long. /V A VMS/DOS-style option. -p This option has two paragraphs in the description. This is the first. This is the second. Blank lines may be omitted between options (as above) or left in (as here and below). --very-long-option A VMS-style option. Note the adjustment for the required two spaces. --an-even-longer-option The description can also start on the next line. -2, --two This option has two variants. -f FILE, --file=FILE These two options are synonyms; both have arguments. -f <[path]file> Option argument placeholders must start with a letter or be wrapped in angle brackets. -d Angle brackets are also required if an option expects more than one argument. ^D
    -a

    Output all.

    -c arg

    Output just arg.

    --long

    Output all day long.

    /V

    A VMS/DOS-style option.

    -p

    This option has two paragraphs in the description. This is the first.

    This is the second. Blank lines may be omitted between options (as above) or left in (as here and below).

    --very-long-option

    A VMS-style option. Note the adjustment for the required two spaces.

    --an-even-longer-option

    The description can also start on the next line.

    -2, --two

    This option has two variants.

    -f FILE, --file=FILE

    These two options are synonyms; both have arguments.

    -f <[path]file>

    Option argument placeholders must start with a letter or be wrapped in angle brackets.

    -d <src dest>

    Angle brackets are also required if an option expects more than one argument.

    ``` ================================================ FILE: test/command/10328.md ================================================ Unwrap divs if they only have the `nonincremental` or `incremental` classes. ``` % pandoc --incremental --from=markdown --to=revealjs ## First slide ::: nonincremental 1. Note 1 2. Note 2 3. Note 3 ::: ## Second Slide 1. Note 1 2. Note 2 3. Note 3 ^D

    First slide

    1. Note 1
    2. Note 2
    3. Note 3

    Second Slide

    1. Note 1
    2. Note 2
    3. Note 3
    ``` ================================================ FILE: test/command/10338-rst-multiple-header-rows.md ================================================ ``` % pandoc -f rst -t native Multiple Headers ================ ========== ========= Header A1 Header A2 Header B1 Header B2 ========== ========= body a1 body a1 body b1 body b2 ========== ========= Headless ======== ========== ========= body a1 body a1 body b1 body b2 ========== ========= End Section =========== ^D [ Header 1 ( "multiple-headers" , [] , [] ) [ Str "Multiple" , Space , Str "Headers" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "A1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "A2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "B1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "B2" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "body" , Space , Str "a1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "body" , Space , Str "a1" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "body" , Space , Str "b1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "body" , Space , Str "b2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 1 ( "headless" , [] , [] ) [ Str "Headless" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "body" , Space , Str "a1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "body" , Space , Str "a1" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "body" , Space , Str "b1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "body" , Space , Str "b2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 1 ( "end-section" , [] , [] ) [ Str "End" , Space , Str "Section" ] ] ``` ================================================ FILE: test/command/10385.md ================================================ ```` % pandoc -t asciidoc C+ C++ C+++ `++` `` ` `` \`hi\` `hi\there`` ok` ``` ++` ``` ^D C{plus} C{plus}{plus} C{plus}{plus}{plus} `{plus}{plus}` `++`++` ++`++hi++`++ `hi++\++there++``++ ok` .... ++` .... ```` ================================================ FILE: test/command/10390.md ================================================ ``` % pandoc -f mediawiki {| class="wikitable" |+ Overview of basic table markup ! Key |- | Value |} ^D
    Overview of basic table markup

    Key

    Value

    ``` ================================================ FILE: test/command/10414.md ================================================ ``` % pandoc -f textile -t html from 30.-100. text, inside - after dash ^D

    from 30.–100. text, inside - after dash

    ``` ================================================ FILE: test/command/10459.md ================================================ ``` % pandoc -s --shift-heading-level-by=-1 -f djot -t native # hi ^D Pandoc Meta { unMeta = fromList [ ( "title" , MetaInlines [ Str "hi" ] ) ] } [ Div ( "hi" , [ "section" ] , [] ) [] ] ``` ================================================ FILE: test/command/10484.md ================================================ ``` % pandoc -frst -tmarkdown_strict - One issue fixed: `issue 123`_. - One change merged: `Big change `_. - Improved the `home page `_. - One more `small change`__. .. _issue 123: https://github.com/joe/project/issues/123 .. _pull 234: https://github.com/joe/project/pull/234 __ https://github.com/joe/project/issues/999 ^D - One issue fixed: [issue 123](https://github.com/joe/project/issues/123). - One change merged: [Big change](https://github.com/joe/project/pull/234). - Improved the [home page](https://example.com/homepage). - One more [small change](https://github.com/joe/project/issues/999). ``` ================================================ FILE: test/command/10490.md ================================================ ``` % pandoc -f mediawiki -t html {|class="wikitable" style="text-align: center;" |- !rowspan=3 style=""|Witness program version !colspan=4 style=""|Hash size |- !Mainnet !Testnet !Mainnet !Testnet |- |0||p2||QW||7Xh||T7n |- |1||p4||QY||7Xq||T7w |} ^D

    Witness program version

    Hash size

    Mainnet

    Testnet

    Mainnet

    Testnet

    0

    p2

    QW

    7Xh

    T7n

    1

    p4

    QY

    7Xq

    T7w

    ``` ================================================ FILE: test/command/10491.md ================================================ ``` % pandoc -f mediawiki -t gfm {| class="wikitable" |- ! !0 !1 !2 !3 !4 !5 !6 !7 |- !+0 |q||p||z||r||y||9||x||8 |- !+8 |g||f||2||t||v||d||w||0 |- !+16 |s||3||j||n||5||4||k||h |- !+24 |c||e||6||m||u||a||7||l |} ^D | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |-----|-----|-----|-----|-----|-----|-----|-----|-----| | +0 | q | p | z | r | y | 9 | x | 8 | | +8 | g | f | 2 | t | v | d | w | 0 | | +16 | s | 3 | j | n | 5 | 4 | k | h | | +24 | c | e | 6 | m | u | a | 7 | l | ``` ================================================ FILE: test/command/10497.md ================================================ ``` % pandoc -f rst a.__b__ a__b__ __foo__ ^D

    a.__b__

    a__b__

    __foo__

    ``` ================================================ FILE: test/command/10537.md ================================================ ``` % pandoc -f pod -t html =encoding utf8 =head1 NAME Test document ^D

    NAME

    Test document

    ``` ``` % pandoc -f pod -t html =encoding utf8 =head1 NAME Test document ^D

    NAME

    Test document

    ``` ================================================ FILE: test/command/10594.md ================================================ ``` % pandoc -f docbook -t native header inside listing // not rendered in any output format! first step ^D [ Div ( "" , [] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Plain [ Str "header" , Space , Str "inside" , Space , Str "listing" ] ] , OrderedList ( 1 , LowerAlpha , DefaultDelim ) [ [ Para [ Str "first" , Space , Str "step" ] ] ] ] ] ``` ================================================ FILE: test/command/10621.md ================================================ ``` % pandoc -f markdown -t html [test](url "title") ^D

    test

    ``` ================================================ FILE: test/command/10631.md ================================================ GFM has funny treatment of escaped braces, requiring `\\{` instead of `\{`. ``` % pandoc -f gfm -t markdown $\\{x,y\\}$ ^D $\{x,y\}$ ``` ================================================ FILE: test/command/10635.md ================================================ # Support for KOMA's `\minisec` command ``` % pandoc -f latex -t native \minisec{Montage} Zunächst suche man das Mauseloch. ^D [ Header 6 ( "montage" , [ "unnumbered" , "unlisted" ] , [] ) [ Str "Montage" ] , Para [ Str "Zun\228chst" , Space , Str "suche" , Space , Str "man" , Space , Str "das" , Space , Str "Mauseloch." ] ] ``` ================================================ FILE: test/command/10643.md ================================================ ``` % pandoc -f html -t native

    AB

    ^D [ Para [ Str "A" , RawInline (Format "html") "" , Str "B" ] ] ``` ================================================ FILE: test/command/10650.md ================================================ ``` % pandoc -t typst --wrap=preserve 1\. I don't want this to be an enumerated list. 2\. No, I don't: only a number before a paragraph. ^D \1. I don't want this to be an enumerated list. \2. No, I don't: only a number before a paragraph. ``` ================================================ FILE: test/command/10659.md ================================================ ``` % pandoc -f latex -t native \includegraphics[ width=5cm, ]{abc.jpg} ^D [ Para [ Image ( "" , [] , [ ( "width" , "5cm" ) ] ) [ Str "image" ] ( "abc.jpg" , "" ) ] ] ``` ``` % pandoc -f latex -t native \includegraphics[% width=5cm,% ]{abc.jpg} ^D [ Para [ Image ( "" , [] , [ ( "width" , "5cm" ) ] ) [ Str "image" ] ( "abc.jpg" , "" ) ] ] ``` ``` % pandoc -f latex -t native \includegraphics[width=5cm]{% abc.jpg% } ^D [ Para [ Image ( "" , [] , [ ( "width" , "5cm" ) ] ) [ Str "image" ] ( "abc.jpg" , "" ) ] ] ``` ================================================ FILE: test/command/10672.md ================================================ ``` % pandoc | This | Is | |:-----|:---| | With \ | Cells ^D
    This Is
    With \
    Cells
    ``` ================================================ FILE: test/command/10708.md ================================================ ``` % pandoc -f html -t mediawiki
    Case 1: Both subsets are non-empty
    In this case, …
    ^D ; Case 1: Both subsets are non-empty : In this case, … ``` ``` % pandoc -f mediawiki -t html ; term : definition ^D
    term
    definition
    ``` ================================================ FILE: test/command/10730.md ================================================ ``` % pandoc -f org -t native =hi there= ^D [ Para [ Code ( "" , [ "verbatim" ] , [] ) "hi there" ] ] ``` ================================================ FILE: test/command/10747.md ================================================ ``` % pandoc -t typst [Mark]{.mark} ^D #highlight[Mark] ``` ================================================ FILE: test/command/10755.md ================================================ ``` % pandoc -f native -t markdown [ Figure ( "" , [] , [] ) (Caption Nothing [ Para [ Str "An" , Space , Str "image." ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "An" , Space , Str "image." ] ( "media/rId20.jpg" , "" ) ] ] ] ^D ![An image.](media/rId20.jpg) ``` ``` % pandoc -f native -t markdown -t markdown-implicit_figures [ Figure ( "" , [] , [] ) (Caption Nothing [ Para [ Str "An" , Space , Str "image." ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "An" , Space , Str "image." ] ( "media/rId20.jpg" , "" ) ] ] ] ^D
    An image.
    ``` ``` % pandoc -f native -t markdown -t markdown-implicit_figures-raw_html [ Figure ( "" , [] , [] ) (Caption Nothing [ Para [ Str "An" , Space , Str "image." ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "An" , Space , Str "image." ] ( "media/rId20.jpg" , "" ) ] ] ] ^D :::: figure ![An image.](media/rId20.jpg) ::: caption An image. ::: :::: ``` ================================================ FILE: test/command/10758.md ================================================ ``` % pandoc -f native -t markdown [ Figure ( "" , [] , [] ) (Caption Nothing [ Para [ Str "Foo" , Space , Emph [ Str "emphasis" ] ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "Foo" , Space , Str "emphasis" ] ( "media/rId20.jpg" , "" ) ] ] ] ^D ![Foo *emphasis*](media/rId20.jpg) ``` ================================================ FILE: test/command/10781.md ================================================ ``` % pandoc -f latex -t markdown --wrap=preserve This is \texttt{--flag} with dashes. This is the same --flag without a code. ^D This is `--flag` with dashes. This is the same --flag without a code. ``` ================================================ FILE: test/command/10791.md ================================================ ``` % pandoc -t opendocument Aboard **the luxury cruise ship Heart of the Ocean[^1] in the Atlantic Ocean**... [^1]: **Heart of the Ocean** (海洋之心) – The Heart of the Ocean ^D Aboard the luxury cruise ship Heart of the Ocean1Heart of the Ocean (海洋之心) – The Heart of the Ocean in the Atlantic Ocean ``` ================================================ FILE: test/command/10805.md ================================================ ``` % pandoc -t typst -f latex \begin{equation} \label{eq:U} U = A \end{equation} ^D $ U = A $ ``` ``` % pandoc -t typst -f latex \begin{equation} \label{eq:U 2} U = A \end{equation} ^D $ U = A $#label("eq:U 2") ``` ================================================ FILE: test/command/10812.md ================================================ Check that the `four_space_rule` extension works for plain writer. ``` % pandoc -f markdown -t plain+four_space_rule This is the title Here we fix: - a - b - c ^D This is the title Here we fix: - a - b - c ``` Check that the `four_space_rule` extension is off by default. ``` % pandoc -f markdown -t plain This is the title Here we fix: - a - b - c ^D This is the title Here we fix: - a - b - c ``` ================================================ FILE: test/command/10816.md ================================================ ``` % pandoc -f markdown -t typst # Test① ^D = Test① #label("test①") ``` ================================================ FILE: test/command/10825.md ================================================ ``` % pandoc -f docbook -t html Literallayout test Literallayout without class First line. Second line. Third line, indented two spaces. Literallayout with normal class First line. Second line. Third line, indented two spaces. Literallayout with monospaced First line. Second line. Third line, indented two spaces. ^D

    Literallayout without class

    First line.
    Second line.
      Third line, indented two spaces.

    Literallayout with normal class

    First line.
    Second line.
      Third line, indented two spaces.

    Literallayout with monospaced

    First line.
    Second line.
      Third line, indented two spaces.
    ``` ================================================ FILE: test/command/10836.md ================================================ ``` % pandoc -f org -t latex Some equation here \begin{equation} x = y \end{equation} where $x$ is something important. ^D Some equation here \begin{equation} x = y \end{equation} where \(x\) is something important. ``` ================================================ FILE: test/command/10848.md ================================================ ``` % pandoc -f html -t markdown
    A F
    C B H
    D E G
    ^D +---+---+---+---+---+ | A | F | +---+-------+-------+ | C | B | H | +---+---+---+---+---+ | D | E | G | +-------+-------+---+ ``` ``` % pandoc -f html -t markdown
    A J F
    C B H
    D
    K
    ^D +---+---+-------+---+ | A | J | F | +---+---+-------+ | | C | B | H | | | +---+ | | | | D | | | | +---+-------+---+ | | K | +---+---------------+ ``` ``` % pandoc -f html -t markdown-simple_tables-multiline_tables-pipe_tables
    a
    ^D +---+---+ | a | | +---+---+ | | | +---+---+ ``` ================================================ FILE: test/command/10855.md ================================================ ``` % pandoc -t markdown +----------+----+ | h1 | h2 | +:===:+===:+:===+ | A | B | +-----+----+----+ | C | D | E | +-----+----+----+ ^D +-------------+----+ | h1 | h2 | +:====:+=====:+:===+ | A | B | +------+------+----+ | C | D | E | +------+------+----+ ``` ``` % pandoc -t markdown +-----+----+----+ | h1 | h2 | h3 | +:===:+===:+:===+ | A | B | +-----+----+----+ | C | D | E | +-----+----+----+ ^D +------+------+----+ | h1 | h2 | h3 | +:====:+=====:+:===+ | A | B | +------+------+----+ | C | D | E | +------+------+----+ ``` ``` % pandoc -t markdown +:------:+:-----------:+ | hello | ![a](b.png) | +--------+-------------+ | hello | ![c](b.png) | +--------+-------------+ ^D +:------:+:-----------:+ | hello | ![a](b.png) | +--------+-------------+ | hello | ![c](b.png) | +--------+-------------+ ``` ``` % pandoc -t markdown +:------:+:-----------:+ | hello | ![a](b.png) | +--------+-------------+ | hello | ![c](b.png) | +--------+-------------+ ^D +:------:+:-----------:+ | hello | ![a](b.png) | +--------+-------------+ | hello | ![c](b.png) | +--------+-------------+ ``` ================================================ FILE: test/command/10862.md ================================================ ``` % pandoc --embed-resources ^D ``` ================================================ FILE: test/command/10867.md ================================================ ``` % pandoc -f native -t markdown-raw_html [ Figure ( "fig:foo" , [] , [ ( "label" , "1.1" ) ] ) (Caption Nothing [ Plain [ Str "Figure" , Space , Str "1.1:" , Space , Str "Figure" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "Figure" , Space , Str "1.1:" , Space , Str "Figure" ] ( "./image.png" , "" ) ] ] ] ^D :::: {#fig:foo .figure label="1.1"} ![Figure 1.1: Figure](./image.png) ::: caption Figure 1.1: Figure ::: :::: ``` ================================================ FILE: test/command/10884.md ================================================ ``` % pandoc -f markdown -t markdown header1 header2 --------- --------- r1c1 r1c2 r2c1 r2c2 : mycaption {#myid .myclass key="value"} ^D header1 header2 --------- --------- r1c1 r1c2 r2c1 r2c2 : mycaption {#myid .myclass key="value"} ``` ================================================ FILE: test/command/10889.md ================================================ ``` % pandoc apple : pomaceous fruit ^D
    apple

    pomaceous

    fruit

    ``` ================================================ FILE: test/command/10890.md ================================================ ``` % pandoc --tab-stop=2 --from=native --to=markdown [ DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "pomaceous" ] , Para [ Str "fruit" ] ] ] ) ] ] ^D apple : pomaceous fruit ``` ================================================ FILE: test/command/10912.md ================================================ ``` % pandoc -f docbook -t native Para1 Para2 ^D [ OrderedList ( 4 , Decimal , DefaultDelim ) [ [ Para [ Str "Para1" ] ] , [ Para [ Str "Para2" ] ] ] ] ``` ================================================ FILE: test/command/10915.md ================================================ ``` % pandoc -f latex \newcommand{\a}{\ifmmode x \else y \fi} $\a$ and \a ^D

    x and y

    ``` ================================================ FILE: test/command/10919.md ================================================ # Org output with smart quotes turned on ``` % pandoc -t org+smart_quotes -s "It's nice" she said. ^D #+options: ':t "It's nice" she said. ``` Same test, but with special strings turned off. ``` % pandoc -t org+smart_quotes-special_strings -s "It's nice" she said. ^D #+options: ':t #+options: -:nil "It’s nice" she said. ``` ================================================ FILE: test/command/10926.md ================================================ ```` % pandoc -f html -t gfm
    test
    ^D ``` ruby test ``` ```` ```` % pandoc -f html -t gfm
    test
    ^D ``` ruby test ``` ```` ================================================ FILE: test/command/10942.md ================================================ 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" , "" ) ] ] ] ``` ================================================ FILE: test/command/10965.md ================================================ ``` % pandoc -f markdown -t typst ::: {lang="en"} This text should be in English. ::: ^D #block[ #set text(lang: "en"); This text should be in English. ] ``` ``` % pandoc -f markdown -t typst ::: {lang="fr"} Ce texte devrait être en français. ::: ^D #block[ #set text(lang: "fr"); Ce texte devrait être en français. ] ``` ``` % pandoc -f markdown -t typst ::: {lang="de-DE"} Dieser Text sollte auf Deutsch sein. ::: ^D #block[ #set text(lang: "de"); Dieser Text sollte auf Deutsch sein. ] ``` ``` % pandoc -f markdown -t typst ::: {lang=""} This should not have lang set. ::: ^D #block[ This should not have lang set. ] ``` ================================================ FILE: test/command/10983.md ================================================ ``` % pandoc --citeproc --csl command/chicago-fullnote-bibliography.csl -t plain --- references: - id: test4 type: blog-post title: "Username as author" author: - brtw container-title: "Reddit" issued: year: 2004 suppress-bibliography: true ... [@test4] ^D [1] [1] brtw, “Username as Author,” Reddit, 2004. ``` ================================================ FILE: test/command/10984.md ================================================ ``` % pandoc -f html -t djot

    Hi ^D {#foo .a .b} # Hi ``` In this one the id is suppressed by the djot writer because the same one would be automatically generated by the djot reader: ``` % pandoc -f html -t djot

    Introduction

    ^D ## Introduction ``` ================================================ FILE: test/command/11006.md ================================================ ``` % pandoc -f html -t asciidoc
    • Paragraph one

      Paragraph two to force a list continuation

      • First nested
      • Second nested

    How about ordered lists?

    1. Paragraph one

      Paragraph two to force a list continuation

      1. Nested item

    With non-default attributes:

    1. Paragraph one

      Paragraph two to force a list continuation

      1. Nested item
    ^D * Paragraph one + Paragraph two to force a list continuation ** First nested ** Second nested How about ordered lists? . Paragraph one + Paragraph two to force a list continuation .. Nested item With non-default attributes: . Paragraph one + Paragraph two to force a list continuation [start=5] .. Nested item ``` ================================================ FILE: test/command/11013.md ================================================ ``` % pandoc --csl command/chicago-fullnote-bibliography.csl -C --wrap=none -t plain --- references: - id: test10 type: blog-post title: "Test 10: Lowercase username in brackets, Works!" author: - literal: "[deleted]" container-title: "Reddit" issued: year: 2009 ... blah [@test10]. ^D blah.[1] [deleted]. “Test 10: Lowercase Username in Brackets, Works!” Reddit, 2009. [1] [deleted], “Test 10: Lowercase Username in Brackets, Works!” Reddit, 2009. ``` ================================================ FILE: test/command/11014.md ================================================ ``` % pandoc -f djot -t html {.foo} - bar ^D
    • bar
    ``` ================================================ FILE: test/command/11017.md ================================================ ``` % pandoc -t markdown -f typst #show heading: smallcaps = Introduction ^D # [Introduction]{.smallcaps} ``` ================================================ FILE: test/command/11046.md ================================================ ``` % pandoc --citeproc -t plain+smart --csl command/chicago-note-bibliography.csl --- references: - id: doe title: Title type: book date: 2006 author: John Doe ... blah blah [@doe]---blah blah. ^D blah blah[1]---blah blah. John Doe. Title, n.d. [1] John Doe, Title. ``` ================================================ FILE: test/command/11047.md ================================================ ``` % pandoc -t typst --wrap=auto Full-time study: 2001-2003; Thesis submission: Nov 2005; Award: Jul 2006. ^D Full-time study: 2001-2003; Thesis submission: Nov 2005; Award: Jul \2006. ``` ================================================ FILE: test/command/11048.md ================================================ ``` % pandoc -f biblatex -t markdown -s @article{khatri2021spooky, title = {Spooky action at a global distance: analysis of space-based entanglement distribution for the quantum internet}, author = {Khatri, Sumeet and Brady, Anthony J and Desporte, Ren{\'e}e A and Bart, Manon P and Dowling, Jonathan P}, journal = {{npj} Quantum Information}, volume = {7}, number = {1}, pages = {4}, doi = {10.1038/s41534-020-00327-5}, url = {https://doi.org/10.1038/s41534-020-00327-5}, year = {2021}, publisher = {Nature Publishing Group UK London}, } ^D --- nocite: "[@*]" references: - author: - family: Khatri given: Sumeet - family: Brady given: Anthony J - family: Desporte given: Renée A - family: Bart given: Manon P - family: Dowling given: Jonathan P container-title: "[npj]{.nocase} Quantum Information" doi: 10.1038/s41534-020-00327-5 id: khatri2021spooky issue: 1 issued: 2021 page: 4 publisher: Nature Publishing Group UK London title: "Spooky action at a global distance: Analysis of space-based entanglement distribution for the quantum internet" title-short: Spooky action at a global distance type: article-journal url: "https://doi.org/10.1038/s41534-020-00327-5" volume: 7 --- ``` ================================================ FILE: test/command/11090/ch1.typ ================================================ == Chapter One #figure( image("media/image1.png"), caption: [An image.] ) ================================================ FILE: test/command/11090.md ================================================ ``` % pandoc -f typst -t native #include "command/11090/ch1.typ" == Chapter Two #figure( image("command/11090/media/image1.png"), caption: [This is an image.] ) ^D [ Header 2 ( "" , [] , [] ) [ Str "Chapter" , Space , Str "One" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Para [ Str "An" , Space , Str "image." ] ]) [ Para [ Image ( "" , [] , [] ) [] ( "command/11090/media/image1.png" , "" ) ] ] , Header 2 ( "" , [] , [] ) [ Str "Chapter" , Space , Str "Two" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Para [ Str "This" , Space , Str "is" , Space , Str "an" , Space , Str "image." ] ]) [ Para [ Image ( "" , [] , [] ) [] ( "command/11090/media/image1.png" , "" ) ] ] ] ``` ================================================ FILE: test/command/11101.md ================================================ ``` % pandoc -f typst -t native First paragraph. #pagebreak() Second paragraph. ^D [ Para [ Str "First" , Space , Str "paragraph." ] , Div ( "" , [ "page-break" ] , [ ( "wrapper" , "1" ) ] ) [ HorizontalRule ] , Para [ Str "Second" , Space , Str "paragraph." ] ] ``` ================================================ FILE: test/command/11113.md ================================================ ``` % pandoc command/11113.docx -t plain ^D Colored:❤️😏📖👌😒🤍 BW: 😊︎ ``` ================================================ FILE: test/command/11124.md ================================================ The heading is "unlinked" before adding it to the TOC. ``` % pandoc --to=latex {-} ========================= ^D \section*{\texorpdfstring{\url{http://example.com/}}{http://example.com/}}\label{httpexample.com} \addcontentsline{toc}{section}{{http://example.com/}} ``` ================================================ FILE: test/command/11128.md ================================================ ``` % pandoc -f html -t markdown_strict+pipe_tables-raw_html
    Subject Grade
    Physics Practical A
    Theory B+
    ^D | **Subject** | | **Grade** | |-------------|-----------|-----------| | Physics | Practical | A | | | Theory | B+ | ``` ================================================ FILE: test/command/11140.md ================================================ ``` % pandoc -f rst -t native .. figure:: image.png :alt: my alt Caption ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Caption" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "my" , Space , Str "alt" ] ( "image.png" , "" ) ] ] ] ``` ``` % pandoc -f rst -t native .. figure:: image.png Caption ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Caption" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "Caption" ] ( "image.png" , "" ) ] ] ] ``` ``` % pandoc -f native -t markdown [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Caption" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "my", Space, Str "alt" ] ( "image.png" , "" ) ] ] ] ^D ![Caption](image.png){alt="my alt"} ``` Ignore `\pandocbounded`: ``` % pandoc -f latex -t native \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,alt={Caption}]{image.png}} \caption{Caption} \end{figure} ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Caption" ] ]) [ Para [ Image ( "" , [] , [] ) [ Str "Caption" ] ( "image.png" , "" ) ] ] ] ``` ================================================ FILE: test/command/11150.md ================================================ ``` % pandoc -f rst === ===== int float === ===== 10 9.90 -10 9.90 === ===== ^D
    int float
    10 9.90
    -10 9.90
    ``` ================================================ FILE: test/command/11162.md ================================================ ``` % pandoc -f native -t rst [ BulletList [ [ Plain [ Str "list" ] , CodeBlock ( "" , [ "ruby" ] , [] ) "code = 7 # comment" ] , [ Plain [ Str "second" , Space , Str "item" ] ] ] ] ^D - list .. code:: ruby code = 7 # comment - second item ``` ================================================ FILE: test/command/11188.md ================================================ Parsing PARAMETERS of Org-mode blocks ``` % pandoc -f org --to=native #+attr_html: :width 10px #+BEGIN_myex :this that huhu #+END_myex ^D [ Div ( "" , [ "myex" ] , [ ( "width" , "10px" ) , ( "this" , "that" ) ] ) [ Para [ Str "huhu" ] ] ] ``` Python-style parameters are accepted, too. ``` % pandoc -f org --to=native #+BEGIN_myblock width=10px [[image.svg][logo]] #+END_myblock ^D [ Div ( "" , [ "myblock" ] , [ ( "width" , "10px" ) ] ) [ Para [ Span ( "" , [ "spurious-link" ] , [ ( "target" , "image.svg" ) ] ) [ Emph [ Str "logo" ] ] ] ] ] ``` The fallback is to put the remainder of the line into a `parameters` attribute. ``` % pandoc -f org --to=native #+BEGIN_myblock these are parameters in an unsupported format /OK/ #+END_myblock ^D [ Div ( "" , [ "myblock" ] , [ ( "parameters" , "these are parameters in an unsupported format" ) ] ) [ Para [ Emph [ Str "OK" ] ] ] ] ``` Also works on dynamic blocks. ``` % pandoc -f org --to=markdown #+BEGIN: clocktable :scope subtree :maxlevel 3 #+CAPTION: Clock summary at [2025-10-18 Sat 17:23] | Headline | Time | |--------------+--------| | *Total time* | *0:00* | #+END: ^D ::: {.clocktable scope="subtree" maxlevel="3"} Headline Time ---------------- ---------- **Total time** **0:00** : Clock summary at \[2025-10-18 Sat 17:23\] ::: ``` ================================================ FILE: test/command/11210.md ================================================ ``` % pandoc -t typst -f man .PP .IR login (1) .PP and a regular (paren) that should not be escaped. ^D #emph[login]\(1) and a regular (paren) that should not be escaped. ``` ================================================ FILE: test/command/11211.md ================================================ ``` % pandoc -f rtf -t native {\field{\*\fldinst{\rtlch\ab0\ai0\af2\alang1025\afs22\ltrch\b0\i0\fs22\lang1033\langnp1033\langfe1033\langfenp1033 \loch\af2\dbch\af2\hich\f2\insrsid10976062\strike0\ulnone\cf1 HYPERLINK "https://example.com"}{\*\datafield 08d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b28000000680074007400700073003a002f002f006500780061006d0070006c0065002e0063006f006d000000}} {\fldrslt{\rtlch\ab0\ai0\af2\alang1025\afs22\ltrch\b0\i0\fs22\lang1033\langnp1033\langfe1033\langfenp1033\loch\af2\dbch\af2\hich\f2\strike0\ul\cf2 link}}} ^D [ Para [ Underline [ Link ( "" , [] , [] ) [ Str "link" ] ( "https://example.com" , "" ) ] ] ] ``` ================================================ FILE: test/command/11253.md ================================================ ``` % pandoc -f markdown -t native \ifstrequal{hello}{hello}{TRUE}{FALSE} \ifstrequal{hello}{world}{TRUE}{FALSE} ^D [ Para [ RawInline (Format "tex") "TRUE" , SoftBreak , RawInline (Format "tex") "FALSE" ] ] ``` ``` % pandoc -f markdown-latex_macros -t native \ifstrequal{hello}{hello}{TRUE}{FALSE} \ifstrequal{hello}{world}{TRUE}{FALSE} ^D [ Para [ RawInline (Format "tex") "\\ifstrequal{hello}{hello}{TRUE}{FALSE}" , SoftBreak , RawInline (Format "tex") "\\ifstrequal{hello}{world}{TRUE}{FALSE}" ] ] ``` ================================================ FILE: test/command/1126.md ================================================ ``` % pandoc -f html -t latex \begin{eqnarray} A&=&B,\\ C&=&D \end{eqnarray} ^D \textbackslash begin\{eqnarray\} A\&=\&B,\textbackslash\textbackslash{} C\&=\&D \textbackslash end\{eqnarray\} ``` ``` % pandoc -f html+raw_tex -t latex

    See \eqref{myeq}.

    \begin{eqnarray} A&=&B,\\ C&=&D \\label{myeq} \end{eqnarray} ^D See \eqref{myeq}. \begin{eqnarray} A&=&B,\\ C&=&D \\label{myeq} \end{eqnarray} ``` ================================================ FILE: test/command/11266.md ================================================ ``` % pandoc -t latex $$ \begin{eqnarray} S &\rightarrow& a A \nonumber \\ A &\rightarrow& d B \ | \ b A \ | \ c A \nonumber \\ B &\rightarrow& a c \ | \ b C \ | \ c A \nonumber \\ C &\rightarrow& \epsilon \nonumber \end{eqnarray} $$ ^D \begin{eqnarray} S &\rightarrow& a A \nonumber \\ A &\rightarrow& d B \ | \ b A \ | \ c A \nonumber \\ B &\rightarrow& a c \ | \ b C \ | \ c A \nonumber \\ C &\rightarrow& \epsilon \nonumber \end{eqnarray} ``` ================================================ FILE: test/command/11270.md ================================================ ``` % pandoc -f markdown -t native -s --- header-includes: | \makeatletter \beamer@ignorenonframefalse \makeatother ... ^D Pandoc Meta { unMeta = fromList [ ( "header-includes" , MetaBlocks [ RawBlock (Format "tex") "\\makeatletter\n\\beamer@ignorenonframefalse\n\\makeatother" ] ) ] } [] ``` ================================================ FILE: test/command/11299.md ================================================ ``` % pandoc -f mediawiki -t native x x x This is ''highlighted'' ^D [ Para [ Str "" ] , 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" ] ] ] ] ``` ================================================ FILE: test/command/11300.md ================================================ ``` % pandoc -f docbook -t native -s Book title Book subtitle Chapter title My sentence ^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 Chapter title My sentence Book title Book subtitle ^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" ] ] ``` ================================================ FILE: test/command/11309.md ================================================ ``` % pandoc -f rst -t native Cho\ **co**\ late ^D [ Para [ Str "Cho" , Strong [ Str "co" ] , Str "late" ] ] ``` ================================================ FILE: test/command/11312.md ================================================ ``` % pandoc -f mediawiki -t texinfo foofoo bar baz foofoo ^D @node Top @top Top foofoo @example bar baz @end example foofoo ``` ================================================ FILE: test/command/11323.md ================================================ ``` % pandoc -f rst foo- bar ^D
    foo-

    bar

    ``` ================================================ FILE: test/command/11341.md ================================================ ``` % pandoc -t commonmark -f html

    The Hobbit
    or
    There and Back Again

    ^D The Hobbit\ or\ There and Back Again ==================== ``` ================================================ FILE: test/command/11342.md ================================================ 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

    Function graphs.

    Graph for function *A*.

    Graph for function *B*.

    ``` ================================================ FILE: test/command/11348.md ================================================ ``` % 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" ] ] ``` ================================================ FILE: test/command/11362.md ================================================ ``` % 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#. ``` ================================================ FILE: test/command/11364.md ================================================ ``` % 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 ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/11374.md ================================================ ``` % pandoc -f native -t asciidoc [ Para [ SmallCaps [ Str "foo" ] ] ] ^D [smallcaps]#foo# ``` ================================================ FILE: test/command/11384.md ================================================ ``` % 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$ ``` ================================================ FILE: test/command/11409.md ================================================ ``` % pandoc -t native text^[sup]{.class}^ ^D [ Para [ Str "text" , Superscript [ Span ( "" , [ "class" ] , [] ) [ Str "sup" ] ] ] ] ``` ================================================ FILE: test/command/11420.md ================================================ ```` % pandoc -t revealjs --syntax-highlighting=idiomatic # Slide ```python def hello(): print("Hello") ``` ^D

    Slide

    def hello():
        print("Hello")
    ```` ================================================ FILE: test/command/11422.md ================================================ ``` % pandoc -f docbook -t markdown Example without a title. ^D ::: example Example without a title. ::: ``` ================================================ FILE: test/command/11450.md ================================================ Test for \footnotemark and \footnotetext (issue #11450) ``` % pandoc -f latex -t native Text\footnotemark{}. \footnotetext{The footnote content.} ^D [ Para [ Str "Text" , Note [ Para [ Str "The" , Space , Str "footnote" , Space , Str "content." ] ] , Str "." ] ] ``` With explicit footnote numbers: ``` % pandoc -f latex -t native First\footnotemark[1] and second\footnotemark[2]. \footnotetext[1]{First note.} \footnotetext[2]{Second note.} ^D [ Para [ Str "First" , Note [ Para [ Str "First" , Space , Str "note." ] ] , Space , Str "and" , Space , Str "second" , Note [ Para [ Str "Second" , Space , Str "note." ] ] , Str "." ] ] ``` Mixed with regular footnotes: ``` % pandoc -f latex -t native Text\footnotemark[1] and more\footnote{Regular footnote.} \footnotetext[1]{Marked footnote.} ^D [ Para [ Str "Text" , Note [ Para [ Str "Marked" , Space , Str "footnote." ] ] , Space , Str "and" , Space , Str "more" , Note [ Para [ Str "Regular" , Space , Str "footnote." ] ] ] ] ``` ================================================ FILE: test/command/11455.md ================================================ ``` % pandoc -f textile -t html | foo | bar | | table * item 1 * item 2 * item 3 | xxx | ^D
    foo bar

    table

    • item 1
    • item 2
    • item 3
    xxx
    ``` ================================================ FILE: test/command/11463.md ================================================ ``` % pandoc -t typst 'hi' and \' ^D 'hi' and \' ``` ``` % pandoc -f markdown -t typst | pandoc -f typst -t markdown "hi" \" 'hi' and \' ^D "hi" \" 'hi' and \' ``` ================================================ FILE: test/command/11479.md ================================================ ``` % pandoc -f docbook -t gfm Test. Test 2. ^D > [!IMPORTANT] > - Test. > > - Test 2. ``` ``` % pandoc -f gfm -t gfm > [!IMPORTANT] > - Test. > > - Test 2. ^D > [!IMPORTANT] > - Test. > > - Test 2. ``` ================================================ FILE: test/command/11486/scroll.revealjs ================================================ $if(view)$ scrollActivationWidth: $scrollActivationWidth$, $if(scrollSnap)$ scrollSnap: '$scrollSnap/nowrap$', $else$ scrollSnap: false, $endif$ $if(scrollProgressAuto)$ scrollProgress: 'auto', $elseif(scrollProgress)$ scrollProgress: $scrollProgress$, $else$ scrollProgress: false, $endif$ $endif$ ================================================ FILE: test/command/11486.md ================================================ ``` % pandoc -t revealjs --template=command/11486/scroll.revealjs --- view: scroll --- ^D scrollActivationWidth: 0, scrollSnap: 'mandatory', scrollProgress: 'auto', ``` ``` % pandoc -t revealjs --template=command/11486/scroll.revealjs --- view: scroll scrollSnap: false --- ^D scrollActivationWidth: 0, scrollSnap: false, scrollProgress: 'auto', ``` ``` % pandoc -t revealjs --template=command/11486/scroll.revealjs --- view: scroll scrollSnap: proximity --- ^D scrollActivationWidth: 0, scrollSnap: 'proximity', scrollProgress: 'auto', ``` ``` % pandoc -t revealjs --template=command/11486/scroll.revealjs --- view: scroll scrollActivationWidth: 500 --- ^D scrollActivationWidth: 500, scrollSnap: 'mandatory', scrollProgress: 'auto', ``` ``` % pandoc -t revealjs --template=command/11486/scroll.revealjs --- view: scroll scrollProgress: true --- ^D scrollActivationWidth: 0, scrollSnap: 'mandatory', scrollProgress: true, ``` ``` % pandoc -t revealjs --template=command/11486/scroll.revealjs --- view: scroll scrollProgress: false --- ^D scrollActivationWidth: 0, scrollSnap: 'mandatory', scrollProgress: false, ``` ``` % pandoc -t revealjs --template=command/11486/scroll.revealjs --- view: scroll scrollProgress: auto --- ^D scrollActivationWidth: 0, scrollSnap: 'mandatory', scrollProgress: 'auto', ``` ================================================ FILE: test/command/11490.md ================================================ ``` % pandoc -t markdown <&lt; ^D \<\< ``` ``` % pandoc -t markdown &#xa0; ^D \  ``` ================================================ FILE: test/command/11494.md ================================================ External images should not use [[File:...]] syntax. Inline external image: ``` % pandoc -f native -t mediawiki [Para [Image ("",[],[]) [] ("https://example.org/image.png", "")]] ^D https://example.org/image.png ``` Inline local image (should still use [[File:]]): ``` % pandoc -f native -t mediawiki [Para [Image ("",[],[]) [] ("local.png", "")]] ^D [[File:local.png]] ``` Figure with external image (from markdown): ``` % pandoc -f markdown -t mediawiki ![](https://example.org/figure.png) ^D https://example.org/figure.png ``` Local image (from markdown, no caption): ``` % pandoc -f markdown -t mediawiki ![](local.png) ^D [[File:local.png]] ``` Figure with caption and external image: ``` % pandoc -f markdown -t mediawiki ![Caption](https://example.org/figure.png) ^D
    https://example.org/figure.png
    ``` Figure with caption and local image: ``` % pandoc -f markdown -t mediawiki ![Caption](local.png) ^D
    [[File:local.png|Caption]]
    ``` SimpleFigure with external image (native format with fig: title): ``` % pandoc -f native -t mediawiki [Figure ("",[],[]) (Caption Nothing []) [Para [Image ("",[],[]) [Str "caption"] ("https://example.org/figure.png", "fig:")]]] ^D
    https://example.org/figure.png
    ``` SimpleFigure with local image (native format with fig: title): ``` % pandoc -f native -t mediawiki [Figure ("",[],[]) (Caption Nothing []) [Para [Image ("",[],[]) [Str "caption"] ("local.png", "fig:")]]] ^D
    [[File:local.png|thumb|none|alt=caption|caption]]
    ``` ================================================ FILE: test/command/11498.md ================================================ ``` % pandoc -f markdown -t icml ![](command/11498.png){object-style="myStyle"} ^D $ID/Embedded ``` ``` % pandoc -s -f markdown -t icml ![](command/11498.png){object-style="myStyle"} ^D $ID/NormalCharacterStyle LeftAlign . 10 $ID/NormalParagraphStyle $ID/None $ID/Embedded ``` ================================================ FILE: test/command/11511.md ================================================ ``` % pandoc -f html -t typst

    torch.utils.data.Dataset

    ^D #link("http://torch.utils.data")[torch.utils.data]\.Dataset ``` ================================================ FILE: test/command/11534.md ================================================ ``` % pandoc -f markdown+lists_without_preceding_blankline+alerts -t native :::: caution ::: title Caution ::: Foo bar, wuppie fluppie! :::: ^D [ Div ( "" , [ "caution" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Caution" ] ] , Para [ Str "Foo" , Space , Str "bar," , Space , Str "wuppie" , Space , Str "fluppie!" ] ] ] ``` ``` % pandoc -f markdown+lists_without_preceding_blankline -t native hello - a - b c ^D [ Para [ Str "hello" ] , BulletList [ [ Plain [ Str "a" ] ] , [ Plain [ Str "b" , SoftBreak , Str "c" ] ] ] ] ``` ================================================ FILE: test/command/1166.md ================================================ See #1166 and . ``` % pandoc -f rst -t html5 ===== ===== col 1 col 2 ===== ===== 1 Second column of row 1. 2 Second column of row 2. Second line of paragraph. 3 - Second column of row 3. - Second item in bullet list (row 3, column 2). \ Row 4; column 1 will be empty. ===== ===== ^D
    col 1 col 2
    1 Second column of row 1.
    2 Second column of row 2. Second line of paragraph.
    3
    • Second column of row 3.
    • Second item in bullet list (row 3, column 2).
    Row 4; column 1 will be empty.
    ``` ================================================ FILE: test/command/1279.md ================================================ ``` % pandoc -s -t markdown --- author: 'John Doe[^1]' date: 2014 title: My Article --- [^1]: Dept. of This and That ^D --- author: John Doe[^1] date: 2014 title: My Article --- [^1]: Dept. of This and That ``` ================================================ FILE: test/command/1390.md ================================================ ``` % pandoc -f latex -t native \newcommand\foo{+} Testing: $\mu\foo\eta$. ^D [ Para [ Str "Testing:" , Space , Math InlineMath "\\mu+\\eta" , Str "." ] ] ``` ================================================ FILE: test/command/1592.md ================================================ ``` % pandoc -t native [hi]{.smallcaps} ^D [ Para [ SmallCaps [ Str "hi" ] ] ] ``` ``` % pandoc -t native [hi]{style="font-variant: small-caps;"} ^D [ Para [ SmallCaps [ Str "hi" ] ] ] ``` ``` % pandoc -t native hi ^D [ Para [ SmallCaps [ Str "hi" ] ] ] ``` ``` % pandoc -f html -t native

    hi

    ^D [ Para [ SmallCaps [ Str "hi" ] ] ] ``` ``` % pandoc -f html -t native

    hi

    ^D [ Para [ SmallCaps [ Str "hi" ] ] ] ``` ``` % pandoc -f native -t html [Para [SmallCaps [Str "hi"]]] ^D

    hi

    ``` ``` % pandoc -f native -t markdown [Para [SmallCaps [Str "hi"]]] ^D [hi]{.smallcaps} ``` ``` % pandoc -f html -t native foo ^D [ Plain [ Span ( "" , [] , [ ( "dir" , "ltr" ) ] ) [ Str "foo" ] ] ] ``` ``` % pandoc -f html -t native foobarbaz ^D [ Plain [ Span ( "" , [] , [ ( "dir" , "rtl" ) ] ) [ Str "foo" , Span ( "" , [] , [ ( "dir" , "ltr" ) ] ) [ Str "bar" ] , Str "baz" ] ] ] ``` ``` % pandoc -f html -t native

    This text will go right to left.

    ^D [ Para [ Span ( "" , [] , [ ( "dir" , "rtl" ) ] ) [ Str "This" , Space , Str "text" , Space , Str "will" , Space , Str "go" , Space , Str "right" , SoftBreak , Str "to" , Space , Str "left." ] ] ] ``` ================================================ FILE: test/command/1608.md ================================================ ``` % pandoc -f latex -t native \newtheorem{theorem}{Theorem} \newtheorem{corollary}[theorem]{Corollary} \newtheorem{lemma}[theorem]{Lemma} \theoremstyle{definition} \newtheorem{definition}[theorem]{Definition} \theoremstyle{remark} \newtheorem{remark}{Remark} \begin{definition}[right-angled triangles] \label{def:tri} A \emph{right-angled triangle} is a triangle whose sides of length~\(a\), \(b\) and~\(c\), in some permutation of order, satisfies \(a^2+b^2=c^2\). \end{definition} \begin{lemma} The triangle with sides of length~\(3\), \(4\) and~\(5\) is right-angled. \end{lemma} \begin{proof} This lemma follows from \cref{def:tri} since \(3^2+4^2=9+16=25=5^2\). \end{proof} \begin{theorem}[Pythagorean triplets] \label{thm:py} Triangles with sides of length \(a=p^2-q^2\), \(b=2pq\) and \(c=p^2+q^2\) are right-angled triangles. \end{theorem} \begin{remark} These are all pretty interesting facts. \end{remark} ^D [ Div ( "def:tri" , [ "definition" ] , [] ) [ Para [ Strong [ Str "Definition" , Space , Str "1" ] , Space , Str "(right-angled" , Space , Str "triangles)." , Space , Space , Str "A" , Space , Emph [ Str "right-angled" , Space , Str "triangle" ] , Space , Str "is" , Space , Str "a" , Space , Str "triangle" , Space , Str "whose" , Space , Str "sides" , Space , Str "of" , Space , Str "length\160" , Math InlineMath "a" , Str "," , Space , Math InlineMath "b" , Space , Str "and\160" , Math InlineMath "c" , Str "," , Space , Str "in" , Space , Str "some" , Space , Str "permutation" , Space , Str "of" , Space , Str "order," , Space , Str "satisfies" , Space , Math InlineMath "a^2+b^2=c^2" , Str "." ] ] , Div ( "" , [ "lemma" ] , [] ) [ Para [ Strong [ Str "Lemma" , Space , Str "2" ] , Str "." , Space , Space , Emph [ Str "The" , Space , Str "triangle" , Space , Str "with" , Space , Str "sides" , Space , Str "of" , Space , Str "length\160" , Math InlineMath "3" , Str "," , Space , Math InlineMath "4" , Space , Str "and\160" , Math InlineMath "5" , Space , Str "is" , Space , Str "right-angled." ] ] ] , Div ( "" , [ "proof" ] , [] ) [ Para [ Emph [ Str "Proof." ] , Space , Str "This" , Space , Str "lemma" , Space , Str "follows" , Space , Str "from" , Space , Link ( "" , [] , [ ( "reference-type" , "ref+label" ) , ( "reference" , "def:tri" ) ] ) [ Str "1" ] ( "#def:tri" , "" ) , Space , Str "since" , Space , Math InlineMath "3^2+4^2=9+16=25=5^2" , Str "." , Str "\160\9723" ] ] , Div ( "thm:py" , [ "theorem" ] , [] ) [ Para [ Strong [ Str "Theorem" , Space , Str "3" ] , Space , Str "(Pythagorean" , Space , Str "triplets)." , Space , Space , Emph [ Str "Triangles" , Space , Str "with" , Space , Str "sides" , Space , Str "of" , Space , Str "length" , Space , Math InlineMath "a=p^2-q^2" , Str "," , Space , Math InlineMath "b=2pq" , Space , Str "and" , Space , Math InlineMath "c=p^2+q^2" , Space , Str "are" , Space , Str "right-angled" , Space , Str "triangles." ] ] ] , Div ( "" , [ "remark" ] , [] ) [ Para [ Emph [ Str "Remark" , Space , Str "1" ] , Str "." , Space , Space , Str "These" , Space , Str "are" , Space , Str "all" , Space , Str "pretty" , Space , Str "interesting" , Space , Str "facts." ] ] ] ``` ================================================ FILE: test/command/1629.md ================================================ ``` % pandoc -t latex --syntax-highlighting=idiomatic bla bla `a % b` *bla bla `a % b`* ^D bla bla \passthrough{\lstinline!a \% b!} \emph{bla bla \passthrough{\lstinline!a \% b!}} ``` ================================================ FILE: test/command/168.md ================================================ ``` % pandoc -t native :::::::::: warning :::::::::::: This is the warning! 1. list 2. another ::: {#myid .class key=val} nested div ::: ::::::::::::::::::::::::::::::: ^D [ Div ( "" , [ "warning" ] , [] ) [ Para [ Str "This" , Space , Str "is" , Space , Str "the" , Space , Str "warning!" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "list" ] ] , [ Plain [ Str "another" ] ] ] , Div ( "myid" , [ "class" ] , [ ( "key" , "val" ) ] ) [ Para [ Str "nested" , Space , Str "div" ] ] ] ] ``` ``` % pandoc -t native foo ::: bar ^D [ Para [ Str "foo" , SoftBreak , Str ":::" , SoftBreak , Str "bar" ] ] ``` ``` % pandoc -t native ::::: Warning Here is a paragraph. And another. ::::: ^D [ Div ( "" , [ "Warning" ] , [] ) [ Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "paragraph." ] , Para [ Str "And" , Space , Str "another." ] ] ] ``` ================================================ FILE: test/command/1710.md ================================================ ``` % pandoc -t revealjs # Slide one
    - a - b
    - c - d
    ok
    ^D

    Slide one

    • a
    • b
    • c
    • d

    ok

    ``` ``` % pandoc -t beamer # Slide one
    - a - b
    - c - d
    ok
    ^D \begin{frame}{Slide one} \protect\phantomsection\label{slide-one} \begin{columns}[T] \begin{column}{0.4\linewidth} \begin{itemize} \tightlist \item a \item b \end{itemize} \end{column} \begin{column}{0.4\linewidth} \begin{itemize} \tightlist \item c \item d \end{itemize} \end{column} \begin{column}{0.1\linewidth} ok \end{column} \end{columns} \end{frame} ``` ================================================ FILE: test/command/1718.md ================================================ ``` % pandoc -t native Note[^1]. [^1]: the first note. [^2]: the second, unused, note. ^D 2> [WARNING] Note with key '2' defined at line 5 column 1 but not used. [ Para [ Str "Note" , Note [ Para [ Str "the" , Space , Str "first" , Space , Str "note." ] ] , Str "." ] ] ``` ================================================ FILE: test/command/1745.md ================================================ ``` % pandoc -f latex+auto_identifiers -t html \section{Six favourite beers} \subsection{Jovaru Alus}\label{jovaru-alus} \section{Farmhouse brewers} \subsection{Jovaru Alus} ^D

    Six favourite beers

    Jovaru Alus

    Farmhouse brewers

    Jovaru Alus

    ``` ================================================ FILE: test/command/1762.md ================================================ ``` % pandoc -t latex # One {.unlisted} # Two {.unnumbered} # Three {.unlisted .unnumbered} ^D \section{One}\label{one} \section*{Two}\label{two} \addcontentsline{toc}{section}{Two} \section*{Three}\label{three} ``` ================================================ FILE: test/command/1773.md ================================================ ``` % pandoc -f latex+raw_tex -t native \noindent hi ^D [ Para [ RawInline (Format "latex") "\\noindent " , Str "hi" ] ] ``` ================================================ FILE: test/command/1841.md ================================================ ``` % pandoc
    *one* [a link](http://google.com)
    ^D
    one a link
    ``` ``` % pandoc
    *one* [a link](http://google.com)
    ^D
    one a link
    ``` ================================================ FILE: test/command/1881.md ================================================ ``` % pandoc -f html -t native
    Demonstration of simple table syntax.
    Right Left Center Default
    12 12 12 12
    ^D [ Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f html -t native
    12 12 12 12
    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/1905.md ================================================ ``` % pandoc -f latex-auto_identifiers -t html \chapter{chapone} \part{partone} \chapter{chaptwo} \section{secone} ^D

    chapone

    partone

    chaptwo

    secone

    ``` ``` % pandoc -f latex-auto_identifiers -t html \chapter{chapone} \chapter{chaptwo} \section{secone} ^D

    chapone

    chaptwo

    secone

    ``` ``` % pandoc -f latex-auto_identifiers -t html \section{secone} ^D

    secone

    ``` ================================================ FILE: test/command/2103.md ================================================ ``` % pandoc -t latex | A happy pandoc user said "fix this bug please | or I'll go crazy!" ^D A happy pandoc user said ``fix this bug please\\ or I'll go crazy!'' ``` ================================================ FILE: test/command/2118.md ================================================ ``` % pandoc -f latex -t native \newcommand{\inclgraph}{\includegraphics[width=0.8\textwidth]} \begin{figure}[ht] \inclgraph{setminus.png} \caption{Set subtraction} \label{fig:setminus} \end{figure} ^D [ Figure ( "fig:setminus" , [] , [ ( "latex-placement" , "ht" ) ] ) (Caption Nothing [ Plain [ Str "Set" , Space , Str "subtraction" ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "80%" ) ] ) [] ( "setminus.png" , "" ) ] ] ] ``` ================================================ FILE: test/command/2228.md ================================================ ``` % pandoc -f markdown+smart -t latex+smart *foo*'s 'foo' ^D \emph{foo}'s `foo' ``` ================================================ FILE: test/command/2337.md ================================================ ``` % pandoc -t asciidoc -f html ][ ^D http://example.com[++][++] ``` ================================================ FILE: test/command/2378.md ================================================ Ensure that we don't get duplicated footnotes when a note occurs in a header cell and `\endfirsthead` is used. ``` % pandoc -t latex | x | y[^fn] | |-|-| |1|2| : a table [^fn]: a footnote ^D \begin{longtable}[]{@{}ll@{}} \caption{a table}\tabularnewline \toprule\noalign{} x & y\footnote{a footnote} \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} x & y{} \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot 1 & 2 \\ \end{longtable} ``` ================================================ FILE: test/command/2397.md ================================================ ``` % pandoc -f markdown_mmd # Chapter 1: A long name of chapter [Chapter 1] See [Chapter 1]. ^D

    Chapter 1: A long name of chapter

    See Chapter 1.

    ``` ================================================ FILE: test/command/2434.md ================================================ ``` % pandoc -t opendocument 1. a 2. b 1. alpha 2. beta * gamma ^D a b alpha beta gamma ``` ``` % pandoc -t opendocument (@) text some text a) sub item 1 b) sub item 2 more text -- this line is missing in the odt output ^D text some text sub item 1 sub item 2 more text – this line is missing in the odt output ``` ================================================ FILE: test/command/2465.md ================================================ ``` % pandoc -f textile -t native This list starts: # one # two This list should continue at 3: #3 three # four This list should restart at 1: # one again # two again ^D [ Para [ Str "This" , Space , Str "list" , Space , Str "starts:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] ] ] , Para [ Str "This" , Space , Str "list" , Space , Str "should" , Space , Str "continue" , Space , Str "at" , Space , Str "3:" ] , OrderedList ( 3 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "three" ] ] , [ Plain [ Str "four" ] ] ] , Para [ Str "This" , Space , Str "list" , Space , Str "should" , Space , Str "restart" , Space , Str "at" , Space , Str "1:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "one" , Space , Str "again" ] ] , [ Plain [ Str "two" , Space , Str "again" ] ] ] ] ``` ================================================ FILE: test/command/2549.md ================================================ ``` % pandoc -f latex -t native \hypertarget{foo}{% \section{A section}\label{foo} } ^D [ Header 1 ( "foo" , [] , [] ) [ Str "A" , Space , Str "section" ] ] ``` ``` % pandoc -f latex -t native \hypertarget{bar}{% \section{A section}\label{foo} } ^D [ Div ( "bar" , [] , [] ) [ Header 1 ( "foo" , [] , [] ) [ Str "A" , Space , Str "section" ] ] ] ``` ``` % pandoc -f latex -t native Bar \hypertarget{foo}{Foo} ^D [ Para [ Str "Bar" , Space , Span ( "foo" , [] , [] ) [ Str "Foo" ] ] ] ``` ``` % pandoc -f latex -t native \hypertarget{foo}{% \begin{verbatim} bar \end{verbatim} } ^D [ Div ( "foo" , [] , [] ) [ CodeBlock ( "" , [] , [] ) "bar" ] ] ``` ================================================ FILE: test/command/2552.md ================================================ ``` % pandoc --strip-comments Foo bar bazboop ^D

    Foo

    bar

    bazboop

    ``` ================================================ FILE: test/command/256.md ================================================ ``` % pandoc --abbreviations=command/abbrevs -t native Foo. bar baz h.k. and e.g. and Mr. Brown. ^D [ Para [ Str "Foo.\160bar" , Space , Str "baz" , Space , Str "h.k.\160and" , Space , Str "e.g." , Space , Str "and" , Space , Str "Mr." , Space , Str "Brown." ] ] ``` ``` % pandoc -t native Foo. bar baz h.k. and e.g. and Mr. Brown. ^D [ Para [ Str "Foo." , Space , Str "bar" , Space , Str "baz" , Space , Str "h.k." , Space , Str "and" , Space , Str "e.g.\160and" , Space , Str "Mr.\160Brown." ] ] ``` ================================================ FILE: test/command/2602.md ================================================ ``` % pandoc [a] [b] [b]: url ^D

    [a] b

    ``` ``` % pandoc -f markdown+spaced_reference_links [a] [b] [b]: url ^D

    a

    ``` ================================================ FILE: test/command/2606.md ================================================ ``` % pandoc -f mediawiki -t html5 {| | * hello |} ^D

    * hello

    ``` ``` % pandoc -f mediawiki -t html5 {| | * hello |} ^D
    • hello
    ``` ``` % pandoc -f mediawiki -t html5 {| | * hello |} ^D

    * hello

    ``` ``` % pandoc -f mediawiki -t html5 * * hi ^D
    • * hi
    ``` ================================================ FILE: test/command/262.md ================================================ ``` % pandoc -f rst `hello`_ and `goodbye`_ .. _hello: .. _goodbye: example.com ^D

    hello and goodbye

    ``` ``` % pandoc -f rst `hello`_ `goodbye`_ .. _hello: .. _goodbye: paragraph ^D

    hello goodbye

    paragraph

    ``` ================================================ FILE: test/command/2649.md ================================================ ``` % pandoc -f mediawiki -t html5 {| class="wikitable" style="line-height: 1.0" |- bgcolor="#efefef" |} ^D
    ``` ``` % pandoc -f mediawiki -t html5 {| border="4" cellspacing="2" cellpadding="0" WIDTH="100%" |----- | peildatum Simbase || november 2005 || colspan=2 | '''uitslagen Flohrgambiet''' |----- | totaal aantal partijen Simbase || 7.316.773 | wit wint || 53% |----- | percentage (en partijen) Flohrgambiet | 0.023 % (1.699) || zwart wint || 27% |----- | percentage Flohrgambiet in aug 2003 | 0.035 % || remise || 20% |} ^D

    peildatum Simbase

    november 2005

    uitslagen Flohrgambiet

    totaal aantal partijen Simbase

    7.316.773

    wit wint

    53%

    percentage (en partijen) Flohrgambiet

    0.023 % (1.699)

    zwart wint

    27%

    percentage Flohrgambiet in aug 2003

    0.035 %

    remise

    20%

    ``` ``` % pandoc -f mediawiki -t html5 {| class="wikitable" style="text-align:center; font-size:95%" valign="top" | ! Plaats ! Rijder ! Aantal |- | 1 |align=left| {{FR-VLAG}} [[Sébastien Loeb]] | 78 |- | 2 |align=left| {{FR-VLAG}} '''[[Sébastien Ogier]]''' | 38 |- | 10 |align=left| {{FI-VLAG}} [[Hannu Mikkola]] | 18 |} ^D

    Plaats

    Rijder

    Aantal

    1

    Sébastien Loeb

    78

    2

    Sébastien Ogier

    38

    10

    Hannu Mikkola

    18

    ``` ================================================ FILE: test/command/2662.md ================================================ ``` % pandoc -t html -f rst --wrap=none .. image:: http://url.to.image/foo.png :align: left :height: 100px :width: 200 px :scale: 300 % :alt: alternate text ^D

    alternate text

    ``` ================================================ FILE: test/command/2834.md ================================================ Nested grid tables. ``` % pandoc -f html -t markdown --columns=72
    some text
    ^D +---------------------------------------------------------------------------+ | +-----------------------------------------------------------------------+ | | | ----------- | | | | some text | | | | ----------- | | | +-----------------------------------------------------------------------+ | +---------------------------------------------------------------------------+ ``` ================================================ FILE: test/command/2874.md ================================================ ``` % pandoc -f html -t latex
    ^D {}\strut \\ ``` ``` % pandoc -f html -t latex
    ^D \protect\phantomsection\label{foo}{}\strut \\ ``` ================================================ FILE: test/command/2994.md ================================================ ``` % pandoc -f markdown -t docx -o - | pandoc -f docx -t markdown --track-changes=all I want [I left a comment.]{.comment-start id="0" author="Jesse Rosenthal" date="2016-05-09T16:13:00Z"}some text to have a comment []{.comment-end id="0"}on it. ^D I want [I left a comment.]{.comment-start id="0" author="Jesse Rosenthal" date="2016-05-09T16:13:00Z"}some text to have a comment []{.comment-end id="0"}on it. ``` ================================================ FILE: test/command/3113.md ================================================ ``` % pandoc -f latex -t native \begin{eqnarray} A&=&B,\\ C&=&D,\\ %\end{eqnarray} %\begin{eqnarray} E&=&F \end{eqnarray} ^D [ Para [ Math DisplayMath "\\begin{eqnarray}\nA&=&B,\\\\\nC&=&D,\\\\\n%\\end{eqnarray}\n%\\begin{eqnarray}\nE&=&F\n\\end{eqnarray}" ] ] ``` ================================================ FILE: test/command/3123.md ================================================ ``` % pandoc -f markdown -t native ^D [ RawBlock (Format "html") "" ] ``` ``` % pandoc -f markdown -t native a ^D [ Para [ Str "a" , RawInline (Format "html") "" ] ] ``` ================================================ FILE: test/command/3236.md ================================================ ``` % pandoc -f latex -t native \newcommand{\mycolor}{red} \includegraphics[width=17cm]{\mycolor /header} Magnificent \mycolor{} header. ^D [ Para [ Image ( "" , [] , [ ( "width" , "17cm" ) ] ) [ Str "image" ] ( "red/header" , "" ) , SoftBreak , Str "Magnificent" , Space , Str "red" , Space , Str "header." ] ] ``` ================================================ FILE: test/command/3257.md ================================================ ``` % pandoc -t native (ik ^D [ Para [ Str "ik" ] ] ``` ================================================ FILE: test/command/3309.md ================================================ Certain environments should be treated as inline unless they are surrounded by blank lines: ``` % pandoc -t latex --wrap=preserve Lorem ipsum dolor sit amet, \begin{equation} E = mc^2, \end{equation} consectetur adipiscing elit. ^D Lorem ipsum dolor sit amet, \begin{equation} E = mc^2, \end{equation} consectetur adipiscing elit. ``` ``` % pandoc -t latex --wrap=preserve Lorem ipsum dolor sit amet, \begin{equation} E = mc^2, \end{equation} consectetur adipiscing elit. ^D Lorem ipsum dolor sit amet, \begin{equation} E = mc^2, \end{equation} consectetur adipiscing elit. ``` ``` % pandoc -t latex --wrap=preserve The formula \begin{math} x = y \end{math} should be inline. ^D The formula \begin{math} x = y \end{math} should be inline. ``` ================================================ FILE: test/command/3314.md ================================================ See #3315 and . ``` % pandoc -f org -t html5 +-----------+-------+----------+ | First | 12.0 | Example | | | | row | | | | spanning | | | | lines | +-----------+-------+----------+ | Second | 5.0 | Another | +-----------+-------+----------+ ^D
    First 12.0 Example row spanning lines
    Second 5.0 Another
    ``` ================================================ FILE: test/command/3324.md ================================================ ``` % pandoc -t latex Signatures \ \ ___________________________\ Peter Foobar\ *The Foo Company* ^D Signatures \hfill\break \hfill\break \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\\ Peter Foobar\\ \emph{The Foo Company} ``` ================================================ FILE: test/command/3337.md ================================================ ``` % pandoc -f html -t markdown
    a
    12
    ^D --- --- a 1 2 --- --- ``` ================================================ FILE: test/command/3348.md ================================================ ``` % pandoc -t native --columns=72 ----- ------------------------------------------------ foo bar foo this is a long line of text ----- ------------------------------------------------ ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidth 8.333333333333333e-2 ) , ( AlignLeft , ColWidth 0.6805555555555556 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "foo" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "bar" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "foo" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "long" , SoftBreak , Str "line" , Space , Str "of" , Space , Str "text" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/3401.md ================================================ See #3401 and ``` % pandoc -f org -t native #+MACRO: HELLO /Hello, $1/ {{{HELLO(World)}}} ^D [ Para [ Emph [ Str "Hello," , Space , Str "World" ] ] ] ``` Inverted argument order ``` % pandoc -f org -t native #+MACRO: A $2,$1 {{{A(1,2)}}} ^D [ Para [ Str "2,1" ] ] ``` ================================================ FILE: test/command/3407.md ================================================ ``` % pandoc -f native -t rst [Para [Code ("",["interpreted-text"],[("role","foo")]) "text"]] ^D :foo:`text` ``` ``` % pandoc -f rst -t native :foo:`text` ^D [ Para [ Code ( "" , [ "interpreted-text" ] , [ ( "role" , "foo" ) ] ) "text" ] ] ``` ================================================ FILE: test/command/3422.md ================================================ See #3422 ``` % pandoc -t latex --syntax-highlighting=idiomatic `int main(int argc, const char *argv[]);`{.c} ^D \passthrough{\lstinline[language=C]!int main(int argc, const char *argv[]);!} ``` ================================================ FILE: test/command/3432.md ================================================ List-table with header-rows and widths options. ``` % pandoc -f rst .. list-table:: Frozen Delights! :widths: 15 10 30 :header-rows: 1 * - Treat - Quantity - Description * - Albatross - 2.99 - On a stick! * - Crunchy Frog - 1.49 - If we took the bones out, it wouldn't be crunchy, now would it? * - Gannet Ripple - 1.99 - On a stick! ^D
    Frozen Delights!
    Treat Quantity Description
    Albatross 2.99 On a stick!
    Crunchy Frog 1.49 If we took the bones out, it wouldn't be crunchy, now would it?
    Gannet Ripple 1.99 On a stick!
    ``` List-table whose widths is "auto". ``` % pandoc -f rst .. list-table:: Frozen Delights! :header-rows: 1 :widths: auto * - Treat - Quantity - Description * - Albatross - 2.99 - On a stick! * - Crunchy Frog - 1.49 - If we took the bones out, it wouldn't be crunchy, now would it? * - Gannet Ripple - 1.99 - On a stick! ^D
    Frozen Delights!
    Treat Quantity Description
    Albatross 2.99 On a stick!
    Crunchy Frog 1.49 If we took the bones out, it wouldn't be crunchy, now would it?
    Gannet Ripple 1.99 On a stick!
    ``` List-table with header-rows which is bigger than 1. Only the first row is treated as a header. ``` % pandoc -f rst .. list-table:: Frozen Delights! :header-rows: 2 * - Treat - Quantity - Description * - Albatross - 2.99 - On a stick! * - Crunchy Frog - 1.49 - If we took the bones out, it wouldn't be crunchy, now would it? * - Gannet Ripple - 1.99 - On a stick! ^D
    Frozen Delights!
    Treat Quantity Description
    Albatross 2.99 On a stick!
    Crunchy Frog 1.49 If we took the bones out, it wouldn't be crunchy, now would it?
    Gannet Ripple 1.99 On a stick!
    ``` List-table without header-rows. ``` % pandoc -f rst .. list-table:: Frozen Delights! * - Albatross - 2.99 - On a stick! * - Crunchy Frog - 1.49 - If we took the bones out, it wouldn't be crunchy, now would it? * - Gannet Ripple - 1.99 - On a stick! ^D
    Frozen Delights!
    Albatross 2.99 On a stick!
    Crunchy Frog 1.49 If we took the bones out, it wouldn't be crunchy, now would it?
    Gannet Ripple 1.99 On a stick!
    ``` List-table with empty cells. You need a space after '-', otherwise the row will disappear. Parser for Bulletlists causes this restriction. ``` % pandoc -f rst .. list-table:: Frozen Delights! :header-rows: 2 * - Treat - Quantity - Description * - Albatross - 2.99 - * - Crunchy Frog - - If we took the bones out, it wouldn't be crunchy, now would it? * - Gannet Ripple - 1.99 - On a stick! ^D
    Frozen Delights!
    Treat Quantity Description
    Albatross 2.99
    Crunchy Frog If we took the bones out, it wouldn't be crunchy, now would it?
    Gannet Ripple 1.99 On a stick!
    ``` List-table with a cell having a bulletlist ``` % pandoc -f rst .. list-table:: Frozen Delights! * - Albatross - 2.99 - + On a stick! + In a cup! * - Crunchy Frog - 1.49 - If we took the bones out, it wouldn't be crunchy, now would it? * - Gannet Ripple - 1.99 - On a stick! ^D
    Frozen Delights!
    Albatross 2.99
    • On a stick!
    • In a cup!
    Crunchy Frog 1.49 If we took the bones out, it wouldn't be crunchy, now would it?
    Gannet Ripple 1.99 On a stick!
    ``` ================================================ FILE: test/command/3432a.md ================================================ ``` % pandoc -f rst * - a - b * - c - d ^D
      • a
      • b
      • c
      • d
    ``` ================================================ FILE: test/command/3450.md ================================================ ``` % pandoc -fmarkdown-implicit_figures ![image](lalune.jpg){height=2em} ^D

    image

    ``` ``` % pandoc -fmarkdown-implicit_figures -t latex ![image](lalune.jpg){height=2em} ^D \includegraphics[width=\linewidth,height=2em,keepaspectratio,alt={image}]{lalune.jpg} ``` ================================================ FILE: test/command/3475.md ================================================ RST implicit internal links to headers: ``` % pandoc -f rst Years ----- Years_ ^D

    Years

    Years

    ``` ``` % pandoc -f rst Years_ Years ----- ^D

    Years

    Years

    ``` ``` % pandoc -f rst Years and years --------------- `Years and years`_ ^D

    Years and years

    Years and years

    ``` ``` % pandoc -f rst Years and *years* ----------------- `Years and years`_ ^D

    Years and years

    Years and years

    ``` ================================================ FILE: test/command/3487.md ================================================ ``` % pandoc -f html -t markdown Some text
    • element
    ^D Some text - element ``` ================================================ FILE: test/command/3494.md ================================================ ``` % pandoc -f latex --quiet \begin{table}[h!] \begin{tabular}{r|l|l} {\large \textbf{ﺍ}} && \\ \textbf{ﺄﺤﺴﻨﺘـ(ﻭﺍ) IV} & \em{ʾaḥsant(ū)} & thank you \\ \newpage \emph{blah} & \emph{blah} & \emph{blah} \\ blah & blah & blah \\ \end{tabular} \end{table} ^D
    ﺄﺤﺴﻨﺘـ(ﻭﺍ) IV ʾaḥsant(ū) thank you
    blah blah blah
    blah blah blah
    ``` ================================================ FILE: test/command/3497.md ================================================ Escape list markers at beginning of paragraph: ``` % pandoc -t markdown \* ok \+ ok \- ok 1\. ok a\. ok ^D \* ok \+ ok \- ok 1\. ok a\. ok ``` Here we don't need to escape because there's no space: ``` % pandoc -t markdown \+ok \-ok 1.ok ^D +ok -ok 1.ok ``` Also escape things that might become line blocks or tables: ``` % pandoc -t markdown \| hi \| ^D \| hi \| ``` ================================================ FILE: test/command/3499.md ================================================ Org-mode tables can't be on the same line as list markers: ``` % pandoc -f org - |something| - |else| ^D
    • |something|
    • else
    ``` ================================================ FILE: test/command/3510-export.latex ================================================ \emph{Hello} ================================================ FILE: test/command/3510-src.hs ================================================ putStrLn outString ================================================ FILE: test/command/3510-subdoc.org ================================================ * Subsection Included text Lorem ipsum. ================================================ FILE: test/command/3510.md ================================================ See ``` % pandoc -f org -t native Text #+include: "command/3510-subdoc.org" #+INCLUDE: "command/3510-src.hs" src haskell #+INCLUDE: "command/3510-export.latex" export latex More text ^D [ Para [ Str "Text" ] , Header 1 ( "subsection" , [] , [] ) [ Str "Subsection" ] , Para [ Str "Included" , Space , Str "text" ] , Plain [ Str "Lorem" , Space , Str "ipsum." ] , CodeBlock ( "" , [ "haskell" ] , [] ) "putStrLn outString\n" , RawBlock (Format "latex") "\\emph{Hello}" , Para [ Str "More" , Space , Str "text" ] ] ``` ================================================ FILE: test/command/3511.md ================================================ ``` % pandoc -t native - a - b - c - code 1000. one not continuation ^D [ BulletList [ [ Plain [ Str "a" ] , BulletList [ [ Plain [ Str "b" ] , BulletList [ [ Plain [ Str "c" ] ] ] ] ] ] , [ CodeBlock ( "" , [] , [] ) "code" ] ] , OrderedList ( 1000 , Decimal , Period ) [ [ Plain [ Str "one" ] ] ] , CodeBlock ( "" , [] , [] ) "not continuation" ] ``` ``` % pandoc -t native -f markdown+four_space_rule - a - b - c - not code 1000. one continuation ^D [ BulletList [ [ Plain [ Str "a" ] ] , [ Plain [ Str "b" ] , BulletList [ [ Plain [ Str "c" ] ] ] ] , [ CodeBlock ( "" , [] , [] ) "not code" ] ] , OrderedList ( 1000 , Decimal , Period ) [ [ Para [ Str "one" ] , Para [ Str "continuation" ] ] ] ] ``` ================================================ FILE: test/command/3512.md ================================================ ``` % pandoc -f markdown-auto_identifiers #hi ^D

    #hi

    ``` ``` % pandoc -f markdown-auto_identifiers-space_in_atx_header #hi ^D

    hi

    ``` ================================================ FILE: test/command/3516.md ================================================ Correctly handle empty row: ``` % pandoc -f markdown -t rst +---+---+ | 1 | 2 | +---+---+ | | | +---+---+ ^D +---+---+ | 1 | 2 | +---+---+ | | | +---+---+ ``` Temporarily added these to figure out what is happening on Windows builds. ``` % pandoc -f markdown -t native +---+---+ | 1 | 2 | +---+---+ | | | +---+---+ ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 5.555555555555555e-2 ) , ( AlignDefault , ColWidth 5.555555555555555e-2 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f native -t rst [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 5.555555555555555e-2) ,(AlignDefault,ColWidth 5.555555555555555e-2)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) []]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "2"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) []]])] (TableFoot ("",[],[]) [])] ^D +---+---+ | | | +===+===+ | 1 | 2 | +---+---+ | | | +---+---+ ``` ================================================ FILE: test/command/3518.md ================================================ ``` % pandoc -f latex -t plain $\alpha^2 \cdot \alpha^{2+3} \equiv \alpha^7$ ^D α² ⋅ α² ⁺ ³ ≡ α⁷ ``` ================================================ FILE: test/command/3523.md ================================================ ``` % pandoc -f latex -t html \documentclass{article} \usepackage{epigraph} \begin{document} \epigraph{ Das Recht hat kein Dasein f{\"u}r sich, sein Wesen vielmehr ist das Leben des Menschen selbst, von einer besonderen Seite angesehen. Das Recht hat kein Dasein f{\"u}r sich, sein Wesen vielmehr ist das Leben des Menschen selbst, von einer besonderen Seite angesehen. \begin{itemize} \item hey \item hey \item hey \end{itemize} }{ Friedrich Carl von Savigny } \end{document} ^D

    Das Recht hat kein Dasein für sich, sein Wesen vielmehr ist das Leben des Menschen selbst, von einer besonderen Seite angesehen.

    Das Recht hat kein Dasein für sich, sein Wesen vielmehr ist das Leben des Menschen selbst, von einer besonderen Seite angesehen.

    • hey

    • hey

    • hey

    Friedrich Carl von Savigny

    ``` ================================================ FILE: test/command/3526.md ================================================ ``` % pandoc -t rst +--+---+ | | | +--+---+ | | | +--+---+ ^D +---+---+ | | | +---+---+ | | | +---+---+ ``` ================================================ FILE: test/command/3529.md ================================================ ``` % pandoc -t markdown-simple_tables-pipe_tables-grid_tables+multiline_tables A B -- -- 7 8 9 10 ^D -------- A B --- ---- 7 8 9 10 -------- ``` ================================================ FILE: test/command/3530.md ================================================ ``` % pandoc -f latex -t native \subfile{command/sub-file-chapter-1} \subfile{command/sub-file-chapter-2} ^D [ Header 1 ( "chapter-1" , [] , [] ) [ Str "Chapter" , Space , Str "1" ] , Para [ Str "This" , Space , Str "is" , Space , Str "Chapter" , Space , Str "1," , Space , Str "provided" , Space , Str "in" , Space , Str "a" , Space , Str "sub" , Space , Str "file." ] , Header 1 ( "chapter-2" , [] , [] ) [ Str "Chapter" , Space , Str "2" ] , Para [ Str "This" , Space , Str "is" , Space , Str "Chapter" , Space , Str "2," , Space , Str "provided" , Space , Str "in" , Space , Str "a" , Space , Str "second" , Space , Str "sub" , Space , Str "file." ] ] ``` ``` % pandoc -flatex+raw_tex -t native \subfile{command/sub-file-chapter-1} \subfile{command/sub-file-chapter-2} ^D [ RawBlock (Format "latex") "\\subfile{command/sub-file-chapter-1}" , RawBlock (Format "latex") "\\subfile{command/sub-file-chapter-2}" ] ``` ================================================ FILE: test/command/3531.md ================================================ ``` % pandoc -t mediawiki --wrap=preserve * This is a list item. * This is a list item in Markdown. It is continued in the next line. * It has a sub-item. * This is the next list item. A paragraph can span multiple lines without being broken into pieces. ^D * This is a list item. * This is a list item in Markdown. It is continued in the next line. ** It has a sub-item. * This is the next list item. A paragraph can span multiple lines without being broken into pieces. ``` ================================================ FILE: test/command/3533-rst-csv-tables.csv ================================================ "Albatross", 2.99, "On a stick!" "Crunchy Frog", 1.49, "If we took the bones out, it wouldn't be crunchy, now would it?" ================================================ FILE: test/command/3533-rst-csv-tables.md ================================================ ``` % pandoc -f rst -t native .. csv-table:: Test :widths: 10, 5, 10 :header: Flavor,Price,Slogan :file: command/3533-rst-csv-tables.csv ^D [ Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Test" ] ]) [ ( AlignDefault , ColWidth 0.4 ) , ( AlignDefault , ColWidth 0.2 ) , ( AlignDefault , ColWidth 0.4 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Flavor" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Price" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Slogan" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Albatross" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2.99" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "On" , Space , Str "a" , Space , Str "stick!" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Crunchy" , Space , Str "Frog" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1.49" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "If" , Space , Str "we" , Space , Str "took" , Space , Str "the" , Space , Str "bones" , Space , Str "out," , Space , Str "it" , Space , Str "wouldn't" , Space , Str "be" , SoftBreak , Str "crunchy," , Space , Str "now" , Space , Str "would" , Space , Str "it?" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f rst -t native .. csv-table:: Test :header-rows: 1 :quote: ' :delim: space '' 'a' 'b' 'cat''s' 3 4 'dog''s' 2 3 ^D [ Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Test" ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "cat's" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "dog's" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f rst -t native .. csv-table:: Test :escape: \ "1","\"" ^D [ Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Test" ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "\"" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/3534.md ================================================ ``` % pandoc -f latex -t html I want to explain the interface of \lstinline[language=Java]{public class MyClass}. ^D

    I want to explain the interface of public class MyClass.

    ``` ``` % pandoc -f latex -t html I want to explain the interface of \lstinline{public class MyClass}. ^D

    I want to explain the interface of public class MyClass.

    ``` ``` % pandoc -f latex -t native I want to explain the interface of \lstinline[language=Java]{public class MyClass}. ^D [ Para [ Str "I" , Space , Str "want" , Space , Str "to" , Space , Str "explain" , Space , Str "the" , Space , Str "interface" , Space , Str "of" , Space , Code ( "" , [ "java" ] , [] ) "public class MyClass" , Str "." ] ] ``` ``` % pandoc -f latex -t html I want to explain the interface of \mintinline{java}{public class MyClass}. ^D

    I want to explain the interface of public class MyClass.

    ``` ``` % pandoc -f latex -t html I want to explain the interface of \mintinline{java}|public class MyClass|. ^D

    I want to explain the interface of public class MyClass.

    ``` ``` % pandoc -f latex -t native I want to explain the interface of \mintinline[linenos]{java}{public class MyClass}. ^D [ Para [ Str "I" , Space , Str "want" , Space , Str "to" , Space , Str "explain" , Space , Str "the" , Space , Str "interface" , Space , Str "of" , Space , Code ( "" , [ "java" ] , [] ) "public class MyClass" , Str "." ] ] ``` ================================================ FILE: test/command/3537.md ================================================ Generalized raw attributes. ```` % pandoc -t native ```{=ms} .MACRO foo bar ``` ^D [ RawBlock (Format "ms") ".MACRO\nfoo bar" ] ```` ```` % pandoc -t native Hi `there`{=ms}. ^D [ Para [ Str "Hi" , Space , RawInline (Format "ms") "there" , Str "." ] ] ```` ```` % pandoc -t native ~~~ {=ms} .MACRO foo bar ~~~ ^D [ RawBlock (Format "ms") ".MACRO\nfoo bar" ] ```` ================================================ FILE: test/command/3539.md ================================================ # Commands of [glossaries package](ftp://ftp.tu-chemnitz.de/pub/tex/macros/latex/contrib/glossaries/glossaries-code.pdf) ``` % pandoc -f latex -t native Many programming languages provide \glspl{API}. Each \gls{API} should provide a documentation. ^D [ Para [ Str "Many" , Space , Str "programming" , Space , Str "languages" , Space , Str "provide" , Space , Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "plural+short" ) ] ) [ Str "APIs" ] , Str "." , Space , Str "Each" , Space , Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+short" ) ] ) [ Str "API" ] , Space , Str "should" , Space , Str "provide" , Space , Str "a" , Space , Str "documentation." ] ] ``` ``` % pandoc -f latex -t native \Glsdesc{API} XYZ ist not as performant as \glsdesc{API} ZXY. ^D [ Para [ Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+long" ) ] ) [ Str "API" ] , Space , Str "XYZ" , Space , Str "ist" , Space , Str "not" , Space , Str "as" , Space , Str "performant" , Space , Str "as" , Space , Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+long" ) ] ) [ Str "API" ] , Space , Str "ZXY." ] ] ``` ``` % pandoc -f latex -t native \Acrlong{API} XYZ ist not as performant as \acrlong{API} ZXY. ^D [ Para [ Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+long" ) ] ) [ Str "API" ] , Space , Str "XYZ" , Space , Str "ist" , Space , Str "not" , Space , Str "as" , Space , Str "performant" , Space , Str "as" , Space , Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+long" ) ] ) [ Str "API" ] , Space , Str "ZXY." ] ] ``` ``` % pandoc -f latex -t native \Acrfull{API} XYZ ist not as performant as \acrfull{API} ZXY. ^D [ Para [ Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+full" ) ] ) [ Str "API" ] , Space , Str "XYZ" , Space , Str "ist" , Space , Str "not" , Space , Str "as" , Space , Str "performant" , Space , Str "as" , Space , Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+full" ) ] ) [ Str "API" ] , Space , Str "ZXY." ] ] ``` ``` % pandoc -f latex -t native \Acrshort{API} XYZ ist not as performant as \acrshort{API} ZXY. ^D [ Para [ Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+abbrv" ) ] ) [ Str "API" ] , Space , Str "XYZ" , Space , Str "ist" , Space , Str "not" , Space , Str "as" , Space , Str "performant" , Space , Str "as" , Space , Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+abbrv" ) ] ) [ Str "API" ] , Space , Str "ZXY." ] ] ``` # Commands of [acronym package](ftp://ftp.mpi-sb.mpg.de/pub/tex/mirror/ftp.dante.de/pub/tex/macros/latex/contrib/acronym/acronym.pdf) ``` % pandoc -f latex -t native Many programming languages provide \acp{API}. Each \ac{API} should provide a documentation. ^D [ Para [ Str "Many" , Space , Str "programming" , Space , Str "languages" , Space , Str "provide" , Space , Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "plural+short" ) ] ) [ Str "APIs" ] , Str "." , Space , Str "Each" , Space , Span ( "" , [] , [ ( "acronym-label" , "API" ) , ( "acronym-form" , "singular+short" ) ] ) [ Str "API" ] , Space , Str "should" , Space , Str "provide" , Space , Str "a" , Space , Str "documentation." ] ] ``` ================================================ FILE: test/command/3558.md ================================================ ``` % pandoc -t native \multi hello \endmulti ^D [ RawBlock (Format "tex") "\\multi" , Para [ Str "hello" ] , RawBlock (Format "tex") "\\endmulti" ] ``` ================================================ FILE: test/command/3568.md ================================================ ``` % pandoc -t man normal *italic **bold in the middle** only italic* normal. normal **bold `code` more bold** normal. normal `code` normal. ^D .PP normal \f[I]italic \f[BI]bold in the middle\f[I] only italic\f[R] normal. .PP normal \f[B]bold \f[CB]code\f[B] more bold\f[R] normal. .PP normal \f[CR]code\f[R] normal. ``` ================================================ FILE: test/command/3570.md ================================================ ``` % pandoc -f markdown+autolink_bare_uris **Notes:** ^D

    Notes:

    ``` ================================================ FILE: test/command/3577.md ================================================ ``` % pandoc -f latex -t html5 --quiet \begin{figure}[ht] \begin{subfigure}{0.45\textwidth} \centering \includegraphics{img1.jpg} \caption{Caption 1} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics{img2.jpg} \caption{Caption 2} \end{subfigure} \caption{Subfigure with Subfloat} \end{figure} ^D
    Caption 1
    Caption 2
    Subfigure with Subfloat
    ``` ``` % pandoc -f latex -t html5 \begin{figure}[ht] \includegraphics{img1.jpg} \caption{Caption 3} \end{figure} ^D
    Caption 3
    ``` ================================================ FILE: test/command/3585.md ================================================ ``` % pandoc -f mediawiki+smart -t native "Hello" Same but bzip2 it and nice it zfs send tank/storage/data/svn@daily-2014-03-20_00.00.00--2w | nice -15 bzip2 | ssh user@hyper.somewhere.org "> /storage/c-3po/tank-storage-data-svn.dmp.bz2" ^D [ Para [ Quoted DoubleQuote [ Str "Hello" ] ] , Para [ Str "Same" , Space , Str "but" , Space , Str "bzip2" , Space , Str "it" , Space , Str "and" , Space , Str "nice" , Space , Str "it" , Space , Code ( "" , [] , [] ) "zfs send tank/storage/data/svn@daily-2014-03-20_00.00.00--2w | nice -15 bzip2 | ssh user@hyper.somewhere.org \"> /storage/c-3po/tank-storage-data-svn.dmp.bz2\"" ] ] ``` ``` % pandoc -f mediawiki -t native "Hello" ^D [ Para [ Str "\"Hello\"" ] ] ``` ================================================ FILE: test/command/3587.md ================================================ ``` % pandoc -f latex -t native \SI[round-precision=2]{1}{m} is equal to \SI{1000}{mm} ^D [ Para [ Str "1\160m" , Space , Str "is" , Space , Str "equal" , Space , Str "to" , Space , Str "1000\160mm" ] ] ``` ``` % pandoc -f latex -t native \SI[round-precision=2]{1}[\$]{} is equal to \SI{0.938094}{\euro} ^D [ Para [ Str "$\160\&1" , Space , Str "is" , Space , Str "equal" , Space , Str "to" , Space , Str "0.938094\160\8364" ] ] ``` ``` % pandoc -f latex -t native \SI{30}{\milli\meter} ^D [ Para [ Str "30\160mm" ] ] ``` ``` % pandoc -f latex -t native \SI{6}{\gram} ^D [ Para [ Str "6\160g" ] ] ``` ``` % pandoc -f latex -t native \SI{25}{\square\meter} ^D [ Para [ Str "25\160m" , Superscript [ Str "2" ] ] ] ``` ``` % pandoc -f latex -t native \SI{18.2}{\degreeCelsius} ^D [ Para [ Str "18.2\160\176C" ] ] ``` ``` % pandoc -f latex -t native \SI{18.2}{\celsius} ^D [ Para [ Str "18.2\160\176C" ] ] ``` # SIrange tests ## Integer range with simple common units ``` % pandoc -f latex -t native \SIrange{10}{20}{\gram} ^D [ Para [ Str "10\160g\8211\&20\160g" ] ] ``` ``` % pandoc -f latex -t native \SIrange{35}{9}{\milli\meter} ^D [ Para [ Str "35\160mm\8211\&9\160mm" ] ] ``` ``` % pandoc -f latex -t native \SIrange{4}{97367265}{\celsius} ^D [ Para [ Str "4\160\176C\8211\&97367265\160\176C" ] ] ``` ## Decimal range with simple units ``` % pandoc -f latex -t native \SIrange{4.5}{97367265.5}{\celsius} ^D [ Para [ Str "4.5\160\176C\8211\&97367265.5\160\176C" ] ] ``` ## Squared, cubed etc. units ``` % pandoc -f latex -t native \SIrange{10}{20}{\square\meter} ^D [ Para [ Str "10\160m" , Superscript [ Str "2" ] , Str "\8211\&20\160m" , Superscript [ Str "2" ] ] ] ``` ``` % pandoc -f latex -t native \SIrange{10}{20}{\cubic\meter} ^D [ Para [ Str "10\160m" , Superscript [ Str "3" ] , Str "\8211\&20\160m" , Superscript [ Str "3" ] ] ] ``` ``` % pandoc -f latex -t native \SIrange{10}{20}{\raisetothe{4}\meter} ^D [ Para [ Str "10\160m" , Superscript [ Str "4" ] , Str "\8211\&20\160m" , Superscript [ Str "4" ] ] ] ``` ``` % pandoc -f latex -t native \SIrange{10}{20}{\meter\squared} ^D [ Para [ Str "10\160m" , Superscript [ Str "2" ] , Str "\8211\&20\160m" , Superscript [ Str "2" ] ] ] ``` ``` % pandoc -f latex -t native \SIrange{10}{20}{\meter\cubed} ^D [ Para [ Str "10\160m" , Superscript [ Str "3" ] , Str "\8211\&20\160m" , Superscript [ Str "3" ] ] ] ``` ``` % pandoc -f latex -t native \SIrange{10}{20}{\meter\tothe{4}} ^D [ Para [ Str "10\160m" , Superscript [ Str "4" ] , Str "\8211\&20\160m" , Superscript [ Str "4" ] ] ] ``` ## Ignore round precision `round-precision` option appears to be ignored by `\SI` as of 7c6dbd37e, so `\SIrange` will ignore it as well. ``` % pandoc -f latex -t native \SIrange[round-precision=2]{10}{20}{\gram} ^D [ Para [ Str "10\160g\8211\&20\160g" ] ] ``` ``` % pandoc -f latex -t native \SIrange[round-precision=2]{10.0}{20.25}{\gram} ^D [ Para [ Str "10.0\160g\8211\&20.25\160g" ] ] ``` ================================================ FILE: test/command/3596.md ================================================ ``` % pandoc -f html -t markdown-raw_html-bracketed_spans-native_spans
    • foo
    • bar
    • baz
    ^D - foo - bar - baz ``` ``` % pandoc -f html -t markdown-raw_html-bracketed_spans-native_spans
    • foo
    • bar
      • subbar
    • baz
    ^D - foo - bar - subbar - baz ``` ``` % pandoc -f html -t markdown
    • foo
    • bar
    • baz
    ^D - foo - [bar]{#id} - baz ``` ``` % pandoc -f html -t markdown
    • foo

    • bar

    • baz

    ^D - foo - ::: {#id} bar ::: - baz ``` ================================================ FILE: test/command/3615.md ================================================ ``` % pandoc -f html -t markdown --reference-links foo Foo ^D [foo][] [Foo][1] [foo]: a [1]: b ``` ``` % pandoc -f html -t markdown --reference-links foo Foo ^D [foo][] [Foo] [foo]: a ``` ================================================ FILE: test/command/3619.md ================================================ ``` % pandoc -f html -t markdown --reference-links bar: baz ^D [bar][]: baz [bar]: foo ``` ``` % pandoc -f html -t markdown --reference-links bar(baz) ^D [bar][](baz) [bar]: foo ``` ``` % pandoc -f html -t markdown_strict --reference-links foo
    bar ^D [foo][] [bar] [foo]: a [bar]: b ``` ================================================ FILE: test/command/3630.md ================================================ ``` % pandoc -f markdown -t markdown --reference-links ![foo](bar.png){#myId} ^D ![foo] [foo]: bar.png {#myId} ``` ================================================ FILE: test/command/3667.md ================================================ ``` % pandoc -f textile | "link text":http://example.com/ | ^D
    link text
    ``` ================================================ FILE: test/command/3674.md ================================================ Make sure we don't get duplicate reference links, even with `--reference-location=section`. ``` % pandoc --reference-links -t markdown --reference-location=section # a ![](a) # b ![](b) ^D # a ![][1] [1]: a # b ![][2] [2]: b ``` Subsidiary issue: allow line break between reference link url/title and attributes: ``` % pandoc [a] [a]: url {.class} ^D

    a

    ``` ================================================ FILE: test/command/3675.md ================================================ ```` % pandoc -t rst ```python print("hello") ``` > block quote ^D .. code:: python print("hello") .. block quote ```` ================================================ FILE: test/command/3681.md ================================================ ``` % pandoc -f latex -t native \newcommand{\cicd}{CI/CD\xspace} Software developers create \cicd pipelines to… Following issue can be resolved by \cicd: ^D [ Para [ Str "Software" , Space , Str "developers" , Space , Str "create" , Space , Str "CI/CD" , Space , Str "pipelines" , Space , Str "to\8230" , Space , Str "Following" , Space , Str "issue" , Space , Str "can" , Space , Str "be" , Space , Str "resolved" , Space , Str "by" , Space , Str "CI/CD:" ] ] ``` ``` % pandoc -f latex -t native \newcommand{\cicd}{CI/CD\xspace} \cicd\footnote{\url{https://en.wikipedia.org/wiki/CI/CD}} is awesome. ^D [ Para [ Str "CI/CD" , Note [ Para [ Link ( "" , [ "uri" ] , [] ) [ Str "https://en.wikipedia.org/wiki/CI/CD" ] ( "https://en.wikipedia.org/wiki/CI/CD" , "" ) ] ] , Space , Str "is" , Space , Str "awesome." ] ] ``` ``` % pandoc -f latex -t native \newcommand{\cicd}{CI/CD\xspace} \newcommand{\pipeline}{pipeline\xspace} \cicd\pipeline. ^D [ Para [ Str "CI/CD" , Space , Str "pipeline." ] ] ``` ================================================ FILE: test/command/3690.md ================================================ ``` % pandoc - [o] _hi_ ^D
    • [o] hi
    ``` ================================================ FILE: test/command/3701.md ================================================ ``` % pandoc --reference-location=block -t markdown --reference-links --wrap=preserve [a](u) [a](u) [a](u2) [A](u) [a](u){.foo} [a](u3) ^D [a] [a]: u [a] [a]: u [a][1] [A][] [a][2] [1]: u2 [A]: u [2]: u {.foo} [a][3] [3]: u3 ``` ``` % pandoc [a] [a]: u [a] [a]: u [a][1] [A][] [a][2] [1]: u2 [A]: u [2]: u {.foo} [a][3] [3]: u3 ^D

    a

    a

    a A a

    a

    ``` ================================================ FILE: test/command/3706.md ================================================ Results marker can be hidden in block attributes (#3706) ``` % pandoc -f org -t native #+begin_src r :exports results :colnames yes data.frame(Id = 1:3, Desc = rep("La",3)) #+end_src #+caption: Lalelu. #+label: tab #+RESULTS: | Id | Desc | |----+------| | 1 | La | | 2 | La | | 3 | La | ^D [ Table ( "tab" , [] , [] ) (Caption Nothing [ Plain [ Str "Lalelu." ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Id" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Desc" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "La" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "La" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "La" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f org -t native #+begin_src R :exports none :colnames yes data.frame(Id = 1:2, Desc = rep("La",2)) #+end_src #+caption: Lalelu. #+label: tab #+RESULTS: | Id | Desc | |----+------| | 1 | La | | 2 | La | ^D [] ``` ================================================ FILE: test/command/3708.md ================================================ ``` % pandoc -f latex -t native \begin{tabular}{cc} A & B\&1 \\ C & D \end{tabular} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B&1" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "C" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "D" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/3715.md ================================================ ``` % pandoc -t markdown -f html --wrap=preserve xx yy zz ww qq ^D xx yy zz ww qq ``` ================================================ FILE: test/command/3716.md ================================================ ``` % pandoc {.foo} ^D

    http://example.com

    ``` ================================================ FILE: test/command/3730.md ================================================ ```` % pandoc nice line\ ``` code ``` ^D

    nice line

    code
    ```` ``` % pandoc # hi\ there ^D

    hi

    there

    ``` ================================================ FILE: test/command/3733.md ================================================ ```` % pandoc -t native - Item1 - Item2 ```yaml some: code ``` ^D [ BulletList [ [ Plain [ Str "Item1" ] ] , [ Plain [ Str "Item2" ] ] ] , CodeBlock ( "" , [ "yaml" ] , [] ) "some: code" ] ```` ================================================ FILE: test/command/3734.md ================================================ ``` % pandoc -t markdown_strict+pipe_tables | aaaaaaaaaaaa | bbbbb | ccccccccccc | |--------------|-------|--------------------------------------------------------------------------| | aaaaaaaaaaaa | | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | ^D | aaaaaaaaaaaa | bbbbb | ccccccccccc | |------------|-------|------------------------------------------------------| | aaaaaaaaaaaa | | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | ``` ``` % pandoc -t markdown_strict+pipe_tables-raw_html | aaaaaaaaaaaa | bbbbb | ccccccccccc | |--------------|-------|--------------------------------------------------------------------------| | aaaaaaaaaaaa | | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | ^D | aaaaaaaaaaaa | bbbbb | ccccccccccc | |------------|-------|------------------------------------------------------| | aaaaaaaaaaaa | | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | ``` ``` % pandoc -t gfm | aaaaaaaaaaaa | bbbbb | ccccccccccc | |--------------|-------|--------------------------------------------------------------------------| | aaaaaaaaaaaa | | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | ^D | aaaaaaaaaaaa | bbbbb | ccccccccccc | |----|----|----| | aaaaaaaaaaaa | | cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc | ``` ================================================ FILE: test/command/3736.md ================================================ ``` % pandoc --wrap=preserve -f html -t markdown

    hi there

    ^D ## hi there ``` ``` % pandoc --wrap=preserve -f html -t markdown

    hi there again

    ^D ## hi *there again* ``` ``` % pandoc --wrap=preserve -f html -t markdown

    hi
    there

    ^D ## hi there ``` ================================================ FILE: test/command/3752.md ================================================ ``` % pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths --verbose -t docx -o - | pandoc -f docx -t plain ^D 2> [INFO] Loaded command/chap1/spider.png from command/chap1/spider.png 2> [INFO] Loaded command/chap2/spider.png from command/chap2/spider.png 2> [INFO] Loaded command/chap1/../../lalune.jpg from command/chap1/../../lalune.jpg Chapter one A spider: [spider] Another spider: [another spider] The moon: [moon] Link to spider picture. URL left alone: manual. Absolute path left alone: absolute. Link to fragment: chapter two. Empty path: empty. Chapter two A spider: [spider] ``` ``` % pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths -t html ^D

    Chapter one

    A spider: spider

    Another spider: another spider

    The moon: moon

    Link to spider picture.

    URL left alone: manual.

    Absolute path left alone: absolute.

    Link to fragment: chapter two.

    Empty path: empty.

    Chapter two

    A spider: spider

    ``` ``` % pandoc command/chap1/text.md command/chap2/text.md -f commonmark+rebase_relative_paths -t html ^D

    Chapter one

    A spider: spider

    Another spider: another spider

    The moon: moon

    Link to spider picture.

    URL left alone: manual.

    Absolute path left alone: absolute.

    Link to fragment: chapter two.

    Empty path: empty.

    Chapter two

    A spider: spider

    ``` ================================================ FILE: test/command/3755.md ================================================ ``` % pandoc -t native -s --- title: 'Titel' date: '22. Juni 2017' --- ^D Pandoc Meta { unMeta = fromList [ ( "date" , MetaInlines [ Str "22." , Space , Str "Juni" , Space , Str "2017" ] ) , ( "title" , MetaInlines [ Str "Titel" ] ) ] } [] ``` ``` % pandoc -t native -s --- title: "
    foo
    \n" date: | 22. Juni 2017 --- ^D Pandoc Meta { unMeta = fromList [ ( "date" , MetaBlocks [ OrderedList ( 22 , Decimal , Period ) [ [ Plain [ Str "Juni" , Space , Str "2017" ] ] ] ] ) , ( "title" , MetaBlocks [ Div ( "" , [] , [] ) [ Plain [ Str "foo" ] ] ] ) ] } [] ``` ================================================ FILE: test/command/3771.md ================================================ ``` % pandoc -f html -t org
    Today is a nice day.
    Tomorrow will be rainy.
    ^D Today is a nice day. <> Tomorrow will be rainy. ``` ================================================ FILE: test/command/3773.md ================================================ ``` % pandoc -t markdown A. \# B. \+ C. \* D. o E. o or \* ^D A. \# B. \+ C. \* D. o E. o or \* ``` ================================================ FILE: test/command/3779.md ================================================ ``` % pandoc -f latex -t native \newcommand{\fakeitemize}[1]{ \begin{itemize} #1 \end{itemize} } \newcommand{\testcmd}[1]{ #1 } \fakeitemize{ \item Pandoc is 100\% awesome. } \begin{itemize} \item Pandoc is 200\% awesome. \end{itemize} \testcmd{ Pandoc is 300\% awesome. } ^D [ BulletList [ [ Para [ Str "Pandoc" , Space , Str "is" , Space , Str "100%" , Space , Str "awesome." ] ] ] , BulletList [ [ Para [ Str "Pandoc" , Space , Str "is" , Space , Str "200%" , Space , Str "awesome." ] ] ] , Para [ Str "Pandoc" , Space , Str "is" , Space , Str "300%" , Space , Str "awesome." ] ] ``` ================================================ FILE: test/command/3792.md ================================================ Make sure metadata values are treated as strings, and properly escaped. ``` % pandoc -t markdown -s -M title=" *that*" ok ^D --- title: \ \*that\* --- ok ``` ================================================ FILE: test/command/3794.md ================================================ ``` % pandoc -f html -t native

    hello

    ^D [ Div ( "" , [] , [] ) [ Para [ Str "hello" ] ] ] ``` ================================================ FILE: test/command/3803.md ================================================ ``` % pandoc -f markdown+raw_tex -t latex \begin{blah*} *ok* \end{blah*} ^D \begin{blah*} *ok* \end{blah*} ``` ================================================ FILE: test/command/3804.md ================================================ ``` % pandoc -t native \titleformat{\chapter}[display]{\normalfont\large\bfseries}{第\thechapter{}章}{20pt}{\Huge} ^D [ RawBlock (Format "tex") "\\titleformat{\\chapter}[display]{\\normalfont\\large\\bfseries}{\31532\\thechapter{}\31456}{20pt}{\\Huge}" ] ``` ================================================ FILE: test/command/3816.md ================================================ ``` % pandoc --mathjax -t html5 --wrap=preserve This is an equation: \begin{equation} y+2 = 3 \end{equation} This is a system of equations: \begin{align*} x^2+y^2 & = 2 \\ \sin(y) & = 0.5 \end{align*} This is Euler's formula: \begin{eqnarray*} e^{i\pi} + 1 & = & 0. \end{eqnarray*} ^D

    This is an equation: \[\begin{equation} y+2 = 3 \end{equation}\]

    This is a system of equations: \[\begin{align*} x^2+y^2 & = 2 \\ \sin(y) & = 0.5 \end{align*}\]

    This is Euler’s formula: \[\begin{eqnarray*} e^{i\pi} + 1 & = & 0. \end{eqnarray*}\]

    ``` ================================================ FILE: test/command/3824.md ================================================ ``` % pandoc -f native -t dokuwiki [BulletList [[Para [Str "hi"] ,CodeBlock ("",[],[]) " there"] ,[Para [Str "ok"]]]] ^D * hi there * ok ``` ================================================ FILE: test/command/3840.md ================================================ ``` % pandoc [@Alhazen1572-qk, V.9]: "competentius est" ^D

    [@Alhazen1572-qk, V.9]: “competentius est”

    ``` ``` % pandoc -f markdown-citations [@Alhazen1572-qk, V.9]: "competentius est" [@Alhazen1572-qk, V.9] ^D

    @Alhazen1572-qk, V.9

    ``` ================================================ FILE: test/command/3853.md ================================================ ``` % pandoc -f latex -t native \newtoggle{ebook} \toggletrue{ebook} \iftoggle{ebook}{ ebook }% { not ebook }% more \togglefalse{ebook} \iftoggle{ebook}{% ebook }{ not ebook }% more hello \iftoggle{ebook}{ebook}{noebook} ^D [ Para [ Str "ebook" , SoftBreak , Str "more" ] , Para [ Str "not" , Space , Str "ebook" , SoftBreak , Str "more" ] , Para [ Str "hello" , Space , Str "noebook" ] ] ``` ================================================ FILE: test/command/3880.md ================================================ ``` % pandoc -f rst -t native .. include:: command/3880.txt ^D [ Para [ Str "hi" ] ] ``` ================================================ FILE: test/command/3880.txt ================================================ hi ================================================ FILE: test/command/3916.md ================================================ ``` % pandoc -f textile -t native # text text
    blabla
    # more ^D [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "text" , Space , Str "text" ] , CodeBlock ( "" , [] , [] ) "blabla" ] , [ Plain [ Str "more" ] ] ] ] ``` ================================================ FILE: test/command/3937.md ================================================ ``` % pandoc -t rst # My Great Section {#mysection} # Other section ^D .. _mysection: My Great Section ================ Other section ============= ``` ================================================ FILE: test/command/3947.md ================================================ ``` % pandoc -t native \newpage Code block Another Code block ^D [ RawBlock (Format "tex") "\\newpage" , CodeBlock ( "" , [] , [] ) "Code block\n\nAnother Code block" ] ``` ================================================ FILE: test/command/3958.md ================================================ ``` % pandoc -f latex -t native \texttt{"hi"} ^D [ Para [ Code ( "" , [] , [] ) "\"hi\"" ] ] ``` ``` % pandoc -f latex -t native \texttt{``hi''} ^D [ Para [ Code ( "" , [] , [] ) "\8216\8216hi\8217\8217" ] ] ``` ``` % pandoc -f latex -t native \texttt{`hi'} ^D [ Para [ Code ( "" , [] , [] ) "\8216hi\8217" ] ] ``` ================================================ FILE: test/command/3968.md ================================================ ``` % pandoc --top-level-division=chapter -t context # Chapter ## Section ^D \startchapter[title={Chapter},reference={chapter}] \startsection[title={Section},reference={section}] \stopsection \stopchapter ``` ================================================ FILE: test/command/3971.md ================================================ ``` % pandoc -f latex -t native \documentclass{article} \include{command/3971b} \code{f} \end{document} ^D [ Para [ Code ( "" , [] , [] ) "f" ] ] ``` ================================================ FILE: test/command/3971b.tex ================================================ \newcommand{\code}[1]{\texttt{#1}} \begin{document} ================================================ FILE: test/command/3974.md ================================================ ``` % pandoc -f native -t rst [Code ("",[],[]) "``"] ^D :literal:`\`\`` ``` ================================================ FILE: test/command/3978.md ================================================ ``` % pandoc -t rst foo_bar*baz ^D foo_bar*baz ``` ================================================ FILE: test/command/3983.md ================================================ ``` % pandoc -f latex+raw_tex -t native \def\filename@area{foo:bar:baz} \makeatletter \graphicspath\expandafter{\expandafter{\filename@area}}% \makeatother ^D [ RawBlock (Format "latex") "\\makeatletter" , RawBlock (Format "latex") "\\makeatother" ] ``` ``` % pandoc -f latex+raw_tex -t native \makeatletter \newcommand\urlfootnote@[1]{\footnote{\url@{#1}}} \DeclareRobustCommand{\urlfootnote}{\hyper@normalise\urlfootnote@} \makeatother ^D [ RawBlock (Format "latex") "\\makeatletter" , RawBlock (Format "latex") "\\makeatother" ] ``` ``` % pandoc -f latex+raw_tex -t native \def\foo{bar} \expandafter\bam\foo ^D [ RawBlock (Format "latex") "\\bambar" ] ``` ================================================ FILE: test/command/3989.md ================================================ ``` % pandoc -f markdown -t native foo foo ^D [ Para [ Span ( "" , [] , [ ( "title" , "1st line of text
    2nd line of text" ) ] ) [ Str "foo" ] , SoftBreak , Span ( "" , [] , [ ( "title" , "1st line of text
    2nd line of text" ) ] ) [ Str "foo" ] ] ] ``` ================================================ FILE: test/command/4007.md ================================================ ``` % pandoc -f latex -t native \newcommand\arrow\to $a\arrow b$ ^D [ Para [ Math InlineMath "a\\to b" ] ] ``` ``` % pandoc -f latex -t native \newcommand\pfeil[1]{\to #1} $a\pfeil b$ ^D [ Para [ Math InlineMath "a\\to b" ] ] ``` ``` % pandoc -f latex -t native \newcommand\fleche{\to} $a\fleche b$ ^D [ Para [ Math InlineMath "a\\to b" ] ] ``` ================================================ FILE: test/command/4012.md ================================================ ``` % pandoc -f markdown-implicit_figures ![image] [image]: http://example.com/image.jpg {height=35mm} ^D

    image

    ``` ================================================ FILE: test/command/4016.md ================================================ ``` % pandoc -t beamer # Level 2 blocks
    ## Block one - Item
    ## Block two - Item
    ^D \begin{frame}{Level 2 blocks} \protect\phantomsection\label{level-2-blocks} \begin{columns}[T] \begin{column}{0.4\linewidth} \begin{block}{Block one} \protect\phantomsection\label{block-one} \begin{itemize} \tightlist \item Item \end{itemize} \end{block} \end{column} \begin{column}{0.6\linewidth} \begin{block}{Block two} \protect\phantomsection\label{block-two} \begin{itemize} \tightlist \item Item \end{itemize} \end{block} \end{column} \end{columns} \end{frame} ``` ================================================ FILE: test/command/4019.md ================================================ ``` % pandoc --wrap=preserve This works! This fails? ^D

    This works! This fails?

    ``` ================================================ FILE: test/command/4038.md ================================================ ``` % pandoc -f gfm -t gfm # ~~Header~~ ^D # ~~Header~~ ``` ================================================ FILE: test/command/4054.md ================================================ ``` % pandoc -t native -s -M title=New % Old ^D Pandoc Meta { unMeta = fromList [ ( "title" , MetaString "New" ) ] } [] ``` ``` % pandoc -t native -s -M foo=1 -M foo=2 ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaList [ MetaString "1" , MetaString "2" ] ) ] } [] ``` ================================================ FILE: test/command/4056.md ================================================ ``` % pandoc -f markdown -t native \parbox[t]{0.4\textwidth}{ \begin{shaded} \end{shaded} } ^D [ RawBlock (Format "tex") "\\parbox[t]{0.4\\textwidth}{\n\\begin{shaded}\n\\end{shaded}\n}" ] ``` ``` % pandoc -f latex -t native \begin{tabular}{l*{2}{r}} Blah & Foo & Bar \\ \end{tabular} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Blah" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Foo" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bar" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/4061.md ================================================ ``` % pandoc -t markdown-simple_tables-multiline_tables-pipe_tables +-----------------------------------+ | Text [^1] | +-----------------------------------+ [^1]: Footnote. ^D +-----------------------------------+ | Text [^1] | +-----------------------------------+ [^1]: Footnote. ``` ================================================ FILE: test/command/4062.md ================================================ ``` % pandoc -t latex Sentence blah.\footnote[][-.5in]{I'm a footnote} ^D Sentence blah.\footnote[][-.5in]{I'm a footnote} ``` ================================================ FILE: test/command/4063.md ================================================ ``` % pandoc -f html -t native
    1 2
    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.3 ) , ( AlignDefault , ColWidth 0.7 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/4068.md ================================================ ``` % pandoc -f mediawiki -t native [https://domain.com/script.php?a=1&b=2&c=&d=4 open productname bugs] [http://domain.com?a=. open productname bugs] ^D [ Para [ Link ( "" , [] , [] ) [ Str "open" , Space , Str "productname" , Space , Str "bugs" ] ( "https://domain.com/script.php?a=1&b=2&c=&d=4" , "" ) ] , Para [ Str "[" , Link ( "" , [] , [] ) [ Str "http://domain.com?a=" ] ( "http://domain.com?a=" , "" ) , Str "." , Space , Str "open" , Space , Str "productname" , Space , Str "bugs]" ] ] ``` ================================================ FILE: test/command/4091.md ================================================ ``` % pandoc -f latex \alert<3>{foo} ^D

    foo

    ``` ================================================ FILE: test/command/4102.md ================================================ SmallCaps spans can have additional attributes. ``` % pandoc -t latex -f markdown [Populus]{.smallcaps lang=la} [Romanus]{.smallcaps} ^D \foreignlanguage{latin}{\textsc{Populus}} \textsc{Romanus} ``` ================================================ FILE: test/command/4113.md ================================================ ``` % pandoc -t gfm ::::{.bug} I am a [bug]{#bug}. :::: ^D
    I am a bug.
    ``` ================================================ FILE: test/command/4119.md ================================================ ``` % pandoc -t native | col1 | col2 | | ---- | ---- | | 1 | 2 | ::: {.notes} ::: not a caption! :::::::::::::::: ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col2" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Div ( "" , [ "notes" ] , [] ) [ Para [ Str "not" , Space , Str "a" , Space , Str "caption!" ] ] ] ``` ================================================ FILE: test/command/4125.md ================================================ ``` % pandoc ^D ``` ================================================ FILE: test/command/4134.md ================================================ ``` % pandoc -f latex -t native Hello.\ world. ^D [ Para [ Str "Hello.\160world." ] ] ``` ``` % pandoc -f latex -t native Hello.\ world. ^D [ Para [ Str "Hello.\160world." ] ] ``` ``` % pandoc -f latex -t native Hello.\ World. ^D [ Para [ Str "Hello.\160" ] , Para [ Str "World." ] ] ``` ================================================ FILE: test/command/4156.md ================================================ ``` % pandoc -f rst .. _`SOMEID`: foo ^D

    foo

    ``` ================================================ FILE: test/command/4159.md ================================================ ``` % pandoc -f markdown -t native \newcommand{\gen}{a\ Gen\ b} abc ^D [ RawBlock (Format "tex") "\\newcommand{\\gen}{a\\ Gen\\ b}" , Para [ Str "abc" ] ] ``` ================================================ FILE: test/command/4162.md ================================================ ``` % pandoc -f html -t native
    hi

     there
    ^D [ LineBlock [ [ Str "hi" ] , [] , [ Str "\160there" ] ] ] ``` ================================================ FILE: test/command/4164.md ================================================ ``` % pandoc -f opml -t markdown test ^D # test ## try Here is inline html: ::: {} ``{=html} bla bla ::: ``` ``` % pandoc -f opml-raw_html-native_divs -t markdown test ^D # test ## try Here is inline html: \ \ bla bla \ ``` ================================================ FILE: test/command/4171.md ================================================ ``` % pandoc -f org -t org Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [fn:1] a [fn:1] b ^D Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [fn:1] a [fn:1] b ``` ``` % pandoc -f org -t org Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [fn:1] a [fn:1] b ^D Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [fn:1] a [fn:1] b ``` Similar bug: "-" should not be wrapped: ``` % pandoc -f org -t org aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - abc ^D aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - abc ``` ================================================ FILE: test/command/4172.md ================================================ Test that text wrapping does not move note reference [1] to the beginning of the line, where it would become a note. ``` % pandoc -f muse -t muse Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [1] a [1] b ^D Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [1] a [1] b ``` SoftBreak test: ``` % pandoc -f muse -t muse Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [1] a [1] b ^D Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [1] a [1] b ``` ================================================ FILE: test/command/4183.md ================================================ ``` % pandoc -f html -t native
    bar
    ^D [ Figure ( "" , [] , [] ) (Caption Nothing []) [ Plain [ Image ( "" , [] , [] ) [ Str "bar" ] ( "foo" , "" ) ] ] ] ``` ``` % pandoc -f html -t native
    bar
    baz
    ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Div ( "" , [] , [] ) [ Plain [ Str "baz" ] ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "bar" ] ( "foo" , "" ) ] ] ] ``` ``` % pandoc -f html -t native

    baz

    ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Para [ Emph [ Str "baz" ] ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "foo" , "" ) ] ] ] ``` ================================================ FILE: test/command/4186.md ================================================ ``` % pandoc -f org -t native #+begin_example -i This should retain the four leading spaces #+end_example ^D [ CodeBlock ( "" , [] , [] ) " This should retain the four leading spaces\n" ] ``` ``` % pandoc -f org -t html - depth 1 #+name: bob #+begin_example -i Vertical alignment is four spaces beyond the appearance of the word "depth". #+end_example - depth 2 #+begin_example Vertically aligned with the second appearance of the word "depth". #+end_example #+begin_example -i 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 that the entire block must be a component of the inner list element. #+end_example Still inside the inner list element #+name: carrie #+begin_example 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. #+end_example Still in the shallower list element since the preceding example block forced the deeper list element to terminate. Outside all lists. ^D
    • depth 1

          Vertical alignment is four spaces beyond the appearance of the word "depth".
      
      • depth 2

        Vertically aligned with the second appearance of the word "depth".
        
            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
            that the entire block must be a component of the
            inner list element.
        

        Still inside the inner list element

      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.
      

      Still in the shallower list element since the preceding example block forced the deeper list element to terminate.

    Outside all lists.

    ``` ================================================ FILE: test/command/4193.md ================================================ ``` % pandoc -f rst -t native - a - b ^D [ BulletList [ [ Plain [ Str "a" ] ] , [ Plain [ Str "b" ] ] ] ] ``` ================================================ FILE: test/command/4199.md ================================================ ``` % pandoc -f latex -t native \foreignlanguage{ngerman}{foo} ^D [ Para [ Span ( "" , [] , [ ( "lang" , "de-DE" ) ] ) [ Str "foo" ] ] ] ``` ================================================ FILE: test/command/4208.md ================================================ ``` % pandoc -t latex What is a _piffle_? Mark the correct answer(s): \begin{TAB}(@)[6pt]{|l|c|}{|c|c|c|} (a) a subnormal woffle & $\Box$ \\ (b) an infinite-dimensional baffle & $\Box$ \\ (c) an inverted first-order triffle & $\Box$ \\ \end{TAB} ^D What is a \emph{piffle}? Mark the correct answer(s): \begin{TAB}(@)[6pt]{|l|c|}{|c|c|c|} (a) a subnormal woffle & $\Box$ \\ (b) an infinite-dimensional baffle & $\Box$ \\ (c) an inverted first-order triffle & $\Box$ \\ \end{TAB} ``` ================================================ FILE: test/command/4235.md ================================================ ``` % pandoc --id-prefix=foo This.^[Has a footnote.] ^D

    This.1


    1. Has a footnote.↩︎

    ``` ================================================ FILE: test/command/4240.md ================================================ ``` % pandoc -f rst -s -t native ===== Title ===== -------- Subtitle -------- header1 ======= header2 ------- .. _id: header3 ~~~~~~~ .. _id2: .. _id3: header4 ~~~~~~~ ^D Pandoc Meta { unMeta = fromList [ ( "subtitle" , MetaInlines [ Str "Subtitle" ] ) , ( "title" , MetaInlines [ Str "Title" ] ) ] } [ Header 1 ( "header1" , [] , [] ) [ Str "header1" ] , Header 2 ( "header2" , [] , [] ) [ Str "header2" ] , Header 3 ( "id" , [] , [] ) [ Str "header3" ] , Header 3 ( "id3" , [] , [] ) [ Str "header4" , Span ( "id2" , [] , [] ) [] ] ] ``` ================================================ FILE: test/command/4253.md ================================================ ``` % pandoc -f latex -t native \newcommand{\noop}[1]{#1} \noop{\newcommand{\foo}[1]{#1}} \foo{hi} ^D [ Para [ Str "hi" ] ] ``` ================================================ FILE: test/command/4254.md ================================================ ``` % pandoc -f rst -t latex .. math:: x &= y\\ y &= z ^D \[\begin{aligned} x &= y\\ y &= z \end{aligned}\] ``` ================================================ FILE: test/command/4280.md ================================================ ``` % pandoc -f rst -t native Driver ------ ^D [ Header 1 ( "driver" , [] , [] ) [ Str "Driver" ] ] ``` ================================================ FILE: test/command/4281.md ================================================ ``` % pandoc -t native :::: {.a} - ::: {.b} text ::: ::: {.c} text ::: :::: ^D [ Div ( "" , [ "a" ] , [] ) [ BulletList [ [ Div ( "" , [ "b" ] , [] ) [ Para [ Str "text" ] ] , Div ( "" , [ "c" ] , [] ) [ Para [ Str "text" ] ] ] ] ] ] ``` ================================================ FILE: test/command/4284.md ================================================ ``` % pandoc -f org -t native #+EXCLUDE_TAGS:apple cat bye dog % * This should not appear :apple: * NOEXPORT should appear if not specified in EXCLUDE_TAGS :noexport: * This should not appear :cat:hi:laptop: ** Children of headers with excluded tags should not appear :xylophone: * This should not appear :%: ^D [ Header 1 ( "noexport-should-appear-if-not-specified-in-excludetags" , [] , [] ) [ Str "NOEXPORT" , Space , Str "should" , Space , Str "appear" , Space , Str "if" , Space , Str "not" , Space , Str "specified" , Space , Str "in" , Space , Str "EXCLUDE" , Subscript [ Str "TAGS" ] , Space , Span ( "" , [ "tag" ] , [ ( "tag-name" , "noexport" ) ] ) [ SmallCaps [ Str "noexport" ] ] ] ] ``` ``` % pandoc -f org -t native #+EXCLUDE_TAGS:elephant * This should not appear :elephant: * This should appear :fawn: ^D [ Header 1 ( "this-should-appear" , [] , [] ) [ Str "This" , Space , Str "should" , Space , Str "appear" , Space , Span ( "" , [ "tag" ] , [ ( "tag-name" , "fawn" ) ] ) [ SmallCaps [ Str "fawn" ] ] ] ] ``` ``` % pandoc -f org -t native #+EXCLUDE_TAGS: giraffe #+EXCLUDE_TAGS: hippo * This should not appear :giraffe: * This should not appear :hippo: * This should appear :noexport: ^D [ Header 1 ( "this-should-appear" , [] , [] ) [ Str "This" , Space , Str "should" , Space , Str "appear" , Space , Span ( "" , [ "tag" ] , [ ( "tag-name" , "noexport" ) ] ) [ SmallCaps [ Str "noexport" ] ] ] ] ``` ``` % pandoc -f org -t native #+EXCLUDE_TAGS: * NOEXPORT should appear if not specified in EXCLUDE_TAGS :noexport: ^D [ Header 1 ( "noexport-should-appear-if-not-specified-in-excludetags" , [] , [] ) [ Str "NOEXPORT" , Space , Str "should" , Space , Str "appear" , Space , Str "if" , Space , Str "not" , Space , Str "specified" , Space , Str "in" , Space , Str "EXCLUDE" , Subscript [ Str "TAGS" ] , Space , Span ( "" , [ "tag" ] , [ ( "tag-name" , "noexport" ) ] ) [ SmallCaps [ Str "noexport" ] ] ] ] ``` ================================================ FILE: test/command/4306.md ================================================ ``` % pandoc -f latex -t native \documentclass{article} \usepackage{hyperref} \begin{document} The file id is \nolinkurl{ESP_123_5235}. \end{document} ^D [ Para [ Str "The" , Space , Str "file" , Space , Str "id" , Space , Str "is" , Space , Code ( "" , [] , [] ) "ESP_123_5235" , Str "." ] ] ``` ================================================ FILE: test/command/4320.md ================================================ ``` % pandoc -f native -t rst --wrap=none [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.3) ,(AlignDefault,ColWidth 0.3)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "one"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "two"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "ports"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [BlockQuote [Para [Strong [Str "thisIsGoingToBeTooLongAnyway"]]]]]])] (TableFoot ("",[],[]) [])] ^D +--------------------+-------------------------------------+ | one | two | +====================+=====================================+ | ports | **thisIsGoingToBeTooLongAnyway** | +--------------------+-------------------------------------+ ``` ================================================ FILE: test/command/4374.md ================================================ ``` % pandoc -f latex -t native \cite{a% } ^D [ Para [ Cite [ Citation { citationId = "a" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cite{a%\n}" ] ] ] ``` ================================================ FILE: test/command/4382.md ================================================ ``` % pandoc -f rst -t native - ===== ^D [ BulletList [ [] ] , HorizontalRule ] ``` ================================================ FILE: test/command/4420.md ================================================ ``` % pandoc -f native -t rst [Para [Image ("",["align-right"],[("width","100px")]) [Str "image"] ("foo.png","fig:test")]] ^D |image| .. |image| image:: foo.png :width: 100px ``` ================================================ FILE: test/command/4424.md ================================================ ``` % pandoc -f latex -t native \documentclass{article} \usepackage[sortlocale=en_GB]{biblatex} \begin{document} Test \end{document} ^D [ Para [ Str "Test" ] ] ``` ================================================ FILE: test/command/4442.md ================================================ ``` % pandoc -f markdown -t latex \newcommand{\myFruit}{Mango\xspace} \myFruit is the king of fruits. ^D \newcommand{\myFruit}{Mango\xspace} Mango is the king of fruits. ``` ================================================ FILE: test/command/4454.md ================================================ ``` % pandoc -f rst -t native • a • b ^D [ BulletList [ [ Plain [ Str "a" ] ] , [ Plain [ Str "b" ] ] ] ] ``` ================================================ FILE: test/command/4465.md ================================================ ``` % pandoc -f html -t markdown
    1. An ordered list can contain block-level elements ind html, it means that divs are also allowed.
    2. Let's see the problem!
      This is an example.
    ^D 1. An ordered list can contain block-level elements ind html, it means that divs are also allowed. 2. Let\'s see the problem! ::: example This is an example. ::: ``` ================================================ FILE: test/command/4470.md ================================================ ``` % pandoc -r latex -w plain \newcommand{\foo}{123}\renewcommand\foo{456}\foo ^D 456 ``` ``` % pandoc -r latex -w plain \newcommand\foo{123}\renewcommand\foo{456}\foo ^D 456 ``` ``` % pandoc -r latex -w plain \newcommand{\foo}{123}\renewcommand{\foo}{456}\foo ^D 456 ``` ``` % pandoc -r latex -w plain \newcommand\foo{123}\renewcommand{\foo}{456}\foo ^D 456 ``` ================================================ FILE: test/command/4499.md ================================================ ``` % pandoc -f latex -t html \mbox{abc def} ghi ^D

    abc def ghi

    ``` ``` % pandoc -f latex+raw_tex -t native \mbox{abc def} ^D [ Para [ RawInline (Format "latex") "\\mbox{abc def}" ] ] ``` ``` % pandoc -f latex -t html abc \mbox{\textit{def ghi} jkl} mno ^D

    abc def ghi jkl mno

    ``` ``` % pandoc -f latex -t html abc \mbox{def \\ ghi} jkl ^D

    abc defghi jkl

    ``` ``` % pandoc -f latex -t html abc \mbox{def ghi} ^D

    abc def ghi

    ``` ``` % pandoc -f latex -t html abc \mbox{def \textit{ghi \\ jkl} mno} pqr ^D

    abc def ghijkl mno pqr

    ``` ``` % pandoc -f latex -t html \hbox{abc def} ghi ^D

    abc def ghi

    ``` ``` % pandoc -f latex+raw_tex -t native \hbox{abc def} ^D [ Para [ RawInline (Format "latex") "\\hbox{abc def}" ] ] ``` ``` % pandoc -f latex -t html abc \hbox{\textit{def ghi} jkl} mno ^D

    abc def ghi jkl mno

    ``` ``` % pandoc -f latex -t html abc \hbox{def \\ ghi} jkl ^D

    abc defghi jkl

    ``` ``` % pandoc -f latex -t html abc \hbox{def ghi} ^D

    abc def ghi

    ``` ``` % pandoc -f latex -t html abc \hbox{def \textit{ghi \\ jkl} mno} pqr ^D

    abc def ghijkl mno pqr

    ``` ================================================ FILE: test/command/4513.md ================================================ ``` % pandoc -f textile -t native |_. heading 1 |_. heading 2| ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "heading" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "heading" , Space , Str "2" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/4527.md ================================================ This command comes through as regular text: ``` % pandoc -f latex+raw_tex -t native \maketitle ^D [ RawBlock (Format "latex") "\\maketitle" ] ``` ``` % pandoc -f latex -t native \maketitle ^D [] ``` ``` % pandoc -f latex -t rst \maketitle Hello. ^D Hello. ``` ================================================ FILE: test/command/4528.md ================================================ # Rendering small caps, superscripts and subscripts with and without `raw_html` ## Small caps ``` % pandoc --wrap=none -f latex -t commonmark-raw_html This has \textsc{small caps} in it. ^D This has SMALL CAPS in it. ``` ``` % pandoc --wrap=none -f latex -t commonmark+raw_html This has \textsc{small caps} in it. ^D This has small caps in it. ``` ``` % pandoc --wrap=none -f latex -t markdown_strict+raw_html This has \textsc{small caps} in it. ^D This has small caps in it. ``` ## Strikeout ``` % pandoc --wrap=none -f html -t commonmark-raw_html-strikeout This has strikeout in it. ^D This has strikeout in it. ``` ``` % pandoc --wrap=none -f html -t commonmark+raw_html-strikeout This has strikeout in it. ^D This has strikeout in it. ``` ``` % pandoc --wrap=none -f html -t commonmark-raw_html+strikeout This has strikeout in it. ^D This has ~~strikeout~~ in it. ``` ``` % pandoc --wrap=none -f html -t commonmark+raw_html+strikeout This has strikeout in it. ^D This has ~~strikeout~~ in it. ``` ``` % pandoc --wrap=none -f html -t markdown_strict-raw_html-strikeout This has strikeout in it. ^D This has strikeout in it. ``` ``` % pandoc --wrap=none -f html -t markdown_strict+raw_html-strikeout This has strikeout in it. ^D This has strikeout in it. ``` ``` % pandoc --wrap=none -f html -t markdown_strict-raw_html+strikeout This has strikeout in it. ^D This has ~~strikeout~~ in it. ``` ``` % pandoc --wrap=none -f html -t markdown_strict+raw_html+strikeout This has strikeout in it. ^D This has ~~strikeout~~ in it. ``` ## Superscript ``` % pandoc --wrap=none -f html -t commonmark-raw_html This has superscript in it and 2 3 again. With emphasis: 2 3. With letters: foo. With a span: 2. ^D This has ^(superscript) in it and ² ³ again. With emphasis: ^(*2* 3). With letters: ^(foo). With a span: ². ``` ``` % pandoc --wrap=none -f html -t commonmark+raw_html This has superscript in it and 2 again. ^D This has superscript in it and 2 again. ``` ``` % pandoc --wrap=none -f html -t markdown_strict-raw_html-superscript This has superscript in it and 2 again. ^D This has ^(superscript) in it and ² again. ``` ``` % pandoc --wrap=none -f html -t markdown_strict+raw_html-superscript This has superscript in it and 2 again. ^D This has superscript in it and 2 again. ``` ``` % pandoc --wrap=none -f html -t markdown_strict+raw_html+superscript This has superscript in it and 2 again. ^D This has ^superscript^ in it and ^2^ again. ``` ## Subscript ``` % pandoc --wrap=none -f html -t commonmark-raw_html This has subscript in it and 2 3 again. With emphasis: 2 3. With letters: foo. With a span: 2. ^D This has _(subscript) in it and ₂ ₃ again. With emphasis: _(*2* 3). With letters: _(foo). With a span: ₂. ``` ``` % pandoc --wrap=none -f html -t commonmark+raw_html This has subscript in it and 2 again. ^D This has subscript in it and 2 again. ``` ``` % pandoc --wrap=none -f html -t markdown_strict-raw_html-subscript This has subscript in it and 2 again. ^D This has _(subscript) in it and ₂ again. ``` ``` % pandoc --wrap=none -f html -t markdown_strict+raw_html-subscript This has subscript in it and 2 again. ^D This has subscript in it and 2 again. ``` ``` % pandoc --wrap=none -f html -t markdown_strict+raw_html+subscript This has subscript in it and 2 again. ^D This has ~subscript~ in it and ~2~ again. ``` ================================================ FILE: test/command/4529.md ================================================ ``` % pandoc -f latex -t plain+gutenberg \chapter{First chapter}\label{sec:chp1} The next chapter is Chapter~\ref{sec:chp2}. \section{First section}\label{sec:chp1sec1} The next section is Section~\ref{sec:chp2sec1}. \chapter{Second chapter}\label{sec:chp2} The previous chapter is Chapter~\ref{sec:chp1}. \section{First section}\label{sec:chp2sec1} The previous section is Section~\ref{sec:chp1sec1}. ^D FIRST CHAPTER The next chapter is Chapter 2. First section The next section is Section 2.1. SECOND CHAPTER The previous chapter is Chapter 1. First section The previous section is Section 1.1. ``` ================================================ FILE: test/command/4545.md ================================================ ``` % pandoc -t asciidoc Test 1 [my text] Test 2 ^D Test 1 ++[++my text++]++ Test 2 ``` ``` % pandoc -t asciidoc 4\. foo ^D {empty}4. foo ``` ================================================ FILE: test/command/4550.md ================================================ ``` % pandoc -f markdown-smart -t ms A ‘simple’ example ^D .LP A \(oqsimple\(cq example ``` ================================================ FILE: test/command/4553.md ================================================ ``` % pandoc -f latex -t native foo \include{command/bar} ^D [ Para [ Str "foo" ] , Para [ Emph [ Str "hi" , Space , Str "there" ] ] ] ``` ``` % pandoc -f latex -t native foo \input{command/bar} ^D [ Para [ Str "foo" , Space , Emph [ Str "hi" , Space , Str "there" ] ] ] ``` ================================================ FILE: test/command/4564.md ================================================ ``` % pandoc -f native -t rst --list-tables [Table ("",[],[]) (Caption Nothing [Plain [Str "Here",Space,Str "is",Space,Str "a",Space,Str "caption."]]) [(AlignDefault,ColWidth 0.1527777777777778) ,(AlignDefault,ColWidth 0.1388888888888889) ,(AlignDefault,ColWidth 0.16666666666666666) ,(AlignDefault,ColWidth 0.375)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Centered",SoftBreak,Str "Header"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Left",SoftBreak,Str "Aligned"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Right",SoftBreak,Str "Aligned"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Default",Space,Str "aligned"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "First"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain []] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12.0"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",SoftBreak,Str "spans",Space,Str "multiple",Space,Str "lines."]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Second"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "row"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5.0"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]])] (TableFoot ("",[],[]) [])] ^D .. list-table:: Here is a caption. :widths: 11 10 12 27 :header-rows: 1 * - Centered Header - Left Aligned - Right Aligned - Default aligned * - First - - 12.0 - Example of a row that spans multiple lines. * - Second - row - 5.0 - Here’s another one. Note the blank line between rows. ``` ================================================ FILE: test/command/4576.md ================================================ ``` % pandoc -f latex -t native $\rho_\text{D$_2$O}=866$ ^D [ Para [ Math InlineMath "\\rho_\\text{D$_2$O}=866" ] ] ``` ================================================ FILE: test/command/4578.md ================================================ ``` % pandoc -t markdown ------ ------- --------------- --------------------- One row 12.0 Example of a row that spans multiple lines. ------ ------- --------------- --------------------- ^D ------ ------- --------------- --------------------- One row 12.0 Example of a row that spans multiple lines. ------ ------- --------------- --------------------- ``` ================================================ FILE: test/command/4579.md ================================================ ``` % pandoc -f rst -t native .. list-table:: :header-rows: 1 * - Foo - Bar * - spam - ham ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Foo" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bar" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "spam" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "ham" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/4589.md ================================================ ``` % pandoc -f markdown -t latex \newcommand{\one}[1]{#1} \newcommand{\two}[1]{#1} Formatting *is* working **here**. But sticking \one{two }\two{commands} together *breaks* formatting. ^D \newcommand{\one}[1]{#1} \newcommand{\two}[1]{#1} Formatting \emph{is} working \textbf{here}. But sticking two commands together \emph{breaks} formatting. ``` ================================================ FILE: test/command/4594.md ================================================ ``` % pandoc -f markdown -t latex Some **bold** text here. \begin{figure}[htbp] \centering \def\svgwidth{\columnwidth} \import{img/}{vectors.pdf_tex} \caption{Some caption.} \end{figure} Some *italic* text here. ^D Some \textbf{bold} text here. \begin{figure}[htbp] \centering \def\svgwidth{\columnwidth} \import{img/}{vectors.pdf_tex} \caption{Some caption.} \end{figure} Some \emph{italic} text here. ``` ================================================ FILE: test/command/4598.md ================================================ ``` % pandoc -f rst `x`__ __ `xy`_ .. _`xy`: http://xy.org ^D

    x

    ``` ================================================ FILE: test/command/4624.md ================================================ ``` % pandoc -f latex -t native \begin{Verbatim}[key1=value1] code1 \end{Verbatim} \begin{lstlisting}[key2=value2] code2 \end{lstlisting} \begin{verbatim} code3 \end{verbatim} \begin{verbatim} code4 \end{verbatim} \begin{verbatim} code5\end{verbatim} ^D [ CodeBlock ( "" , [] , [ ( "key1" , "value1" ) ] ) "code1\n" , CodeBlock ( "" , [] , [ ( "key2" , "value2" ) ] ) "code2\n " , CodeBlock ( "" , [] , [] ) "code3" , CodeBlock ( "" , [] , [] ) "code4" , CodeBlock ( "" , [] , [] ) "code5" ] ``` ================================================ FILE: test/command/4635.md ================================================ ``` % pandoc -f markdown -t native (cf. foo) ^D [ Para [ Str "(cf." , SoftBreak , Str "foo)" ] ] ``` ``` % pandoc -f markdown -t native a (cf. foo) ^D [ Para [ Str "a" , Space , Str "(cf." , SoftBreak , Str "foo)" ] ] ``` ``` % pandoc -f markdown -t native cf. foo ^D [ Para [ Str "cf." , SoftBreak , Str "foo" ] ] ``` ``` % pandoc -f markdown -t native a cf. foo ^D [ Para [ Str "a" , Space , Str "cf." , SoftBreak , Str "foo" ] ] ``` ================================================ FILE: test/command/4637.md ================================================ ``` % pandoc -t latex more \indextext{dogs}' than \indextext{cats}' ^D more \indextext{dogs}' than \indextext{cats}' ``` ================================================ FILE: test/command/4639.md ================================================ ``` % pandoc -t html --mathjax \begin{equation} E=mc^2 \end{equation} ^D

    \[\begin{equation} E=mc^2 \end{equation}\]

    ``` ================================================ FILE: test/command/4653.md ================================================ ``` % pandoc -t latex \let\tex\TeX \renewcommand{\TeX}{\tex\xspace} ^D \let\tex\TeX \renewcommand{\TeX}{\tex\xspace} ``` ================================================ FILE: test/command/4667.md ================================================ ``` % pandoc -t latex --- header-includes: - \newcommand{\blandscape}{\begin{landscape}} - \newcommand{\elandscape}{\end{landscape}} ... \blandscape testing \elandscape ^D \begin{landscape} testing \end{landscape} ``` ================================================ FILE: test/command/4669.md ================================================ ``` % pandoc -f latex -t native {\tt <-} \begin{verbatim} while (n > 0) { \end{verbatim} ^D [ Para [ Span ( "" , [] , [] ) [ Code ( "" , [] , [] ) "<-" ] ] , CodeBlock ( "" , [] , [] ) " while (n > 0) {" ] ``` ``` % pandoc -f latex -t native \begin{itemize} \item<1> one \item<2-3,5> two \item<2| @alert> three \item four \item five \end{itemize} ^D [ BulletList [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] , [ Para [ Str "three" ] ] , [ Para [ Str "four" ] ] , [ Para [ Str "five" ] ] ] ] ``` ================================================ FILE: test/command/4677.md ================================================ ``` % pandoc --to "markdown-bracketed_spans-fenced_divs-link_attributes-simple_tables-multiline_tables-grid_tables-pipe_tables-fenced_code_attributes-markdown_in_html_blocks-table_captions-smart" ![Caption](img.png){#img:1} ^D
    Caption
    ``` ================================================ FILE: test/command/4690.md ================================================ ``` % pandoc -t beamer # title :::: {.columns} ::: {.column width="8%"} content ::: ::: {.column width="84%"} content2 ::: :::: ^D \begin{frame}{title} \protect\phantomsection\label{title} \begin{columns}[T] \begin{column}{0.08\linewidth} content \end{column} \begin{column}{0.84\linewidth} content2 \end{column} \end{columns} \end{frame} ``` ================================================ FILE: test/command/4715.md ================================================ ``` % pandoc -f rst -t native .. toctree:: :name: tree1 :class: foo bar :caption: Indice dei contenuti :numbered: :maxdepth: 3 premessa.rst acquisizione-software.rst riuso-software.rst ^D [ Div ( "tree1" , [ "toctree" , "foo" , "bar" ] , [ ( "caption" , "Indice dei contenuti" ) , ( "numbered" , "" ) , ( "maxdepth" , "3" ) ] ) [ Para [ Str "premessa.rst" , SoftBreak , Str "acquisizione-software.rst" , SoftBreak , Str "riuso-software.rst" ] ] ] ``` ================================================ FILE: test/command/4722.md ================================================ ``` % pandoc -f tikiwiki -t native *Level 1 *Level 1 **Level 2 ***Level 3 *Level 1 ^D [ BulletList [ [ Plain [ Str "Level" , Space , Str "1" ] ] , [ Plain [ Str "Level" , Space , Str "1" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "2" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "3" ] ] ] ] ] ] , [ Plain [ Str "Level" , Space , Str "1" ] ] ] ] ``` ``` % pandoc -f tikiwiki -t native #Level 1 #Level 1 ##Level 2 ###Level 3 #Level 1 ^D [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Level" , Space , Str "1" ] ] , [ Plain [ Str "Level" , Space , Str "1" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Level" , Space , Str "2" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Level" , Space , Str "3" ] ] ] ] ] ] , [ Plain [ Str "Level" , Space , Str "1" ] ] ] ] ``` ================================================ FILE: test/command/4742.md ================================================ Check that the commonmark reader handles the `ascii_identifiers` extension properly. ``` % pandoc -f commonmark+gfm_auto_identifiers+ascii_identifiers -t native # non ascii ⚠️ räksmörgås ^D [ Header 1 ( "non-ascii-warning-raksmorgas" , [] , [] ) [ Str "non" , Space , Str "ascii" , Space , Str "\9888\65039" , Space , Str "r\228ksm\246rg\229s" ] ] ``` Note that the emoji here is actually a composite character, formed from \9888 and \65039. The latter is a combining mark, so it survives... ``` % pandoc -f commonmark+gfm_auto_identifiers-ascii_identifiers -t native # non ascii ⚠️ räksmörgås ^D [ Header 1 ( "non-ascii-warning-r\228ksm\246rg\229s" , [] , [] ) [ Str "non" , Space , Str "ascii" , Space , Str "\9888\65039" , Space , Str "r\228ksm\246rg\229s" ] ] ``` `gfm` should have `ascii_identifiers` disabled by default. ``` % pandoc -f gfm -t native # non ascii ⚠️ räksmörgås ^D [ Header 1 ( "non-ascii-warning-r\228ksm\246rg\229s" , [] , [] ) [ Str "non" , Space , Str "ascii" , Space , Str "\9888\65039" , Space , Str "r\228ksm\246rg\229s" ] ] ``` ================================================ FILE: test/command/4743.md ================================================ Test that emojis are wrapped in Span ``` % pandoc -f commonmark+emoji -t native My:thumbsup:emoji:heart: ^D [ Para [ Str "My" , Span ( "" , [ "emoji" ] , [ ( "data-emoji" , "thumbsup" ) ] ) [ Str "\128077" ] , Str "emoji" , Span ( "" , [ "emoji" ] , [ ( "data-emoji" , "heart" ) ] ) [ Str "\10084\65039" ] ] ] ``` ``` % pandoc -f markdown+emoji -t native My:thumbsup:emoji:heart: ^D [ Para [ Str "My" , Span ( "" , [ "emoji" ] , [ ( "data-emoji" , "thumbsup" ) ] ) [ Str "\128077" ] , Str "emoji" , Span ( "" , [ "emoji" ] , [ ( "data-emoji" , "heart" ) ] ) [ Str "\10084\65039" ] ] ] ``` ``` % pandoc -f commonmark+emoji -t html :zero: header ============= My:thumbsup:emoji:heart:x :hearts: xyz ^D

    0️⃣ header

    My👍emoji❤️x ♥️ xyz

    ``` ================================================ FILE: test/command/4746.md ================================================ ``` % pandoc -f latex -t native \begin{tabular}{c c} \begin{tabular}{c} a \\ \end{tabular} & \\ \end{tabular} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "a" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/4748.md ================================================ ``` % pandoc -f org -t rst Before example block. #+begin_example This is in an example block. #+end_example After example block. ^D Before example block. :: This is in an example block. After example block. ``` ================================================ FILE: test/command/4768.md ================================================ ``` % pandoc -f latex -t plain \def\foo#1!#2!#3{#1 or #2 and #3} \foo aa!bbb bbb!{ccc} ^D aa or bbb bbb and ccc ``` ================================================ FILE: test/command/4781.md ================================================ ``` % pandoc -t native Markdown parsed *here* \include{command/bar} *But not here* ^D [ Para [ Str "Markdown" , Space , Str "parsed" , Space , Emph [ Str "here" ] ] , RawBlock (Format "tex") "\\include{command/bar}" , Para [ Emph [ Str "But" , Space , Str "not" , Space , Str "here" ] ] ] ``` ``` % pandoc -t native *here* \input{command/bar} *But not here* ^D [ Para [ Emph [ Str "here" ] , Space , RawInline (Format "tex") "\\input{command/bar}" ] , Para [ Emph [ Str "But" , Space , Str "not" , Space , Str "here" ] ] ] ``` ================================================ FILE: test/command/4794.md ================================================ ``` % pandoc -f markdown -t mediawiki | Column1 | Column2 | Column3 | | ------- | ------- | ------- | | text | | text | ^D {| class="wikitable" |- ! Column1 ! Column2 ! Column3 |- | text | | text |} ``` ================================================ FILE: test/command/4805-beamer-columns-alignment.md ================================================ ``` % pandoc -t beamer :::: { .columns } ::: { .column align=center } ::: ::: { .column align=bottom } ::: :::: :::: { .columns align=bottom .onlytextwidth } ::: { .column align=top } ::: ::: { .column align=top-baseline } ::: :::: :::: { .columns totalwidth=7em } :::: ^D \begin{frame} \begin{columns}[T] \begin{column}[c]{0.48\linewidth} \end{column} \begin{column}[b]{0.48\linewidth} \end{column} \end{columns} \begin{columns}[b,onlytextwidth] \begin{column}[T]{0.48\linewidth} \end{column} \begin{column}[t]{0.48\linewidth} \end{column} \end{columns} \begin{columns}[T,totalwidth=7em] \end{columns} \end{frame} ``` ================================================ FILE: test/command/4811.md ================================================ No blank lines in inline interpreted roles: ``` % pandoc -f rst -t native `no blank`:myrole: ^D [ Para [ Str "`no" ] , Para [ Str "blank`:myrole:" ] ] ``` Backslash escape behaves properly in interpreted roles: ``` % pandoc -f rst -t native `hi\ there`:sup: `hi\ there`:code: ^D [ Para [ Superscript [ Str "hithere" ] ] , Para [ Code ( "" , [] , [] ) "hi\\ there" ] ] ``` Backtick followed by alphanumeric doesn't end the span: ``` % pandoc -f rst -t native `hi`there`:myrole: ^D [ Para [ Code ( "" , [ "interpreted-text" ] , [ ( "role" , "myrole" ) ] ) "hi`there" ] ] ``` Newline is okay, as long as not blank: ``` % pandoc -f rst -t native `hi there`:myrole: ^D [ Para [ Code ( "" , [ "interpreted-text" ] , [ ( "role" , "myrole" ) ] ) "hi\nthere" ] ] ``` Use span for title-reference: ``` % pandoc -f rst -t native `default` ^D [ Para [ Span ( "" , [ "title-ref" ] , [] ) [ Str "default" ] ] ] ``` ================================================ FILE: test/command/4817.md ================================================ ``` % pandoc -t native -s --- foo: - bar: bam ... ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaList [ MetaMap (fromList [ ( "bar" , MetaInlines [ Str "bam" ] ) ]) ] ) ] } [] ``` ================================================ FILE: test/command/4819.md ================================================ ``` % pandoc -f markdown -t native -s --- foo: 42 ... ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaInlines [ Str "42" ] ) ] } [] ``` ``` % pandoc -f markdown -t native -s --- foo: true ... ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaBool True ) ] } [] ``` ``` % pandoc -f markdown -t native -s --- foo: True ... ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaBool True ) ] } [] ``` ``` % pandoc -f markdown -t native -s --- foo: FALSE ... ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaBool False ) ] } [] ``` ``` % pandoc -f markdown -t native -s --- foo: no ... ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaBool False ) ] } [] ``` ================================================ FILE: test/command/4832.md ================================================ ``` % pandoc -f latex -t native \url{http://example.com/foo%20bar.htm} ^D [ Para [ Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/foo%20bar.htm" ] ( "http://example.com/foo%20bar.htm" , "" ) ] ] ``` ``` % pandoc -f latex -t native \url{http://example.com/foo{bar}.htm} ^D [ Para [ Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/foo{bar}.htm" ] ( "http://example.com/foo{bar}.htm" , "" ) ] ] ``` ``` % pandoc -f latex -t native \href{http://example.com/foo%20bar}{Foobar} ^D [ Para [ Link ( "" , [] , [] ) [ Str "Foobar" ] ( "http://example.com/foo%20bar" , "" ) ] ] ``` ================================================ FILE: test/command/4833.md ================================================ ``` % pandoc -f native -t rst [Div ("",["warning"],[]) [Div ("",["title"],[]) [Para [Str "Warning"]] ,Para [Str "Hi"]]] ^D .. warning:: Hi ``` ``` % pandoc -f native -t rst [Div ("",["unknown"],[]) [Para [Str "Hi"]]] ^D .. container:: unknown Hi ``` ================================================ FILE: test/command/4842.md ================================================ ``` % pandoc -f latex -t native \l ^D [ Para [ Str "\322" ] ] ``` ================================================ FILE: test/command/4845.md ================================================ ``` % pandoc -f html -t native x leading trailing space x ^D [ Plain [ Str "x" , Space , Link ( "" , [] , [] ) [ Str "leading" , Space , Str "trailing" , Space , Str "space" ] ( "/foo" , "" ) , Space , Str "x" ] ] ``` ================================================ FILE: test/command/4848.md ================================================ ``` % pandoc -f latex -t native \enquote*{hi} ^D [ Para [ Quoted SingleQuote [ Str "hi" ] ] ] ``` ``` % pandoc -f latex -t native \foreignquote{italian}{hi} ^D [ Para [ Quoted DoubleQuote [ Span ( "" , [] , [ ( "lang" , "it" ) ] ) [ Str "hi" ] ] ] ] ``` ``` % pandoc -f latex -t native \hyphenquote*{italian}{hi} ^D [ Para [ Quoted SingleQuote [ Span ( "" , [] , [ ( "lang" , "it" ) ] ) [ Str "hi" ] ] ] ] ``` ``` % pandoc -f latex -t native Lorem ipsum \blockquote{dolor sit amet} consectetuer. ^D [ Para [ Str "Lorem" , Space , Str "ipsum" ] , BlockQuote [ Para [ Str "dolor" , Space , Str "sit" , Space , Str "amet" ] ] , Para [ Str "consectetuer." ] ] ``` ``` % pandoc -f latex -t native Lorem ipsum \blockcquote[198]{Knu86}{dolor sit amet} consectetuer. ^D [ Para [ Str "Lorem" , Space , Str "ipsum" ] , BlockQuote [ Para [ Str "dolor" , Space , Str "sit" , Space , Str "amet" ] , Para [ Cite [ Citation { citationId = "Knu86" , citationPrefix = [] , citationSuffix = [ Str "198" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [] ] ] , Para [ Str "consectetuer." ] ] ``` ``` % pandoc -f latex -t native Lorem ipsum \foreignblockquote{italian}{dolor sit amet} consectetuer. ^D [ Para [ Str "Lorem" , Space , Str "ipsum" ] , BlockQuote [ Div ( "" , [] , [ ( "lang" , "it" ) ] ) [ Para [ Str "dolor" , Space , Str "sit" , Space , Str "amet" ] ] ] , Para [ Str "consectetuer." ] ] ``` ================================================ FILE: test/command/4860.md ================================================ ``` % pandoc -f rst -t native This is broken_. .. ***** REFERENCES FOLLOW ***** .. _broken: http://google.com ^D [ Para [ Str "This" , Space , Str "is" , Space , Link ( "" , [] , [] ) [ Str "broken" ] ( "http://google.com" , "" ) , Str "." ] ] ``` ================================================ FILE: test/command/4877.md ================================================ ``` % pandoc -f html -t native My ^D [ Plain [ Str "My" , Space , Math InlineMath "\\mathcal{D}" ] ] ``` ``` % pandoc -f html -t native ^D [ Plain [ Math DisplayMath "\\mathcal{D}" ] ] ``` ================================================ FILE: test/command/4880.md ================================================ ``` % pandoc -t latex $x=y%comment$ ^D \(x=y%comment \) ``` ================================================ FILE: test/command/4885.md ================================================ ``` % pandoc -f org -t markdown This won't show the command. src_maxima[:exports none :results raw]{tex('integrate(sin((e^x)/pi),x,0,inf));} $$\int_{0}^{\infty }{\sin \left({{e^{x}}\over{\pi}}\right)\;dx}$$ ^D This won\'t show the command. $$\int_{0}^{\infty }{\sin \left({{e^{x}}\over{\pi}}\right)\;dx}$$ ``` ================================================ FILE: test/command/4908.md ================================================ ``` % pandoc -f markdown_mmd+fancy_lists+example_lists -t native -t plain (@) Example one (@) Example two some text (@) Example three ^D (1) Example one (2) Example two some text (3) Example three ``` ================================================ FILE: test/command/4913.md ================================================ ``` % pandoc -f markdown -t html [https://pandoc.org](https://pandoc.org) ^D

    https://pandoc.org

    ``` ``` % pandoc -f markdown -t markdown [https://pandoc.org](https://pandoc.org) ^D ``` ``` % pandoc -f markdown -t html ^D

    https://pandoc.org

    ``` ``` % pandoc -f markdown -t html {.foo} ^D

    https://pandoc.org

    ``` ``` % pandoc -f markdown -t html ^D

    ``` ================================================ FILE: test/command/4919.md ================================================ ``` % pandoc -f rst -t native .. _`tgtmath`: .. math:: :name: V = \frac{K}{r^2} ^D [ Div ( "tgtmath" , [] , [] ) [ BlockQuote [ Para [ Math DisplayMath "V = \\frac{K}{r^2}" ] ] ] ] ``` ================================================ FILE: test/command/4928.md ================================================ ``` % pandoc -f latex -t native \cites(Multiprenote)(multipostnote)[23][42]{Knu86}[65]{Nie72} ^D [ Para [ Cite [ Citation { citationId = "Knu86" , citationPrefix = [ Str "Multiprenote" , Space , Str "23" ] , citationSuffix = [ Str "42" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "Nie72" , citationPrefix = [] , citationSuffix = [ Str "65" , Str "," , Space , Str "multipostnote" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cites(Multiprenote)(multipostnote)[23][42]{Knu86}[65]{Nie72}" ] ] ] ``` ``` % pandoc -f latex -t native \cites(Multiprenote)()[23][42]{Knu86}[65]{Nie72} ^D [ Para [ Cite [ Citation { citationId = "Knu86" , citationPrefix = [ Str "Multiprenote" , Space , Str "23" ] , citationSuffix = [ Str "42" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "Nie72" , citationPrefix = [] , citationSuffix = [ Str "65" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cites(Multiprenote)()[23][42]{Knu86}[65]{Nie72}" ] ] ] ``` ``` % pandoc -f latex -t native \cites()(multipostnote)[23][42]{Knu86}[65]{Nie72} ^D [ Para [ Cite [ Citation { citationId = "Knu86" , citationPrefix = [ Str "23" ] , citationSuffix = [ Str "42" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "Nie72" , citationPrefix = [] , citationSuffix = [ Str "65" , Str "," , Space , Str "multipostnote" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cites()(multipostnote)[23][42]{Knu86}[65]{Nie72}" ] ] ] ``` ``` % pandoc -f latex -t native \cites()()[23][42]{Knu86}[65]{Nie72} ^D [ Para [ Cite [ Citation { citationId = "Knu86" , citationPrefix = [ Str "23" ] , citationSuffix = [ Str "42" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "Nie72" , citationPrefix = [] , citationSuffix = [ Str "65" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cites()()[23][42]{Knu86}[65]{Nie72}" ] ] ] ``` ``` % pandoc -f latex -t native \cites(multipostnote)[23][42]{Knu86}[65]{Nie72} ^D [ Para [ Cite [ Citation { citationId = "Knu86" , citationPrefix = [ Str "23" ] , citationSuffix = [ Str "42" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "Nie72" , citationPrefix = [] , citationSuffix = [ Str "65" , Str "," , Space , Str "multipostnote" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cites(multipostnote)[23][42]{Knu86}[65]{Nie72}" ] ] ] ``` ``` % pandoc -f latex -t native \cites(Multiprenote)(multipostnote){Knu86} ^D [ Para [ Cite [ Citation { citationId = "Knu86" , citationPrefix = [ Str "Multiprenote" ] , citationSuffix = [ Str "," , Space , Str "multipostnote" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cites(Multiprenote)(multipostnote){Knu86}" ] ] ] ``` ``` % pandoc -f latex -t native \footcites(Multiprenote)(multipostnote)[23][42]{Knu86}[65]{Nie72} ^D [ Para [ Note [ Para [ Cite [ Citation { citationId = "Knu86" , citationPrefix = [ Str "Multiprenote" , Space , Str "23" ] , citationSuffix = [ Str "42" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "Nie72" , citationPrefix = [] , citationSuffix = [ Str "65" , Str "," , Space , Str "multipostnote" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\footcites(Multiprenote)(multipostnote)[23][42]{Knu86}[65]{Nie72}" ] , Str "." ] ] ] ] ``` ================================================ FILE: test/command/4933.md ================================================ ``` % pandoc -f latex -t native \includegraphics{lalune} ^D [ Para [ Image ( "" , [] , [] ) [ Str "image" ] ( "lalune.jpg" , "" ) ] ] ``` ================================================ FILE: test/command/4960.md ================================================ ``` % pandoc -t latex --biblatex [@a1;@a2;@a3] ^D \autocite{a1,a2,a3} ``` ``` % pandoc -t latex --biblatex @a1 [@a2;@a3] ^D \textcite{a1,a2,a3} ``` ``` % pandoc -t latex --biblatex [@a1, blah; @a2; see @a3] ^D \autocites[blah]{a1}{a2}[see][]{a3} ``` ================================================ FILE: test/command/5010.md ================================================ ``` % pandoc -f latex -t latex \(\left\{ \begin{matrix} y\,\,\,\, \geqq \,\,\, f\,(\, x\,)\,\, \\ y\,\,\, \leqq \,\,\, g\,(\, x\,)\, \\ \end{matrix} \right.\ \) ^D \(\left\{ \begin{matrix} y\,\,\,\, \geqq \,\,\, f\,(\, x\,)\,\, \\ y\,\,\, \leqq \,\,\, g\,(\, x\,)\, \\ \end{matrix} \right.\ \) ``` ``` % pandoc -f markdown -t latex $x\ $ ^D \(x\ \) ``` ================================================ FILE: test/command/5014.md ================================================ ``` % pandoc -f html -t native
    Name
    Accounts
    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Name" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Accounts" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/5039.md ================================================ ``` % pandoc -f man -t plain .ds foo bar .ds bar baz \*[\*[foo]] ^D baz ``` ``` % pandoc -f man -t plain .ds foo [bar] .ds bar baz \*\*[foo] ^D baz ``` ``` % pandoc -f man -t html .ds B-Font B .ds I-Font I .ds R-Font R \f\*[B-Font]certtool\fP ^D

    certtool

    ``` ================================================ FILE: test/command/5050.md ================================================ ``` % pandoc -t rst x ^D x ``` ================================================ FILE: test/command/5053.md ================================================ ``` % pandoc —_legibility_— ^D

    legibility

    ``` ``` % pandoc _filename_|_filetype_ ^D

    filename|filetype

    ``` ================================================ FILE: test/command/5071.md ================================================ ``` % pandoc -f markdown -t html --number-sections ## First section ### Subhead ##### Subhead with gap ## Second section ^D

    1 First section

    1.1 Subhead

    1.1.0.1 Subhead with gap

    2 Second section

    ``` ``` % pandoc -f markdown -t html --number-sections ## First section ### Subhead # Higher-level section ## Sub ^D

    0.1 First section

    0.1.1 Subhead

    1 Higher-level section

    1.1 Sub

    ``` For backwards compatibility, we want it to work the old way, giving numbers like 0.1, when `--number-offset` is used: ``` % pandoc -f markdown -t html --number-sections --number-offset=2,2,2 ## First section ### Subhead ^D

    2.3 First section

    2.3.1 Subhead

    ``` ``` % pandoc -f markdown -t html --number-sections --number-offset=0,2,2 ## First section ### Subhead ^D

    0.3 First section

    0.3.1 Subhead

    ``` ================================================ FILE: test/command/5072.md ================================================ ``` % pandoc -t latex -i 1. one 2. two ^D \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item one \item two \end{enumerate} ``` ================================================ FILE: test/command/5079.md ================================================ ``` % pandoc -f html -t native
    Cell
    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/5080.md ================================================ ``` % pandoc -t asciidoc [foo *bar*]{.small .red key=val} ^D [.small .red]#foo _bar_# ``` ``` % pandoc -f html -t asciidoc SMALL ^D [.small]#SMALL# ``` ================================================ FILE: test/command/5081.md ================================================ ``` % pandoc -f rst Go to `g`_ `g `_. ^D

    Go to g g.

    ``` ================================================ FILE: test/command/5099.md ================================================ ``` % pandoc -t native (@citation ^D [ Para [ Str "(" , Cite [ Citation { citationId = "citation" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 1 , citationHash = 0 } ] [ Str "@citation" ] ] ] ``` ``` % pandoc -t native ('asd') ^D [ Para [ Str "(" , Quoted SingleQuote [ Str "asd" ] , Str ")" ] ] ``` ================================================ FILE: test/command/5107.md ================================================ ``` % pandoc -f muse -t dokuwiki - foo 1. bar - baz ^D * foo - bar * baz ``` ``` % pandoc -f muse -t dokuwiki - foo 1. bar 2. baz ^D * foo - bar - baz ``` ================================================ FILE: test/command/5116.md ================================================ ``` % pandoc -t latex ![This is a figure.](img.jpg) Right Left Center Default ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 : Demonstration of simple table syntax. ^D \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,alt={This is a figure.}]{img.jpg}} \caption{This is a figure.} \end{figure} \begin{longtable}[]{@{}rlcl@{}} \caption{Demonstration of simple table syntax.}\tabularnewline \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot 12 & 12 & 12 & 12 \\ 123 & 123 & 123 & 123 \\ 1 & 1 & 1 & 1 \\ \end{longtable} ``` ``` % pandoc -t latex --figure-caption-position=above --table-caption-position=above ![This is a figure.](img.jpg) Right Left Center Default ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 : Demonstration of simple table syntax. ^D \begin{figure} \centering \caption{This is a figure.} \pandocbounded{\includegraphics[keepaspectratio,alt={This is a figure.}]{img.jpg}} \end{figure} \begin{longtable}[]{@{}rlcl@{}} \caption{Demonstration of simple table syntax.}\tabularnewline \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot 12 & 12 & 12 & 12 \\ 123 & 123 & 123 & 123 \\ 1 & 1 & 1 & 1 \\ \end{longtable} ``` ``` % pandoc -t latex --figure-caption-position=below --table-caption-position=below ![This is a figure.](img.jpg) Right Left Center Default ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 : Demonstration of simple table syntax. ^D \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,alt={This is a figure.}]{img.jpg}} \caption{This is a figure.} \end{figure} \begin{longtable}[]{@{}rlcl@{}} \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \tabularnewline \caption{Demonstration of simple table syntax.} \endlastfoot 12 & 12 & 12 & 12 \\ 123 & 123 & 123 & 123 \\ 1 & 1 & 1 & 1 \\ \end{longtable} ``` ================================================ FILE: test/command/5119.md ================================================ ``` % pandoc -t docbook hi ^D hi ``` ================================================ FILE: test/command/512.md ================================================ ``` % pandoc -f rst `click here`__ or `click here`__ .. _link1: http://www.example.com/ .. _link2: http://johnmacfarlane.net/pandoc/ __ link1_ __ link2_ ^D

    click here or click here

    ``` Multiple indirection: ``` % pandoc -f rst `click here`__ .. _link1: link2_ .. _link2: http://johnmacfarlane.net/pandoc/ __ link1_ ^D

    click here

    ``` Loop detection: ``` % pandoc -f rst `click here`__ .. _link1: link2_ .. _link2: link1_ __ link1_ ^D 2> [WARNING] Circular reference 'link1' at line 8 column 1

    click here

    ``` ================================================ FILE: test/command/5121.md ================================================ ``` % pandoc -f markdown -t markdown_strict ![My caption](./my-figure.jpg){width=500px} ## Header 2 ^D
    My caption
    ## Header 2 ``` ================================================ FILE: test/command/5128.md ================================================ ``` % pandoc -f org -t rst --columns=78 | Option | Meaning | |--------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | =<= | Left alignment, additional characters are added to the right (default for string). | | =>= | Right alignment, additional characters are added to the left. | | =^= | Centered , the same amount of characters is added to the left and the right. | | === | Padding. If a numeric value is printed with a sign, then additional characters are added after the sign. Otherwise it behaves like "=>=". This option is only available for numbers (default for numbers). | ^D +--------+-------------------------------------------------------------------+ | Option | Meaning | +========+===================================================================+ | ``<`` | Left alignment, additional characters are added to the right | | | (default for string). | +--------+-------------------------------------------------------------------+ | ``>`` | Right alignment, additional characters are added to the left. | +--------+-------------------------------------------------------------------+ | ``^`` | Centered , the same amount of characters is added to the left and | | | the right. | +--------+-------------------------------------------------------------------+ | ``=`` | Padding. If a numeric value is printed with a sign, then | | | additional characters are added after the sign. Otherwise it | | | behaves like "``>``". This option is only available for numbers | | | (default for numbers). | +--------+-------------------------------------------------------------------+ ``` ================================================ FILE: test/command/5177.md ================================================ This should not give a "Prelude.read: no parse" error: ``` % pandoc -M foo=1e -s -t markdown hi ^D --- foo: 1e --- hi ``` ================================================ FILE: test/command/5178.md ================================================ ``` % pandoc -f rst -t org .. code:: haskell :number-lines: 42 main = putStrLn "Hello World!" unsafePerformIO main ^D #+begin_src haskell -n 42 main = putStrLn "Hello World!" unsafePerformIO main #+end_src ``` ``` % pandoc -f org -t native #+begin_src lisp -n 20 (+ 1 1) #+end_src #+begin_src lisp +n 10 (+ 2 2) #+end_src ^D [ CodeBlock ( "" , [ "commonlisp" , "numberLines" ] , [ ( "org-language" , "lisp" ) , ( "startFrom" , "20" ) ] ) "(+ 1 1)\n" , CodeBlock ( "" , [ "commonlisp" , "numberLines" , "continuedSourceBlock" ] , [ ( "org-language" , "lisp" ) , ( "startFrom" , "10" ) ] ) "(+ 2 2)\n" ] ``` ``` % pandoc -f native -t org [CodeBlock ("",["commonlisp","numberLines"],[("org-language","lisp"),("startFrom","20")]) "(+ 1 1)\n" ,CodeBlock ("",["commonlisp","numberLines","continuedSourceBlock"],[("org-language","lisp"),("startFrom","10")]) "(+ 2 2)\n"] ^D #+begin_src lisp -n 20 (+ 1 1) #+end_src #+begin_src lisp +n 10 (+ 2 2) #+end_src ``` ================================================ FILE: test/command/5182.md ================================================ ``` % pandoc -f rst -t native .. include:: command/5182.txt ^D [ CodeBlock ( "" , [ "python" , "numberLines" ] , [] ) "def func(x):\n return y" ] ``` ================================================ FILE: test/command/5182.txt ================================================ .. code::python :number-lines: def func(x): return y ================================================ FILE: test/command/5195.md ================================================ ``` % pandoc -f markdown_strict -t gfm+hard_line_breaks Hello there ^D Hello there ``` ================================================ FILE: test/command/5233.md ================================================ ``` % pandoc -f latex -t plain foo \endinput bar ^D foo ``` ================================================ FILE: test/command/5241.md ================================================ ``` % pandoc -f markdown -t html5 ^D ``` ================================================ FILE: test/command/5271.md ================================================ ``` % pandoc -f markdown -t native -s --- abstract: | This is the abstract. It consists of two paragraphs. ... ^D Pandoc Meta { unMeta = fromList [ ( "abstract" , MetaBlocks [ Para [ Str "This" , Space , Str "is" , Space , Str "the" , Space , Str "abstract." ] , Para [ Str "It" , Space , Str "consists" , Space , Str "of" , Space , Str "two" , Space , Str "paragraphs." ] ] ) ] } [] ``` ================================================ FILE: test/command/5285.md ================================================ ``` % pandoc -t native - a b - a - b ^D [ BulletList [ [ Para [ Str "a" ] , Para [ Str "b" ] ] , [ Para [ Str "a" ] ] , [ Para [ Str "b" ] ] ] ] ``` ``` % pandoc -t native - foo foo - foo > foo ^D [ BulletList [ [ Para [ Str "foo" ] , Para [ Str "foo" ] ] , [ Para [ Str "foo" ] , BlockQuote [ Para [ Str "foo" ] ] ] ] ] ``` ================================================ FILE: test/command/5304.md ================================================ ```` % pandoc -f markdown -t markdown ``` {.markdown} `«sträng»` `` «sträng» `` ``` «sträng» ``` ````«sträng»```` ... ``` ^D ``` markdown `«sträng»` `` «sträng» `` ``` «sträng» ``` ````«sträng»```` ... ``` ```` ================================================ FILE: test/command/5321.md ================================================ ``` % pandoc -f jats -t native

    bar

    baz
    ^D [ Figure ( "fig-1" , [] , [] ) (Caption Nothing [ Plain [ Str "bar" ] ]) [ Para [ Image ( "" , [] , [] ) [ Str "baz" ] ( "foo.png" , "" ) ] ] ] ``` ``` % pandoc -f jats -t native foo

    bar

    baz
    ^D [ Figure ( "fig-1" , [] , [] ) (Caption Nothing [ Plain [ Str "foo" , LineBreak , Str "bar" ] ]) [ Para [ Image ( "" , [] , [] ) [ Str "baz" ] ( "foo.png" , "" ) ] ] ] ``` ================================================ FILE: test/command/5340.md ================================================ ``` % pandoc -f html -t latex --wrap=preserve https://example.com/foo-bar https://example.com/foo--bar https://example.com/foo-bar https://example.com/foo--bar https://example.com/foo%2D%2Dbar ^D \url{https://example.com/foo-bar} \url{https://example.com/foo--bar} \url{https://example.com/foo\%2Dbar} \url{https://example.com/foo\%2D\%2Dbar} \url{https://example.com/foo\%2D\%2Dbar} ``` ================================================ FILE: test/command/5360.md ================================================ ``` % pandoc -t native ::: {.foo}
    hi
    ::: ^D [ Div ( "" , [ "foo" ] , [] ) [ RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "
    " , Plain [ Str "hi" ] , RawBlock (Format "html") "
    " ] ] ``` ================================================ FILE: test/command/5367.md ================================================ ``` % pandoc -t latex hello[^1] : Sample table.[^2] ----------- Fruit[^3] ----------- Bans[^4] ----------- dolly[^5] [^1]: doc footnote [^2]: caption footnote [^3]: header footnote [^4]: table cell footnote [^5]: doc footnote ^D hello\footnote{doc footnote} \begin{longtable}[]{@{} >{\centering\arraybackslash}p{(\linewidth - 0\tabcolsep) * \real{0.1667}}@{}} \caption[Sample table.]{Sample table.\footnote{caption footnote}}\tabularnewline \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Fruit\footnote{header footnote} \end{minipage} \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Fruit{} \end{minipage} \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot Bans\footnote{table cell footnote} \\ \end{longtable} dolly\footnote{doc footnote} ``` ================================================ FILE: test/command/5368.md ================================================ ``` % pandoc -t native 1. foo ![bar](bar.png) 2. foo2 ![bar2](bar2.png) 3. foo3 ![foo3](foo3.png) Quux. ^D [ OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "foo" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "bar" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "bar" ] ( "bar.png" , "" ) ] ] ] , [ Para [ Str "foo2" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "bar2" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "bar2" ] ( "bar2.png" , "" ) ] ] ] , [ Para [ Str "foo3" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "foo3" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "foo3" ] ( "foo3.png" , "" ) ] ] ] ] , Para [ Str "Quux." ] ] ``` ================================================ FILE: test/command/5369.md ================================================ ``` % pandoc -f native -t markdown [Div ("",[],[("tags","[\"o\\ne\",\"two\"]")]) [] ] ^D ::: {tags="[\"o\\ne\",\"two\"]"} ::: ``` ================================================ FILE: test/command/5407.md ================================================ ``` % pandoc -t latex --wrap=preserve hi there?“ hi there!“ hi there?‘ hi there!‘ hi there! ^D hi there?{\kern0pt}`` hi there!{\kern0pt}`` hi there?{\kern0pt}` hi there!{\kern0pt}` hi there! ``` ================================================ FILE: test/command/5410.md ================================================ ``` % pandoc -f man -t native .ie n \{\ 'br\} ^D [ Para [ LineBreak ] ] ``` ================================================ FILE: test/command/5416.md ================================================ ``` % pandoc -f dokuwiki -t native { ^D [ Para [ Str "{" ] ] ``` ``` % pandoc -f dokuwiki -t native {{ ^D [ Para [ Str "{{" ] ] ``` ================================================ FILE: test/command/5420.md ================================================ ``` % pandoc --syntax-highlighting=idiomatic -t latex `int a = 1;`{.cpp style=cpp} ^D \passthrough{\lstinline[language={C++}, style=cpp]!int a = 1;!} ``` ================================================ FILE: test/command/5439.md ================================================ ``` % pandoc -t latex namespace\fshyp{}container ^D namespace\fshyp{}container ``` ================================================ FILE: test/command/5446.md ================================================ ``` % pandoc -f latex -t rst {\em test test \/} ^D *test test* ``` ================================================ FILE: test/command/5474-figures.md ================================================ ``` % pandoc -t opendocument+native_numbering ![First image](lalune.jpg) ![Second image](lalune.jpg) ^D Figure 1: First image Figure 2: Second image ``` ================================================ FILE: test/command/5474-tables.md ================================================ ``` % pandoc -t opendocument+native_numbering Right Left ------- ------ 12 11 : First table Right Left ------- ------ 13 14 : Second Table ^D Table 1: First table Right Left 12 11 Table 2: Second Table Right Left 13 14 ``` ================================================ FILE: test/command/5476.md ================================================ ``` % pandoc -t latex ![moon^[the moon]](test/lalune.jpg) ^D \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,alt={moon}]{test/lalune.jpg}} \caption[moon]{moon\footnotemark{}} \end{figure} \footnotetext{the moon} ``` ================================================ FILE: test/command/5495.md ================================================ ``` % pandoc -t markdown --reference-links All because of [1](#one) link... [This](#foo) will break Pandoc. [This](#bar) will make you laugh. ^D All because of [1] link... [This] will break Pandoc. [This][2] will make you laugh. [1]: #one [This]: #foo [2]: #bar ``` ================================================ FILE: test/command/5519.md ================================================ ~~~ % pandoc -t markdown ``````{.attr} ``` code ``` `````` ^D ```` attr ``` code ``` ```` ~~~ ================================================ FILE: test/command/5529.md ================================================ ``` % pandoc -t latex ~~`hello world`~~ ~~_`hello world`_~~ ^D \st{\mbox{\texttt{hello\ world}}} \st{\emph{\mbox{\texttt{hello\ world}}}} ``` ================================================ FILE: test/command/5540.md ================================================ ``` % pandoc -f latex -t native \begin{lstlisting}[language=myfunnylanguage] Stay pure! \end{lstlisting} ^D [ CodeBlock ( "" , [ "myfunnylanguage" ] , [ ( "language" , "myfunnylanguage" ) ] ) "Stay pure!" ] ``` ================================================ FILE: test/command/5541-localLink.md ================================================ ``` % pandoc -f markdown -t icml -s # Header 1 this is some text ## Header 2 some more text that [links to](#header-1) the first header. And this links to [some text](#spanner) in 2.1. ## Header 2.1 if you can read this text, [and it's linked]{#spanner} - all good! ^D $ID/NormalCharacterStyle LeftAlign . 10 $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle Header 1
    this is some text
    Header 2
    some more text that the first header. And this links to in 2.1.
    Header 2.1
    if you can read this text, and it’s linked - all good!
    Black HyperlinkTextDestination/#spanner Black HyperlinkTextDestination/#header-1
    ``` ================================================ FILE: test/command/5541-nesting.md ================================================ ``` % pandoc -f html -t icml -s
    ^D $ID/NormalCharacterStyle LeftAlign . 10 $ID/NormalParagraphStyle $ID/Embedded ``` ================================================ FILE: test/command/5541-urlLink.md ================================================ ``` % pandoc -f markdown -t icml -s # Header 1 this is some text ## Header 2 some more text that [links to](https://www.pandoc.org) Pandoc. ^D $ID/NormalCharacterStyle LeftAlign . 10 $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle Header 1
    this is some text
    Header 2
    some more text that Pandoc.
    Black HyperlinkURLDestination/https%3a//www.pandoc.org
    ``` ================================================ FILE: test/command/5543.md ================================================ ``` % pandoc -t markdown 1\. item : description ^D 1\. item : description ``` ================================================ FILE: test/command/5549.md ================================================ ``` % pandoc -t native ## [] ^D [ Header 2 ( "section" , [] , [] ) [] , Para [ Str "[]" ] ] ``` ================================================ FILE: test/command/5565.md ================================================ ``` % pandoc -t asciidoc ***hi*** ^D *_hi_* ``` ================================================ FILE: test/command/5566.md ================================================ ``` % pandoc -t asciidoc -f html foo foo foo foo ^D fo[.c]##o## f[.c]##o##o [.c]##f##oo [.c]#foo# ``` ================================================ FILE: test/command/5574.md ================================================ No highlighting inside heading: ``` % pandoc -t latex # `foo`{.cpp} ^D \section{\texorpdfstring{\texttt{foo}}{foo}}\label{foo} ``` ================================================ FILE: test/command/5619.md ================================================ ``` % pandoc -f rst -t native .. figure:: img1.jpg :width: 1in :name: test The caption. Here's what piggybacking on caption would look like {#fig:1} ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "The" , Space , Str "caption." , Space , Str "Here's" , Space , Str "what" , Space , Str "piggybacking" , Space , Str "on" , Space , Str "caption" , Space , Str "would" , Space , Str "look" , Space , Str "like" , Space , Str "{#fig:1}" ] ]) [ Plain [ Image ( "test" , [] , [ ( "width" , "1in" ) ] ) [ Str "The" , Space , Str "caption." , Space , Str "Here's" , Space , Str "what" , Space , Str "piggybacking" , Space , Str "on" , Space , Str "caption" , Space , Str "would" , Space , Str "look" , Space , Str "like" , Space , Str "{#fig:1}" ] ( "img1.jpg" , "" ) ] ] ] ``` ================================================ FILE: test/command/5620.md ================================================ ``` % pandoc -t man `-o`, `--output=`*OUTFILE* : Write output to *OUTFILE* instead of `stdout`(3) ^D .TP \f[CR]\-o\f[R], \f[CR]\-\-output=\f[R]\f[I]OUTFILE\f[R] Write output to \f[I]OUTFILE\f[R] instead of \f[CR]stdout\f[R](3) ``` ================================================ FILE: test/command/5627.md ================================================ ``` % pandoc -t html ## Example 1. One 2. Two `-->somethingsomethingsomethingsomething elseonetwo LeftAlign . 10 $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle Header 1
    this is some text
    Header 2
    some more text that Pandoc.
    and some text that the first header
    Black HyperlinkTextDestination/#header-1 Black HyperlinkURLDestination/https%3a//www.pandoc.org ``` ================================================ FILE: test/command/6699.md ================================================ ``` % pandoc -f rst -t native .. class:: allowframebreaks title ----- text ^D [ Header 1 ( "title" , [ "allowframebreaks" ] , [] ) [ Str "title" ] , Para [ Str "text" ] ] ``` ================================================ FILE: test/command/6709.md ================================================ Tabs must be expanded even if --file-scope is used ```` % pandoc -t native --file-scope --tab-stop=2 ``` if true; then echo "yup" fi ``` ^D [ CodeBlock ( "" , [] , [] ) "if true; then\n echo \"yup\"\nfi" ] ```` ================================================ FILE: test/command/6719.md ================================================ ``` % pandoc -f docbook -t native emphasized text ^D [ Para [ Emph [ Str "emphasized" , Space ] , Str "text" ] ] ``` ``` % pandoc -f jats -t native

    hi there

    ^D [ Para [ Emph [ Space , Str "hi" , Space ] , Str "there" ] ] ``` ================================================ FILE: test/command/6723.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/chicago-fullnote-bibliography.csl references: - id: doe type: article author: - family: Doe given: John DOI: 10.1109/5.771073 issued: - year: 2020 title: An article ... Blah [@doe]. # References {-} ^D Blah.[^1] # References {#references .unnumbered} :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-doe .csl-entry} Doe, John. "An Article," 2020. . ::: :::: [^1]: John Doe, "An Article," 2020, . ``` ================================================ FILE: test/command/6739.md ================================================ ``` % pandoc -f gfm * `--argument` This item does not have a pipe character * `--argA | --argB` This item has a pipe character ^D
    • --argument This item does not have a pipe character
    • --argA | --argB This item has a pipe character
    ``` ``` % pandoc --mathjax -f gfm+tex_math_dollars * $|x|$ * $|y|$ ^D
    • \(|x|\)
    • \(|y|\)
    ``` ================================================ FILE: test/command/6740.md ================================================ ``` % pandoc -t gfm ^D ``` ================================================ FILE: test/command/6741.md ================================================ ``` % pandoc --citeproc --csl command/ieee.csl -t plain --- title: 'A Simple Citations Test' author: 'Joanna Doe' references: - author: - family: Mathôt given: S container-title: Annual review of vision science DOI: 10.1146/annurev-vision-030320-062352 id: mathot2020 issued: 2020 title: "Tuning the senses: How the pupil shapes vision at the earliest stage." title-short: Tuning the senses type: article-journal - author: - family: Zokaei given: Nahid - family: Board given: Alexander G. - family: Manohar given: Sanjay G. - family: Nobre given: Anna C. container-title: Proceedings of the National Academy of Sciences of the United States of America DOI: 10.1073/pnas.1909959116 id: zokaei2019 issued: 2019 page: 201909959 title: Modulation of the pupillary response by the content of visual working memory type: article-journal - author: - family: Suzuki given: Y. - family: Minami given: T. - family: Laeng given: B. - family: Nakauchi given: S. container-title: Acta Psychologica DOI: 10.1016/j.actpsy.2019.102882 id: suzuki2019 issued: 2019 page: 102882 title: "Colorful glares: Effects of colors on brightness illusions measured with pupillometry." title-short: Colorful glares type: article-journal volume: 198 - author: - family: Brainard given: David H. - family: Hurlbert given: Anya container-title: Current Biology DOI: 10.1016/j.cub.2015.05.020 id: brainard2015a issue: 13 issued: 2015 page: R551-R554 title: "Colour vision: Understanding \\#TheDress" title-short: Colour vision type: article-journal volume: 25 - author: - family: Cavanagh given: Patrick container-title: Limits of vision editor: - family: Crody-Dillon given: John R. id: cavanagh1991 issued: 1991 page: 234-250 publisher: CRC Press title: Vision at equiluminance type: chapter - author: - family: Brainard given: David H. container-title: Annual Review of Vision Science DOI: 10.1146/annurev-vision-082114-035341 id: brainard2015 issued: 2015 page: 519-546 title: Color and the cone mosaic type: article-journal volume: 1 ... # First Heading # Here is some **text**. And a reference [@brainard2015;@brainard2015a]. # Second Heading # Here is some *text*[@cavanagh1991]. And a reference [@cavanagh1991;@brainard2015]. # Third Heading # Here is some [text]{.underline}. And a reference [@mathot2020;@zokaei2019;@suzuki2019]. ::: {#refs} ## Bibliography ## ::: ^D First Heading Here is some text. And a reference [1], [2]. Second Heading Here is some text[3]. And a reference [1], [3]. Third Heading Here is some text. And a reference [4]–[6]. Bibliography [1] D. H. Brainard, “Color and the cone mosaic,” Annual Review of Vision Science, vol. 1, pp. 519–546, 2015. [2] D. H. Brainard and A. Hurlbert, “Colour vision: Understanding #TheDress,” Current Biology, vol. 25, no. 13, pp. R551–R554, 2015. [3] P. Cavanagh, “Vision at equiluminance,” in Limits of vision, J. R. Crody-Dillon, Ed. CRC Press, 1991, pp. 234–250. [4] S. Mathôt, “Tuning the senses: How the pupil shapes vision at the earliest stage.” Annual review of vision science, 2020. [5] N. Zokaei, A. G. Board, S. G. Manohar, and A. C. Nobre, “Modulation of the pupillary response by the content of visual working memory,” Proceedings of the National Academy of Sciences of the United States of America, p. 201909959, 2019. [6] Y. Suzuki, T. Minami, B. Laeng, and S. Nakauchi, “Colorful glares: Effects of colors on brightness illusions measured with pupillometry.” Acta Psychologica, vol. 198, p. 102882, 2019. ``` ================================================ FILE: test/command/6752.md ================================================ ``` % pandoc -f biblatex -t csljson @xdata{XDPubAlfredAKnopf,    publisher   = {Alfred A.~Knopf},    address     = {New York, NY} } @book{Klinkenborg2012,    author      = {Verlyn Klinkenborg},    title       = {Several short sentences about writing},    date        = {2012},    xdata       = {XDPubAlfredAKnopf}, } ^D [ { "author": [ { "family": "Klinkenborg", "given": "Verlyn" } ], "id": "Klinkenborg2012", "issued": { "date-parts": [ [ 2012 ] ] }, "publisher": "Alfred A. Knopf", "publisher-place": "New York, NY", "title": "Several short sentences about writing", "type": "book" } ] ``` ================================================ FILE: test/command/6755.md ================================================ ``` %pandoc -f native -t opendocument [Div ("divId",[],[]) [Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "div."]], Para [Span ("spanId",[],[]) [Str "And",Space,Str "here",Space,Str "is",Space,Str "a",Space,Str "span."]]] ^D Here is a div. And here is a span. ``` ================================================ FILE: test/command/6765.md ================================================ ``` % pandoc --citeproc -t plain @book_chapter_1 [55] says blah. --- csl: command/chicago-fullnote-bibliography.csl references: - id: book_chapter_1 author: - family: Author given: Ann container-title: A book editor: - family: Editor given: Edward issued: 2014 page: 48-67 publisher: A publisher publisher-place: A place title: A book chapter type: chapter ... ^D Ann Author[1] says blah. Author, Ann. “A Book Chapter.” In A Book, edited by Edward Editor, 48–67. A place: A publisher, 2014. [1] “A Book Chapter,” in A Book, ed. Edward Editor (A place: A publisher, 2014), 55. ``` ================================================ FILE: test/command/6768.md ================================================ ``` % pandoc -tcommonmark ::: custom_div This is a div ::: ^D
    This is a div
    ``` ``` % pandoc -tcommonmark-raw_html ::: custom_div This is a div ::: ^D This is a div ``` ================================================ FILE: test/command/6774.md ================================================ ``` % pandoc -f native -t opendocument --quiet [Header 1 ("chapter1",[],[]) [Str "The",Space,Str "Chapter"] ,Para [Str "Chapter",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "The",Space,Str "Chapter"] ("#chapter1","")]] ^D The Chapter Chapter 1 references The Chapter ``` ``` % pandoc -f native -t opendocument+xrefs_name --quiet [Header 1 ("chapter1",[],[]) [Str "The",Space,Str "Chapter"] ,Para [Str "Chapter",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "The",Space,Str "Chapter"] ("#chapter1","")] ,Figure ("lalune",[],[]) (Caption Nothing [Para [Str "Voyage dans la Lune"]]) [Plain [Image ("",[],[]) [Str "lalune"] ("lalune.jpg","")]] ,Para [Str "Image",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "La",Space,Str "Lune"] ("#lalune","")]] ^D The Chapter Chapter 1 references The Chapter Voyage dans la Lune Image 1 references La Lune ``` ``` % pandoc -f native -t opendocument+xrefs_number --quiet [Header 1 ("chapter1",[],[]) [Str "The",Space,Str "Chapter"] ,Para [Str "Chapter",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "The",Space,Str "Chapter"] ("#chapter1","")] ,Figure ("lalune",[],[]) (Caption Nothing [Para [Str "lalune"]]) [Plain [Image ("",[],[]) [Str "lalune"] ("lalune.jpg","Voyage dans la Lune")]] ,Para [Str "Image",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "La",Space,Str "Lune"] ("#lalune","")]] ^D The Chapter Chapter 1 references lalune Image 1 references ``` ``` % pandoc -f native -t opendocument+xrefs_number+xrefs_name --quiet [Header 1 ("chapter1",[],[]) [Str "The",Space,Str "Chapter"] ,Para [Str "Chapter",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "The",Space,Str "Chapter"] ("#chapter1","")] ,Figure ("lalune",[],[]) (Caption Nothing [Para [Str "Voyage dans la Lune"]]) [Plain [Image ("",[],[]) [Str "lalune"] ("lalune.jpg","")]] ,Para [Str "Image",Space,Str "1",Space,Str "references",Space,Link ("",[],[]) [Str "La",Space,Str "Lune"] ("#lalune","")]] ^D The Chapter Chapter 1 references The Chapter Voyage dans la Lune Image 1 references La Lune ``` ================================================ FILE: test/command/6783.md ================================================ ``` % pandoc -f markdown -t plain -s --citeproc --- references: - author: - family: Jupyter given: Project container-title: Proceedings of the 17th Python in Science Conference id: 'ref-1' issued: 2018 title: 'Binder 2.0 - Reproducible, interactive, sharable environments for science at scale' type: 'paper-conference' volume: ... This is a test[@ref-1]. ^D This is a test(Jupyter 2018). Jupyter, Project. 2018. “Binder 2.0 - Reproducible, Interactive, Sharable Environments for Science at Scale.” Proceedings of the 17th Python in Science Conference. ``` ================================================ FILE: test/command/6791.md ================================================ ``` % pandoc -f docbook -t native --quiet 2 1 ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.25 ) , ( AlignDefault , ColWidth 0.25 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/6792.md ================================================ ``` % pandoc -f native -t opendocument -s --quiet [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.25) ,(AlignDefault,ColWidth 0.25)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Para [Str "2"]] ,Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Para [Str "1"]]]])] (TableFoot ("",[],[]) [])] ^D 2 1 ``` ================================================ FILE: test/command/6796.md ================================================ ``` % pandoc -f latex -t markdown \newcommand{\cL}{\mathcal{L}} \newcommand{\til}[1]{\tilde{#1}} $$\til\cL$$ \newcommand{\mc}[1]{\mathcal{#1}} \newcommand{\dL}{\mc{L}} $$\til\dL$$ ^D $$\tilde{\mathcal{L}}$$ $$\tilde{\mathcal{L}}$$ ``` ================================================ FILE: test/command/6802.md ================================================ ``` % pandoc -f latex -t native \blockquote[test][]{quote} ^D [ BlockQuote [ Para [ Str "quote" ] , Para [ Str "test" ] ] ] ``` ================================================ FILE: test/command/6821.md ================================================ ``` % pandoc -f native -t latex [ Para [ Code ( "" , [ "python" ] , [] ) "x = 5" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "test" , Space , Code ( "" , [ "python" ] , [] ) "x = 5" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "test" , Space , Code ( "" , [ "python" ] , [] ) "x = 5" ] ( "test.png" , "" ) ] ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Code ( "" , [ "cpp" ] , [] ) "caption" ] ]) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] ] ]) , Para [ Code ( "" , [ "cpp" ] , [] ) "caption" ] ] ^D \VERB|\NormalTok{x }\OperatorTok{=} \DecValTok{5}| \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,alt={This is a test x = 5}]{test.png}} \caption{This is a test \protect\VERB|\NormalTok{x }\OperatorTok{=} \DecValTok{5}|} \end{figure} \begin{longtable}[]{@{}l@{}} \caption{\protect\VERB|\NormalTok{caption}|}\tabularnewline \toprule\noalign{} \endfirsthead \endhead \midrule\noalign{} A \\ \bottomrule\noalign{} \endlastfoot \end{longtable} \VERB|\NormalTok{caption}| ``` ================================================ FILE: test/command/6836.md ================================================ ``` % pandoc -t native [@buchanan] (@foo) See @foo. [@buchanan] ^D [ Para [ Cite [ Citation { citationId = "buchanan" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 1 , citationHash = 0 } ] [ Str "[@buchanan]" ] ] , OrderedList ( 1 , Example , TwoParens ) [ [] ] , Para [ Str "See" , Space , Str "1." ] , Para [ Cite [ Citation { citationId = "buchanan" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 2 , citationHash = 0 } ] [ Str "[@buchanan]" ] ] ] ``` ================================================ FILE: test/command/6837.md ================================================ ``` % pandoc -t markdown --markdown-headings=setext ## Hi ### Ok ^D Hi -- ### Ok ``` ``` % pandoc -t markdown+lhs # Hi ^D 2> [WARNING] Rendering heading 'Hi' as a paragraph. 2> ATX headings cannot be used in literate Haskell, because '#' is not 2> allowed in column 1. Consider using --markdown-headings=setext. Hi ``` ``` % pandoc -t markdown --markdown-headings=atx Hi -- ^D ## Hi ``` ================================================ FILE: test/command/6844.md ================================================ Negative numbers with siunitx ``` % pandoc -f latex -t native \SI{123}{\celsius} ^D [ Para [ Str "123\160\176C" ] ] ``` ``` % pandoc -f latex -t native \SI{-123}{\celsius} ^D [ Para [ Str "\8722\&123\160\176C" ] ] ``` ``` % pandoc -f latex -t native \SI{+123}{\celsius} ^D [ Para [ Str "123\160\176C" ] ] ``` ================================================ FILE: test/command/6855.md ================================================ ``` % pandoc -t markdown (@a) one (@b) two See (@a--@b) ^D (1) one (2) two See (1--2) ``` ================================================ FILE: test/command/6858.md ================================================ ``` % pandoc -t markdown -f man .TH FvwmAnimate 1 "Date" Fvwm "Fvwm Modules" .UC .SH NAME \fBFvwmAnimate\fP \- the fvwm animate module .SH SYNOPSIS Module FvwmAnimate [ModuleAlias] .IP "*FvwmAnimate: Color \fBcolor\fP" Tells \fBFvwmAnimate\fP what color to draw with. The color is "XOR'ed" (exclusive ORed) onto the background. .IP "*FvwmAnimate: Pixmap \fBpixmap\fP" Tells \fBFvwmAnimate\fP to use \fBpixmap\fP to draw with. This can be useful if \fB*FvwmAnimate: Color\fP gives poor results. ^D # NAME **FvwmAnimate** - the fvwm animate module # SYNOPSIS Module FvwmAnimate \[ModuleAlias\] \*FvwmAnimate: Color color : Tells **FvwmAnimate** what color to draw with. The color is \"XOR\'ed\" (exclusive ORed) onto the background. \*FvwmAnimate: Pixmap pixmap : Tells **FvwmAnimate** to use **pixmap** to draw with. This can be useful if **\*FvwmAnimate: Color** gives poor results. ``` ``` % pandoc -t markdown -f man .IP "\[bu]" hi .IP "\[bu]" there ^D - hi - there ``` ================================================ FILE: test/command/6869.md ================================================ ``` % pandoc -f latex -t native \cite[„Aber“]{key} ^D [ Para [ Cite [ Citation { citationId = "key" , citationPrefix = [] , citationSuffix = [ Str "\8222Aber\8220" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cite[\8222Aber\8220]{key}" ] ] ] ``` ================================================ FILE: test/command/6873.md ================================================ ``` % pandoc -f latex -t native --citeproc \cite[„Etwas […{]} auslassen“]{key} ^D 2> [WARNING] Citeproc: citation key not found [ Para [ Cite [ Citation { citationId = "key" , citationPrefix = [] , citationSuffix = [ Str "\8222Etwas" , Space , Str "[\8230" , Span ( "" , [] , [] ) [ Str "]" ] , Space , Str "auslassen\8220" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(" , Strong [ Str "key?" ] , Str "\8222Etwas" , Space , Str "[\8230" , Span ( "" , [] , [] ) [ Str "]" ] , Space , Str "auslassen\8220)" ] ] ] ``` ================================================ FILE: test/command/6890.md ================================================ ``` % pandoc -t native --citeproc --- references: - author: - family: Früchtel given: Frank - family: Budde given: Wolfgang - family: Cyprian given: Gudrun edition: 3 id: fruchtel-sozialer-2013a issued: 2013 language: de-DE publisher: Springer VS publisher-place: Wiesbaden, Germany title: "Sozialer Raum und Soziale Arbeit Fieldbook: Methoden und Techniken" title-short: Sozialer Raum und Soziale Arbeit Fieldbook type: book --- @fruchtel-sozialer-2013a Some text.[^1] [^1]: @fruchtel-sozialer-2013a ^D [ Para [ Cite [ Citation { citationId = "fruchtel-sozialer-2013a" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 1 , citationHash = 0 } ] [ Str "Fr\252chtel" , Space , Str "et" , Space , Str "al." , Space , Str "(2013)" ] ] , Para [ Str "Some" , Space , Str "text." , Note [ Para [ Cite [ Citation { citationId = "fruchtel-sozialer-2013a" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 2 , citationHash = 0 } ] [ Str "Fr\252chtel" , Space , Str "et" , Space , Str "al." , Space , Str "(2013)" ] ] ] ] , Div ( "refs" , [ "references" , "csl-bib-body" , "hanging-indent" ] , [] ) [ Div ( "ref-fruchtel-sozialer-2013a" , [ "csl-entry" ] , [] ) [ Para [ Str "Fr\252chtel," , Space , Str "Frank," , Space , Str "Wolfgang" , Space , Str "Budde," , Space , Str "and" , Space , Str "Gudrun" , Space , Str "Cyprian." , Space , Str "2013." , Space , Emph [ Str "Sozialer" , Space , Str "Raum" , Space , Str "und" , Space , Str "Soziale" , Space , Str "Arbeit" , Space , Str "Fieldbook:" , Space , Str "Methoden" , Space , Str "und" , Space , Str "Techniken" ] , Str "." , Space , Str "3rd" , Space , Str "ed." , Space , Str "Springer" , Space , Str "VS." ] ] ] ] ``` ================================================ FILE: test/command/6925.md ================================================ ``` % pandoc -f latex -t markdown \documentclass{amsart} \newtheorem{thm}{Theorem}[section] \theoremstyle{definition} \newtheorem{thm2}[section]{Theorem} \begin{document} \begin{thm} a \begin{figure} \includegraphics[]{1.png} \end{figure} \end{thm} \begin{thm2} a \begin{figure} \includegraphics[]{1.png} \end{figure} \end{thm2} \end{document} ^D ::: thm **Theorem 1**. *a* ![](1.png) ::: ::: thm2 **Theorem 1**. a ![](1.png) ::: ``` ================================================ FILE: test/command/6948.md ================================================ Treat an image alone in its paragraph (but not a figure) as an independent image: ``` % pandoc -f native -t rst [Para [Image ("",["align-center"],[]) [Str "https://pandoc.org/diagram.jpg"] ("https://pandoc.org/diagram.jpg","")]] ^D |https://pandoc.org/diagram.jpg| .. |https://pandoc.org/diagram.jpg| image:: https://pandoc.org/diagram.jpg ``` Here we just omit the center attribute as it's not valid: ``` % pandoc -f native -t rst [Para [Str "hi",Space,Image ("",["align-center"],[]) [Str "https://pandoc.org/diagram.jpg"] ("https://pandoc.org/diagram.jpg","")]] ^D hi |https://pandoc.org/diagram.jpg| .. |https://pandoc.org/diagram.jpg| image:: https://pandoc.org/diagram.jpg ``` But we can use top, middle, or bottom alignment: ``` % pandoc -f native -t rst [Para [Str "hi",Space,Image ("",["align-top"],[]) [Str "https://pandoc.org/diagram.jpg"] ("https://pandoc.org/diagram.jpg","")]] ^D hi |https://pandoc.org/diagram.jpg| .. |https://pandoc.org/diagram.jpg| image:: https://pandoc.org/diagram.jpg :align: top ``` ================================================ FILE: test/command/6951.md ================================================ ``` % pandoc --citeproc -t plain --bibliography command/biblio.bib --- references: - id: foo title: Crazy type: book ... [@foo; @item1] ^D (Crazy, n.d.; Doe 2005) Crazy. n.d. Doe, John. 2005. First Book. Cambridge University Press. ``` ================================================ FILE: test/command/6958.md ================================================ Add thin space between single and double quotes. ``` % pandoc -t latex+smart --- lang: en-GB --- '["On the Outside"]{}: Constructing Cycling Citizenship.' ^D `\,{``On the Outside''}: Constructing Cycling Citizenship.' ``` ================================================ FILE: test/command/6959.md ================================================ ``` % pandoc -t docx -o - | pandoc -f docx -t markdown --track-changes=all [This is the comment]{.comment-start id="1" author="Mike" date="2020-12-17T16:53:00Z"} [Here is my reply]{.comment-start id="2" author="Mike" date="2020-12-17T17:39:00Z"} This is the content being commented on [[]{.comment-end id="2"}]{.comment-end id="1"} ^D [This is the comment]{.comment-start id="1" author="Mike" date="2020-12-17T16:53:00Z"} [Here is my reply]{.comment-start id="2" author="Mike" date="2020-12-17T17:39:00Z"} This is the content being commented on [[]{.comment-end id="2"}]{.comment-end id="1"} ``` ================================================ FILE: test/command/6970.md ================================================ ``` % pandoc -f html -t native https://example.com ^D [ Plain [ Link ( "foo" , [ "bar" , "baz" ] , [ ( "target" , "_blank" ) ] ) [ Str "https://example.com" ] ( "https://example.com" , "" ) ] ] ``` ================================================ FILE: test/command/6992.md ================================================ ``` % pandoc -f mediawiki -t native {| class="wikitable" !colspan=4 | template request |- | mode || No || String || MUST be "template" or omitted |} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 4) [ Para [ Str "template" , Space , Str "request" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "mode" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "No" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "String" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "MUST" , Space , Str "be" , Space , Str "\"template\"" , Space , Str "or" , Space , Str "omitted" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/6993.md ================================================ ``` % pandoc -f mediawiki -t native '''Should be bold ''' ^D [ Para [ Strong [ Str "Should" , Space , Str "be" , Space , Str "bold" ] ] ] ``` ``` % pandoc -f mediawiki -t native ''' Should be bold''' ^D [ Para [ Strong [ Str "Should" , Space , Str "be" , Space , Str "bold" ] ] ] ``` ``` % pandoc -f mediawiki -t native '' Should be emph '' ^D [ Para [ Emph [ Str "Should" , Space , Str "be" , Space , Str "emph" ] ] ] ``` ================================================ FILE: test/command/7003.md ================================================ ``` % pandoc -f latex -t native \documentclass{article} \usepackage{listings} \lstset{basicstyle=\ttfamily} \begin{filecontents*}[overwrite]{example.tex} \documentclass{article} \begin{document} \section{Bar} This a Bar section \end{document} \end{filecontents*} \begin{document} \section{With lstlisting environment} \begin{lstlisting} \documentclass{article} \begin{document} \section{Foo} This a Foo section \end{document} \end{lstlisting} \section{With lstinputlisting command} \lstinputlisting{example.tex} \end{document} ^D [ Header 1 ( "with-lstlisting-environment" , [] , [] ) [ Str "With" , Space , Str "lstlisting" , Space , Str "environment" ] , CodeBlock ( "" , [] , [] ) "\\documentclass{article}\n\\begin{document}\n\\section{Foo}\nThis a Foo section\n\\end{document}" , Header 1 ( "with-lstinputlisting-command" , [] , [] ) [ Str "With" , Space , Str "lstinputlisting" , Space , Str "command" ] , CodeBlock ( "" , [ "latex" ] , [] ) "\\documentclass{article}\n\\begin{document}\n\\section{Bar}\nThis a Bar section\n\\end{document}" ] ``` ================================================ FILE: test/command/7006.md ================================================ ``` % pandoc -t html Test.[^fn] [^fn]: Foo: ![Caption.](/image.jpg) ^D

    Test.1


    1. Foo:

      Caption.
      ↩︎
    ``` ================================================ FILE: test/command/7009.md ================================================ ``` % pandoc -t gfm 3. a 4. b ^D 3. a 4. b ``` ================================================ FILE: test/command/7016.md ================================================ ``` % pandoc --citeproc --to=jats_archiving --standalone --- csl: command/apa.csl references: - id: doe type: article author: - family: Doe given: Jane container-title: Proceedings of the Academy of Test Inputs doi: 10.x/nope issued: 2021 title: Another article ... Blah [@doe]. ^D

    Blah (Doe, 2021).

    Doe, J. (2021). Another article. Proceedings of the Academy of Test Inputs. doi:10.x/nope
    ``` ================================================ FILE: test/command/7041.md ================================================ ``` % pandoc -f html -t jats
    Fly, you fools!
    ^D

    Fly, you fools!

    ``` ================================================ FILE: test/command/7042.md ================================================ ``` % pandoc -f markdown -t jats_publishing+element_citations --citeproc -s --- nocite: "[@*]" references: - author: - family: Jane given: Doe container-title: Public Library of Tests id: year-month issued: 1999-08 title: Year and month type: article-journal - accessed: 1999-01-22 author: - family: Negidius given: Numerius - literal: others container-title: Public Library of Tests id: access-date issued: 1911-10-03 title: Entry with access date and et al. type: article-journal - author: - family: Beethoven given: Ludwig dropping-particle: van - family: Bray given: Jan non-dropping-particle: de container-title: Public Library of Tests id: name-particles issued: 1820 title: Name particles, dropping and non-dropping type: article-journal - author: - 宮水 三葉 - 立花 瀧 title: Big Book of Tests id: book-with-japanese-authors issued: 2016 type: book - author: - family: Watson given: J. D. - family: Crick given: F. H. C. container-title: Nature doi: '10.1038/171737a0' id: full-journal-article-entry issue: 4356 issued: '1953-04-01' page: 737-738 pmid: 13054692 title: 'Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid' type: article-journal volume: 171 ... ^D
    JaneDoe Year and month Public Library of Tests 199908 NegidiusNumerius Entry with access date and et al. Public Library of Tests 19111003 19990122 BeethovenLudwig van de BrayJan Name particles, dropping and non-dropping Public Library of Tests 1820 宮水 三葉 立花 瀧 Big Book of Tests 2016 WatsonJ. D. CrickF. H. C. Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid Nature 19530401 171 4356 10.1038/171737a0 13054692 737 738
    ``` ================================================ FILE: test/command/7058.md ================================================ ``` % pandoc -f latex -t markdown 5\(-\)8 \(x\) ^D 5$-$``{=html}8 $x$ ``` ================================================ FILE: test/command/7064.md ================================================ ``` % pandoc -f rst -t html .. csv-table:: Changes :header: "Version", "Date", "Description" :widths: 15, 15, 70 :delim: $ 0.1.0 $ 18/02/2013 $ Initial Release ^D
    Changes
    Version Date Description
    0.1.0 18/02/2013 Initial Release
    ``` ================================================ FILE: test/command/7067.md ================================================ ``` % pandoc -t biblatex --- references: - id: garaud author: - family: Garaud given: Marcel container-title: Bulletin de la Societé des antiquaires de l’Ouest collection-title: 4 issued: - year: 1967 language: fr-FR page: 11-27 title: Recherches sur les défrichements dans la Gâtine poitevine aux XI^e^ et XII^e^ siècles type: article-journal volume: 9 ... ^D @article{garaud, author = {Garaud, Marcel}, title = {Recherches sur les défrichements dans la Gâtine poitevine aux XI\textsuperscript{e} et XII\textsuperscript{e} siècles}, journal = {Bulletin de la Societé des antiquaires de l’Ouest}, series = {4}, volume = {9}, pages = {11-27}, date = {1967}, langid = {fr-FR} } ``` ``` % pandoc -t bibtex --- references: - id: garaud author: - family: Garaud given: Marcel container-title: Bulletin de la Société des antiquaires de l’Ouest collection-title: 4 issued: - year: 1967 language: fr-FR page: 11-27 title: Recherches sur les défrichements dans la Gâtine poitevine aux XI^e^ et XII^e^ siècles type: article-journal volume: 9 ... ^D @article{garaud, author = {Garaud, Marcel}, title = {{Recherches sur les défrichements dans la Gâtine poitevine aux XI\textsuperscript{e} et XII\textsuperscript{e} siècles}}, journal = {Bulletin de la Société des antiquaires de l’Ouest}, series = {4}, volume = {9}, pages = {11-27}, year = {1967} } ``` This tests the titlecasing of a word with an accented second letter: ``` % pandoc -t bibtex --- references: - id: garaud author: - family: Garaud given: Marcel container-title: English Journal issued: - year: 1967 language: en-US title: Research on the défrichements in the Gâtine poitevine type: article-journal volume: 9 ... ^D @article{garaud, author = {Garaud, Marcel}, title = {Research on the Défrichements in the {Gâtine} Poitevine}, journal = {English Journal}, volume = {9}, year = {1967} } ``` ================================================ FILE: test/command/7080.md ================================================ ``` % pandoc -f markdown_mmd -t native ![][image] [image]: image.png width=100px height=150px ^D [ Para [ Image ( "" , [] , [ ( "width" , "100px" ) , ( "height" , "150px" ) ] ) [] ( "image.png" , "" ) ] ] ``` ================================================ FILE: test/command/7092.md ================================================ ``` % pandoc -flatex+raw_tex -t native \newcommand{\em}[1]{\emph{#1}} \em{\parseMe{foo}} ^D [ Para [ Emph [ RawInline (Format "latex") "\\parseMe{foo}" ] ] ] ``` ================================================ FILE: test/command/7099.md ================================================ ``` % pandoc -f html -t native --verbose ^D 2> [INFO] Skipped '' at input line 1 column 16 [] ``` ``` % pandoc -f html -t native --verbose ^D 2> [INFO] Fetching h:invalid@url... 2> [WARNING] Could not fetch resource h:invalid@url: Could not fetch h:invalid@url 2> InvalidUrlException "h:invalid@url" "Invalid scheme" 2> [INFO] Skipped '' at input line 1 column 29 [] ``` ================================================ FILE: test/command/7112.md ================================================ ``` % pandoc -f rst .. csv-table:: setting, ``echo PATH="path"`` ^D
    setting echo PATH="path"
    ``` ================================================ FILE: test/command/7129.md ================================================ ``` % pandoc -f latex -t native \begin{tabular}{ll} \hline FOO & BAR \\ \hline foo & \verb|b&r| \\ \hline \end{tabular} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "FOO" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "BAR" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "foo" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Code ( "" , [] , [] ) "b&r" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/7132.md ================================================ ``` % pandoc -f markdown -t org --columns=72 - This line has exactly the wrong number of characters before the number 5. - Long line ending with a number (this time it is in parentheses and a 23) ^D - This line has exactly the wrong number of characters before the number 5. - Long line ending with a number (this time it is in parentheses and a 23) ``` ================================================ FILE: test/command/7134.md ================================================ ``` % pandoc -f rst -t native This is a paragraph. This is a block quote. .. This should be a second block quote. ^D [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "paragraph." ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." ] ] , BlockQuote [ Para [ Str "This" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "second" , Space , Str "block" , Space , Str "quote." ] ] ] ``` ================================================ FILE: test/command/7145.md ================================================ ``` % pandoc -f mediawiki -t native Maecenas at sapien tempor, pretium turpis ut, imperdiet augue.This is a multiline reference with empty linebreaks Nulla ut massa eget ex venenatis lobortis id in eros. ^D [ Para [ Str "Maecenas" , Space , Str "at" , Space , Str "sapien" , Space , Str "tempor," , Space , Str "pretium" , Space , Str "turpis" , Space , Str "ut," , Space , Str "imperdiet" , Space , Str "augue." , Note [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "multiline" ] , Para [ Str "reference" , SoftBreak , RawInline (Format "html") "" , Str "with" , RawInline (Format "html") "" , SoftBreak , Str "empty" ] , Para [ Str "linebreaks" ] ] , Space , Str "Nulla" , Space , Str "ut" , Space , Str "massa" , Space , Str "eget" , Space , Str "ex" , Space , Str "venenatis" , Space , Str "lobortis" , Space , Str "id" , Space , Str "in" , Space , Str "eros." ] ] ``` ================================================ FILE: test/command/7155.md ================================================ ``` % pandoc -f markdown+tex_math_single_backslash -t native \(x\) \[x\] \\(x\\) \\[x\\] ^D [ Para [ Math InlineMath "x" , Space , Math DisplayMath "x" , SoftBreak , Str "\\(x\\)" , Space , Str "\\[x\\]" ] ] ``` ``` % pandoc -f markdown+tex_math_double_backslash -t native \(x\) \[x\] \\(x\\) \\[x\\] ^D [ Para [ Str "(x)" , Space , Str "[x]" , SoftBreak , Math InlineMath "x" , Space , Math DisplayMath "x" ] ] ``` ================================================ FILE: test/command/7172.md ================================================ ``` % pandoc -t markdown - one - two ^D - one - two ``` ``` % pandoc -t markdown+four_space_rule - one - two ^D - one - two ``` ================================================ FILE: test/command/7173.md ================================================ ``` % pandoc -f docbook -t latex
    Untitled Document Word 1+2 1+2
    ^D Word \[1 + 2\] ``` ``` % pandoc -f docbook -t latex
    Untitled Document Word 1+2 1+2
    ^D Word \[1 + 2\] ``` ``` % pandoc -f docbook -t latex
    Untitled Document Word 1+2 1+2
    ^D Word \[1 + 2\] ``` ``` % pandoc -f docbook -t latex
    Untitled Document Word 1+2 1+2
    ^D Word \[1 + 2\] ``` ``` % pandoc -f docbook -t latex
    Untitled Document Word 1+2 1+2
    ^D Word \[1 + 2\] ``` ================================================ FILE: test/command/7181.md ================================================ ``` % pandoc -t latex ![Global frog population.](slides.pdf){page=13,trim=1cm,clip,width=4cm} ^D \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,page=13,trim=1cm,clip,width=4cm,alt={Global frog population.}]{slides.pdf}} \caption{Global frog population.} \end{figure} ``` ================================================ FILE: test/command/7201.md ================================================ ``` % pandoc -t revealjs --slide-level=1 # Overview :::::::::::::: {.columns} ::: {.column width="40%"} Left column ::: ::: {.column width="60%"} * First list * subitem . . . * Second list * subitem ::: :::::::::::::: ^D

    Overview

    Left column

    • First list
      • subitem
    • Second list
      • subitem
    ``` ================================================ FILE: test/command/7208.md ================================================ ``` % pandoc -t gfm \ ^D \ ``` ================================================ FILE: test/command/7214.md ================================================ ``` % pandoc +------------+----------+------------------+ |日本語 | の文字列 | words in english | +------------+----------+------------------+ |abc defghij | def | xyz | +------------+----------+------------------+ ^D
    日本語 の文字列 words in english
    abc defghij def xyz
    ``` ================================================ FILE: test/command/7216.md ================================================ ``` % pandoc -t latex "This is some text in quotes. Another paragraph by the same speaker follows. The first paragraph should have no close quote. "The second paragraph should have open and close quotes." | "Open quote on this line, | Close quote on the next line." | "Quotes on the same line." ^D ``This is some text in quotes. Another paragraph by the same speaker follows. The first paragraph should have no close quote. ``The second paragraph should have open and close quotes.'' ``Open quote on this line,\\ Close quote on the next line.''\\ ``Quotes on the same line.'' ``` ================================================ FILE: test/command/7219.md ================================================ ``` % pandoc --citeproc -t plain --- references: - author: - family: Reese given: Trevor R. collection-title: 3rd series container-title: William and Mary Quarterly id: reese issued: 1958 language: en-US page: 168-190 title: Georgia in Anglo-Spanish diplomacy, 1736-1739 type: article-journal volume: 15 ... and also in -@reese ^D and also in (1958) Reese, Trevor R. 1958. “Georgia in Anglo-Spanish Diplomacy, 1736-1739.” William and Mary Quarterly, 3rd series, vol. 15: 168–90. ``` ================================================ FILE: test/command/7266.md ================================================ ``` % pandoc -f biblatex -t biblatex -s @article{id, annote = "annotation" } ^D @article{id, annote = {annotation} } ``` ================================================ FILE: test/command/7272.md ================================================ ``` % pandoc -t latex -f html
    text
    text2
    ^D {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{} >{\raggedright\arraybackslash}p{(\linewidth - 0\tabcolsep) * \real{1.0000}}@{}} \toprule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \begin{minipage}[t]{\linewidth}\raggedright { text\\ text2 }\strut \end{minipage} \\ \end{longtable} } ``` ================================================ FILE: test/command/7278.md ================================================ ``` % pandoc -t beamer # Slide Some blocks: ## example block title {.example} text in block ## alert block title {.alert} text in block ## block title text in block ^D \begin{frame}{Slide} \protect\phantomsection\label{slide} Some blocks: \begin{exampleblock}{example block title} \protect\phantomsection\label{example-block-title} text in block \end{exampleblock} \begin{alertblock}{alert block title} \protect\phantomsection\label{alert-block-title} text in block \end{alertblock} \begin{block}{block title} \protect\phantomsection\label{block-title} text in block \end{block} \end{frame} ``` ================================================ FILE: test/command/7282.md ================================================ Don't crash on unmatched closing tag. ``` % pandoc -f html -t native ^D [] ``` ================================================ FILE: test/command/7288.md ================================================ ``` % pandoc -f rst -t ms .. list-table:: :widths: 50 50 :header-rows: 1 * - Left - Right * - Long text that should be easy to break up into multiple lines - Another long text that should be easy to break up into multiple lines Bar ^D .PP .na .nr LLold \n[LL] .TS delim(@@) tab( ); lw(35.0n) lw(35.0n). T{ Left T} T{ Right T} _ T{ .nr LL 35.0n .LP Long text that should be easy to break up into multiple lines T} T{ .nr LL 35.0n .PP Another long text that should be easy to break up into multiple lines .PP Bar T} .TE .nr LL \n[LLold] .ad ``` ================================================ FILE: test/command/7299.md ================================================ ``` % pandoc -f latex -t plain $1-{\ensuremath{r}\xspace}$ ^D 1 − r ``` ``` % pandoc -f latex -t plain \newcommand{\foo}{Foo\xspace} $\text{\foo bar}$ ^D Foo bar ``` ``` % pandoc -f latex -t plain a\xspace b ^D a b ``` ================================================ FILE: test/command/7321.md ================================================ ``` % pandoc -t plain --citeproc --wrap=none --- references: - id: fenner2012a title: One-click science marketing author: - family: Fenner given: Martin container-title: Nature Materials volume: 11 issue: 4 publisher: Nature Publishing Group type: article-journal issued: year: 2012 --- [@fenner2012a, $a^2+b^2=c^2$] ^D (Fenner 2012, a² + b² = c²) Fenner, Martin. 2012. “One-Click Science Marketing.” Nature Materials 11 (4). ``` ================================================ FILE: test/command/7323.md ================================================ ``` % pandoc --citeproc -t plain --- references: - id: smith author: John Smith issued: 2019 title: Insects type: book ... @smith [chap. 6] @smith [chapter 6] @smith [Chap. 6] @smith [Chapter 6] ^D John Smith (2019, chap. 6) John Smith (2019, chap. 6) John Smith (2019, chap. 6) John Smith (2019, chap. 6) John Smith. 2019. Insects. ``` ================================================ FILE: test/command/7324.md ================================================ ``` % pandoc --citeproc -t plain --- references: - id: smith author: John Smith issued: 2019 title: Insects type: book ... @smith [, among others] @smith [ among others] @smith [among others] ^D John Smith (2019, among others) John Smith (2019 among others) John Smith (2019, among others) John Smith. 2019. Insects. ``` ================================================ FILE: test/command/7326.md ================================================ Table with row and column spans ``` % pandoc -f html -t asciidoc
    Header 1 Header 2 Header 3 Header 4
    body row 1 column 2 column 3 column 4
    body row 2 Cells may span columns. fff
    body row 3 Cells may span rows.
    • Cells
    • can
    • contain
    • blocks.
    • This is a very long line of text
    • Python
    • b
    • c
    body row 4
    ^D [width="100%",cols="17%,16%,15%,52%",options="header",] |=== |Header 1 |Header 2 |Header 3 |Header 4 |body row 1 |column 2 |column 3 |column 4 |body row 2 2+|Cells may span columns. |fff |body row 3 .2+|Cells may span rows. .2+a| * Cells * can * contain * blocks. .2+a| * This is a very long line of text * http://www.python.org/[Python] * b * c |body row 4 |=== ``` Header and footer. AsciiDoc only supports 1 header and 1 footer row. So for multiple header and/or footer rows all the extra rows become part of the table body. ``` % pandoc -f html -t asciidoc
    Inputs Output
    A B A or B
    False False False
    True False True
    False True True
    True True True
    A B A or B
    Inputs Output
    Header
    Body 1-1 Body 2-1
    Footer
    ^D [cols=",,",options="header,footer",] |=== 2+|Inputs |Output |A |B |A or B |False |False |False |True |False |True |False |True |True |True |True |True |A |B |A or B 2+|Inputs |Output |=== [cols=",",options="header,footer",] |=== 2+|Header |Body 1-1 |Body 2-1 2+|Footer |=== ``` Table without header but with footer rows ``` % pandoc -f html -t asciidoc
    False False False
    True False True
    False True True
    True True True
    A B A or B
    Inputs Output
    ^D [width="100%",cols="37%,37%,26%",options="footer",] |=== |False |False |False |True |False |True |False |True |True |True |True |True |A |B |A or B 2+|Inputs |Output |=== ``` Adjust row span for multiple header rows ``` % pandoc -f html -t asciidoc
    Location Temperature 1961-1990 in degree Celsius
    min mean max
    Antarctica -89.2 N/A 19.8
    Earth -89.2 14 56.7
    Temperature 1961-1990 in degree Celsius Location
    min mean max
    -89.2 N/A 19.8 Antarctica
    -89.2 14 56.7 Earth
    Temperature 1961-1990 in degree Celsius Location Extra
    min mean max Extra 2
    -89.2 N/A 19.8 Antarctica Extra 3
    -89.2 14 56.7 Earth Extra 4
    Header 1-1 Header 1-2
    Header 2-1
    Header 3-1 Header 3-2 Header 3-3
    Body 1-1 Body 1-2
    Body 2-1 Body 2-2
    Header 1-1 Header 1-2 Header 1-3
    Header 2-1
    Header 3-1 Header 3-2 Header 3-3
    ^D [width="63%",cols="49%,17%,17%,17%",options="header",] |=== |Location 3+|Temperature 1961-1990 in degree Celsius | |min |mean |max |Antarctica |-89.2 |N/A |19.8 |Earth |-89.2 |14 |56.7 |=== [width="63%",cols="19%,17%,17%,47%",options="header",] |=== 3+|Temperature 1961-1990 in degree Celsius |Location |min |mean |max | |-89.2 |N/A |19.8 |Antarctica |-89.2 |14 |56.7 |Earth |=== [width="65%",cols="20%,16%,16%,27%,21%",options="header",] |=== 3+|Temperature 1961-1990 in degree Celsius |Location |Extra |min |mean |max | |Extra 2 |-89.2 |N/A |19.8 |Antarctica |Extra 3 |-89.2 |14 |56.7 |Earth |Extra 4 |=== [cols=",,",options="header",] |=== |Header 1-1 2+|Header 1-2 |Header 2-1 2+| |Header 3-1 |Header 3-2 |Header 3-3 .2+|Body 1-1 2+|Body 1-2 |Body 2-1 |Body 2-2 |=== [cols=",,",options="header",] |=== |Header 1-1 |Header 1-2 |Header 1-3 | |Header 2-1 | |Header 3-1 |Header 3-2 |Header 3-3 |=== ``` Adjust row span in multiple footer rows. ``` % pandoc -f html -t asciidoc
    Body 1-1 Body 1-2 Body 1-3
    Footer 1-1/2 Footer 1-3
    Footer 2-1 Span 3 Footer 2-3
    Span 2 Footer 3-3
    Footer 4-3
    Body 1-1 Body 1-2 Body 1-3 Body 1-4 Body 1-5
    Body 2-1 Body 2-2 Body 2-3 Body 2-4 Body 2-5
    Footer 1-1/2/3 Footer 1-4 Footer 1-5
    Footer 2-1 Span 3 Footer 2-3 Footer 2-4/5
    Span 2 Footer 3-3/4 Span 2
    Span 3 Footer 4-4
    Footer 5-1 Footer 5-2 Footer 5-4 Span 2
    Footer 6-1/2 Footer 6-4
    Body 1 Body 2 Body 3 Body 4
    Footer 1-1 Span 3 Span 2
    Footer 2-1
    Footer 3-1 Footer 3-4
    Body 1-1 Body 1-2 Body 1-3 Body 1-4
    Footer 1-1/2 Span 6 Footer 1-4
    Span 3 Footer 2-2 Footer 2-4
    Footer 3-2 Footer 3-4
    Footer 4-2 Footer 4-4
    Footer 5-1 Footer 5-2 Span 2
    Footer 6-1/2
    ^D [width="100%",cols="40%,40%,20%",options="footer",] |=== |Body 1-1 |Body 1-2 |Body 1-3 2+|Footer 1-1/2 |Footer 1-3 |Footer 2-1 .2+|Span 3 |Footer 2-3 |Span 2 |Footer 3-3 | | |Footer 4-3 |=== [cols=",,,,",options="footer",] |=== |Body 1-1 |Body 1-2 |Body 1-3 |Body 1-4 |Body 1-5 |Body 2-1 |Body 2-2 |Body 2-3 |Body 2-4 |Body 2-5 3+|Footer 1-1/2/3 |Footer 1-4 |Footer 1-5 |Footer 2-1 .3+|Span 3 |Footer 2-3 2+|Footer 2-4/5 .2+|Span 2 2+|Footer 3-3/4 .2+|Span 2 .2+|Span 3 |Footer 4-4 |Footer 5-1 |Footer 5-2 |Footer 5-4 |Span 2 2+|Footer 6-1/2 | |Footer 6-4 | |=== [cols=",,,",options="footer",] |=== |Body 1 |Body 2 |Body 3 |Body 4 |Footer 1-1 2.2+|Span 3 .2+|Span 2 |Footer 2-1 |Footer 3-1 2+| |Footer 3-4 |=== [cols=",,,",options="footer",] |=== |Body 1-1 |Body 1-2 |Body 1-3 |Body 1-4 2+|Footer 1-1/2 .5+|Span 6 |Footer 1-4 .3+|Span 3 |Footer 2-2 |Footer 2-4 |Footer 3-2 |Footer 3-4 |Footer 4-2 |Footer 4-4 |Footer 5-1 |Footer 5-2 |Span 2 2+|Footer 6-1/2 | | |=== ``` Individual cell alignments ``` % pandoc -f native -t asciidoc [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , Space , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 2) (ColSpan 2) [ Plain [ Str "Center" , Space , Str "Headers" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , Space , Str "Header" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" , Space , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , Space , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , Space , Str "Header" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 2) (ColSpan 1) [ Plain [ Str "Right" , Space , Str "Body" ] ] , Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 2) [ Plain [ Str "Left" , Space , Str "Body" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" , Space , Str "Body" ] ] , Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , Space , Str "Body" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 3) [ Plain [ Str "Center" , Space , Str "Footer" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 2) [ Plain [ Str "Left" , Space , Str "Footer" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 3) (ColSpan 1) [ Plain [ Str "Center" , Space , Str "Footer" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 2) [ Plain [ Str "Right" , Space , Str "Footer" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 2) [ Plain [ Str "Center" , Space , Str "Footer" ] ] ] ]) ] ^D [cols=",,",options="header,footer",] |=== <|Left Header 2+^|Center Headers >|Right Header 2+| ^|Center Header >|Right Header <|Left Header .2+>|Right Body 2+<|Left Body ^|Center Body >|Right Body 3+^|Center Footer 2+<|Left Footer .2+^|Center Footer 2+>|Right Footer 2+^|Center Footer | |=== ``` Adjust row span for empty rows and handle empty rows in general ``` % pandoc -f native -t asciidoc [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "1-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "1-3" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "2-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "2-3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 2) [ Plain [ Str "Body" , Space , Str "1-1/2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 5) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "5" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "2-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "2-2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "3-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "3-2" ] ] ] , Row ( "" , [] , [] ) [] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 5) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Footer" , Space , Str "1-2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 2) [ Plain [ Str "Span" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Footer" , Space , Str "3-2/3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Footer" , Space , Str "4-2/3" ] ] ] ]) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "1-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "1-2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "4" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 5) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "2-2" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "3-2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "3-3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "4-2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "4-3" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "6-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "6-2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "6-3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Footer" , Space , Str "1-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 8) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "8" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Footer" , Space , Str "1-3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Footer" , Space , Str "2-3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Footer" , Space , Str "4-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Footer" , Space , Str "4-3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Footer" , Space , Str "5-1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [] ]) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [ "rowspan-cell" ] , [] ) AlignDefault (RowSpan 6) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "1-2" ] ] , Cell ( "" , [ "rowspan-cell" ] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [ "rowspan-cell" ] , [] ) AlignDefault (RowSpan 3) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [ "rowspan-cell" ] , [] ) AlignDefault (RowSpan 4) (ColSpan 1) [ Plain [ Str "Span" , Space , Str "4" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "3-2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "4-2" ] ] ] , Row ( "" , [] , [] ) [] , Row ( "" , [] , [] ) [] ] ] (TableFoot ( "" , [] , [] ) []) ] ^D [cols=",,",options="header,footer",] |=== |Header 1-1 |Span 3 |Header 1-3 |Header 2-1 | |Header 2-3 2+|Body 1-1/2 .3+|Span 5 |Body 2-1 |Body 2-2 |Body 3-1 |Body 3-2 | | | .3+|Span 5 2+|Footer 1-2 2+|Span 2 2+|Footer 3-2/3 | 2+|Footer 4-2/3 |=== [cols=",,",options="footer",] |=== |Body 1-1 |Body 1-2 .2+|Span 4 .3+|Span 5 |Body 2-2 |Body 3-2 |Body 3-3 |Body 4-2 |Body 4-3 | | | |Body 6-1 |Body 6-2 |Span 6-3 |Footer 1-1 .5+|Span 8 |Footer 1-3 .2+|Span 3 |Footer 2-3 |Span 2 |Footer 4-1 |Footer 4-3 |Footer 5-1 |Span 3 |Span 2 | | |=== [cols=",,",] |=== .5+|Span 6 |Body 1-2 .2+|Span 2 .2+|Span 3 .3+|Span 4 |Body 3-2 |Body 4-2 | | | | | | |=== ``` ================================================ FILE: test/command/7329.md ================================================ ``` % pandoc -f markdown -t org - @item1 - @item1 [p. 12] - @item1 [p.12; see also @item2] - [@item1] - [-@item1] - [see @item1 p. 12] - [see @item1, p. 12] - [see @item1, p. 12 and *passim*] - [@item1;@item2] - [see @item1; @item2] ^D - [cite/t:@item1] - [cite/t:@item1 p. 12] - [cite/t:@item1 p.12; see also @item2] - [cite:@item1] - [cite/na:@item1] - [cite:see @item1 p. 12] - [cite:see @item1 p. 12] - [cite:see @item1 p. 12 and /passim/] - [cite:@item1; @item2] - [cite:see @item1; @item2] ``` ``` % pandoc -f markdown -t org -C --bibliography command/biblio.bib - [@item1] ^D - (Doe 2005) <> <> Doe, John. 2005. /First Book/. Cambridge University Press. ``` ``` % pandoc -f markdown -t org-citations -C --bibliography command/biblio.bib [@item1] ^D (Doe 2005) <> <> Doe, John. 2005. /First Book/. Cambridge University Press. ``` ``` % pandoc -f org -t markdown - [cite/t:@item1] - [cite/t:@item1 p. 12] - [cite/t:@item1 p.12; see also @item2] - [cite:@item1] - [cite/na:@item1] - [cite:see @item1 p. 12] - [cite:see @item1 p. 12 and /passim/] - [cite:@item1; @item2] - [cite:see @item1; @item2] ^D - @item1 - @item1 [p. 12] - @item1 [p.12; see also @item2] - [@item1] - [-@item1] - [see @item1 p. 12] - [see @item1 p. 12 and *passim*] - [@item1; @item2] - [see @item1; @item2] ``` ================================================ FILE: test/command/7339.md ================================================ ``` % pandoc -f gfm -s -t native --- title: Test --- Hi ^D Pandoc Meta { unMeta = fromList [ ( "title" , MetaInlines [ Str "Test" ] ) ] } [ Para [ Str "Hi" ] ] ``` ================================================ FILE: test/command/7340.md ================================================ ``` % pandoc -f latex -t native \(*\) ^D [ Para [ Math InlineMath "*" ] ] ``` ================================================ FILE: test/command/7376.md ================================================ ``` % pandoc --citeproc -t plain --- references: - id: item1 type: book author: - family: Doe given: Jane issued: 2020 title: The title ... ^D ``` ================================================ FILE: test/command/7394.md ================================================ ``` % pandoc -f markdown -t plain --citeproc --- csl: command/chicago-fullnote-bibliography.csl references: - author: - family: Wandt given: Manfred edition: 6 id: wandt2014ges-sv issued: 2014 publisher: Franz Vahlen publisher-place: München title: Gesetzliche schuldverhältnisse title-short: Gesetzl SV type: book - author: - family: Smith given: Zenda edition: 6 id: smith2015 issued: 2015 publisher: Macmillan publisher-place: New York title: A verb and a noun type: book --- Hi^[@wandt2014ges-sv.]. Hi^[[@wandt2014ges-sv].]. Hi^[[See also @wandt2014ges-sv].]. Hi^[See also @wandt2014ges-sv.]. Hi^[@wandt2014ges-sv [@smith2015].]. Hi^[[@wandt2014ges-sv; @smith2015].]. Hi [@wandt2014ges-sv]. Hi [see also @wandt2014ges-sv]. ^D Hi[1]. Hi[2]. Hi[3]. Hi[4]. Hi[5]. Hi[6]. Hi.[7] Hi.[8] Smith, Zenda. A Verb and a Noun. 6th ed. New York: Macmillan, 2015. Wandt, Manfred. Gesetzliche Schuldverhältnisse. 6th ed. München: Franz Vahlen, 2014. [1] Manfred Wandt, Gesetzliche Schuldverhältnisse, 6th ed. (München: Franz Vahlen, 2014). [2] Wandt. [3] See also Wandt. [4] See also Wandt. [5] Wandt, Zenda Smith, A Verb and a Noun, 6th ed. (New York: Macmillan, 2015). [6] Wandt, Gesetzl SV; Smith, A Verb and a Noun. [7] Wandt, Gesetzl SV. [8] See also Wandt. ``` ================================================ FILE: test/command/7397.md ================================================ ```` % pandoc -t markdown ~~~~ { .haskell startFrom="100"} qsort [] = [] qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs) ~~~~ ^D ``` {.haskell startFrom="100"} qsort [] = [] qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs) ``` ```` ================================================ FILE: test/command/7400.md ================================================ ``` % pandoc -t native -s --- # Comment only ... ^D Pandoc Meta { unMeta = fromList [] } [] ``` ================================================ FILE: test/command/7416.md ================================================ ``` % pandoc -f markdown -t html ![caption](../media/rId25.jpg "title"){alt="alt"} ^D
    alt
    caption
    ``` ``` % pandoc -f markdown -t html ![caption](../media/rId25.jpg "title") ^D
    caption
    ``` ================================================ FILE: test/command/7434.md ================================================ ``` % pandoc -f markdown -t native \begin{proof} \newcommand{\x}{\left.\right.} \x \end{proof} 1234567890abcdefghi [\*\a](x) ^D [ RawBlock (Format "tex") "\\begin{proof}\n\\newcommand{\\x}{\\left.\\right.}\n\\left.\\right.\n\\end{proof}" , Para [ Str "1234567890abcdefghi" ] , Para [ Link ( "" , [] , [] ) [ Str "*" , RawInline (Format "tex") "\\a" ] ( "x" , "" ) ] ] ``` ================================================ FILE: test/command/7436.md ================================================ ``` % pandoc -f rst -t native .. include:: command/three.txt :code: .. include:: command/three.txt :literal: .. include:: command/three.txt ^D [ CodeBlock ( "" , [ "" ] , [ ( "code" , "" ) ] ) "1st line.\n2nd line.\n3rd line.\n" , CodeBlock ( "" , [ "" ] , [ ( "literal" , "" ) ] ) "1st line.\n2nd line.\n3rd line.\n" , Para [ Str "1st" , Space , Str "line." , SoftBreak , Str "2nd" , Space , Str "line." , SoftBreak , Str "3rd" , Space , Str "line." ] ] ``` ================================================ FILE: test/command/7473.md ================================================ ``` % pandoc -f rst -t native .. figure:: example.png :figclass: foo bar :align: right :width: 1in This is a caption. ^D [ Figure ( "" , [ "foo" , "bar" , "align-right" ] , [] ) (Caption Nothing [ Plain [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "caption." ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "1in" ) ] ) [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "caption." ] ( "example.png" , "" ) ] ] ] ``` ================================================ FILE: test/command/7482.md ================================================ ``` % pandoc -f html -t org
    Last N credits Average grade
    140 17.06571429
    84 17.95595238
    64 18.9734375
    36 19.12777778
    29 19.18275862
    19 19.00526316
    ^D | Last N credits | Average grade | | 140 | 17.06571429 | | 84 | 17.95595238 | | 64 | 18.9734375 | | 36 | 19.12777778 | | 29 | 19.18275862 | | 19 | 19.00526316 | ``` ================================================ FILE: test/command/7494.md ================================================ ``` % pandoc -f latex -t plain \def\foo{BAR} {\foo \def\foo{BAZ} \foo } \foo ^D BAR BAZ BAR ``` ``` % pandoc -f latex -t plain \def\foo{BAR} {\foo \gdef\foo{BAZ} \foo } \foo ^D BAR BAZ BAZ ``` ``` % pandoc -f latex -t plain \def\foo{BAR} {\foo \global\def\foo{BAZ} \foo } \foo ^D BAR BAZ BAZ ``` ``` % pandoc -f latex -t plain \newcommand{\aaa}{BBB} { \renewcommand{\aaa}{AAA} \aaa } \aaa ^D AAA BBB ``` ``` % pandoc -f latex -t markdown \newcommand{\aaa}{BBB} \begin{quote} \renewcommand{\aaa}{AAA} \aaa \end{quote} \aaa ^D > AAA BBB ``` ================================================ FILE: test/command/7497.md ================================================ ```` % pandoc -f markdown-raw_tex -t latex ``` \end{verbatim} \LaTeX \begin{verbatim} ``` ^D \begin{Shaded} \begin{Highlighting}[] \NormalTok{\textbackslash{}end\{verbatim\}} \NormalTok{\textbackslash{}LaTeX} \NormalTok{\textbackslash{}begin\{verbatim\}} \end{Highlighting} \end{Shaded} ```` ```` % pandoc -f markdown-raw_tex -t latex hi[^1] [^1]: ``` \end{Verbatim} \LaTeX \begin{Verbatim} ``` ^D hi\footnote{\begin{Shaded} \begin{Highlighting}[] \NormalTok{\textbackslash{}end\{Verbatim\}} \NormalTok{\textbackslash{}LaTeX} \NormalTok{\textbackslash{}begin\{Verbatim\}} \end{Highlighting} \end{Shaded} } ```` ================================================ FILE: test/command/7512.md ================================================ ``` % pandoc -t native -f latex \begin{equation*} [d,\delta]=0. \end{equation*} ^D [ Para [ Math DisplayMath "\\begin{equation*}\n[d,\\delta]=0.\n\\end{equation*}" ] ] ``` ``` % pandoc -t native -f latex \begin{table}[htb] \begin{tabular}{|c|c|} $W$ & rel. err. \\[0mm] [$\mu$m] & [\%]\\ \end{tabular} \end{table} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "W" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "rel." , Space , Str "err." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "[" , Math InlineMath "\\mu" , Str "m]" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "[%]" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/7520.md ================================================ ``` % pandoc -f org -t native -s :PROPERTIES: :ID: d5b18943-98a3-4b2a-a545-41d17bf50f3e :END: #+title: Common Ground ^D Pandoc Meta { unMeta = fromList [ ( "id" , MetaString "d5b18943-98a3-4b2a-a545-41d17bf50f3e" ) , ( "title" , MetaInlines [ Str "Common" , Space , Str "Ground" ] ) ] } [] ``` ================================================ FILE: test/command/7521.md ================================================ ``` % pandoc --strip-comments - one - two ^D
    • one
    • two
    ``` ================================================ FILE: test/command/7525.md ================================================ ``` % pandoc -f latex -t native \texttt{Normal code. \emph{Emph and code.} \textsc{\textbf{Bold small caps.}} \sout{Strikeout. \underline{Strikeout and underline.}}} ^D [ Para [ Code ( "" , [] , [] ) "Normal code. " , Emph [ Code ( "" , [] , [] ) "Emph and code." ] , Code ( "" , [] , [] ) " " , SmallCaps [ Strong [ Code ( "" , [] , [] ) "Bold small caps." ] ] , Code ( "" , [] , [] ) " " , Strikeout [ Code ( "" , [] , [] ) "Strikeout. " , Underline [ Code ( "" , [] , [] ) "Strikeout and underline." ] ] ] ] ``` ``` % pandoc -f html -t native hi ^D [ Plain [ Strong [ Code ( "" , [] , [] ) "hi" ] ] ] ``` ``` % pandoc -f mediawiki -t native ''hey'' ^D [ Para [ Emph [ Code ( "" , [] , [] ) "hey" ] ] ] ``` ================================================ FILE: test/command/7529.md ================================================ ``` % pandoc -f html -t asciidoc https://example.com/show.cgi?id=hi--there--everyone ^D link:++https://example.com/show.cgi?id=hi--there--everyone++[] ``` ================================================ FILE: test/command/7546.md ================================================ ``` % pandoc -t html -f native Span ("", [], [("","")]) [] ^D ``` ================================================ FILE: test/command/7557.md ================================================ ``` % pandoc -f org -t native - 11. and 12. 09. meeting ^D [ BulletList [ [ Plain [ Str "11." , Space , Str "and" , Space , Str "12." , Space , Str "09." , Space , Str "meeting" ] ] ] ] ``` ================================================ FILE: test/command/7568.md ================================================ ``` % pandoc -f rst While `Labyrinth Lord: Revised Edition`_ (LLRE; PDF and POD) has been criticized for not being a completely faithful retro-clone of the Moldvay/Cook/Marsh Basic/Expert D&D rules (B/X), I think it still holds a useful spot. .. _Labyrinth Lord\: Revised Edition: https://www.drivethrurpg.com/product/64332/Labyrinth-Lord-Revised-Edition ^D

    While Labyrinth Lord: Revised Edition (LLRE; PDF and POD) has been criticized for not being a completely faithful retro-clone of the Moldvay/Cook/Marsh Basic/Expert D&D rules (B/X), I think it still holds a useful spot.

    ``` ================================================ FILE: test/command/7573.md ================================================ ``` % pandoc --preserve-tabs
    1. one
    ^D
    1. one
    ``` ================================================ FILE: test/command/7582.md ================================================ ``` % pandoc -t revealjs # Slide ::: columns :::: column Left column :::: . . . :::: column Right column :::: ::: ^D

    Slide

    Left column

    Right column

    ``` ================================================ FILE: test/command/7589.md ================================================ ``` % pandoc -f html -t native
    experience expertise paradigms haskell name image
    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "experience" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "expertise" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "paradigms" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "haskell" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "name" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "image" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/7615.md ================================================ ``` % pandoc -f latex -t native \texttt{\^} ^D [ Para [ Code ( "" , [] , [] ) "^" ] ] ``` ================================================ FILE: test/command/7623.md ================================================ ``` % pandoc -t native [link $(0,1]$](url) ^D [ Para [ Link ( "" , [] , [] ) [ Str "link" , Space , Math InlineMath "(0,1]" ] ( "url" , "" ) ] ] ``` ================================================ FILE: test/command/7632.md ================================================ ``` % pandoc -t native (@a) First case [link to (@a)](url) ^D [ OrderedList ( 1 , Example , TwoParens ) [ [ Plain [ Str "First" , Space , Str "case" ] ] ] , Para [ Link ( "" , [] , [] ) [ Str "link" , Space , Str "to" , Space , Str "(1)" ] ( "url" , "" ) ] ] ``` ``` % pandoc -t native [@a]{.class} ^D [ Para [ Span ( "" , [ "class" ] , [] ) [ Cite [ Citation { citationId = "a" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 1 , citationHash = 0 } ] [ Str "@a" ] ] ] ] ``` ``` % pandoc -t native [@a](url) ^D [ Para [ Link ( "" , [] , [] ) [ Cite [ Citation { citationId = "a" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 1 , citationHash = 0 } ] [ Str "@a" ] ] ( "url" , "" ) ] ] ``` ================================================ FILE: test/command/7668.md ================================================ ``` % pandoc -f bibtex -t csljson %@Book{JW82, author = {Richard A. Johnson and Dean W. Wichern}, title = {Applied Multivariate Statistical Analysis}, publisher= {Prentice-Hall}, year = {1983} } @Book{JW83, author = {Richard %A. B. Johnson}, % title = {Multivariate Analysis}, year = "% 1983" } ^D [ { "author": [ { "family": "Johnson", "given": "Richard B." } ], "id": "JW83", "issued": { "date-parts": [ [ 1983 ] ] }, "type": "book" } ] ``` ================================================ FILE: test/command/7678.md ================================================ ``` % pandoc -f bibtex -t csljson @misc{doe, author = "Jane Doe", title = "Work", year = "2021", url = "%20and%20" } ^D [ { "URL": "%20and%20", "author": [ { "family": "Doe", "given": "Jane" } ], "id": "doe", "issued": { "date-parts": [ [ 2021 ] ] }, "title": "Work", "type": "" } ] ``` ``` % pandoc -f bibtex -t csljson @misc{doe, author = "Jane Doe", title = "Work", year = "2021", doi = "%20and%20" } ^D [ { "DOI": "%20and%20", "author": [ { "family": "Doe", "given": "Jane" } ], "id": "doe", "issued": { "date-parts": [ [ 2021 ] ] }, "title": "Work", "type": "" } ] ``` ================================================ FILE: test/command/7691.md ================================================ ``` % pandoc command/7691.docx ^D

    Some text

    ``` ================================================ FILE: test/command/7692.md ================================================ ``` % pandoc -t markdown [https://example.com](https://example.com){.clz} ^D [https://example.com](https://example.com){.clz} ``` ``` % pandoc -f markdown -t html | pandoc -f html -t markdown ^D ``` ================================================ FILE: test/command/7697.md ================================================ ``` % pandoc -f rst -t mediawiki .. _refsubpage1: heading ------- ref to top of this section: `refsubpage1`_. ^D = heading = ref to top of this section: [[#refsubpage1|refsubpage1]]. ``` ``` % pandoc -f markdown -t mediawiki # Heading {#foo} ^D = Heading = ``` ``` % pandoc -f markdown -t mediawiki # My Heading {#My_Heading} ^D = My Heading = ``` ================================================ FILE: test/command/7713.md ================================================ ``` % pandoc | aaaaaaaaaaaa | bbbbb | ccccccccccc | | --- | --- | --- | | | | cccccccccc cccccccccc cccccccccc cccccccccc cccccccccc cccccccccc | ^D
    aaaaaaaaaaaa bbbbb ccccccccccc
    cccccccccc cccccccccc cccccccccc cccccccccc cccccccccc cccccccccc
    ``` ================================================ FILE: test/command/7723.md ================================================ ``` % pandoc -t native Bug![^1] [^1]: Note. ^D [ Para [ Str "Bug!" , Note [ Para [ Str "Note." ] ] ] ] ``` ================================================ FILE: test/command/7726.md ================================================ ``` % pandoc -t markdown \# Hi \## Hi \### Hi \#### hi and #hi ^D \# Hi \## Hi \### Hi \#### hi and #hi ``` ================================================ FILE: test/command/7738.md ================================================ ``` % pandoc ![_](url.png) image ^D

    _ image

    ``` ================================================ FILE: test/command/7743.md ================================================ ``` % pandoc -f markdown+mark -t html ==Hi== ^D

    Hi

    ``` ``` % pandoc -f markdown+mark -t latex ==Hi== ^D \hl{Hi} ``` ``` % pandoc -f markdown+mark -t rst ==Hi== ^D :mark:`Hi` ``` ``` % pandoc -f html -t markdown+mark Hi ^D ==Hi== ``` ``` % pandoc -f html -t markdown Hi ^D [Hi]{.mark} ``` ``` % pandoc -f rst -t markdown+mark :mark:`Hi` ^D ==Hi== ``` ``` % pandoc -f markdown+mark -t docx -o - | pandoc -f docx -t markdown+mark ==Hi== ^D ==Hi== ``` ``` % pandoc -f latex -t markdown+mark \hl{Hi} ^D ==Hi== ``` ================================================ FILE: test/command/7761.md ================================================ ``` % pandoc --citeproc --csl command/chicago-fullnote-bibliography.csl -t plain --- references: - id: noauthor issued: 2020 publisher: Oxford University Press publisher-place: Oxford title: Title type: book - id: author author: - family: Jones given: Jim issued: 2021 title: Title type: book url: "https://duckduckgo.com/cite2021" --- Text.[^n] [^n]: See @author. Another example of a sea level mapping tool with similar limitations is @noauthor. ^D Text.[1] Jones, Jim. Title, 2021. https://duckduckgo.com/cite2021. Title. Oxford: Oxford University Press, 2020. [1] See Jim Jones, Title, 2021, https://duckduckgo.com/cite2021. Another example of a sea level mapping tool with similar limitations is Title (Oxford: Oxford University Press, 2020). ``` ================================================ FILE: test/command/7778.md ================================================ Here the thing to remember is that block level structure indications take precedence over inline level structure indications: ``` % pandoc Term : Def ^D
    Term
    Def <!–
    comment def –>
    ``` ``` % pandoc Term : Def test ^D
    Term
    Def test <!–
    comment def and –>
    ``` ``` % pandoc Term : Def `code : comment def more code` ^D
    Term
    Def `code
    comment def more code`
    ``` ================================================ FILE: test/command/7803.md ================================================ ``` % pandoc -f html -t asciidoc ^D * link:x.htm[_Xx_]_,_ * link:x.htm[_Xx_]_,,_ * link:x.htm[_Xx_]_1_ * link:x.htm[_Xx_]1__1__ * link:x.htm[_Xx_]_bcd_ * link:x.htm[_Xx_]a__bcd__ * link:x.htm[_Xx_]a__bcd__e * link:x.htm[_Xx_] _,_ * link:x.htm[_Xx_],_,_ * link:x.htm[Xx]_,_ ``` ================================================ FILE: test/command/7808.md ================================================ Wiki links should have no display text, if their display text matches their target. ``` % pandoc -f mediawiki -t mediawiki [[Help]] [[Butter|Butter]] [[Bubbles|Everyone loves bubbles]] ^D [[Help]] [[Butter]] [[Bubbles|Everyone loves bubbles]] ``` ================================================ FILE: test/command/7810.md ================================================ ``` % pandoc -f org -t org - #+begin_example aa #+end_example - test ^D - #+begin_example aa #+end_example - test ``` ``` % pandoc -f org -t org - a - - a - a ^D - a - - a - a ``` ``` % pandoc -f org -t org - a - b - b - a - a ^D - a - b - b - a - a ``` ================================================ FILE: test/command/7813-meta.yaml ================================================ --- abstract: | a footnote[^1] in abstract. [^1]: content ... ================================================ FILE: test/command/7813.md ================================================ ``` % pandoc --metadata-file command/7813-meta.yaml -t native -s Hi ^D Pandoc Meta { unMeta = fromList [ ( "abstract" , MetaBlocks [ Para [ Str "a" , Space , Str "footnote" , Note [ Para [ Str "content" ] ] , Space , Str "in" , Space , Str "abstract." ] ] ) ] } [ Para [ Str "Hi" ] ] ``` ================================================ FILE: test/command/7826.md ================================================ ``` % pandoc -t plain --citeproc --- bibliography: command/biblio.bib suppress-bibliography: true csl: command/american-medical-association.csl notes-after-punctuation: true --- In numerous recent works [@item1; @item2], statistician Foo and Bar have criticized XXX. ^D In numerous recent works,^(1,2) statistician Foo and Bar have criticized XXX. ``` ``` % pandoc -t plain --citeproc --- bibliography: command/biblio.bib suppress-bibliography: true csl: command/american-medical-association.csl --- In numerous recent works [@item1; @item2], statistician Foo and Bar have criticized XXX. ^D In numerous recent works^(1,2), statistician Foo and Bar have criticized XXX. ``` ``` % pandoc -t plain --citeproc --- bibliography: command/biblio.bib suppress-bibliography: true csl: command/chicago-fullnote-bibliography.csl notes-after-punctuation: false --- In numerous recent works [@item1; @item2], statistician Foo and Bar have criticized XXX. ^D In numerous recent works[1], statistician Foo and Bar have criticized XXX. [1] John Doe, First Book (Cambridge: Cambridge University Press, 2005); John Doe, “Article,” Journal of Generic Studies 6 (2006): 33–34. ``` ``` % pandoc -t plain --citeproc --- bibliography: command/biblio.bib suppress-bibliography: true csl: command/chicago-fullnote-bibliography.csl --- In numerous recent works [@item1; @item2], statistician Foo and Bar have criticized XXX. ^D In numerous recent works,[1] statistician Foo and Bar have criticized XXX. [1] John Doe, First Book (Cambridge: Cambridge University Press, 2005); John Doe, “Article,” Journal of Generic Studies 6 (2006): 33–34. ``` ``` % pandoc -t plain --citeproc --- bibliography: command/biblio.bib suppress-bibliography: true notes-after-punctuation: true --- In numerous recent works [@item1; @item2], statistician Foo and Bar have criticized XXX. ^D In numerous recent works (Doe 2005, 2006), statistician Foo and Bar have criticized XXX. ``` ================================================ FILE: test/command/7847.md ================================================ ``` % pandoc -f html -t markdown_strict+pipe_tables
    aaa bbb ccc
    Consequat nisi sit amet nibh. Nunc mi tortor, tristique sit amet, rhoncus porta, malesuada elementum, nisi. ccc
    ^D | aaa | bbb | ccc | |---------------|------------------------------------------|---------------| | Consequat nisi sit amet nibh. Nunc mi tortor, tristique sit amet, rhoncus porta, malesuada elementum, nisi. | | ccc | ``` ``` % pandoc -f html -t markdown_strict+pipe_tables
    aaa bbb ccc
    Consequat nisi sit amet nibh. Nunc mi tortor, tristique sit amet, rhoncus porta, malesuada elementum, nisi. ccc
    ^D | aaa | bbb | ccc | |----|----|----| | Consequat nisi sit amet nibh. Nunc mi tortor, tristique sit amet, rhoncus porta, malesuada elementum, nisi. | | ccc | ``` ``` % pandoc -f html -t commonmark+pipe_tables
    aaa bbb ccc
    Consequat nisi sit amet nibh. Nunc mi tortor, tristique sit amet, rhoncus porta, malesuada elementum, nisi. bbb ccc
    ^D | aaa | bbb | ccc | |----|----|----| | Consequat nisi sit amet nibh. Nunc mi tortor, tristique sit amet, rhoncus porta, malesuada elementum, nisi. | bbb | ccc | ``` ================================================ FILE: test/command/7857.md ================================================ ``` % pandoc --slide-level=2 -t beamer # section ::: notes my note ::: ## slide ok ^D \section{section}\label{section} \note{my note} \begin{frame}{slide} \protect\phantomsection\label{slide} ok \end{frame} ``` ================================================ FILE: test/command/7858.md ================================================ ``` % pandoc -t html --columns=50 --wrap=auto `neuth asontue stheuosnt aeosunth asnoetuh asneotuh snatehou snatoehu sntahe ousntahoe unstaheou sntaheou aoeu sthoeu sntaoeusnth ansoetuhs atoeuh saonteu` ^D

    neuth asontue stheuosnt aeosunth asnoetuh asneotuh snatehou snatoehu sntahe ousntahoe unstaheou sntaheou aoeu sthoeu sntaoeusnth ansoetuhs atoeuh saonteu

    ``` ================================================ FILE: test/command/7861/metadata/placeholder ================================================ ================================================ FILE: test/command/7861.md ================================================ ``` % pandoc -s -t native --data-dir=command/7861 --metadata-file=../../7861.yaml Hello ^D 2> Could not find metadata file ../../7861.yaml => 98 ``` ================================================ FILE: test/command/7861.yaml ================================================ ================================================ FILE: test/command/7863.md ================================================ ``` % pandoc -f commonmark+yaml_metadata_block+sourcepos -t native --- key: | value ... Text ^D [ Div ( "" , [] , [ ( "wrapper" , "1" ) , ( "data-pos" , "8:1-9:1" ) ] ) [ Para [ Span ( "" , [] , [ ( "wrapper" , "1" ) , ( "data-pos" , "8:1-8:5" ) ] ) [ Str "Text" ] ] ] ] ``` ================================================ FILE: test/command/7871.md ================================================ ``` % pandoc -f html -t html
    a
    ^D
    a
    ``` ================================================ FILE: test/command/7884.md ================================================ ``` % pandoc -f html+epub_html_exts -t native

    Chapter 1

    Here is a footnote.1

    Here is a second footnote.2


    1. This is a test↩︎

    2. This is another test↩︎

    ^D [ Div ( "chapter-1" , [ "section" , "level1" ] , [ ( "number" , "1" ) ] ) [ Header 1 ( "" , [] , [ ( "number" , "1" ) ] ) [ Str "Chapter" , Space , Str "1" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote." , Note [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "test" ] ] ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "second" , Space , Str "footnote." , Note [ Para [ Str "This" , Space , Str "is" , Space , Str "another" , Space , Str "test" ] ] ] ] ] ``` ================================================ FILE: test/command/7894.md ================================================ ``` % pandoc -f ris -t csljson TY - BOOK ID - Chang_Keisler_Model_Theory AU - Chang, C. C. AU - Keisler, H. Jerome PY - 1990 ET - 3 TI - Model Theory PU - North-Holland Press PP - Amsterdam KW - model theory KW - logic ER - TY - JOUR AU - Shannon, Claude E. PY - 1948 DA - July TI - A Mathematical Theory of Communication T2 - Bell System Technical Journal SP - 379 EP - 423 VL - 27 ER - TY - JOUR T1 - On computable numbers, with an application to the Entscheidungsproblem A1 - Turing, Alan Mathison JO - Proc. of London Mathematical Society VL - 47 IS - 1 KW - decidability KW - computability SP - 230 EP - 265 Y1 - 1937 ER - ^D [ { "author": [ { "family": "Chang", "given": "C. C." }, { "family": "Keisler", "given": "H. Jerome" } ], "edition": "3", "id": "Chang_Keisler_Model_Theory", "issued": { "date-parts": [ [ 1990 ] ] }, "keyword": "logic, model theory", "publisher-place": "Amsterdam", "title": "Model Theory", "type": "book" }, { "author": [ { "family": "Shannon", "given": "Claude E." } ], "container-title": "Bell System Technical Journal", "id": "Shannon_1948", "issued": { "date-parts": [ [ 1948 ] ] }, "page": "379-423", "title": "A Mathematical Theory of Communication", "type": "article-journal", "volume": "27" }, { "author": [ { "family": "Turing", "given": "Alan Mathison" } ], "container-title": "Proc. of London Mathematical Society", "id": "Turing_1937", "issue": "1", "issued": { "date-parts": [ [ 1937 ] ] }, "keyword": "computability, decidability", "page": "230-265", "title": "On computable numbers, with an application to the Entscheidungsproblem", "type": "article-journal", "volume": "47" } ] ``` ================================================ FILE: test/command/7919.md ================================================ ``` % pandoc -f markdown single column table | ------------------- | item 1 | item 2 | ^D
    single column table
    item 1
    item 2
    ``` ``` % pandoc -f markdown | single column table | ------------------- | item 1 | item 2 ^D
    single column table
    item 1
    item 2
    ``` ``` % pandoc -f markdown single column table ------------------- item 1 item 2 ^D

    single column table

    item 1 item 2

    ``` ``` % pandoc -f gfm single column table | ------------------- | item 1 | item 2 | ^D
    single column table
    item 1
    item 2
    ``` ``` % pandoc -f gfm | single column table | ------------------- | item 1 | item 2 ^D
    single column table
    item 1
    item 2
    ``` ``` % pandoc -f gfm single column table ------------------- item 1 item 2 ^D

    single column table

    item 1 item 2

    ``` ================================================ FILE: test/command/7920.md ================================================ ``` % pandoc -t native [hi]{#123} ^D [ Para [ Span ( "123" , [] , [] ) [ Str "hi" ] ] ] ``` ``` % pandoc -t markdown [hi]{#123} ^D [hi]{#123} ``` ``` % pandoc -t markdown # Hi {#123} ^D # Hi {#123} ``` ================================================ FILE: test/command/7930.md ================================================ ``` % pandoc -f latex -t native We discuss foobar in notes \ref{note:X} and \ref{note:Y}. Foo.\footnote{\label{note:X}A note. See also note~\ref{note:Y}.} Bar.\footnote{\label{note:Y}Another note. See also note~\ref{note:X}} ^D [ Para [ Str "We" , Space , Str "discuss" , Space , Str "foobar" , Space , Str "in" , Space , Str "notes" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "note:X" ) ] ) [ Str "1" ] ( "#note:X" , "" ) , Space , Str "and" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "note:Y" ) ] ) [ Str "2" ] ( "#note:Y" , "" ) , Str "." ] , Para [ Str "Foo." , Note [ Para [ Span ( "note:X" , [] , [ ( "label" , "note:X" ) ] ) [] , Str "A" , Space , Str "note." , Space , Str "See" , Space , Str "also" , Space , Str "note\160" , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "note:Y" ) ] ) [ Str "2" ] ( "#note:Y" , "" ) , Str "." ] ] ] , Para [ Str "Bar." , Note [ Para [ Span ( "note:Y" , [] , [ ( "label" , "note:Y" ) ] ) [] , Str "Another" , Space , Str "note." , Space , Str "See" , Space , Str "also" , SoftBreak , Str "note\160" , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "note:X" ) ] ) [ Str "1" ] ( "#note:X" , "" ) ] ] ] ] ``` ================================================ FILE: test/command/7939.md ================================================ ``` % pandoc -f latex -t native \def\|{\verb|} \vbox{ \hbox{\|\begin{minipage}[t]%|} } ^D [ Para [ Code ( "" , [] , [] ) "\\begin{minipage}[t]%" ] ] ``` ================================================ FILE: test/command/7941.md ================================================ ``` % pandoc -f commonmark_x -t commonmark_x # Class setup ^D # Class setup ``` ================================================ FILE: test/command/7953.md ================================================ ``` % pandoc -f latex -t native {foo\bgroup bar\egroup } ^D [ Para [ Span ( "" , [] , [] ) [ Str "foo" , Span ( "" , [] , [] ) [ Str "bar" ] ] ] ] ``` ================================================ FILE: test/command/7965.md ================================================ ``` % pandoc -f markdown -t gfm Watson and Crick (1953)
    Watson, J. D., and F. H. C. Crick. 1953. “Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid.” *Nature* 171 (4356): 737–38. .
    ^D Watson and Crick (1953)
    Watson, J. D., and F. H. C. Crick. 1953. “Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid.” *Nature* 171 (4356): 737–38. .
    ``` ``` % pandoc -f markdown -t gfm-raw_html Watson and Crick (1953)
    Watson, J. D., and F. H. C. Crick. 1953. “Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid.” *Nature* 171 (4356): 737–38. .
    ^D Watson and Crick (1953) Watson, J. D., and F. H. C. Crick. 1953. “Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid.” *Nature* 171 (4356): 737–38. . ``` ================================================ FILE: test/command/8003.md ================================================ ``` % pandoc -f markdown+ascii_identifiers # Işık ^D

    Işık

    ``` ================================================ FILE: test/command/8011.md ================================================ ``` % pandoc -f docbook -t gfm ❏ a ✓ b ^D - [ ] a - [x] b ``` ================================================ FILE: test/command/8024.md ================================================ ``` % 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. ``` ================================================ FILE: test/command/8024a.yaml ================================================ defaults: ${.}/8024b.yaml ================================================ FILE: test/command/8024b.yaml ================================================ wrap: auto columns: 15 ================================================ FILE: test/command/8028.md ================================================ ``` % pandoc foo^[a note with multiple paragraphs] ^D

    foo^[a note

    with multiple paragraphs]

    ``` ================================================ FILE: test/command/8047.md ================================================ ``` % pandoc --wrap=none ![](file.jpg){height=10em width=10em style='border: 1px solid black'} ^D

    ``` ================================================ FILE: test/command/8070.md ================================================ ``` % pandoc -f html -t asciidoc

    12.1 manual, Database Backup and Recovery User’s Guide: Chapter 28 Transporting Data Across Platforms, Steps to Transport a Database to a Different Platform Using Backup Sets

    ^D https://docs.oracle.com/database/121/BRADV/rcmxplat.htm#BRADV724[12.1 manual, Database Backup and Recovery User’s Guide: [.enumeration_chapter]#Chapter 28# Transporting Data Across Platforms, Steps to Transport a Database to a Different Platform Using Backup Sets] ``` ================================================ FILE: test/command/8079.md ================================================ ``` % pandoc -f html -t icml
    one
    ^D one
    ``` ================================================ FILE: test/command/8088.md ================================================ ``` % pandoc -t latex --biblatex [@first, 1; @second; @third, 3] ^D \autocites[1]{first}{second}[3]{third} ``` ================================================ FILE: test/command/8097.md ================================================ ``` % pandoc --number-sec --number-offset=2 --wrap=none # One # two ^D

    3 One

    4 two

    ``` ================================================ FILE: test/command/8098.md ================================================ ``` % pandoc -t revealjs --slide-level=2 # Title 1 ## Slide 1 Text. ::: fragment ### Sub Slide header Text. ::: ## Slide 2 Text. ^D

    Title 1

    Slide 1

    Text.

    Sub Slide header

    Text.

    Slide 2

    Text.

    ``` ================================================ FILE: test/command/8110.md ================================================ ``` % pandoc -f mediawiki {| class="wikitable" ! Header text ! Header text ! Header text |- | Example | Example | Example |- | Example | Example | Example |} ^D

    Header text

    Header text

    Header text

    Example

    Example

    Example

    Example

    Example

    Example

    ``` ================================================ FILE: test/command/8131.md ================================================ # TOC in gfm contains no HTML by default ``` % pandoc --to=gfm --toc --standalone # Head Content ^D - [Head](#head) # Head Content ``` # Same in Markdown if link_attributes extension is disabled ``` % pandoc --to=markdown-link_attributes --toc --standalone # Head Content ^D - [Head](#head) # Head Content ``` # IDs are added to TOC with the "attributes" CommonMark extension ``` % pandoc --to=commonmark+gfm_auto_identifiers+attributes --toc -s # Nam a sapien Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ^D - [Nam a sapien](#nam-a-sapien){#toc-nam-a-sapien} # Nam a sapien Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ``` ================================================ FILE: test/command/8150.md ================================================ Nested bullet lists ``` % pandoc -f html -t markdown
    • L1
    • L2
      • L3.1
      • L3.2
    • L4
    ^D - L1 - L2 - L3.1 - L3.2 - L4 ``` Nested ordered lists ``` % pandoc -f html -t markdown
    1. L1
    2. L2
      1. L3.1
      2. L3.2
    ^D 1. L1 2. L2 1. L3.1 2. L3.2 ``` Ordered list nested below an unordered list ``` % pandoc -f html -t markdown
    • L1
    • L2
      1. L3.1
      2. L3.2
    ^D - L1 - L2 1. L3.1 2. L3.2 ``` ================================================ FILE: test/command/8170.md ================================================ ``` % pandoc -f rtf -t html {\rtf1\ansi\ansicpg1252\cocoartf2638 \cocoascreenfonts1\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\froman\fcharset0 Times-Roman;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} \margl1440\margr1440\vieww16800\viewh17280\viewkind0 \pard\tx1080\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li180\fi440\sa120\pardirnatural\partightenfactor0 \f0\fs34 \cf0 In 5 \fs20 \super th \fs34 \nosupersub grade I found a bug.} ^D

    In 5th grade I found a bug.

    ``` ================================================ FILE: test/command/8174.md ================================================ ```` % pandoc -t native ```yaml {#id} some: code ``` ^D [ CodeBlock ( "id" , [ "yaml" ] , [] ) "some: code" ] ```` ```` % pandoc -t native ```yaml {.class #id} some: code ``` ^D [ CodeBlock ( "id" , [ "yaml" , "class" ] , [] ) "some: code" ] ```` ```` % pandoc -t native ```ab``` ^D [ Para [ Code ( "" , [] , [] ) "ab" ] ] ```` ```` % pandoc -t native ```ab```{.class} ^D [ Para [ Code ( "" , [ "class" ] , [] ) "ab" ] ] ```` ```` % pandoc -t native ``` foo}{.bar} test ``` ^D [ Para [ Code ( "" , [] , [] ) "foo}{.bar} test" ] ] ```` ================================================ FILE: test/command/8178.md ================================================ ``` % pandoc -f dokuwiki -t native $\sum_{\substack{(i,j) \in I^2 \i \neq j}}$ ^D [ Para [ RawInline (Format "latex") "$\\sum_{\\substack{(i,j) \\in I^2 \\i \\neq j}}$" ] ] ``` ``` % pandoc -f dokuwiki+tex_math_dollars -t native $mc^2$ ^D [ Para [ Math InlineMath "mc^2" ] ] ``` ``` % pandoc -f dokuwiki -t native $mc^2$ ^D [ Para [ Str "$mc^2$" ] ] ``` ================================================ FILE: test/command/8179.md ================================================ ``` % pandoc -f latex -t native \passthrough{\lstinline!\\textbf\{\}!} ^D [ Para [ Code ( "" , [] , [] ) "\\textbf{}" ] ] ``` ================================================ FILE: test/command/8182.md ================================================ ``` % pandoc -f dokuwiki -t rst This //text and [[https://pandoc.org/|link]] are in italic//. ^D This *text and* `link `__ *are in italic*. ``` ================================================ FILE: test/command/8201.md ================================================ ``` % pandoc -f org -t html [[file:d:/Home/Documents/test.png][Link Test]] ^D

    Link Test

    ``` ================================================ FILE: test/command/8204.md ================================================ Include abstract in Org output. ``` % pandoc --to=org --standalone --- abstract: | This is an *abstract*. It has multiple paragraphs. --- ^D #+begin_abstract This is an /abstract/. It has multiple paragraphs. #+end_abstract ``` Parse abstract environment as *abstract* metadata field. ``` % pandoc --from=org --to=markdown --standalone #+begin_abstract This is an /abstract/. It has multiple paragraphs. #+end_abstract Main text ^D --- abstract: | This is an *abstract*. It has multiple paragraphs. --- Main text ``` Uppercase ABSTRACT environment is not moved to metadata. ``` % pandoc --from=org --to=markdown --standalone #+BEGIN_ABSTRACT Some text. #+END_ABSTRACT ^D ::: ABSTRACT Some text. ::: ``` ================================================ FILE: test/command/8216.md ================================================ Misaligned separators in grid table ``` % pandoc -f markdown -t html : Grid Table +-----------------+:-:+ |Some text |[text]{.class1 .class2 .class3}| +-----------------+---+ |Some text |[text]{.class1 .class2 .class3}| +-----------------+---+ |Some text |[text]{.class1 .class2 .class3}| +-----------------+---+ ^D
    Grid Table
    Some text text
    Some text text
    Some text text
    ``` Missing cell ``` % pandoc -f markdown -t gfm +------+ | text | +------+---+ | text | 1 | +------+---+ ^D | | | |------|-----| | text | | | text | 1 | ``` ================================================ FILE: test/command/8219.md ================================================ ``` % pandoc -f html -t latex
    onetwo
    ^D \begin{longtable}[]{@{}ll@{}} \caption{}\label{test}\tabularnewline \toprule\noalign{} \endfirsthead \endhead \bottomrule\noalign{} \endlastfoot one & two \\ \end{longtable} ``` ================================================ FILE: test/command/8236.md ================================================ ``` % pandoc -f org -t native #+begin_src jupyter-python :session py :display plain import pandas as pd df = pd.read_csv('weight.csv', parse_dates=['Date'], index_col=0) #+end_src ^D [ CodeBlock ( "" , [ "python" , "code" ] , [ ( "session" , "py" ) , ( "display" , "plain" ) ] ) "import pandas as pd\ndf = pd.read_csv('weight.csv', parse_dates=['Date'], index_col=0)\n" ] ``` ================================================ FILE: test/command/8243.md ================================================ ``` % pandoc -f latex \newenvironment{topics}{ \newcommand{\topic}[2]{ \item{##1 / ##2} } Topics: \begin{itemize} } { \end{itemize} } \begin{topics} \topic{Foo}{Bar} \topic{Baz}{Bim} \end{topics} ^D

    Topics:

    • Foo / Bar

    • Baz / Bim

    ``` ================================================ FILE: test/command/8251.md ================================================ ``` % pandoc -f native -t html Link ("",["", "", ""],[]) [Str "foo"] ("https://example.com","") ^D foo ``` ``` % pandoc -f native -t markdown Link ("",["", "", ""],[]) [Str "foo"] ("https://example.com","") ^D [foo](https://example.com){} ``` ================================================ FILE: test/command/8254.md ================================================ ``` % pandoc -t native Wow![@legras_michel_2010] ^D [ Para [ Str "Wow!" , Cite [ Citation { citationId = "legras_michel_2010" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 2 , citationHash = 0 } ] [ Str "[@legras_michel_2010]" ] ] ] ``` ================================================ FILE: test/command/8256.md ================================================ ``` % pandoc -t opendocument -s --- title: | This *is* \ a test author: Someone --- Testing. ^D This isa test Someone Testing. ``` ================================================ FILE: test/command/8257.md ================================================ ``` % pandoc -f markdown -t html5 +------+-------+ | Item | Price | +======+=======+ | Eggs | 5£ | +------+-------+ | Spam | 3£ | +======+=======+ | Sum | 8£ | +======+=======+ ^D
    Item Price
    Eggs
    Spam
    Sum
    ``` ================================================ FILE: test/command/8281.md ================================================ ``` % pandoc -t html # Title test . . . test ^D

    Title

    test

    . . .

    test

    ``` ================================================ FILE: test/command/8302.md ================================================ ``` % pandoc -f org -t markdown some text cite:&long-2004-tecton-evolut ^D some text @long-2004-tecton-evolut ``` ================================================ FILE: test/command/8307.md ================================================ ``` % pandoc -t commonmark -f html
    hello
    ^D
    hello
    ``` ```` % pandoc -t commonmark -f markdown ``` {=html}
    hello
    ``` ^D
    hello
    ```` ================================================ FILE: test/command/8344.md ================================================ ``` % pandoc -t jats -s --- title: |- My\ document ... # Section\ with line break Paragraph\ with line break -------- A B --- --- 1\ 3 2 4 *1\ 5\ 2* 6 7 -------- ^D
    Mydocument Section<break/>

    with line break

    Paragraph with line break

    A B
    12 3 4
    1 2 56
    7
    ``` ================================================ FILE: test/command/8354.md ================================================ ``` % pandoc -f markdown -t html --citeproc --- title: test nocite: '[@*]' references: - author: - family: Fekete given: Jean-Daniel - family: Freire given: Juliana DOI: 10.1109/MCG.2020.3006412 id: feketeExploringReproducibilityVisualization2020 ISSN: 1558-1756 issue: 5 issued: 2020-09 page: 108-119 source: IEEE Xplore title: Exploring Reproducibility in Visualization container-title: IEEE Computer Graphics and Applications type: article-journal volume: 40 --- ## References ^D

    References

    Fekete, Jean-Daniel, and Juliana Freire. 2020. “Exploring Reproducibility in Visualization.” IEEE Computer Graphics and Applications 40 (5): 108–19. https://doi.org/10.1109/MCG.2020.3006412.
    ``` ================================================ FILE: test/command/8364.md ================================================ ``` % pandoc --citeproc -t jats -s --- reference-section-title: Bibliography csl: command/apa.csl references: - author: - family: Doe given: John id: test issued: date-parts: - - 2006 title: Test type: 'article-journal' volume: 81 --- @test ^D

    Doe (2006)

    Bibliography Doe, J. (2006). Test, 81.
    ``` ================================================ FILE: test/command/8365.md ================================================ ``` % pandoc -f jats -t native -s

    Doe (2006)

    Bibliography Doe, J. (2006). Test, 81.
    ^D Pandoc Meta { unMeta = fromList [ ( "references" , MetaList [ MetaMap (fromList [ ( "id" , MetaString "test" ) ]) ] ) ] } [ Para [ Str "Doe" , Space , Str "(2006)" ] , Header 1 ( "" , [] , [] ) [ Str "Bibliography" ] , Div ( "refs" , [] , [] ) [] ] ``` ================================================ FILE: test/command/8380.md ================================================ ``` % pandoc -f man -t rst LC_* ^D LC\_\* ``` These examples of things that don't require escaping are taken from the RST documentation: ``` % pandoc -f native -t rst [Para [Str "2*x a**b O(N**2) e**(x*y) f(x)*f(y) a|b"] ,Para [Str "a**b O(N**2) e**(x*y) f(x)*f(y)"] ] ^D 2*x a**b O(N**2) e**(x*y) f(x)*f(y) a|b a**b O(N**2) e**(x*y) f(x)*f(y) ``` These examples of things that do require escaping are taken from the RST documentation: ``` % pandoc -f native -t rst Str "*4, class_, *args, **kwargs, `TeX-quoted', *ML, *.txt" ^D \*4, class\_, \*args, \**kwargs, \`TeX-quoted', \*ML, \*.txt ``` ================================================ FILE: test/command/8402.md ================================================ ``` % pandoc --toc -s -t markdown ::: {.cell .markdown id="6u8qXoeFGdqt"} # Summary > Expand to see summary ## Overview and Explanation ::: # Details ## inner 1 text ## inner 2 ### inner inner 1 ## inner 3 text ^D - [Summary](#summary){#toc-summary} - [Overview and Explanation](#overview-and-explanation){#toc-overview-and-explanation} - [Details](#details){#toc-details} - [inner 1](#inner-1){#toc-inner-1} - [inner 2](#inner-2){#toc-inner-2} - [inner inner 1](#inner-inner-1){#toc-inner-inner-1} - [inner 3](#inner-3){#toc-inner-3} ::: {#6u8qXoeFGdqt .cell .markdown} # Summary > Expand to see summary ## Overview and Explanation ::: # Details ## inner 1 text ## inner 2 ### inner inner 1 ## inner 3 text ``` ================================================ FILE: test/command/8437.md ================================================ ``` % pandoc -f markdown -t asciidoc [![alt](https://img.shields.io/badge/License-Apache%202.0-blue.svg "title")](http://www.apache.org/licenses/LICENSE-2.0) ^D http://www.apache.org/licenses/LICENSE-2.0[image:https://img.shields.io/badge/License-Apache%202.0-blue.svg[alt,title="title"]] ``` ``` % pandoc -f markdown -t docbook [![alt](https://img.shields.io/badge/License-Apache%202.0-blue.svg "title")](http://www.apache.org/licenses/LICENSE-2.0) ^D title alt ``` ``` % pandoc -f docbook -t markdown title alt ^D [![alt](https://img.shields.io/badge/License-Apache%202.0-blue.svg "title")](http://www.apache.org/licenses/LICENSE-2.0) ``` ================================================ FILE: test/command/8486.md ================================================ # Unnumbered and unlisted headings in ConTeXt ``` % pandoc --to=context # Preface {.unlisted -} Nullam rutrum. # Introduction Nullam eu ante vel est convallis dignissim. # Methods Sed diam. Nulla posuere. Praesent fermentum tempor tellus. Nam vestibulum accumsan nisl. Nam vestibulum accumsan nisl. # References {-} ^D \startsubject[title={Preface},reference={preface},number=no,incrementnumber=no] Nullam rutrum. \stopsubject \startsectionlevel[title={Introduction},reference={introduction}] Nullam eu ante vel est convallis dignissim. \stopsectionlevel \startsectionlevel[title={Methods},reference={methods}] Sed diam. Nulla posuere. Praesent fermentum tempor tellus. Nam vestibulum accumsan nisl. Nam vestibulum accumsan nisl. \stopsectionlevel \startsectionlevel[title={References},reference={references},number=no,incrementnumber=no] \stopsectionlevel ``` ## Semantic Headings with `--top-level-division` ``` % pandoc --to=context --top-level-division=chapter # Bibliography {-} None ^D \startchapter[title={Bibliography},reference={bibliography},number=no,incrementnumber=no] None \stopchapter ``` ================================================ FILE: test/command/8487.md ================================================ ``` % pandoc -f textile -t native p>. p=. Links: ^D [ Div ( "" , [] , [ ( "style" , "text-align:right;" ) ] ) [ Para [] ] , Div ( "" , [] , [ ( "style" , "text-align:center;" ) ] ) [ Para [ Str "Links:" ] ] ] ``` ================================================ FILE: test/command/8504.md ================================================ ``` % pandoc -f markdown -s -t biblatex --- references: - id: Larsson_2016 author: - family: Larsson given: Johan citation-key: Larsson_2016 issued: - year: 2016 medium: Windows title: 'qualpalr: Automatic Generation of Qualitative Color Palettes' type: software URL: https://cran.r-project.org/package=qualpalr version: 0.3.1 ... ^D @software{Larsson_2016, author = {Larsson, Johan}, title = {Qualpalr: {Automatic} {Generation} of {Qualitative} {Color} {Palettes}}, version = {0.3.1}, date = {2016}, url = {https://cran.r-project.org/package=qualpalr} } ``` ``` % pandoc -f markdown -s -t bibtex --- references: - id: Larsson_2016 author: - family: Larsson given: Johan citation-key: Larsson_2016 issued: - year: 2016 medium: Windows title: 'qualpalr: Automatic Generation of Qualitative Color Palettes' type: software URL: https://cran.r-project.org/package=qualpalr version: 0.3.1 ... ^D @misc{Larsson_2016, author = {Larsson, Johan}, title = {Qualpalr: {Automatic} {Generation} of {Qualitative} {Color} {Palettes}}, year = {2016}, url = {https://cran.r-project.org/package=qualpalr} } ``` ``` % pandoc -f biblatex -s -t csljson @software{Larsson_2016, author = {Larsson, Johan}, title = {Qualpalr: {Automatic} {Generation} of {Qualitative} {Color} {Palettes}}, version = {0.3.1}, date = {2016}, url = {https://cran.r-project.org/package=qualpalr} } ^D [ { "URL": "https://cran.r-project.org/package=qualpalr", "author": [ { "family": "Larsson", "given": "Johan" } ], "id": "Larsson_2016", "issued": { "date-parts": [ [ 2016 ] ] }, "title": "Qualpalr: Automatic Generation of Qualitative Color Palettes", "title-short": "Qualpalr", "type": "software", "version": "0.3.1" } ] ``` ================================================ FILE: test/command/8508.md ================================================ ``` % pandoc -t man SEE ALSO ======== * [Milk](https://en.wikipedia.org/wiki/Milk) * [EBNF](https://en.wikipedia.org/wiki/Extended_Backus–Naur_form) ^D .SH SEE ALSO .IP \(bu 2 \c .UR https://en.wikipedia.org/wiki/Milk Milk .UE \c .IP \(bu 2 \c .UR https://en.wikipedia.org/wiki/Extended_Backus–Naur_form EBNF .UE \c ``` ================================================ FILE: test/command/8511.md ================================================ # Conversion of icon-like sequences followed by alphanum char ## to Jira ``` % pandoc -t jira :P :P_ :PA :Pa :P2 ^D \:P \:P\_ :PA :Pa :P2 ``` ## from jira ``` % pandoc -f jira -t markdown \:PA ^D \\:PA ``` ================================================ FILE: test/command/8513.md ================================================ # Round-tripping tags in org ``` % pandoc --from org --to org --columns=72 * Header with tag :tag: ^D * Header with tag :tag: :PROPERTIES: :CUSTOM_ID: header-with-tag :END: ``` Multiple tags ``` % pandoc --from org-auto_identifiers --to org --columns=78 * Header with two tags :first:second: ^D * Header with two tags :first:second: ``` Check that long headings work with tags ``` % pandoc --from org-auto_identifiers --to org --columns=40 * Header with tag and more words which are not that interesting :tag: ^D * Header with tag and more words which are not that interesting :tag: ``` ================================================ FILE: test/command/853.md ================================================ reStructuredText citations. ``` % pandoc -f rst Here is a citation reference: [CIT2002]_. .. [CIT2002] This is the citation. It's just like a footnote, except the label is textual. ^D

    Here is a citation reference: [CIT2002].

    CIT2002

    This is the citation. It's just like a footnote, except the label is textual.

    ``` ================================================ FILE: test/command/8534.md ================================================ ``` % pandoc -f html -t texinfo foo bar baz bar foo ^D @node Top @top Top foo @code{bar }@code{@var{baz}}@code{ bar} foo ``` ================================================ FILE: test/command/8573.md ================================================ ``` % pandoc -f latex -t plain \newenvironment{myenv}[1]{Open#1}{Close} \begin{myenv}{x}Hello\end{myenv} ^D OpenxHelloClose ``` ================================================ FILE: test/command/8611.md ================================================ ``` % pandoc -f markdown -s -t bibtex --- nocite: "[@*]" references: - id: mcdowell:why title: | Why is Sellars's Essay Called "Empiricism and the Philosophy of Mind"? author: - family: McDowell given: John journal: Proceedings of the Aristotelian Society type: chapter container-title: 'Empiricism, Perceptual Knowledge, Normativity, and Realism: Essays on Wilfrid Sellars' editor: - family: deVries given: Willem A. issued: 2009 publisher: Oxford University Press publisher-place: Oxford ... ^D @incollection{mcdowell:why, author = {McDowell, John}, editor = {deVries, Willem A.}, publisher = {Oxford University Press}, title = {Why Is {Sellars’s} {Essay} {Called} “{Empiricism} and the {Philosophy} of {Mind}”?}, booktitle = {Empiricism, Perceptual Knowledge, Normativity, and Realism: Essays on Wilfrid Sellars}, year = {2009}, address = {Oxford} } ``` ================================================ FILE: test/command/8638.md ================================================ # Beamer tables must not use longtable footer ``` % pandoc -t beamer | fruit | price | |---------|------:| | apple | 2.05 | | oranges | 4.25 | ^D \begin{frame} {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}lr@{}} \toprule\noalign{} fruit & price \\ \midrule\noalign{} \endhead apple & 2.05 \\ oranges & 4.25 \\ \bottomrule\noalign{} \end{longtable} } \end{frame} ``` ================================================ FILE: test/command/8652.md ================================================ ``` % pandoc -t native My text^[My note]isnot^[Text] ^D [ Para [ Str "My" , Space , Str "text" , Note [ Para [ Str "My" , Space , Str "note" ] ] , Str "isnot" , Note [ Para [ Str "Text" ] ] ] ] ``` ``` % pandoc -t native ^[note1]test^[note2] ^D [ Para [ Note [ Para [ Str "note1" ] ] , Str "test" , Note [ Para [ Str "note2" ] ] ] ] ``` ================================================ FILE: test/command/8653.md ================================================ ``` % pandoc -f rst -t native .. role:: py:class(emphasis) :py:class:`foo` ^D [ Para [ Emph [ Str "foo" ] ] ] ``` ``` % pandoc -f rst -t native :py:class:`foo` ^D [ Para [ Code ( "" , [ "interpreted-text" ] , [ ( "role" , "py:class" ) ] ) "foo" ] ] ``` ================================================ FILE: test/command/8659.md ================================================ # A single pipe is parsed as an empty table ``` % pandoc -f org -t html | ^D
    ``` # Accepts an empty org table within a grid table cell The misaligned pipe in the first row is treated as an empty table. ``` % pandoc -f org -t html +-----+-----+-----+-----------+ | | | | | +:====+=====+====:+==========:+ | a | b | c | d | +-----+-----+-----+-----------+ ^D
    a b c d
    ``` ================================================ FILE: test/command/8661.md ================================================ ``` % pandoc -f tsv -t gfm a b c 6 ^D | a | b | c | |-----|-----|-----| | | | 6 | ``` ================================================ FILE: test/command/8665.md ================================================ ``` % pandoc -f docbook -t asciidoc h1 h2 !@#$%^&*(){}|~?+-',."<>[]\` col 2 ^D [cols=",",options="header",] |=== <|h1 <|h2 <|!@++#++$%^&++*++()++{++}{vbar}~?{plus}-',."++<>[]\`++ <|col 2 |=== ``` ================================================ FILE: test/command/8666.md ================================================ ``` % pandoc -f docbook -t native Code title echo "hello world!" Example title example content Sidebar title sidebar content ^D [ Div ( "my_code_id" , [ "formalpara" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Plain [ Str "Code" , Space , Str "title" ] ] , CodeBlock ( "" , [ "bash" ] , [] ) "echo \"hello world!\"" ] , Div ( "my_example_id" , [ "example" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Plain [ Str "Example" , Space , Str "title" ] ] , Para [ Str "example" , Space , Str "content" ] ] , Div ( "my_sidebar_id" , [ "sidebar" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Plain [ Str "Sidebar" , Space , Str "title" ] ] , Para [ Str "sidebar" , Space , Str "content" ] ] ] ``` ================================================ FILE: test/command/8681.md ================================================ # Include Jira panel title in resulting div ``` % pandoc -f jira -t html {panel:title=This is the title|titleBGColor=#cccccc|bgColor=#eeeeee} Panel content {panel} ^D
    This is the title

    Panel content

    ``` ================================================ FILE: test/command/8689.md ================================================ # Org figures should be rendered implicit figures ``` % pandoc -f org -t markdown+implicit_figures #+label: fig:bsdd-graphql-voyager-orig-detail #+caption: Original bSDD GraphQL Schema: Detail of Classification and ClassificationProperty [[./Classification-ClassificationProperty.png]] ^D ![Original bSDD GraphQL Schema: Detail of Classification and ClassificationProperty](./Classification-ClassificationProperty.png){#fig:bsdd-graphql-voyager-orig-detail} ``` ================================================ FILE: test/command/8711.md ================================================ ``` % pandoc -f html-native_spans -t native

    a b

    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "a" , Space , Str "b" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f html-native_spans+raw_html -t native

    a b

    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ RawInline (Format "html") "" , Str "a" , RawInline (Format "html") "" , Space , RawInline (Format "html") "" , Str "b" , RawInline (Format "html") "" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f html -t native

    a b

    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "foo" ] , [] ) [ Str "a" ] , Space , Span ( "" , [ "bar" ] , [] ) [ Str "b" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/8738.md ================================================ ``` % pandoc -f native -t mediawiki [Link ("",[],[]) [Link ("",[],[]) [Str "test"] ("https://bar.example.com",""),Str "Test"] ("https://foo.example.com","")] ^D [https://foo.example.com testTest] ``` ================================================ FILE: test/command/8745.md ================================================ # Alt text on images ``` % pandoc -f latex -t native \documentclass[12pt]{article} \usepackage{graphicx} \begin{document} Sample text \begin{figure}[ht] \includegraphics[alt={Upside down painting},width=0.5\textwidth]{New_York_City_I.jpg} \caption{New York City by Piet Mondrian (Wikipedia)} \label{fig:Mondrian} \end{figure} \includegraphics[alt={Face of a mandrill}]{mandrill.jpg} \end{document} ^D [ Para [ Str "Sample" , Space , Str "text" ] , Figure ( "fig:Mondrian" , [] , [ ( "latex-placement" , "ht" ) ] ) (Caption Nothing [ Plain [ Str "New" , Space , Str "York" , Space , Str "City" , Space , Str "by" , Space , Str "Piet" , Space , Str "Mondrian" , Space , Str "(Wikipedia)" ] ]) [ Para [ Image ( "" , [] , [ ( "width" , "50%" ) ] ) [ Str "Upside down painting" ] ( "New_York_City_I.jpg" , "" ) ] ] , Para [ Image ( "" , [] , [] ) [ Str "Face of a mandrill" ] ( "mandrill.jpg" , "" ) ] ] ``` ================================================ FILE: test/command/8764.md ================================================ ``` % pandoc -f html -t opendocument
    Header Normal cell (column 2) Normal cell (column 3)
    Normal cell (column 1) Normal cell (column 2) Normal cell (column 3)
    ^D Header Normal cell (column 2) Normal cell (column 3) Normal cell (column 1) Normal cell (column 2) Normal cell (column 3) ``` ================================================ FILE: test/command/8770-block.md ================================================ ``` % pandoc -t html5 --reference-location=block # Section 1 hello[^1] : Sample table.[^2] ----------- Fruit[^3] ----------- Bans[^4] ----------- # Section 2 dolly[^5] [^1]: doc footnote [^2]: caption footnote [^3]: header footnote [^4]: table cell footnote [^5]: doc footnote ^D

    Section 1

    hello1

    Sample table.2
    Fruit3
    Bans4

    Section 2

    dolly5

    ``` ================================================ FILE: test/command/8770-document.md ================================================ ``` % pandoc -t html5 --reference-location=document # Section 1 hello[^1] : Sample table.[^2] ----------- Fruit[^3] ----------- Bans[^4] ----------- # Section 2 dolly[^5] [^1]: doc footnote [^2]: caption footnote [^3]: header footnote [^4]: table cell footnote [^5]: doc footnote ^D

    Section 1

    hello1

    Sample table.2
    Fruit3
    Bans4

    Section 2

    dolly5


    1. doc footnote↩︎

    2. caption footnote↩︎

    3. header footnote↩︎

    4. table cell footnote↩︎

    5. doc footnote↩︎

    ``` ================================================ FILE: test/command/8770-section.md ================================================ ``` % pandoc -t html5 --reference-location=section # Section 1 hello[^1] : Sample table.[^2] ----------- Fruit[^3] ----------- Bans[^4] ----------- # Section 2 dolly[^5] [^1]: doc footnote [^2]: caption footnote [^3]: header footnote [^4]: table cell footnote [^5]: doc footnote ^D

    Section 1

    hello1

    Sample table.2
    Fruit3
    Bans4

    Section 2

    dolly5

    ``` ================================================ FILE: test/command/8777.md ================================================ # Backslash escapes in markdown_strict ``` % pandoc --from=markdown_strict -t html These are not escaped: \~ \" ^D

    These are not escaped: \~ \"

    ``` ================================================ FILE: test/command/8783.md ================================================ ``` % 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" ] ] ``` ================================================ FILE: test/command/8789.md ================================================ # `\multicolumn` with column-type `p` ``` % pandoc --from=latex --to=html \begin{tabular}{rrll} \toprule \textbf{First} & \textbf{Second} & \textbf{Third} & \textbf{Fourth} \\ \toprule 160 & 1 & test & test \\\midrule 160 & 2 & \multicolumn{2}{p{12cm}}{\textit{This is a test:} \begin{equation*} a^2+b^2 = c^2 \end{equation*} }\\ \bottomrule \end{tabular} ^D
    First Second Third Fourth
    160 1 test test
    160 2 This is a test: a2 + b2 = c2
    ``` ================================================ FILE: test/command/8853.md ================================================ ``` % pandoc -f markdown+wikilinks_title_after_pipe --wrap=none [[hi]] and ![[hi]] ^D

    hi and hi

    ``` ================================================ FILE: test/command/8863.md ================================================ ``` % pandoc -f dokuwiki -t native * item 1 * item 1.1 ^D [ BulletList [ [ Plain [ Str "item" , Space , Str "1" ] ] , [ Plain [ Str "item" , Space , Str "1.1" ] ] ] ] ``` ================================================ FILE: test/command/8867.md ================================================ ``` % pandoc -f jats -t native -s © 2023, Ellerman et al 2023 Ellerman et al https://creativecommons.org/licenses/by/4.0/ This document is distributed under a Creative Commons Attribution 4.0 International license. ^D Pandoc Meta { unMeta = fromList [ ( "copyright" , MetaMap (fromList [ ( "holder" , MetaString "Ellerman et al" ) , ( "statement" , MetaString "\169 2023, Ellerman et al" ) , ( "year" , MetaString "2023" ) ]) ) , ( "license" , MetaMap (fromList [ ( "link" , MetaString "https://creativecommons.org/licenses/by/4.0/" ) , ( "text" , MetaString "This document is distributed under a Creative Commons Attribution 4.0 International license." ) , ( "type" , MetaString "open-access" ) ]) ) ] } [] ``` ================================================ FILE: test/command/8869.md ================================================ Org arguments can either be single words, or quoted. ``` % pandoc -f org -t native #+begin_src jupyter-julia :exports both 1 + 2 #+end_src #+begin_src jupyter-julia :exports "both" 1 + 2 #+end_src ^D [ CodeBlock ( "" , [ "julia" , "code" ] , [ ( "exports" , "both" ) ] ) "1 + 2\n" , CodeBlock ( "" , [ "julia" , "code" ] , [ ( "exports" , "both" ) ] ) "1 + 2\n" ] ``` ================================================ FILE: test/command/8872.md ================================================ ``` % pandoc -f latex -t native \documentclass{amsart} \newtheorem{theorem}{Theorem} \begin{document} \begin{theorem}\label{thm} \begin{enumerate} \item \label{item1}text1 \item \label{item2}text2 \item text3 \end{enumerate} \end{theorem} \end{document} ^D [ Div ( "thm" , [ "theorem" ] , [] ) [ Para [ Strong [ Str "Theorem" , Space , Str "1" ] , Str "." , Space , Space , Emph [] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Emph [ Span ( "item1" , [] , [ ( "label" , "item1" ) ] ) [] , Str "text1" ] ] ] , [ Para [ Emph [ Span ( "item2" , [] , [ ( "label" , "item2" ) ] ) [] , Str "text2" ] ] ] , [ Para [ Emph [ Str "text3" ] ] ] ] ] ] ``` ================================================ FILE: test/command/8948.md ================================================ ``` % pandoc --embed-resources ![minimal](command/minimal.svg){.inline-svg} ![minimal](command/minimal.svg){.inline-svg} ^D

    ``` ``` % pandoc --embed-resources ![minimal](command/minimal.svg){.inline-svg} ![minimal](command/minimal.svg){#foo .inline-svg} ^D

    ``` ``` % pandoc --embed-resources ![minimal](command/minimal.svg) ^D
    minimal
    ``` ================================================ FILE: test/command/8956.md ================================================ ``` % pandoc -iNtslidy # One - one - two ^D

    1 One

    • one
    • two
    ``` ```` % pandoc -pstnative ``` after tab ``` ^D Pandoc Meta { unMeta = fromList [] } [ CodeBlock ( "" , [] , [] ) "\tafter tab" ] ```` ``` % pandoc -sfalse hi ^D

    hi

    ``` ``` % pandoc --standalone=false hi ^D

    hi

    ``` ``` % pandoc --standalone=FALSE hi ^D

    hi

    ``` ================================================ FILE: test/command/8957.md ================================================ ``` % pandoc -f latex -t html --wrap=none ![Sample figure caption.](fig.jpg){width="342pt" height="9pc"} ^D

    ![Sample figure caption.](fig.jpg)width="342pt" height="9pc"

    ``` ================================================ FILE: test/command/8966.md ================================================ ``` % pandoc -t typst **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 /\/ International Journal of Geographical Information Science. --- 2017. --- Vol. 31. --- № 8. --- pp.~1485-1520. ``` ================================================ FILE: test/command/8981.md ================================================ ``` % pandoc --wrap=none consectetur [^\[link\]^](#ref "Title") ^D

    consectetur [link]

    ``` ================================================ FILE: test/command/8984.md ================================================ ``` % pandoc -f html -t native
    a b
    c d e
    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 2) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f html -t native
    Heading content
    content2
    ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 1) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "Heading" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "content" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "content2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/8992.md ================================================ ``` % pandoc -t latex [A theorem](https://en.wikipedia.org/wiki/Rice's_theorem). ^D \href{https://en.wikipedia.org/wiki/Rice's_theorem}{A theorem}. ``` ================================================ FILE: test/command/8997.md ================================================ ``` % pandoc -f org -t native * has space after second colon : aaa : : bbb * has no space after second colon : aaa : : bbb ^D [ Header 1 ( "has-space-after-second-colon" , [] , [] ) [ Str "has" , Space , Str "space" , Space , Str "after" , Space , Str "second" , Space , Str "colon" ] , CodeBlock ( "" , [] , [] ) "aaa\n\nbbb\n" , Header 1 ( "has-no-space-after-second-colon" , [] , [] ) [ Str "has" , Space , Str "no" , Space , Str "space" , Space , Str "after" , Space , Str "second" , Space , Str "colon" ] , CodeBlock ( "" , [] , [] ) "aaa\n\nbbb\n" ] ``` ``` % pandoc -f org -t native * only the colon : * only the colon and a space : ^D [ Header 1 ( "only-the-colon" , [] , [] ) [ Str "only" , Space , Str "the" , Space , Str "colon" ] , CodeBlock ( "" , [] , [] ) "\n" , Header 1 ( "only-the-colon-and-a-space" , [] , [] ) [ Str "only" , Space , Str "the" , Space , Str "colon" , Space , Str "and" , Space , Str "a" , Space , Str "space" ] , CodeBlock ( "" , [] , [] ) "\n" ] ``` ================================================ FILE: test/command/9000.md ================================================ ``` % pandoc -t org --wrap=auto --columns=20 [This is a link with a long description that should not wrap](http://example.com) and some regular text after it so we can see how wrapping works. ^D [[http://example.com][This is a link with a long description that should not wrap]] and some regular text after it so we can see how wrapping works. ``` ================================================ FILE: test/command/9002.md ================================================ ``` % pandoc command/9002.docx -t html ^D

    This is my table!

    a b

    See Table 1 This is my table!

    ``` ================================================ FILE: test/command/9017.md ================================================ ``` % pandoc -t jats --citeproc -s --- references: - author: DeGroot id: degroot_probability title: Probability issued: 2002 title: Doc example with single reference --- # I like [@degroot_probability]. ^D
    Doc example with single reference I like

    (DeGroot 2002).

    DeGroot. 2002. Probability.
    ``` ================================================ FILE: test/command/9021.md ================================================ ``` % pandoc -t jats+element_citations --citeproc -s --- references: - author: DeGroot id: degroot_probability title: Probability issued: 2002 title: Doc example with single reference reference-section-title: Ref Title --- # I like [@degroot_probability]. ^D
    Doc example with single reference I like

    (DeGroot 2002).

    Ref Title DeGroot Probability 2002
    ``` ================================================ FILE: test/command/9038.md ================================================ ``` % pandoc -t native ![foo] ^D [ Para [ Str "![foo]" ] ] ``` ================================================ FILE: test/command/9042.md ================================================ ``` % pandoc -f org #+TITLE: Testing * Testing p. lower case P. Upper Case ^D

    Testing

    p. lower case

    P. Upper Case

    ``` ``` % pandoc -f org+fancy_lists #+TITLE: Testing * Testing p. lower case P. Upper Case ^D

    Testing

    1. lower case

    2. Upper Case

    ``` ================================================ FILE: test/command/9043.md ================================================ ``` % pandoc -t latex [**TreeTagger**](https://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/) ^D \href{https://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/}{\textbf{TreeTagger}} ``` ``` % pandoc -t latex [**TreeTagger**](https://www.cis.uni-muenchen.de/%E7schmid/tools/TreeTagger/) ^D \href{https://www.cis.uni-muenchen.de/\%E7schmid/tools/TreeTagger/}{\textbf{TreeTagger}} ``` ================================================ FILE: test/command/9045.md ================================================ ``` % pandoc -t latex ![hi](there.jpg){#foo} ^D \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,alt={hi}]{there.jpg}} \caption{hi}\label{foo} \end{figure} ``` ================================================ FILE: test/command/9047.md ================================================ tests meant to test the fixes of bug [#9047](https://github.com/jgm/pandoc/issues/9047) ``` % pandoc -f html -t html
    ^D
    • foobar
    ``` w/ rawHTML extension a checkbox by itself is kept ``` % pandoc -f html+raw_html -t html+raw_html ^D ``` w/ rawHTML extension, a checkbox in an `li` is handled properly ``` % pandoc -f html+raw_html -t html+raw_html
    • foo
    ^D
    ``` w/o rawHTML extension, a checkbox outside of an `li` is properly ignored ``` % pandoc -f html -t html

    foo

    ^D foo ``` ================================================ FILE: test/command/9088.md ================================================ ``` % pandoc -t latex | 0 - A | 1 - B | 2 - C ^D ~~~0 - A\\ \strut ~~~1 - B\\ \strut ~~~2 - C ``` ================================================ FILE: test/command/9090.md ================================================ ``` % pandoc -f html -t gfm
    invalid head cell
    body cell
    ^D | invalid head cell | |-------------------| | body cell | ``` ================================================ FILE: test/command/9121.md ================================================ ```` % pandoc -t gfm a $$x=y$$ b ^D a ``` math x=y ``` b ```` ```` % pandoc -t gfm a $x=y$ b ^D a $`x=y`$ b ```` ================================================ FILE: test/command/9150.md ================================================ ``` % pandoc -f latex -t native \hfill {\Large \textbf{Theoretische Grundlagen der Informatik}} ^D [ Para [ Span ( "" , [] , [] ) [ Strong [ Str "Theoretische" , Space , Str "Grundlagen" , Space , Str "der" , Space , Str "Informatik" ] ] ] ] ``` ``` % pandoc -f latex+raw_tex -t native \hfill {\Large \textbf{Theoretische Grundlagen der Informatik}} ^D [ Para [ RawInline (Format "latex") "\\hfill " , Span ( "" , [] , [] ) [ RawInline (Format "latex") "\\Large " , Strong [ Str "Theoretische" , Space , Str "Grundlagen" , Space , Str "der" , Space , Str "Informatik" ] ] ] ] ``` ``` % pandoc -f latex+raw_tex -t native \hskip 2pt plus 1pt minus 1pt ^D [ RawBlock (Format "latex") "\\hskip 2pt plus 1pt minus 1pt" ] ``` ================================================ FILE: test/command/9159.md ================================================ ``` % pandoc -t org -f markdown \* See Blah \# not comment \| not table \| ^D ​* See Blah ​# not comment ​| not table | ``` ================================================ FILE: test/command/9171.md ================================================ ``` % pandoc -f markdown -t native [link][link contents]{target="_blank"} [link2](https://example.com){target="_blank"} [link contents]: https://example.com ^D [ Para [ Link ( "" , [] , [ ( "target" , "_blank" ) ] ) [ Str "link" ] ( "https://example.com" , "" ) , SoftBreak , Link ( "" , [] , [ ( "target" , "_blank" ) ] ) [ Str "link2" ] ( "https://example.com" , "" ) ] ] ``` ``` % pandoc -f markdown -t native [link][link contents]{#id1 target="_blank"} [link2](https://example.com){target="_blank"} [link contents]: https://example.com {#id2 target="_nonblank"} ^D [ Para [ Link ( "id1" , [] , [ ( "target" , "_blank" ) ] ) [ Str "link" ] ( "https://example.com" , "" ) , SoftBreak , Link ( "" , [] , [ ( "target" , "_blank" ) ] ) [ Str "link2" ] ( "https://example.com" , "" ) ] ] ``` ``` % pandoc -f markdown -t native [link][link contents]{target="_blank"} [link2](https://example.com){target="_blank"} [link contents]: https://example.com {.foo} ^D [ Para [ Link ( "" , [ "foo" ] , [ ( "target" , "_blank" ) ] ) [ Str "link" ] ( "https://example.com" , "" ) , SoftBreak , Link ( "" , [] , [ ( "target" , "_blank" ) ] ) [ Str "link2" ] ( "https://example.com" , "" ) ] ] ``` ================================================ FILE: test/command/9193.md ================================================ ``` % pandoc -t native $a+% b$ ^D [ Para [ Math InlineMath "a+%\nb" ] ] ``` ================================================ FILE: test/command/9196.md ================================================ ``` % pandoc -f rst --wrap=none Define _`my target`, then reference `my target`_. ^D

    Define my target, then reference my target.

    ``` ================================================ FILE: test/command/9201.md ================================================ ``` % pandoc -f man -t markdown .TH "EXAMPLE" "1" .SH "OPTIONS" .TP 8n \fB\-\-help\fR This is line one. .sp This is line two. ^D # OPTIONS **\--help** : This is line one. This is line two. ``` ================================================ FILE: test/command/9202.md ================================================ ``` % pandoc -f latex -t native \begin{otherlanguage}{french} Bonjour. \end{otherlanguage} ^D [ Div ( "" , [] , [ ( "lang" , "fr" ) ] ) [ Para [ Str "Bonjour." ] ] ] ``` ``` % pandoc -f latex -t native \begin{otherlanguage*}{french} Bonjour. \end{otherlanguage*} ^D [ Div ( "" , [ "otherlanguage*" ] , [] ) [ Para [ Span ( "" , [] , [] ) [ Str "french" ] , SoftBreak , Str "Bonjour." ] ] ] ``` ``` % pandoc -f latex -t native \textfrench{Bonjour} ^D [ Para [ Span ( "" , [] , [ ( "lang" , "fr" ) ] ) [ Str "Bonjour" ] ] ] ``` ``` % pandoc -f latex -t native \foreignlanguage{swissgerman}{Guten tag} ^D [ Para [ Span ( "" , [] , [ ( "lang" , "de-CH-1901" ) ] ) [ Str "Guten" , Space , Str "tag" ] ] ] ``` ``` % pandoc -f latex -t native \begin{french} Bonjour. \end{french} ^D [ Div ( "" , [] , [ ( "lang" , "fr" ) ] ) [ Para [ Str "Bonjour." ] ] ] ``` ================================================ FILE: test/command/9209.md ================================================ ``` % pandoc -t latex ## [Section]{#Section3} ^D \subsection{\texorpdfstring{\protect\hypertarget{Section3}{}{Section}}{Section}}\label{section} ``` ================================================ FILE: test/command/9218.md ================================================ # Escape special lines in Org-mode code blocks ```` % pandoc -f markdown -t org ``` org ** Click Application *** Imports #+begin_src py from addict import Dict #+end_src ``` ^D #+begin_src org ,** Click Application ,*** Imports ,#+begin_src py from addict import Dict ,#+end_src #+end_src ```` ================================================ FILE: test/command/9236.md ================================================ ``` % pandoc -t typst ![minimal](command/minimal.svg){width=3in} ![minimal](command/minimal.svg){width=3in height=2in} ![minimal](command/minimal.svg) And inline: ![minimal](command/minimal.svg){height=2in} and ![minimal](command/minimal.svg). ^D #figure(image("command/minimal.svg", width: 3in, alt: "minimal"), caption: [ minimal ] ) #figure(image("command/minimal.svg", height: 2in, width: 3in, alt: "minimal"), caption: [ minimal ] ) #figure(image("command/minimal.svg", alt: "minimal"), caption: [ minimal ] ) And inline: #box(image("command/minimal.svg", height: 2in, alt: "minimal")) and #box(image("command/minimal.svg", alt: "minimal")). ``` ================================================ FILE: test/command/9275.md ================================================ ``` % pandoc -t latex --biblatex [@scott2000, p. 33] [@scott2000, pp. 33-34 and elsewhere; @scott2001, ch. 4] ^D \autocite[33]{scott2000} \autocites[33-34 and elsewhere]{scott2000}[ch.~4]{scott2001} ``` ``` % pandoc -t latex --biblatex -Mlang=de [@scott2000, p. 33] [@scott2000, S. 33] ^D \autocite[p.~33]{scott2000} \autocite[33]{scott2000} ``` ================================================ FILE: test/command/9279.md ================================================ ``` % pandoc -f org -t native #+LABEL: tbl:Cosasexample #+CAPTION: Cosas +-------+-------+ | cosas | cosas | | cosas | cosas | +=======+=======+ | cosas | cosas | | cosas | cosas | +-------+-------+ ^D [ Table ( "tbl:Cosasexample" , [] , [] ) (Caption Nothing [ Plain [ Str "Cosas" ] ]) [ ( AlignDefault , ColWidth 0.1111111111111111 ) , ( AlignDefault , ColWidth 0.1111111111111111 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "cosas" , SoftBreak , Str "cosas" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "cosas" , SoftBreak , Str "cosas" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "cosas" , SoftBreak , Str "cosas" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "cosas" , SoftBreak , Str "cosas" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f org -t markdown #+LABEL: tbl:Cosasexample #+CAPTION: Cosas +-------+-------+ | cosas | cosas | | cosas | cosas | +=======+=======+ | cosas | cosas | | cosas | cosas | +-------+-------+ ^D --------------- cosas cosas cosas cosas ------- ------- cosas cosas cosas cosas --------------- : Cosas {#tbl:Cosasexample} ``` ``` % pandoc -f org -t markdown #+LABEL: tbl:Cosasexample +-------+-------+ | cosas | cosas | | cosas | cosas | +=======+=======+ | cosas | cosas | | cosas | cosas | +-------+-------+ ^D --------------- cosas cosas cosas cosas ------- ------- cosas cosas cosas cosas --------------- ``` ``` % pandoc -f org -t markdown #+CAPTION: Cosas +-------+-------+ | cosas | cosas | | cosas | cosas | +=======+=======+ | cosas | cosas | | cosas | cosas | +-------+-------+ ^D --------------- cosas cosas cosas cosas ------- ------- cosas cosas cosas cosas --------------- : Cosas ``` ================================================ FILE: test/command/9293.md ================================================ ``` % pandoc -f mediawiki -t native * Linearity in the first argument: *:\begin{align} \langle a \mathbf u, \mathbf v\rangle &= a \langle \mathbf u, \mathbf v\rangle. \\ \langle \mathbf u + \mathbf v, \mathbf w\rangle &= \langle \mathbf u, \mathbf w\rangle+ \langle \mathbf v, \mathbf w\rangle. \end{align} ^D [ BulletList [ [ Plain [ Str "Linearity" , Space , Str "in" , Space , Str "the" , Space , Str "first" , Space , Str "argument:" ] , Para [ Math DisplayMath "\\begin{align}\n\\langle a \\mathbf u, \\mathbf v\\rangle &= a \\langle \\mathbf u, \\mathbf v\\rangle. \\\\\n\\langle \\mathbf u + \\mathbf v, \\mathbf w\\rangle &= \\langle \\mathbf u, \\mathbf w\\rangle+ \\langle \\mathbf v, \\mathbf w\\rangle.\n\\end{align}" ] ] ] ] ``` ================================================ FILE: test/command/934.md ================================================ ``` % pandoc -f latex -t native \newcommand{\ddb}[2]{ \textit{``#1''} \textbf{#2} } \ddb{This should be italic and in quotes}{And this is the attribution} ^D [ Para [ Emph [ Quoted DoubleQuote [ Str "This" , Space , Str "should" , Space , Str "be" , Space , Str "italic" , Space , Str "and" , Space , Str "in" , Space , Str "quotes" ] ] ] , Para [ Strong [ Str "And" , Space , Str "this" , Space , Str "is" , Space , Str "the" , Space , Str "attribution" ] ] ] ``` ================================================ FILE: test/command/9346.md ================================================ ``` % pandoc -f markdown -t markdown_strict+pipe_tables |V1 |V2 |V3 |V4 |V5 |V6 |V7 |V8 |V9 |V10 |V11 |V12 |V13 |V14 |V15 |V16 |V17 |V18 |V19 |V20 |V21 |V22 |V23 |V24 |V25 |V26 |V27 |V28 |V29 |V30 |V31 |V32 |V33 |V34 |V35 |V36 |V37 |V38 |V39 |V40 |V41 |V42 |V43 |V44 |V45 |V46 |V47 |V48 |V49 |V50 |V51 |V52 |V53 |V54 |V55 |V56 |V57 |V58 |V59 |V60 |V61 |V62 |V63 |V64 |V65 |V66 |V67 |V68 |V69 |V70 |V71 |V72 | |:--|:--|:--|:--|:--|:--|:--|:--|:--|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---|:---| ^D | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 | V11 | V12 | V13 | V14 | V15 | V16 | V17 | V18 | V19 | V20 | V21 | V22 | V23 | V24 | V25 | V26 | V27 | V28 | V29 | V30 | V31 | V32 | V33 | V34 | V35 | V36 | V37 | V38 | V39 | V40 | V41 | V42 | V43 | V44 | V45 | V46 | V47 | V48 | V49 | V50 | V51 | V52 | V53 | V54 | V55 | V56 | V57 | V58 | V59 | V60 | V61 | V62 | V63 | V64 | V65 | V66 | V67 | V68 | V69 | V70 | V71 | V72 | |:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-| ``` ================================================ FILE: test/command/9350.md ================================================ ``` % pandoc -f html -t latex
    ^D {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}l@{}} \toprule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \\ \end{longtable} } ``` ================================================ FILE: test/command/9358.md ================================================ Caption belongs to the second table: ``` % pandoc -f docx -t html command/9358.docx ^D

    Lorem gipsum

    A B
    C D

    Lorem ipsum

    Table 1 Numbers from 1 to 4

    1 2
    3 4

    Lorem yipsum

    ``` ================================================ FILE: test/command/9366.md ================================================ ``` % pandoc -f native -t latex [ Para [ Strikeout [ Link ( "" , [] , [] ) [ Str "Example" ] ( "https://example.com" , "" ) ] ] , Para [ Strikeout [ Link ( "" , [] , [] ) [ Str "https://example.com" ] ( "https://example.com" , "" ) ] ] , Para [ Strikeout [ Link ( "" , [] , [] ) [ Str "info@example.com" ] ( "mailto:info@example.com" , "" ) ] ] ] ^D \st{\mbox{\href{https://example.com}{Example}}} \st{\mbox{\url{https://example.com}}} \st{\mbox{\href{mailto:info@example.com}{\nolinkurl{info@example.com}}}} ``` ================================================ FILE: test/command/9371.md ================================================ ``` % pandoc -t latex "'In this case (A), I get a kern to separate the quotes,' he said." "'Also in this case (B), I get a kern,' he added." "'But in this case (C), there is no kern,' he continued." "He concluded, 'Nor in this case (D) is there a kern.'" ^D ``\,'In this case (A), I get a kern to separate the quotes,' he said.'' ``\,`Also in this case (B), I get a kern,' he added.'' ``\,`But in this case (C), there is no kern,' he continued.'' ``He concluded, `Nor in this case (D) is there a kern.'\,'' ``` ================================================ FILE: test/command/9386.md ================================================ ``` % pandoc -t typst --wrap=preserve A string of text that is long enough that after 73 chars that includes a / slash creates a newline ^D A string of text that is long enough that after 73 chars that includes a \/ slash creates a newline ``` ``` % pandoc -t typst --wrap=preserve A string of text that is long enough that after 73 chars that includes a / slash creates a newline ^D A string of text that is long enough that after 73 chars that includes a / slash creates a newline ``` ================================================ FILE: test/command/9387.md ================================================ ``` % pandoc -f html -t typst

    test

    See my label.

    ^D #block[ test ] #label("my label") See #link(label("my label"))[my label]. ``` ``` % pandoc -f latex -t typst \cite{10.1117/12.695309} \cite{foo} ^D #cite(label("10.1117/12.695309")) @foo ``` ================================================ FILE: test/command/9388.md ================================================ ``` % pandoc -f latex -t native \begin{figure} \centering \includegraphics[width=0.5\linewidth,height=0.5\textheight]{figures/placeholder} \caption{Example figure} \end{figure} ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Example" , Space , Str "figure" ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "50%" ) , ( "height" , "50%" ) ] ) [] ( "figures/placeholder" , "" ) ] ] ] ``` ================================================ FILE: test/command/9391.md ================================================ ``` % pandoc command/9391.docx -t html ^D

    Lorem ipsum.

    Blue square.

    ``` ================================================ FILE: test/command/9420.md ================================================ ``` % pandoc --embed-resources ![](command/9420.svg){.inline-svg} ^D

    ``` ================================================ FILE: test/command/9445.md ================================================ ``` % pandoc -f man -t markdown \fB--c \fI ^D **\--c** *\* ``` ================================================ FILE: test/command/9452.md ================================================ ``` % pandoc -t typst @something2024 says blah. Here is a sentence [@something2024]. With supplement [@something2024, p. 3]. And just the year [-@something2024]. ^D #cite(, form: "prose") says blah. Here is a sentence @something2024. With supplement @something2024[p.~3]. And just the year (#cite(, form: "year")). ``` ================================================ FILE: test/command/9467.md ================================================ ``` % pandoc --embed-resources ![](command/9467.svg) ^D

    ``` ================================================ FILE: test/command/9472.md ================================================ ``` % pandoc -t latex -s --- lang: de-DE --- More text in English. ['Zitat auf Deutsch.']{lang=de} [Bonjour]{lang=fr} [café]{lang="fr-FR"} ^D % Options for packages loaded elsewhere \PassOptionsToPackage{unicode}{hyperref} \PassOptionsToPackage{hyphens}{url} \documentclass[ french, 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} More text in English. \foreignlanguage{ngerman}{`Zitat auf Deutsch.'} \foreignlanguage{french}{Bonjour} \foreignlanguage{french}{café} \end{document} ``` ================================================ FILE: test/command/9475.md ================================================ ``` % pandoc -f org -t native #+begin_note Useful note. #+end_note #+begin_warning Be careful! #+end_warning #+begin_tip Try this... #+end_tip #+begin_caution Caution #+end_caution #+name: foo #+begin_important Important #+end_important ^D [ Div ( "" , [ "note" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Note" ] ] , Para [ Str "Useful" , Space , Str "note." ] ] , Div ( "" , [ "warning" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Warning" ] ] , Para [ Str "Be" , Space , Str "careful!" ] ] , Div ( "" , [ "tip" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Tip" ] ] , Para [ Str "Try" , Space , Str "this\8230" ] ] , Div ( "" , [ "caution" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Caution" ] ] , Para [ Str "Caution" ] ] , Div ( "foo" , [ "important" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Important" ] ] , Para [ Str "Important" ] ] ] ``` ``` % pandoc -f native -t org [ Div ( "" , [ "note" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Note" ] ] , Para [ Str "Useful" , Space , Str "note." ] ] , Div ( "" , [ "warning" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Warning" ] ] , Para [ Str "Be" , Space , Str "careful!" ] ] , Div ( "" , [ "tip" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Tip" ] ] , Para [ Str "Try" , Space , Str "this\8230" ] ] , Div ( "" , [ "caution" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Caution" ] ] , Para [ Str "Caution" ] ] , Div ( "foo" , [ "important" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Important" ] ] , Para [ Str "Important" ] ] ] ^D #+begin_note Useful note. #+end_note #+begin_warning Be careful! #+end_warning #+begin_tip Try this... #+end_tip #+begin_caution Caution #+end_caution #+name: foo #+begin_important Important #+end_important ``` ================================================ FILE: test/command/9478.md ================================================ ``` % pandoc -t typst --wrap=preserve **- a - - b** ^D #strong[\- a - \- b] ``` ================================================ FILE: test/command/9481.md ================================================ ``` % pandoc -f markdown+wikilinks_title_after_pipe [a](https://example.com) ^D

    a

    ``` ================================================ FILE: test/command/9516.md ================================================ ``` % pandoc --number-sections # One {-} # Two # Three {-} # Four ^D

    One

    1 Two

    Three

    2 Four

    ``` ================================================ FILE: test/command/9517.md ================================================ ``` % pandoc -f latex+raw_tex -t native \begin{tabular}{l} \begin{theorem} Cats \end{theorem} \end{tabular} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ RawBlock (Format "latex") "\\begin{theorem}\nCats\n\\end{theorem}" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/9555.md ================================================ ``` % pandoc -t native -f latex a\@ b\@c \makeatletter a\@ b\@c \makeatother a\@ b\@c ^D [ Para [ Str "a" , Space , Str "bc" ] , Para [ Str "ab" , SoftBreak , Str "a" , Space , Str "bc" ] ] ``` ================================================ FILE: test/command/9569.md ================================================ ``` % pandoc -f docbook -t gfm Some content Spacer. A note with a title. Works, but the title is discarded. Some content ^D > [!NOTE] > Some content Spacer. > [!NOTE] > Some content ``` ================================================ FILE: test/command/9576.md ================================================ ``` % pandoc -t native ::: {#something} Text with a footnote.[^3] [^3]: A footnote. ::: ^D [ Div ( "something" , [] , [] ) [ Para [ Str "Text" , Space , Str "with" , Space , Str "a" , Space , Str "footnote." , Note [ Para [ Str "A" , Space , Str "footnote." ] ] ] ] ] ``` ================================================ FILE: test/command/9579.md ================================================ ``` % pandoc -t native \begin{tabular}{p{2in}} \end{tabular} # Test \begin{tabular}{p{2\linewidth}} \end{tabular} ^D [ RawBlock (Format "tex") "\\begin{tabular}{p{2in}}\n\\end{tabular}" , Header 1 ( "test" , [] , [] ) [ Str "Test" ] , RawBlock (Format "tex") "\\begin{tabular}{p{2\\linewidth}}\n\\end{tabular}" ] ``` ``` % pandoc -f latex -t native \begin{tabular}{p{2in}} \end{tabular} Test \begin{tabular}{p{2\linewidth}} \end{tabular} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Test" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidth 2.0 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/9585.md ================================================ ``` % pandoc -f native -t typst [ Header 2 ( "" , [] , [] ) [ Str "One" ] , Header 2 ( "" , [ "unnumbered", "unlisted" ] , [] ) [ Str "Two" ] , Header 2 ( "" , [] , [] ) [ Str "Three" ] ] ^D == One #heading(level: 2, outlined: false, numbering: none)[Two] == Three ``` ================================================ FILE: test/command/9586.md ================================================ ``` % pandoc -t typst **1. April 2024** ^D #strong[\1. April 2024] ``` ================================================ FILE: test/command/9597.md ================================================ ``` % pandoc -t latex ~~$T$~~ ^D \st{$T$} ``` ================================================ FILE: test/command/9603.md ================================================ ``` % pandoc command/9603.docx -t html -f docx+styles ^D

    A table with a contemporary style:

    Test Column
    1 2
    ``` ================================================ FILE: test/command/9616.md ================================================ ``` % pandoc -f bibtex -t csljson -s @MISC {3252037, TITLE = {Isomorphism between projective varieties $\mathbf{P}^{1}$ and a conic in $\mathbf{P}^{2}$}, URL = {https://math.stackexchange.com/q/3252037} } ^D [ { "URL": "https://math.stackexchange.com/q/3252037", "id": "3252037", "title": "Isomorphism between projective varieties $\\mathbf{P}^{1}$ and a conic in $\\mathbf{P}^{2}$", "type": "" } ] ``` ================================================ FILE: test/command/9630.md ================================================ ``` % pandoc -f dokuwiki * [[https://example.com|https://example.com]] * [[https://example.com/start#monitoring|https://example.com/start#monitoring]] ^D ``` ================================================ FILE: test/command/9632.md ================================================ ``` % pandoc -f dokuwiki [[.:supprimer-profil-windows|]] ^D

    supprimer-profil-windows

    ``` ================================================ FILE: test/command/9635.md ================================================ ``` % pandoc > ::: {.fence} > that is > not closed okay ^D 2> [WARNING] Div at _chunk line 1 column 1 unclosed at _chunk line 5 column 1, closing implicitly.

    that is not closed

    okay

    ``` ================================================ FILE: test/command/9639.md ================================================ ``` % pandoc -f org -t native #+title: repro * heading-name :PROPERTIES: :ID: 123abc :END: ^D [ Header 1 ( "123abc" , [] , [] ) [ Str "heading-name" ] ] ``` ================================================ FILE: test/command/9644.md ================================================ ``` % pandoc -t latex [$T$]{.underline} [$T$]{.mark} ^D \ul{$T$} \hl{$T$} ``` ================================================ FILE: test/command/9652.md ================================================ ```` % pandoc -f markdown -t html --embed-resources ```{=html} ``` ^D ```` ================================================ FILE: test/command/9657.md ================================================ ``` % pandoc -t opendocument [Text here]{custom-style="Strikethrough"} ^D Text here ``` ================================================ FILE: test/command/9676.md ================================================ ``` % pandoc -f latex \def\foo{BAR} \foo \ifstrequal{BAR}{BAR}{\def\foo{BAZ}}{} \foo ^D

    BAR BAZ

    ``` ================================================ FILE: test/command/9700.md ================================================ ``` % pandoc -t mediawiki This is a normal sentence with a manual text footnote\* \* The footnote explains why it couldn't just be inside parentheses \#foobar \#foobar ^D This is a normal sentence with a manual text footnote* \* The footnote explains why it couldn’t just be inside parentheses \#foobar #foobar ``` ================================================ FILE: test/command/9716.md ================================================ ``` % pandoc -f markdown+alerts -t native > [!TIP] > A suggestion. ^D [ Div ( "" , [ "tip" ] , [] ) [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Tip" ] ] , Para [ Str "A" , Space , Str "suggestion." ] ] ] ``` ================================================ FILE: test/command/9777-b.md ================================================ ``` % pandoc -f native -t typst [ Table ( "" , [] , [ ( "typst:figure:kind" , "figure" ) ] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ^D #figure( align(center)[#table( columns: 2, align: (auto,auto,), table.header([f], [b],), table.hline(), [1], [2], )] , kind: figure ) ``` ================================================ FILE: test/command/9777.md ================================================ ``` % pandoc -f native -t typst [ Table ( "" , [ "typst:no-figure" ] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ^D #table( columns: 2, align: (auto,auto,), table.header([f], [b],), table.hline(), [1], [2], ) ``` ================================================ FILE: test/command/9792.md ================================================ ``` % pandoc -t gfm - list item
    - subitem
    item _continue_ **with** formatting - next list item ^D - list item
    - subitem
    item *continue* **with** formatting - next list item ``` ================================================ FILE: test/command/9797.md ================================================ ``` % pandoc -t native -f csv "one_line_break: four_line_breaks: last_line" ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "one_line_break:" , LineBreak , Str "four_line_breaks:" , LineBreak , LineBreak , LineBreak , LineBreak , Str "last_line" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/9805.md ================================================ ``` % pandoc -f rst -t native word.*word ------------ * abc ^D [ Header 1 ( "word.word" , [] , [] ) [ Str "word.*word" ] , BulletList [ [ Plain [ Str "abc" ] ] ] ] ``` ================================================ FILE: test/command/9807.md ================================================ ``` % pandoc -f typst -t native #let foo = [ bar baz ] *#foo* ^D [ Para [ Strong [ SoftBreak , Str "bar" , Space , Str "baz" , SoftBreak ] ] ] ``` ================================================ FILE: test/command/9809.md ================================================ ``` % pandoc -f html -t native
    • Bullet point.
    • Nested line.

    ^D [ BulletList [ [ Para [ Str "Bullet" , Space , Str "point." ] , Para [ Str "Nested" , Space , Str "line." ] ] ] ] ``` ================================================ FILE: test/command/982.md ================================================ ``` % pandoc -f latex -t native \newcommand{\BEQ}{\begin{equation}} \newcommand{\EEQ}{\end{equation}} \BEQ y=x^2 \EEQ ^D [ Para [ Math DisplayMath "\\begin{equation}\ny=x^2\n\\end{equation}" ] ] ``` ================================================ FILE: test/command/9865.md ================================================ ```` % pandoc - example 1 - ``` one two ``` - list item three ^D
    • example 1
    • one
      two
    • list item three
    ```` ```` % pandoc - example 3 - ``` one two ``` - list item three ^D
    • example 3
    • one
      two
    • list item three
    ```` Here is a case that we used to handle differently, but #9865 aligns pandoc's markdown parser with commonmark in letting block level structure take precedence over inline level structure. ```` % pandoc - a - c ^D
    • a <!–

    • b

    –> - c

    ```` ================================================ FILE: test/command/987.md ================================================ ``` % pandoc -f latex -t markdown \documentclass{article} \newenvironment{flubble}{FOO}{BAR} \begin{document} \begin{flubble} grr \end{flubble} \end{document} ^D FOO grr BAR ``` ================================================ FILE: test/command/9878.md ================================================ ``` % pandoc -f textile -t native 15% 50% ^D [ Para [ Str "15%" , Space , Str "50%" ] ] ``` ``` % pandoc -f textile -t native 15%. 70% ^D [ Para [ Str "15%." , Space , Str "70%" ] ] ``` ================================================ FILE: test/command/9902.md ================================================ ``` % pandoc -t native -f latex+raw_tex \kern.1pt ^D [ RawBlock (Format "latex") "\\kern.1pt" ] ``` ================================================ FILE: test/command/9904.md ================================================ ``` % pandoc -f rst -t native --citeproc --bibliography command/biblio.bib The axioms were introduced by :cite:t:`{see}item1`. The axioms were introduced by :cite:t:`item1{p. 1166}`. The axioms were introduced by :cite:t:`{see}item1{p. 1166}`. Axioms were introduced :cite:`{see}item1`. Axioms were introduced :cite:p:`item1{p. 1166}`. Axioms were introduced :cite:p:`{see}item1{p. 1166}`. .. bibliography:: ^D [ Para [ Str "The" , Space , Str "axioms" , Space , Str "were" , Space , Str "introduced" , Space , Str "by" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [ Str "see" ] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(see" , Space , Str "Doe;" , Space , Str "2005)" ] , Str "." , SoftBreak , Str "The" , Space , Str "axioms" , Space , Str "were" , Space , Str "introduced" , Space , Str "by" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [ Str "p." , Space , Str "1166" ] , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } ] [ Str "Doe" , Space , Str "(2005," , Space , Str "1166)" ] , Str "." , SoftBreak , Str "The" , Space , Str "axioms" , Space , Str "were" , Space , Str "introduced" , Space , Str "by" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [ Str "see" ] , citationSuffix = [ Str "p." , Space , Str "1166" ] , citationMode = AuthorInText , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(see" , Space , Str "Doe;" , Space , Str "2005," , Space , Str "1166)" ] , Str "." , SoftBreak , Str "Axioms" , Space , Str "were" , Space , Str "introduced" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [ Str "see" ] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(see" , Space , Str "Doe" , Space , Str "2005)" ] , Str "." , SoftBreak , Str "Axioms" , Space , Str "were" , Space , Str "introduced" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [ Str "p." , Space , Str "1166" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(Doe" , Space , Str "2005," , Space , Str "1166)" ] , Str "." , SoftBreak , Str "Axioms" , Space , Str "were" , Space , Str "introduced" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [ Str "see" ] , citationSuffix = [ Str "p." , Space , Str "1166" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(see" , Space , Str "Doe" , Space , Str "2005," , Space , Str "1166)" ] , Str "." ] , Div ( "refs" , [ "references" , "csl-bib-body" , "hanging-indent" ] , [] ) [ Div ( "ref-item1" , [ "csl-entry" ] , [] ) [ Para [ Str "Doe," , Space , Str "John." , Space , Str "2005." , Space , Emph [ Str "First" , Space , Str "Book" ] , Str "." , Space , Str "Cambridge" , Space , Str "University" , Space , Str "Press." ] ] ] ] ``` ================================================ FILE: test/command/9905.md ================================================ ``` % pandoc -t html5 # Section Ψ {#Ψ} look at [Section Ψ](#Ψ). ^D

    Section Ψ

    look at Section Ψ.

    ``` ``` % pandoc -t html4 # Section Ψ {#Ψ} look at [Section Ψ](#Ψ). ^D

    Section Ψ

    look at Section Ψ.

    ``` ================================================ FILE: test/command/9908.md ================================================ ``` % pandoc -f native -t markdown [ BulletList [ [ BlockQuote [ Para [ Str "a" ] , Para [ Str "b" ] ] ] ] ] ^D - > a > > b ``` ================================================ FILE: test/command/9943.md ================================================ ``` % pandoc -t man -s -Vpandoc-version=X.Y --- title: HELLO section: 1 --- ^D .\" Automatically generated by Pandoc X.Y .\" .TH "HELLO" "1" "" "" ``` ``` % pandoc -t man -s -Vpandoc-version=X.Y --- title: HELLO section: 1 header: Head --- ^D .\" Automatically generated by Pandoc X.Y .\" .TH "HELLO" "1" "" "" "Head" ``` ================================================ FILE: test/command/9945.md ================================================ ``` % pandoc -t typst --dpi 300 ![](image.jpg){width=300 height=300} ^D #box(image("image.jpg", height: 1in, width: 1in)) ``` ``` % pandoc -t typst --dpi 600 ![](image.jpg){width=300px height=300px} ^D #box(image("image.jpg", height: 0.5in, width: 0.5in)) ``` ``` % pandoc -t typst --dpi 600 ![](image.jpg){width=1in height=3cm} ^D #box(image("image.jpg", height: 3cm, width: 1in)) ``` ``` % pandoc -t typst --dpi 600 ![](image.jpg){.foo .bar baz=3} ^D #box(image("image.jpg")) ``` ================================================ FILE: test/command/9953.md ================================================ ``` % pandoc -f markdown -t native -s --- header-includes: | \makeatletter \let\old@verbatim@font=\verbatim@font \def\verbatim@font{% \fontsize{10}{12}% \old@verbatim@font } \makeatother ... ^D Pandoc Meta { unMeta = fromList [ ( "header-includes" , MetaBlocks [ RawBlock (Format "tex") "\\makeatletter\n\\let\\old@verbatim@font=\\verbatim@font\n\\def\\verbatim@font{%\n\\fontsize{10}{12}%\n\\old@verbatim@font\n}\n\\makeatother" ] ) ] } [] ``` ================================================ FILE: test/command/9987.md ================================================ ``` % pandoc -f bibtex -t bibtex @misc{agency, author = {{European Environment Agency}}, } ^D @misc{agency, author = {{European Environment Agency}} } ``` ================================================ FILE: test/command/A.txt ================================================ this is a ================================================ FILE: test/command/B.txt ================================================ here is b ================================================ FILE: test/command/C.txt ================================================ and this is c ================================================ FILE: test/command/D.txt ================================================ here's d ================================================ FILE: test/command/abbrevs ================================================ Foo. h.k. ================================================ FILE: test/command/adjacent_latex_blocks.md ================================================ ``` % pandoc -f markdown -t native \listoffigures \listoftables ^D [ RawBlock (Format "tex") "\\listoffigures" , RawBlock (Format "tex") "\\listoftables" ] ``` ================================================ FILE: test/command/advanced-optical-materials.csl ================================================ ================================================ FILE: test/command/alerts.md ================================================ ``` % pandoc -f rst -t gfm .. note:: This is my note. ^D > [!NOTE] > This is my note. ``` ``` % pandoc -f gfm -t rst > [!WARNING] > Be careful! ^D .. warning:: Be careful! ``` ``` % pandoc -f gfm -t asciidoc > [!TIP] > A tip. ^D [TIP] ==== A tip. ==== ``` ``` % pandoc -f gfm -t docbook > [!TIP] > A tip. ^D Tip A tip. ``` ``` % pandoc -f docbook -t gfm Tip A tip. ^D > [!TIP] > A tip. ``` ================================================ FILE: test/command/american-medical-association.csl ================================================ ================================================ FILE: test/command/annales.csl ================================================ ================================================ FILE: test/command/apa.csl ================================================ ================================================ FILE: test/command/archeologie-medievale.csl ================================================ ================================================ FILE: test/command/ascii.md ================================================ ``` % pandoc -t html --ascii äéıå ^D

    äéıå

    ``` ``` % pandoc -t latex --ascii äéıå ^D \"{a}\'{e}\i\r{a} ``` ``` % pandoc -t man --ascii äéıå ^D .PP \(:a\('e\(.i\(oa ``` ``` % pandoc -t ms --ascii äéıå ^D .LP \(:a\('e\(.i\(oa ``` ``` % pandoc -t docbook --ascii äéıå ^D äéıå ``` ``` % pandoc -t jats --ascii äéıå ^D

    äéıå

    ``` ``` % pandoc -t markdown-smart --ascii "äéıå" ^D “äéıå” ``` # CommonMark tests ``` % pandoc -f commonmark -t commonmark --ascii hello … ok? … bye ^D hello … ok? … bye ``` ``` % pandoc -f commonmark+smart -t commonmark-smart --ascii --wrap=none "hi"...dog's breath---cat 5--6 ^D “hi”…dog’s breath—cat 5–6 ``` ``` % pandoc -f commonmark+smart -t commonmark+smart --ascii "hi"...dog's breath---cat 5--6 ^D "hi"...dog's breath---cat 5--6 ``` ``` % pandoc -f commonmark -t commonmark --ascii foo Ӓ bar ^D foo Ӓ bar ``` ``` % pandoc -f commonmark -t commonmark --ascii \[foo\](bar) ^D \[foo\](bar) ``` ================================================ FILE: test/command/author-in-text-move-note.md ================================================ ``` % pandoc --citeproc --csl command/chicago-fullnote-bibliography.csl -t plain --- references: - id: foo type: book author: John Doe title: A Book ... See @foo [p. 21], as well. ^D See John Doe,[1] as well. John Doe. A Book, n.d. [1] A Book, n.d., 21. ``` ================================================ FILE: test/command/averroes.bib ================================================ @string{anch-ie = {Angew.~Chem. Int.~Ed.}} @string{cup = {Cambridge University Press}} @string{dtv = {Deutscher Taschenbuch-Verlag}} @string{hup = {Harvard University Press}} @string{jams = {J.~Amer. Math. Soc.}} @string{jchph = {J.~Chem. Phys.}} @string{jomch = {J.~Organomet. Chem.}} @string{pup = {Princeton University Press}} @book{averroes/bland, author = {Averroes}, title = {The Epistle on the Possibility of Conjunction with the Active Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}}, date = 1982, editor = {Bland, Kalman P.}, translator = {Bland, Kalman P.}, series = {Moreshet: Studies in {Jewish} History, Literature and Thought}, number = 7, publisher = {Jewish Theological Seminary of America}, location = {New York}, keywords = {primary}, langid = {english}, langidopts = {variant=american}, indextitle = {Epistle on the Possibility of Conjunction, The}, shorttitle = {Possibility of Conjunction}, annotation = {A \texttt{book} entry with a \texttt{series} and a \texttt{number}. Note the concatenation of the \texttt{editor} and \texttt{translator} fields as well as the \texttt{indextitle} field}, } @book{averroes/hannes, author = {Averroes}, title = {Des Averro{\"e}s Abhandlung: \mkbibquote{{\"U}ber die M{\"o}glichkeit der Conjunktion} oder \mkbibquote{{\"U}ber den materiellen Intellekt}}, date = 1892, editor = {Hannes, Ludwig}, translator = {Hannes, Ludwig}, annotator = {Hannes, Ludwig}, publisher = {C.~A. Kaemmerer}, location = {Halle an der Saale}, keywords = {primary}, langid = {german}, sorttitle = {Uber die Moglichkeit der Conjunktion}, indexsorttitle= {Uber die Moglichkeit der Conjunktion}, indextitle = {{\"U}ber die M{\"o}glichkeit der Conjunktion}, shorttitle = {{\"U}ber die M{\"o}glichkeit der Conjunktion}, annotation = {An annotated edition. Note the concatenation of the \texttt{editor}, \texttt{translator}, and \texttt{annotator} fields. Also note the \texttt{shorttitle}, \texttt{indextitle}, \texttt{sorttitle}, and \texttt{indexsorttitle} fields}, } @book{averroes/hercz, author = {Averroes}, title = {Drei Abhandlungen {\"u}ber die Conjunction des separaten Intellects mit dem Menschen}, date = 1869, editor = {Hercz, J.}, translator = {Hercz, J.}, publisher = {S.~Hermann}, location = {Berlin}, keywords = {primary}, langid = {german}, indexsorttitle= {Drei Abhandlungen uber die Conjunction}, indextitle = {Drei Abhandlungen {\"u}ber die Conjunction}, subtitle = {Von Averroes (Vater und Sohn), aus dem Arabischen {\"u}bersetzt von Samuel Ibn Tibbon}, shorttitle = {Drei Abhandlungen}, annotation = {A \texttt{book} entry. Note the concatenation of the \texttt{editor} and \texttt{translator} fields as well as the \texttt{indextitle} and \texttt{indexsorttitle} fields}, } @book{cicero, author = {Cicero, Marcus Tullius}, title = {De natura deorum. {\"U}ber das Wesen der G{\"o}tter}, date = 1995, editor = {Blank-Sangmeister, Ursula}, translator = {Blank-Sangmeister, Ursula}, afterword = {Thraede, Klaus}, language = {langlatin and langgerman}, publisher = {Reclam}, location = {Stuttgart}, langid = {german}, indextitle = {De natura deorum}, shorttitle = {De natura deorum}, annotation = {A bilingual edition of Cicero's \emph{De natura deorum}, with a German translation. Note the format of the \texttt{language} field in the database file, the concatenation of the \texttt{editor} and \texttt{translator} fields, and the \texttt{afterword} field}, } ================================================ FILE: test/command/bar-endinput.tex ================================================ \emph{hi there} \endinput \emph{invisible} ================================================ FILE: test/command/bar.tex ================================================ \emph{hi there} ================================================ FILE: test/command/biblatex-266.md ================================================ ``` % pandoc -f biblatex -t markdown -s @book{goethe2005, langid = {german}, location = {{Frankfurt am Main}}, title = {Not A Real Book}, date = {2005}, author = {family=Goethe, given=Johann Wolfgang, prefix=von, useprefix=false and given=Antonie, prefix=van, family=Leeuwenhoek, useprefix=true}, editor = {Schöne, Albrecht} } ^D --- nocite: "[@*]" references: - author: - dropping-particle: von family: Goethe given: Johann Wolfgang - family: Leeuwenhoek given: Antonie non-dropping-particle: van editor: - family: Schöne given: Albrecht id: goethe2005 issued: 2005 language: de-DE publisher-place: Frankfurt am Main title: Not A Real Book type: book --- ``` ================================================ FILE: test/command/biblatex-aksin.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Aksin et al. 2006) Aksin, Özge, Hayati Türkmen, Levent Artok, Bekir Çetinkaya, Chaoying Ni, Orhan Büyükgüngör, and Erhan Özkal. 2006. “Effect of Immobilization on Catalytic Characteristics of Saturated Pd-N-heterocyclic Carbenes in Mizoroki-Heck Reactions.” *J. Organomet. Chem.* 691 (13): 3027–3036. Formatted with pandoc and apa.csl, 2013-10-23: (Aksin et al., 2006) Aksin, Ö., Türkmen, H., Artok, L., Çetinkaya, B., Ni, C., Büyükgüngör, O., & Özkal, E. (2006). Effect of immobilization on catalytic characteristics of saturated Pd-N-heterocyclic carbenes in Mizoroki-Heck reactions. *J. Organomet. Chem.*, *691*(13), 3027–3036. } @string{ jomch = {J.~Organomet. Chem.} } @Article{aksin, author = {Aks{\i}n, {\"O}zge and T{\"u}rkmen, Hayati and Artok, Levent and {\c{C}}etinkaya, Bekir and Ni, Chaoying and B{\"u}y{\"u}kg{\"u}ng{\"o}r, Orhan and {\"O}zkal, Erhan}, title = {Effect of immobilization on catalytic characteristics of saturated {Pd-N}-heterocyclic carbenes in {Mizoroki-Heck} reactions}, journaltitle = jomch, date = 2006, volume = 691, number = 13, pages = {3027-3036}, indextitle = {Effect of immobilization on catalytic characteristics}, } ^D --- nocite: "[@*]" references: - author: - family: Aksın given: Özge - family: Türkmen given: Hayati - family: Artok given: Levent - family: Çetinkaya given: Bekir - family: Ni given: Chaoying - family: Büyükgüngör given: Orhan - family: Özkal given: Erhan container-title: J. Organomet. Chem. id: aksin issue: 13 issued: 2006 page: 3027-3036 title: Effect of immobilization on catalytic characteristics of saturated Pd-N-heterocyclic carbenes in Mizoroki-Heck reactions type: article-journal volume: 691 --- ``` ================================================ FILE: test/command/biblatex-almendro.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Almendro et al. 1998) Almendro, José L., Jacinto Martín, Alberto Sánchez, and Fernando Nozal. 1998. “Elektromagnetisches Signalhorn.” Formatted with pandoc and apa.csl, 2013-10-23: (Almendro, Martín, Sánchez, & Nozal, 1998) Almendro, J. L., Martín, J., Sánchez, A., & Nozal, F. (1998). Elektromagnetisches Signalhorn. NOTES: - CSL styles’ handling of patent items needs to be improved } @Patent{almendro, author = {Almendro, Jos{\'e} L. and Mart{\'i}n, Jacinto and S{\'a}nchez, Alberto and Nozal, Fernando}, title = {Elektromagnetisches Signalhorn}, number = {EU-29702195U}, date = 1998, location = {countryfr and countryuk and countryde}, hyphenation = {german}, annotation = {This is a patent entry with a location field. The number is given in the number field. Note the format of the location field in the database file. Compare laufenberg, sorace, and kowalik}, } ^D --- nocite: "[@*]" references: - annote: This is a patent entry with a location field. The number is given in the number field. Note the format of the location field in the database file. Compare laufenberg, sorace, and kowalik author: - family: Almendro given: José L. - family: Martín given: Jacinto - family: Sánchez given: Alberto - family: Nozal given: Fernando id: almendro issued: 1998 jurisdiction: France; United Kingdom; Germany language: de-DE number: EU-29702195U title: Elektromagnetisches Signalhorn type: patent --- ``` ================================================ FILE: test/command/biblatex-angenendt.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Angenendt 2002) Angenendt, Arnold. 2002. “In Honore Salvatoris – Vom Sinn und Unsinn der Patrozinienkunde.” *Revue d’Histoire Ecclésiastique* 97: 431–456, 791–823. Formatted with pandoc and apa.csl, 2013-10-23: (Angenendt, 2002) Angenendt, A. (2002). In Honore Salvatoris – Vom Sinn und Unsinn der Patrozinienkunde. *Revue d’Histoire Ecclésiastique*, *97*, 431–456, 791–823. } @Article{angenendt, author = {Angenendt, Arnold}, title = {In Honore Salvatoris~-- Vom Sinn und Unsinn der Patrozinienkunde}, journaltitle = {Revue d'Histoire Eccl{\'e}siastique}, date = 2002, volume = 97, pages = {431--456, 791--823}, hyphenation = {german}, indextitle = {In Honore Salvatoris}, shorttitle = {In Honore Salvatoris}, annotation = {A German article in a French journal. Apart from that, a typical article entry. Note the indextitle field}, } ^D --- nocite: "[@*]" references: - annote: A German article in a French journal. Apart from that, a typical article entry. Note the indextitle field author: - family: Angenendt given: Arnold container-title: Revue d'Histoire Eccl[é]{.nocase}siastique id: angenendt issued: 2002 language: de-DE page: 431-456, 791-823 title: In Honore Salvatoris -- Vom Sinn und Unsinn der Patrozinienkunde title-short: In Honore Salvatoris type: article-journal volume: 97 --- ``` ================================================ FILE: test/command/biblatex-aristotle-anima.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Aristotle 1907) Aristotle. 1907. *De Anima*. Edited by Robert Drew Hicks. Cambridge: Cambridge University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Aristotle, 1907) Aristotle. (1907). *De anima*. (R. D. Hicks, Ed.). Cambridge: Cambridge University Press. } @string{ cup = {Cambridge University Press} } @Book{aristotle:anima, author = {Aristotle}, title = {De Anima}, date = 1907, editor = {Hicks, Robert Drew}, publisher = cup, location = {Cambridge}, keywords = {primary}, hyphenation = {british}, annotation = {A book entry with an author and an editor}, } ^D --- nocite: "[@*]" references: - annote: A book entry with an author and an editor author: - family: Aristotle editor: - family: Hicks given: Robert Drew id: "aristotle:anima" issued: 1907 keyword: primary language: en-GB publisher: Cambridge University Press publisher-place: Cambridge title: De anima type: book --- ``` ================================================ FILE: test/command/biblatex-aristotle-physics.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Aristotle 1929) Aristotle. 1929. *Physics*. Translated by P. H. Wicksteed and F. M. Cornford. New York: G. P. Putnam. Formatted with pandoc and apa.csl, 2013-10-23: (Aristotle, 1929) Aristotle. (1929). *Physics*. (P. H. Wicksteed & F. M. Cornford, Trans.). New York: G. P. Putnam. } @Book{aristotle:physics, author = {Aristotle}, title = {Physics}, date = 1929, translator = {Wicksteed, P. H. and Cornford, F. M.}, publisher = {G. P. Putnam}, location = {New York}, keywords = {primary}, hyphenation = {american}, shorttitle = {Physics}, annotation = {A book entry with a translator field}, } ^D --- nocite: "[@*]" references: - annote: A book entry with a translator field author: - family: Aristotle id: "aristotle:physics" issued: 1929 keyword: primary language: en-US publisher: G. P. Putnam publisher-place: New York title: Physics title-short: Physics translator: - family: Wicksteed given: P. H. - family: Cornford given: F. M. type: book --- ``` ================================================ FILE: test/command/biblatex-aristotle-poetics.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Aristotle 1968) Aristotle. 1968. *Poetics*. Edited by D. W. Lucas. Clarendon Aristotle. Oxford: Clarendon Press. Formatted with pandoc and apa.csl, 2013-10-23: (Aristotle, 1968) Aristotle. (1968). *Poetics*. (D. W. Lucas, Ed.). Oxford: Clarendon Press. } @Book{aristotle:poetics, author = {Aristotle}, title = {Poetics}, date = 1968, editor = {Lucas, D. W.}, series = {Clarendon {Aristotle}}, publisher = {Clarendon Press}, location = {Oxford}, keywords = {primary}, hyphenation = {british}, shorttitle = {Poetics}, annotation = {A book entry with an author and an editor as well as a series field}, } ^D --- nocite: "[@*]" references: - annote: A book entry with an author and an editor as well as a series field author: - family: Aristotle collection-title: Clarendon Aristotle editor: - family: Lucas given: D. W. id: "aristotle:poetics" issued: 1968 keyword: primary language: en-GB publisher: Clarendon Press publisher-place: Oxford title: Poetics title-short: Poetics type: book --- ``` ================================================ FILE: test/command/biblatex-aristotle-rhetoric.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Aristotle 1877) Aristotle. 1877. *The Rhetoric of Aristotle with a Commentary by the Late Edward Meredith Cope*. Edited by Edward Meredith Cope. 3. Cambridge University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Aristotle, 1877) Aristotle. (1877). *The rhetoric of Aristotle with a commentary by the late Edward Meredith Cope*. (E. M. Cope, Ed.) (1-3). Cambridge University Press. NOTES: - biblio2yaml - commentator has no counterpart in CSL } @string{ cup = {Cambridge University Press} } @Book{aristotle:rhetoric, author = {Aristotle}, title = {The Rhetoric of {Aristotle} with a commentary by the late {Edward Meredith Cope}}, date = 1877, editor = {Cope, Edward Meredith}, commentator = {Cope, Edward Meredith}, volumes = 3, publisher = cup, keywords = {primary}, hyphenation = {british}, sorttitle = {Rhetoric of Aristotle}, indextitle = {Rhetoric of {Aristotle}, The}, shorttitle = {Rhetoric}, annotation = {A commented edition. Note the concatenation of the editor and commentator fields as well as the volumes, sorttitle, and indextitle fields}, } ^D --- nocite: "[@*]" references: - annote: A commented edition. Note the concatenation of the editor and commentator fields as well as the volumes, sorttitle, and indextitle fields author: - family: Aristotle editor: - family: Cope given: Edward Meredith id: "aristotle:rhetoric" issued: 1877 keyword: primary language: en-GB number-of-volumes: 3 publisher: Cambridge University Press title: The rhetoric of Aristotle with a commentary by the late Edward Meredith Cope title-short: Rhetoric type: book --- ``` ================================================ FILE: test/command/biblatex-article.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ - contains: - an article entry with just the required fields - an article entry with required and all optional fields - notes: - year, month to be ignored if date is present - journal to be ignored if journaltitle is present - editortype, editoratype, editorbtype, editorctype, pubstate, series contain keys which, unless corresponding CSL terms exist, require locale-specific expansion - limitations: - annotator, commentator, eid, eprint, eprintclass, eprinttype, issuetitle, issuesubtitle, language, origlanguage have no matching counterparts in CSL - for editor, editora, editorb, editorc (plus editortype, editoratype, editorbtype, editorctype) only a subset, editor and director, has matching counterparts in CSL - kludges: - note + addendum -> CSL note - number + issue -> CSL issue - handling of titleaddon - handling of (journal) series - done properly, this should be mapped to some CSL variable (version? edition? collection-number?), CSL styles would have to be adapted - slightly better kludge would map integer to ordinal + "ser." ("3" -> "3rd ser."); localization keys "newseries" -> "new ser.", "oldseries" -> "old ser."; and print all other values as is -- but still wouldn't fit all styles or locales. } @article{article-req, Author = {Author, Ann}, Date = {2013-07-29}, Hyphenation = {english}, Journaltitle = {The Journaltitle}, Title = {An Article Entry with Just the Required Fields}} @article{article-opt, Addendum = {The Addendum}, Annotator = {Annotator, A.}, Author = {Author, Jr., Ann A.}, Commentator = {Commentator, C.}, Date = {2008-12-31}, Doi = {10.1086/520976}, Editor = {Editor, Edward}, Editora = {Editor, A.}, Editorb = {Editor, B.}, Editorc = {Editor, C.}, Eid = {eid}, Eprint = {eprint}, Eprintclass = {eprintclass}, Eprinttype = {eprinttype}, Hyphenation = {english}, Issn = {issn}, Issue = {issue}, Issuesubtitle = {The Issuesubtitle}, Issuetitle = {The Issuetitle}, Journalsubtitle = {The Journalsubtitle}, Journaltitle = {The Journaltitle}, Journal = {The Journal}, Language = {language}, Month = {08}, Year = {2007}, Note = {The Note}, Number = {number}, Origlanguage = {origlanguage}, Pages = {pages}, Pubstate = {inpress}, Series = {newseries}, Subtitle = {The Subtitle}, Title = {An Article Entry with the Required and All Optional Fields}, Titleaddon = {The Titleaddon}, Translator = {Translator, Ted}, Url = {http://foo.bar.baz/}, Urldate = {2013-07-29}, Version = {version}, Volume = {volume}, } ^D --- nocite: "[@*]" references: - author: - family: Author given: Ann container-title: The Journaltitle id: article-req issued: 2013-07-29 language: en-US title: An article entry with just the required fields type: article-journal - accessed: 2013-07-29 author: - family: Author given: Ann A. suffix: Jr. collection-title: New series container-title: "The Journaltitle: The Journalsubtitle" doi: 10.1086/520976 editor: - family: Editor given: Edward id: article-opt issn: issn issue: number, issue issued: 2008-12-31 language: en-US note: The Note. The Addendum page: pages status: in press title: "An article entry with the required and all optional fields: The subtitle. The titleaddon" title-short: An article entry with the required and all optional fields translator: - family: Translator given: Ted type: article-journal url: "http://foo.bar.baz/" version: version volume: volume --- ``` ================================================ FILE: test/command/biblatex-augustine.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Augustine 1995) Augustine, Robert L. 1995. *Heterogeneous Catalysis for the Synthetic Chemist*. New York: Marcel Dekker. Formatted with pandoc and apa.csl, 2013-10-23: (Augustine, 1995) Augustine, R. L. (1995). *Heterogeneous catalysis for the synthetic chemist*. New York: Marcel Dekker. } @Book{augustine, author = {Augustine, Robert L.}, title = {Heterogeneous catalysis for the synthetic chemist}, date = 1995, publisher = {Marcel Dekker}, location = {New York}, hyphenation = {american}, shorttitle = {Heterogeneous catalysis}, annotation = {A plain book entry}, } ^D --- nocite: "[@*]" references: - annote: A plain book entry author: - family: Augustine given: Robert L. id: augustine issued: 1995 language: en-US publisher: Marcel Dekker publisher-place: New York title: Heterogeneous catalysis for the synthetic chemist title-short: Heterogeneous catalysis type: book --- ``` ================================================ FILE: test/command/biblatex-averroes-bland.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Averroes 1982) Averroes. 1982. *The Epistle on the Possibility of Conjunction with the Active Intellect by Ibn Rushd with the Commentary of Moses Narboni*. Kalman P. Bland. Moreshet: Studies in Jewish History, Literature and Thought 7. New York: Jewish Theological Seminary of America. Formatted with pandoc and apa.csl, 2013-10-23: (Averroes, 1982) Averroes. (1982). *The epistle on the possibility of conjunction with the active intellect by Ibn Rushd with the commentary of Moses Narboni*. (K. P. Bland). New York: Jewish Theological Seminary of America. NOTES: - citeproc - term "edited and translated by" missing } @Book{averroes-bland, author = {Averroes}, title = {The Epistle on the Possibility of Conjunction with the Active Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}}, date = 1982, editor = {Bland, Kalman P.}, translator = {Bland, Kalman P.}, series = {Moreshet: {Studies} in {Jewish} History, Literature and Thought}, number = 7, publisher = {Jewish Theological Seminary of America}, location = {New York}, keywords = {primary}, hyphenation = {american}, indextitle = {Epistle on the Possibility of Conjunction, The}, shorttitle = {Possibility of Conjunction}, annotation = {A book entry with a series and a number. Note the concatenation of the editor and translator fields as well as the indextitle field}, } ^D --- nocite: "[@*]" references: - annote: A book entry with a series and a number. Note the concatenation of the editor and translator fields as well as the indextitle field author: - family: Averroes collection-number: 7 collection-title: "Moreshet: Studies in Jewish history, literature and thought" editor: - family: Bland given: Kalman P. id: averroes-bland issued: 1982 keyword: primary language: en-US publisher: Jewish Theological Seminary of America publisher-place: New York title: The epistle on the possibility of conjunction with the active intellect by Ibn Rushd with the commentary of Moses Narboni title-short: Possibility of conjunction translator: - family: Bland given: Kalman P. type: book --- ``` ================================================ FILE: test/command/biblatex-averroes-hannes.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Averroes 1892) Averroes. 1892. *Des Averroës Abhandlung: “Über die Möglichkeit der Conjunktion” oder “Über den materiellen Intellekt”*. Ludwig Hannes. Halle an der Saale: C. A. Kaemmerer. Formatted with pandoc and apa.csl, 2013-10-23: (Averroes, 1892) Averroes. (1892). *Des Averroës Abhandlung: “Über die Möglichkeit der Conjunktion” oder “Über den materiellen Intellekt”*. (L. Hannes). Halle an der Saale: C. A. Kaemmerer. NOTES: - citeproc - term "edited and translated by" missing } @Book{averroes-hannes, author = {Averroes}, title = {Des Averro{\"e}s Abhandlung: \mkbibquote{{\"U}ber die M{\"o}glichkeit der Conjunktion} oder \mkbibquote{{\"U}ber den materiellen Intellekt}}, date = 1892, editor = {Hannes, Ludwig}, translator = {Hannes, Ludwig}, annotator = {Hannes, Ludwig}, publisher = {C.~A. Kaemmerer}, location = {Halle an der Saale}, keywords = {primary}, hyphenation = {german}, sorttitle = {Uber die Moglichkeit der Conjunktion}, indexsorttitle= {Uber die Moglichkeit der Conjunktion}, indextitle = {{\"U}ber die M{\"o}glichkeit der Conjunktion}, shorttitle = {{\"U}ber die M{\"o}glichkeit der Conjunktion}, annotation = {An annotated edition. Note the concatenation of the editor, translator, and annotator fields. Also note the shorttitle, indextitle, sorttitle, and indexsorttitle fields}, } ^D --- nocite: "[@*]" references: - annote: An annotated edition. Note the concatenation of the editor, translator, and annotator fields. Also note the shorttitle, indextitle, sorttitle, and indexsorttitle fields author: - family: Averroes editor: - family: Hannes given: Ludwig id: averroes-hannes issued: 1892 keyword: primary language: de-DE publisher: C. A. Kaemmerer publisher-place: Halle an der Saale title: "Des Averroës Abhandlung: \"Über die Möglichkeit der Conjunktion\" oder \"Über den materiellen Intellekt\"" title-short: Über die Möglichkeit der Conjunktion translator: - family: Hannes given: Ludwig type: book --- ``` ================================================ FILE: test/command/biblatex-averroes-hercz.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Averroes 1869) Averroes. 1869. *Drei Abhandlungen über die Conjunction des separaten Intellects mit dem Menschen: Von Averroes (Vater und Sohn), aus dem Arabischen übersetzt von Samuel Ibn Tibbon*. J. Hercz. Berlin: S. Hermann. Formatted with pandoc and apa.csl, 2013-10-23: (Averroes, 1869) Averroes. (1869). *Drei Abhandlungen über die Conjunction des separaten Intellects mit dem Menschen: Von Averroes (Vater und Sohn), aus dem Arabischen übersetzt von Samuel Ibn Tibbon*. (J. Hercz). Berlin: S. Hermann. NOTES: - citeproc - term "edited and translated by" missing } @Book{averroes-hercz, author = {Averroes}, title = {Drei Abhandlungen {\"u}ber die Conjunction des separaten Intellects mit dem Menschen}, date = 1869, editor = {Hercz, J.}, translator = {Hercz, J.}, publisher = {S.~Hermann}, location = {Berlin}, keywords = {primary}, hyphenation = {german}, indexsorttitle= {Drei Abhandlungen uber die Conjunction}, indextitle = {Drei Abhandlungen {\"u}ber die Conjunction}, subtitle = {Von Averroes (Vater und Sohn), aus dem Arabischen {\"u}bersetzt von Samuel Ibn Tibbon}, shorttitle = {Drei Abhandlungen}, annotation = {A book entry. Note the concatenation of the editor and translator fields as well as the indextitle and indexsorttitle fields}, } ^D --- nocite: "[@*]" references: - annote: A book entry. Note the concatenation of the editor and translator fields as well as the indextitle and indexsorttitle fields author: - family: Averroes editor: - family: Hercz given: J. id: averroes-hercz issued: 1869 keyword: primary language: de-DE publisher: S. Hermann publisher-place: Berlin title: "Drei Abhandlungen über die Conjunction des separaten Intellects mit dem Menschen: Von Averroes (Vater und Sohn), aus dem Arabischen übersetzt von Samuel Ibn Tibbon" title-short: Drei Abhandlungen translator: - family: Hercz given: J. type: book --- ``` ================================================ FILE: test/command/biblatex-baez-article.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Baez and Lauda 2004) Baez, John C., and Aaron D. Lauda. 2004. “Higher-dimensional Algebra V: 2-groups” (version 3). *Theory and Applications of Categories* 12: 423–491. Formatted with pandoc and apa.csl, 2013-10-23: (Baez & Lauda, 2004) Baez, J. C., & Lauda, A. D. (2004). Higher-dimensional algebra V: 2-groups. *Theory and Applications of Categories*, *12*, 423–491. NOTES: - biblio2yaml - eprint: see baez-online } @Article{baez-article, author = {Baez, John C. and Lauda, Aaron D.}, title = {Higher-Dimensional Algebra {V}: 2-Groups}, journaltitle = {Theory and Applications of Categories}, date = 2004, volume = 12, pages = {423-491}, version = 3, eprint = {math/0307200v3}, eprinttype = {arxiv}, hyphenation = {american}, annotation = {An article with eprint and eprinttype fields. Note that the arXiv reference is transformed into a clickable link if hyperref support has been enabled. Compare baez\slash online, which is the same item given as an online entry}, } ^D --- nocite: "[@*]" references: - annote: An article with eprint and eprinttype fields. Note that the arXiv reference is transformed into a clickable link if hyperref support has been enabled. Compare baez/online, which is the same item given as an online entry author: - family: Baez given: John C. - family: Lauda given: Aaron D. container-title: Theory and Applications of Categories id: baez-article issued: 2004 language: en-US page: 423-491 title: "Higher-dimensional algebra V: 2-groups" title-short: Higher-dimensional algebra V type: article-journal url: "https://arxiv.org/abs/math/0307200v3" version: 3 volume: 12 --- ``` ================================================ FILE: test/command/biblatex-baez-online.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Baez and Lauda 2004) Baez, John C., and Aaron D. Lauda. 2004. “Higher-dimensional Algebra V: 2-groups” (version 3). October 27. Formatted with pandoc and apa.csl, 2013-10-23: (Baez & Lauda, 2004) Baez, J. C., & Lauda, A. D. (2004, October 27). Higher-dimensional algebra V: 2-groups. NOTES: - biblio2yaml: - eprinttype = {arxiv}, eprint = {math/0307200v3}, should be converted to a url: http://arxiv.org/abs/math/0307200v3 (prefix http://arxiv.org/abs/ seems to work for all arxiv material) } @Online{baez-online, author = {Baez, John C. and Lauda, Aaron D.}, title = {Higher-Dimensional Algebra {V}: 2-Groups}, date = {2004-10-27}, version = 3, hyphenation = {american}, eprinttype = {arxiv}, eprint = {math/0307200v3}, annotation = {An online reference from arXiv. Note the eprint and eprinttype fields. Compare baez\slash article which is the same item given as an article entry with eprint information}, } ^D --- nocite: "[@*]" references: - annote: An online reference from arXiv. Note the eprint and eprinttype fields. Compare baez/article which is the same item given as an article entry with eprint information author: - family: Baez given: John C. - family: Lauda given: Aaron D. id: baez-online issued: 2004-10-27 language: en-US title: "Higher-dimensional algebra V: 2-groups" title-short: Higher-dimensional algebra V type: webpage url: "https://arxiv.org/abs/math/0307200v3" version: 3 --- ``` ================================================ FILE: test/command/biblatex-basic.md ================================================ ``` % pandoc -f biblatex -t markdown -s @Book{item1, author="John Doe", title="First Book", year="2005", address="Cambridge", publisher="Cambridge University Press" } @Article{item2, author="John Doe", title="Article", year="2006", journal="Journal of Generic Studies", volume="6", pages="33-34" } @InCollection{пункт3, author="John Doe and Jenny Roe", title="Why Water Is Wet", booktitle="Third Book", editor="Sam Smith", publisher="Oxford University Press", address="Oxford", year="2007" } ^D --- nocite: "[@*]" references: - author: - family: Doe given: John id: item1 issued: 2005 publisher: Cambridge University Press publisher-place: Cambridge title: First book type: book - author: - family: Doe given: John container-title: Journal of Generic Studies id: item2 issued: 2006 page: 33-34 title: Article type: article-journal volume: 6 - author: - family: Doe given: John - family: Roe given: Jenny container-title: Third book editor: - family: Smith given: Sam id: пункт3 issued: 2007 publisher: Oxford University Press publisher-place: Oxford title: Why water is wet type: chapter --- ``` ================================================ FILE: test/command/biblatex-bertram.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Bertram and Wentworth 1996) Bertram, Aaron, and Richard Wentworth. 1996. “Gromov Invariants for Holomorphic Maps on Riemann Surfaces.” *J. Amer. Math. Soc.* 9 (2): 529–571. Formatted with pandoc and apa.csl, 2013-10-23: (Bertram & Wentworth, 1996) Bertram, A., & Wentworth, R. (1996). Gromov invariants for holomorphic maps on Riemann surfaces. *J. Amer. Math. Soc.*, *9*(2), 529–571. } @string{ jams = {J.~Amer. Math. Soc.} } @Article{bertram, author = {Bertram, Aaron and Wentworth, Richard}, title = {Gromov invariants for holomorphic maps on {Riemann} surfaces}, journaltitle = jams, date = 1996, volume = 9, number = 2, pages = {529-571}, hyphenation = {american}, shorttitle = {Gromov invariants}, annotation = {An article entry with a volume and a number field}, } ^D --- nocite: "[@*]" references: - annote: An article entry with a volume and a number field author: - family: Bertram given: Aaron - family: Wentworth given: Richard container-title: J. Amer. Math. Soc. id: bertram issue: 2 issued: 1996 language: en-US page: 529-571 title: Gromov invariants for holomorphic maps on Riemann surfaces title-short: Gromov invariants type: article-journal volume: 9 --- ``` ================================================ FILE: test/command/biblatex-bibstring-resolution.md ================================================ ``` % pandoc -f biblatex -t markdown -s @book{item1, Title = {The Title: \bibstring{newseries}}, Hyphenation = {english} } ^D --- nocite: "[@*]" references: - id: item1 language: en-US title: "The title: New series" title-short: The title type: book --- ``` ================================================ FILE: test/command/biblatex-book-averroes.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib} @book{averroes/bland, Annotation = {A book entry with a series and a number. Note the concatenation of the editor and translator fields as well as the indextitle field}, Author = {Averroes}, Date = 1982, Editor = {Bland, Kalman P.}, Hyphenation = {american}, Indextitle = {Epistle on the Possibility of Conjunction, The}, Keywords = {primary}, Location = {New York}, Number = 7, Publisher = {Jewish Theological Seminary of America}, Series = {{Moreshet: Studies in Jewish History, Literature and Thought}}, Shorttitle = {Possibility of Conjunction}, Title = {The Epistle on the Possibility of Conjunction with the Active Intellect by {Ibn Rushd} with the Commentary of {Moses Narboni}}, Translator = {Bland, Kalman P.}} @book{averroes/hannes, Annotation = {An annotated edition. Note the concatenation of the editor, translator, and annotator fields. Also note the shorttitle, indextitle, sorttitle, and indexsorttitle fields}, Annotator = {Hannes, Ludwig}, Author = {Averroes}, Date = 1892, Editor = {Hannes, Ludwig}, Hyphenation = {german}, Indexsorttitle = {Uber die Moglichkeit der Conjunktion}, Indextitle = {Über die Möglichkeit der Conjunktion}, Keywords = {primary}, Location = {Halle an der Saale}, Publisher = {C.~A. Kaemmerer}, Shorttitle = {Über die Möglichkeit der Conjunktion}, Sorttitle = {Uber die Moglichkeit der Conjunktion}, Title = {Des Averroës Abhandlung: \mkbibquote{Über die Möglichkeit der Conjunktion} oder \mkbibquote{Über den materiellen Intellekt}}, Translator = {Hannes, Ludwig}} @book{averroes/hercz, Annotation = {A book entry. Note the concatenation of the editor and translator fields as well as the indextitle and indexsorttitle fields}, Author = {Averroes}, Date = 1869, Editor = {Hercz, J.}, Hyphenation = {german}, Indexsorttitle = {Drei Abhandlungen uber die Conjunction}, Indextitle = {Drei Abhandlungen über die Conjunction}, Keywords = {primary}, Location = {Berlin}, Publisher = {S.~Hermann}, Shorttitle = {Drei Abhandlungen}, Subtitle = {Von Averroes (Vater und Sohn), aus dem Arabischen übersetzt von Samuel Ibn Tibbon}, Title = {Drei Abhandlungen über die Conjunction des separaten Intellects mit dem Menschen}, Translator = {Hercz, J.}} ^D --- nocite: "[@*]" references: - annote: A book entry with a series and a number. Note the concatenation of the editor and translator fields as well as the indextitle field author: - family: Averroes collection-number: 7 collection-title: "[Moreshet: Studies in Jewish History, Literature and Thought]{.nocase}" editor: - family: Bland given: Kalman P. id: averroes/bland issued: 1982 keyword: primary language: en-US publisher: Jewish Theological Seminary of America publisher-place: New York title: The epistle on the possibility of conjunction with the active intellect by Ibn Rushd with the commentary of Moses Narboni title-short: Possibility of conjunction translator: - family: Bland given: Kalman P. type: book - annote: An annotated edition. Note the concatenation of the editor, translator, and annotator fields. Also note the shorttitle, indextitle, sorttitle, and indexsorttitle fields author: - family: Averroes editor: - family: Hannes given: Ludwig id: averroes/hannes issued: 1892 keyword: primary language: de-DE publisher: C. A. Kaemmerer publisher-place: Halle an der Saale title: "Des Averroës Abhandlung: \"Über die Möglichkeit der Conjunktion\" oder \"Über den materiellen Intellekt\"" title-short: Über die Möglichkeit der Conjunktion translator: - family: Hannes given: Ludwig type: book - annote: A book entry. Note the concatenation of the editor and translator fields as well as the indextitle and indexsorttitle fields author: - family: Averroes editor: - family: Hercz given: J. id: averroes/hercz issued: 1869 keyword: primary language: de-DE publisher: S. Hermann publisher-place: Berlin title: "Drei Abhandlungen über die Conjunction des separaten Intellects mit dem Menschen: Von Averroes (Vater und Sohn), aus dem Arabischen übersetzt von Samuel Ibn Tibbon" title-short: Drei Abhandlungen translator: - family: Hercz given: J. type: book --- ``` ================================================ FILE: test/command/biblatex-book-coleridge.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib TODO (as a stopgap): Combine biblatex “volume = 7” and “part = 2” to CSL “volume: 7.2” } @book{coleridge, Annotation = {One (partial) volume of a multivolume book. This is a book entry with a volume and a part field which explicitly refers to the second (physical) part of the seventh (logical) volume. Also note the series and number fields}, Author = {Coleridge, Samuel Taylor}, Date = 1983, Editor = {Coburn, Kathleen and Engell, James and Bate, W. Jackson}, Hyphenation = {british}, Indextitle = {Biographia literaria}, Location = {London}, Maintitle = {The collected works of {Samuel Taylor Coleridge}}, Number = 75, Part = 2, Publisher = {Routledge {and} Kegan Paul}, Series = {Bollingen Series}, Shorttitle = {Biographia literaria}, Title = {Biographia literaria, or {Biographical} sketches of my literary life and opinions}, Volume = 7} ^D --- nocite: "[@*]" references: - annote: One (partial) volume of a multivolume book. This is a book entry with a volume and a part field which explicitly refers to the second (physical) part of the seventh (logical) volume. Also note the series and number fields author: - family: Coleridge given: Samuel Taylor collection-number: 75 collection-title: Bollingen series editor: - family: Coburn given: Kathleen - family: Engell given: James - family: Bate given: W. Jackson id: coleridge issued: 1983 language: en-GB publisher: Routledge and Kegan Paul publisher-place: London title: The collected works of Samuel Taylor Coleridge type: book volume: 7.2 volume-title: Biographia literaria, or Biographical sketches of my literary life and opinions --- ``` ================================================ FILE: test/command/biblatex-book-title-maintitle-series.md ================================================ ``` % pandoc -f biblatex -t markdown -s @book{item1, Author = {Author, Al}, Date = {2013}, Hyphenation = {french}, Location = {Location}, Mainsubtitle = {Mainsubtitle}, Maintitle = {Maintitle}, Maintitleaddon = {Maintitleaddon}, Number = {3}, Publisher = {Publisher}, Series = {Series}, Subtitle = {Subtitle}, Title = {Title of the Book}, Titleaddon = {Titleaddon}, } ^D --- nocite: "[@*]" references: - author: - family: Author given: Al collection-number: 3 collection-title: Series id: item1 issued: 2013 language: fr-FR publisher: Publisher publisher-place: Location title: "Maintitle: Mainsubtitle. Maintitleaddon" type: book volume-title: "Title of the Book: Subtitle. Titleaddon" --- ``` ================================================ FILE: test/command/biblatex-book-vazques-de-parga.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{excerpted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib Note handling of Author = {Vázques{ de }Parga, Luis} } @book{vazques-de-parga, Annotation = {A multivolume book cited as a whole. This is a book entry with volumes, note, sorttitle, and indextitle fields}, Author = {Vázques{ de }Parga, Luis and Lacarra, José María and Uría Ríu, Juan}, Date = 1993, Hyphenation = {spanish}, Indextitle = {Peregrinaciones a Santiago de Compostela, Las}, Location = {Pamplona}, Note = {Ed. facs. de la realizada en 1948--49}, Publisher = {Iberdrola}, Shorttitle = {Peregrinaciones}, Sorttitle = {Peregrinaciones a Santiago de Compostela}, Title = {Las Peregrinaciones a Santiago de Compostela}, Volumes = 3} ^D --- nocite: "[@*]" references: - annote: A multivolume book cited as a whole. This is a book entry with volumes, note, sorttitle, and indextitle fields author: - family: Vázques de Parga given: Luis - family: Lacarra given: José María - family: Uría Ríu given: Juan id: vazques-de-parga issued: 1993 language: es-ES note: Ed. facs. de la realizada en 1948--49 number-of-volumes: 3 publisher: Iberdrola publisher-place: Pamplona title: Las Peregrinaciones a Santiago de Compostela title-short: Peregrinaciones type: book --- ``` ================================================ FILE: test/command/biblatex-brandt.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Brandt and Hoffmann 1987) Brandt, Ahasver von, and Erich Hoffmann. 1987. “Die nordischen Länder von der Mitte des 11. Jahrhunderts bis 1448.” In *Europa im Hoch- und Spätmittelalter*, edited by Ferdinand Seibt, 884–917. Handbuch der europäischen Geschichte 2. Stuttgart: Klett-Cotta. Formatted with pandoc and apa.csl, 2013-10-23: (Brandt & Hoffmann, 1987) Brandt, A. von, & Hoffmann, E. (1987). Die nordischen Länder von der Mitte des 11. Jahrhunderts bis 1448. In F. Seibt (ed.), *Europa im Hoch- und Spätmittelalter* (pp. 884–917). Stuttgart: Klett-Cotta. } @InCollection{brandt, author = {von Brandt, Ahasver and Erich Hoffmann}, editor = {Ferdinand Seibt}, title = {Die nordischen L{\"a}nder von der Mitte des 11.~Jahrhunderts bis 1448}, date = 1987, booktitle = {Europa im Hoch- und Sp{\"a}tmittelalter}, series = {Handbuch der europ{\"a}ischen Geschichte}, number = 2, publisher = {Klett-Cotta}, location = {Stuttgart}, pages = {884-917}, options = {useprefix=false}, hyphenation = {german}, indexsorttitle= {Nordischen Lander von der Mitte des 11. Jahrhunderts bis 1448}, indextitle = {Nordischen L{\"a}nder von der Mitte des 11.~Jahrhunderts bis 1448, Die}, shorttitle = {Die nordischen L{\"a}nder}, annotation = {An incollection entry with a series and a number. Note the format of the printed name and compare the useprefix option in the options field as well as vangennep. Also note the indextitle, and indexsorttitle fields}, } ^D --- nocite: "[@*]" references: - annote: An incollection entry with a series and a number. Note the format of the printed name and compare the useprefix option in the options field as well as vangennep. Also note the indextitle, and indexsorttitle fields author: - dropping-particle: von family: Brandt given: Ahasver - family: Hoffmann given: Erich collection-number: 2 collection-title: Handbuch der europäischen Geschichte container-title: Europa im Hoch- und Spätmittelalter editor: - family: Seibt given: Ferdinand id: brandt issued: 1987 language: de-DE page: 884-917 publisher: Klett-Cotta publisher-place: Stuttgart title: Die nordischen Länder von der Mitte des 11. Jahrhunderts bis 1448 title-short: Die nordischen Länder type: chapter --- ``` ================================================ FILE: test/command/biblatex-britannica.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Preece 2003) Preece, Warren E., ed. 2003. *The New EncyclopæDia Britannica*. 15th ed. 32. Chicago, Ill.: Encyclopædia Britannica. Formatted with pandoc and apa.csl, 2013-10-23: (Preece, 2003) Preece, W. E. (Ed.). (2003). *The new encyclopædia Britannica* (15th ed., 1-32). Chicago, Ill.: Encyclopædia Britannica. NOTES: - biblio2yaml - spurious in Encyclopædia - options = {useeditor=false} has no equivalent in CSL, so citing and alphabetizing by title even though there is an editor does not seem to be possible - citeproc - incorrect camel case: "EncyclopæDia" - term "vols." missing } @Collection{britannica, editor = {Preece, Warren E.}, title = {The New Encyclop{\ae}dia {Britannica}}, date = 2003, edition = 15, volumes = 32, publisher = {Encyclop{\ae}dia Britannica}, location = {Chicago, Ill.}, options = {useeditor=false}, label = {EB}, hyphenation = {british}, sorttitle = {Encyclop{\ae}dia Britannica}, indextitle = {Encyclop{\ae}dia Britannica, The New}, shorttitle = {Encyclop{\ae}dia {Britannica}}, annotation = {This is a collection entry for an encyclopedia. Note the useeditor option in the options field as well as the sorttitle field. We want this entry to be cited and alphabetized by title even though there is an editor. In addition to that, we want the title to be alphabetized under \enquote*{E} rather than \enquote*{T}. Also note the label field which is provided for author-year citation styles}, } ^D --- nocite: "[@*]" references: - annote: This is a collection entry for an encyclopedia. Note the useeditor option in the options field as well as the sorttitle field. We want this entry to be cited and alphabetized by title even though there is an editor. In addition to that, we want the title to be alphabetized under 'E' rather than 'T'. Also note the label field which is provided for author-year citation styles edition: 15 editor: - family: Preece given: Warren E. id: britannica issued: 2003 language: en-GB number-of-volumes: 32 publisher: Encyclopædia Britannica publisher-place: Chicago, Ill. title: The new encyclopædia Britannica title-short: Encyclopædia Britannica type: book --- ``` ================================================ FILE: test/command/biblatex-chiu.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Chiu and Chow 1978) Chiu, Willy W., and We Min Chow. 1978. “A Hybrid Hierarchical Model of a Multiple Virtual Storage (MVS) Operating System.” Research report RC-6947. IBM. Formatted with pandoc and apa.csl, 2013-10-23: (Chiu & Chow, 1978) Chiu, W. W., & Chow, W. M. (1978). *A hybrid hierarchical model of a multiple virtual storage (MVS) operating system* (research report No. RC-6947). IBM. NOTES: - biblio2yaml - "MVS", when not wrapped in {}, gives "mVS", which is probably never intended, or useful (latex converts the whole word to lowercase if unprotected ("MVS" -> "mvs")) } @Report{chiu, author = {Chiu, Willy W. and Chow, We Min}, title = {A Hybrid Hierarchical Model of a Multiple Virtual Storage ({MVS}) Operating System}, type = {resreport}, institution = {IBM}, date = 1978, number = {RC-6947}, hyphenation = {american}, sorttitle = {Hybrid Hierarchical Model of a Multiple Virtual Storage (MVS) Operating System}, indextitle = {Hybrid Hierarchical Model, A}, annotation = {This is a report entry for a research report. Note the format of the type field in the database file which uses a localization key. The number of the report is given in the number field. Also note the sorttitle and indextitle fields}, } ^D --- nocite: "[@*]" references: - annote: This is a report entry for a research report. Note the format of the type field in the database file which uses a localization key. The number of the report is given in the number field. Also note the sorttitle and indextitle fields author: - family: Chiu given: Willy W. - family: Chow given: We Min genre: research report id: chiu issued: 1978 language: en-US number: RC-6947 publisher: IBM title: A hybrid hierarchical model of a multiple virtual storage (MVS) operating system type: report --- ``` ================================================ FILE: test/command/biblatex-cicero.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Cicero 1995) Cicero, Marcus Tullius. 1995. *De natura deorum. Über das Wesen der Götter*. Ursula Blank-Sangmeister. Stuttgart: Reclam. Formatted with pandoc and apa.csl, 2013-10-23: (Cicero, 1995) Cicero, M. T. (1995). *De natura deorum. Über das Wesen der Götter*. (U. Blank-Sangmeister). Stuttgart: Reclam. NOTES: - biblio2yaml - afterword, language: no CSL variables available - citeproc: - term such as "edited and translated by" should appear } @Book{cicero, author = {Cicero, Marcus Tullius}, title = {De natura deorum. {\"U}ber das Wesen der G{\"o}tter}, date = 1995, editor = {Blank-Sangmeister, Ursula}, translator = {Blank-Sangmeister, Ursula}, afterword = {Thraede, Klaus}, language = {langlatin and langgerman}, publisher = {Reclam}, location = {Stuttgart}, hyphenation = {german}, indextitle = {De natura deorum}, shorttitle = {De natura deorum}, annotation = {A bilingual edition of Cicero's \emph{De natura deorum}, with a German translation. Note the format of the language field in the database file, the concatenation of the editor and translator fields, and the afterword field}, } ^D --- nocite: "[@*]" references: - annote: A bilingual edition of Cicero's *De natura deorum*, with a German translation. Note the format of the language field in the database file, the concatenation of the editor and translator fields, and the afterword field author: - family: Cicero given: Marcus Tullius editor: - family: Blank-Sangmeister given: Ursula id: cicero issued: 1995 language: de-DE publisher: Reclam publisher-place: Stuttgart title: De natura deorum. Über das Wesen der Götter title-short: De natura deorum translator: - family: Blank-Sangmeister given: Ursula type: book --- ``` ================================================ FILE: test/command/biblatex-cms.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (*The Chicago Manual of Style: The Essential Guide for Writers, Editors, and Publishers* 2003) *The Chicago Manual of Style: The Essential Guide for Writers, Editors, and Publishers*. 2003. 15th ed. Chicago, Ill.: University of Chicago Press. Formatted with pandoc and apa.csl, 2013-10-23: (*Chicago manual of style*, 2003) *The Chicago manual of style: The essential guide for writers, editors, and publishers*. (2003) (15th ed.). Chicago, Ill.: University of Chicago Press. NOTES: - chicago-author-date.csl should have as in-text citation: (*Chicago Manual of Style* 2003) Same behaviour in Zotero; most probably a style file issue. } @Manual{cms, title = {The {Chicago} Manual of Style}, date = 2003, subtitle = {The Essential Guide for Writers, Editors, and Publishers}, edition = 15, publisher = {University of Chicago Press}, location = {Chicago, Ill.}, isbn = {0-226-10403-6}, label = {CMS}, hyphenation = {american}, sorttitle = {Chicago Manual of Style}, indextitle = {Chicago Manual of Style, The}, shorttitle = {Chicago Manual of Style}, annotation = {This is a manual entry without an author or editor. Note the label field in the database file which is provided for author-year citation styles. Also note the sorttitle and indextitle fields. By default, all entries without an author or editor are alphabetized by title but we want this entry to be alphabetized under \enquote*{C} rather than \enquote*{T}. There's also an isbn field}, } ^D --- nocite: "[@*]" references: - annote: This is a manual entry without an author or editor. Note the label field in the database file which is provided for author-year citation styles. Also note the sorttitle and indextitle fields. By default, all entries without an author or editor are alphabetized by title but we want this entry to be alphabetized under 'C' rather than 'T'. There's also an isbn field edition: 15 id: cms isbn: 0-226-10403-6 issued: 2003 language: en-US publisher: University of Chicago Press publisher-place: Chicago, Ill. title: "The Chicago manual of style: The essential guide for writers, editors, and publishers" title-short: Chicago manual of style type: book --- ``` ================================================ FILE: test/command/biblatex-coleridge.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2015-03-08: (Coleridge 1983) Coleridge, Samuel Taylor. 1983. *The Collected Works of Samuel Taylor Coleridge*. Edited by Kathleen Coburn, James Engell, and W. Jackson Bate. Vol. 7.2. Bollingen Series 75. London: Routledge and Kegan Paul. Formatted with pandoc and apa.csl, 2015-03-08: (Coleridge, 1983) Coleridge, S. T. (1983). *The collected works of Samuel Taylor Coleridge*. (K. Coburn, J. Engell, & W. J. Bate, Eds.) (Vol. 7.2). London: Routledge and Kegan Paul. NOTES: - volume-title currently not implemented by chicago-author-date.csl and apa.csl. } @Book{coleridge, author = {Coleridge, Samuel Taylor}, title = {Biographia literaria, or {Biographical} sketches of my literary life and opinions}, date = 1983, editor = {Coburn, Kathleen and Engell, James and Bate, W. Jackson}, maintitle = {The collected works of {Samuel Taylor Coleridge}}, volume = 7, part = 2, series = {Bollingen Series}, number = 75, publisher = {Routledge {and} Kegan Paul}, location = {London}, hyphenation = {british}, indextitle = {Biographia literaria}, shorttitle = {Biographia literaria}, annotation = {One (partial) volume of a multivolume book. This is a book entry with a volume and a part field which explicitly refers to the second (physical) part of the seventh (logical) volume. Also note the series and number fields}, } ^D --- nocite: "[@*]" references: - annote: One (partial) volume of a multivolume book. This is a book entry with a volume and a part field which explicitly refers to the second (physical) part of the seventh (logical) volume. Also note the series and number fields author: - family: Coleridge given: Samuel Taylor collection-number: 75 collection-title: Bollingen series editor: - family: Coburn given: Kathleen - family: Engell given: James - family: Bate given: W. Jackson id: coleridge issued: 1983 language: en-GB publisher: Routledge and Kegan Paul publisher-place: London title: The collected works of Samuel Taylor Coleridge type: book volume: 7.2 volume-title: Biographia literaria, or Biographical sketches of my literary life and opinions --- ``` ================================================ FILE: test/command/biblatex-companion.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Goossens, Mittelbach, and Samarin 1994) Goossens, Michel, Frank Mittelbach, and Alexander Samarin. 1994. *The LaTeX Companion*. 1st ed. Reading, Mass.: Addison-Wesley. Formatted with pandoc and apa.csl, 2013-10-23: (Goossens, Mittelbach, & Samarin, 1994) Goossens, M., Mittelbach, F., & Samarin, A. (1994). *The LaTeX companion* (1st ed.). Reading, Mass.: Addison-Wesley. } @Book{companion, author = {Goossens, Michel and Mittelbach, Frank and Samarin, Alexander}, title = {The {LaTeX} Companion}, date = 1994, edition = 1, publisher = {Addison-Wesley}, location = {Reading, Mass.}, pagetotal = 528, hyphenation = {american}, sorttitle = {LaTeX Companion}, indextitle = {LaTeX Companion, The}, shorttitle = {LaTeX Companion}, annotation = {A book with three authors. Note the formatting of the author list. By default, only the first name is reversed in the bibliography}, } ^D --- nocite: "[@*]" references: - annote: A book with three authors. Note the formatting of the author list. By default, only the first name is reversed in the bibliography author: - family: Goossens given: Michel - family: Mittelbach given: Frank - family: Samarin given: Alexander edition: 1 id: companion issued: 1994 language: en-US number-of-pages: 528 publisher: Addison-Wesley publisher-place: Reading, Mass. title: The LaTeX companion title-short: LaTeX companion type: book --- ``` ================================================ FILE: test/command/biblatex-cotton.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Cotton et al. 1999) Cotton, Frank Albert, Geoffrey Wilkinson, Carlos A. Murillio, and Manfred Bochmann. 1999. *Advanced Inorganic Chemistry*. 6th ed. Chichester: Wiley. Formatted with pandoc and apa.csl, 2013-10-23: (Cotton, Wilkinson, Murillio, & Bochmann, 1999) Cotton, F. A., Wilkinson, G., Murillio, C. A., & Bochmann, M. (1999). *Advanced inorganic chemistry* (6th ed.). Chichester: Wiley. } @Book{cotton, author = {Cotton, Frank Albert and Wilkinson, Geoffrey and Murillio, Carlos A. and Bochmann, Manfred}, title = {Advanced inorganic chemistry}, date = 1999, edition = 6, publisher = {Wiley}, location = {Chichester}, hyphenation = {british}, annotation = {A book entry with \arabic{author} authors and an edition field. By default, long author and editor lists are automatically truncated. This is configurable}, } ^D --- nocite: "[@*]" references: - annote: A book entry with `\arabic{author}`{=latex} authors and an edition field. By default, long author and editor lists are automatically truncated. This is configurable author: - family: Cotton given: Frank Albert - family: Wilkinson given: Geoffrey - family: Murillio given: Carlos A. - family: Bochmann given: Manfred edition: 6 id: cotton issued: 1999 language: en-GB publisher: Wiley publisher-place: Chichester title: Advanced inorganic chemistry type: book --- ``` ================================================ FILE: test/command/biblatex-crossref-inbook-mvbook.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ crossref, directly from inbook to mvbook } @inbook{inbook-1, Crossref = {mvbook-1}, Title = {Macbeth [title field of inbook-1]}, Date = {1975}, Volume = {3}, Chapter = {7}, Pages = {100-200}, } @mvbook{mvbook-1, Author = {Shakespeare}, Date = {1970/1980}, Title = {Collected Works [title field of mvbook-1]}, Location = {Location}, Publisher = {Publisher}, Volumes = {4}, } ^D --- nocite: "[@*]" references: - author: - family: Shakespeare chapter-number: 7 container-author: - family: Shakespeare container-title: Collected works \[title field of mvbook-1\] id: inbook-1 issued: 1975 number-of-volumes: 4 page: 100-200 publisher: Publisher publisher-place: Location title: Macbeth \[title field of inbook-1\] type: chapter volume: 3 - author: - family: Shakespeare id: mvbook-1 issued: 1970/1980 number-of-volumes: 4 publisher: Publisher publisher-place: Location title: Collected works \[title field of mvbook-1\] type: book --- ``` ================================================ FILE: test/command/biblatex-crossref-nested.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Nested cross-references (see biber manual v 1.7) } @bookinbook{bookinbook-1, Crossref = {book-1}, Title = {Macbeth [title field of bookinbook-1]}, Chapter = {7}, Pages = {100-200}, } @inbook{inbook-1, Crossref = {book-1}, Title = {Macbeth [title field of inbook-1]}, Chapter = {7}, Pages = {100-200}, } @book{book-1, Crossref = {mvbook-1}, Date = {1975}, Title = {Tragedies [title field of book-1]}, Volume = {3} } @mvbook{mvbook-1, Author = {Shakespeare}, Date = {1970/1980}, Title = {Collected Works [title field of mvbook-1]}, Location = {Location}, Publisher = {Publisher}, Volumes = {4} } ^D --- nocite: "[@*]" references: - author: - family: Shakespeare chapter-number: 7 container-author: - family: Shakespeare container-title: Collected works \[title field of mvbook-1\] id: bookinbook-1 issued: 1975 number-of-volumes: 4 page: 100-200 publisher: Publisher publisher-place: Location title: Macbeth \[title field of bookinbook-1\] type: chapter volume: 3 volume-title: Tragedies \[title field of book-1\] - author: - family: Shakespeare chapter-number: 7 container-author: - family: Shakespeare container-title: Collected works \[title field of mvbook-1\] id: inbook-1 issued: 1975 number-of-volumes: 4 page: 100-200 publisher: Publisher publisher-place: Location title: Macbeth \[title field of inbook-1\] type: chapter volume: 3 volume-title: Tragedies \[title field of book-1\] - author: - family: Shakespeare container-author: - family: Shakespeare id: book-1 issued: 1975 number-of-volumes: 4 publisher: Publisher publisher-place: Location title: Collected works \[title field of mvbook-1\] type: book volume: 3 volume-title: Tragedies \[title field of book-1\] - author: - family: Shakespeare id: mvbook-1 issued: 1970/1980 number-of-volumes: 4 publisher: Publisher publisher-place: Location title: Collected works \[title field of mvbook-1\] type: book --- ``` ================================================ FILE: test/command/biblatex-ctan.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (“CTAN: The Comprehensive TeX Archive Network” 2006) “CTAN: The Comprehensive TeX Archive Network.” 2006. . Formatted with pandoc and apa.csl, 2013-10-23: (“CTAN: The Comprehensive TeX Archive Network,” 2006) CTAN: The Comprehensive TeX Archive Network. (2006). Retrieved October 01, 2006, from NOTES: - biblio2yaml - if there is no shorttitle, but title and subtitle, the title alone should also be mapped to title-short - citeproc - citeproc should use title-short (if available) instead of title for in-text citations when there is no author } @Online{ctan, title = {{CTAN}}, date = 2006, url = {http://www.ctan.org}, subtitle = {{The Comprehensive TeX Archive Network}}, urldate = {2006-10-01}, label = {CTAN}, hyphenation = {american}, annotation = {This is an online entry. The \textsc{url}, which is given in the url field, is transformed into a clickable link if hyperref support has been enabled. Note the format of the urldate field (yyyy-mm-dd) in the database file. Also note the label field which may be used as a fallback by citation styles which need an author and\slash or a year}, } ^D --- nocite: "[@*]" references: - accessed: 2006-10-01 annote: This is an online entry. The [url]{.smallcaps}, which is given in the url field, is transformed into a clickable link if hyperref support has been enabled. Note the format of the urldate field (yyyy-mm-dd) in the database file. Also note the label field which may be used as a fallback by citation styles which need an author and/or a year id: ctan issued: 2006 language: en-US title: "CTAN: The Comprehensive TeX Archive Network" title-short: CTAN type: webpage url: "http://www.ctan.org" --- ``` ================================================ FILE: test/command/biblatex-dates.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ - Dates - Not included in tests: - malformed dates and date ranges - literal dates ("13th century"; not supported by biblatex) - seasons (would have to come from parsing the issue field) - uncertain dates (use "doubtfuldate" from biblatex-apa?) - dates with single-digit day or month (not supported by biblatex) - negative dates (not supported by biblatex) - Note: biblatex supports years < 1000 but only if padded with leading zeros - TODO: - either biblio2yaml or, probably better, citeproc should strip leading zeros from days and months (CSL can add leading zeros to its output, but not remove them when they are in its input data) } @article{year-month-old, Author = {Author, Al}, Journal = {Journal}, Month = aug, Title = {Year and Month, bibtex style, supported by biblatex for backwards compatibility}, Year = {1999}} @article{year-month-new, Author = {Author, Al}, Journal = {Journal}, Month = {08}, Title = {Year and Month, biblatex style; note that biblatex does not have a ``day'' field}, Year = {1999}} @article{dates, Author = {Author, Al}, Date = {2012-12-13}, Eventdate = {2011-10-03}, Journal = {Journal}, Month = may, Origdate = {1888-10-01}, Title = {Dates, default biblatex style; year, month to be ignored if date exists}, Urldate = {1999-05-23}, Year = {9999}} @article{date-ranges-different-years, Author = {Author, Al}, Date = {1999-10-14/2010-01-23}, Eventdate = {1999-12/2000-01}, Journal = {Journal}, Origdate = {1888-01-02/1913-12-13}, Title = {Date ranges; different years}, Urldate = {2012-10-12/2013-01-31}} @article{date-ranges-same-year, Author = {Author, Al}, Date = {1999-10-14/1999-10-15}, Eventdate = {1999-10/1999-11}, Journal = {Journal}, Origdate = {1888-01-02/1888-12-13}, Title = {Date ranges; same year}, Urldate = {2012-10-31/2012-11-01}} @article{date-ranges-open, Author = {Author, Al}, Date = {1999-10-14/}, Eventdate = {1999-10/}, Journal = {Journal}, Origdate = {1888-01-02/}, Title = {Date ranges, open-ended}, Urldate = {2012-10-31/}} @article{dates-very-old, Author = {Author, Al}, Date = {0712-12-13}, Eventdate = {0311-10-03}, Journal = {Journal}, Month = may, Origdate = {0088-10-01}, Title = {Dates, year less than 1000}, Urldate = {0999-12-14}} ^D --- nocite: "[@*]" references: - author: - family: Author given: Al container-title: Journal id: year-month-old issued: 1999-08 title: Year and month, bibtex style, supported by biblatex for backwards compatibility type: article-journal - author: - family: Author given: Al container-title: Journal id: year-month-new issued: 1999-08 title: Year and month, biblatex style; note that biblatex does not have a "day" field type: article-journal - accessed: 1999-05-23 author: - family: Author given: Al container-title: Journal event-date: 2011-10-03 id: dates issued: 2012-12-13 original-date: 1888-10-01 title: Dates, default biblatex style; year, month to be ignored if date exists type: article-journal - accessed: 2012-10-12/2013-01-31 author: - family: Author given: Al container-title: Journal event-date: 1999-12/2000-01 id: date-ranges-different-years issued: 1999-10-14/2010-01-23 original-date: 1888-01-02/1913-12-13 title: Date ranges; different years type: article-journal - accessed: 2012-10-31/2012-11-01 author: - family: Author given: Al container-title: Journal event-date: 1999-10/1999-11 id: date-ranges-same-year issued: 1999-10-14/1999-10-15 original-date: 1888-01-02/1888-12-13 title: Date ranges; same year type: article-journal - accessed: 2012-10-31/ author: - family: Author given: Al container-title: Journal event-date: 1999-10/ id: date-ranges-open issued: 1999-10-14/ original-date: 1888-01-02/ title: Date ranges, open-ended type: article-journal - accessed: 0999-12-14 author: - family: Author given: Al container-title: Journal event-date: 0311-10-03 id: dates-very-old issued: 0712-12-13 original-date: 0088-10-01 title: Dates, year less than 1000 type: article-journal --- ``` ================================================ FILE: test/command/biblatex-doody.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Doody 1974) (Matuz 1990) Doody, Terrence. 1974. “Hemingway’s Style and Jake’s Narration.” *The Journal of Narrative Technique* 4 (3): 212–225. Matuz, Roger, ed. 1990. *Contemporary Literary Criticism*. Vol. 61. Detroit: Gale. Formatted with pandoc and apa.csl, 2013-10-23: (Doody, 1974) (Matuz, 1990) Doody, T. (1974). Hemingway’s style and Jake’s narration. *The Journal of Narrative Technique*, *4*(3), 212–225. Matuz, R. (Ed.). (1990). *Contemporary literary criticism* (Vol. 61, pp. 204–208). Detroit: Gale. NOTES - biblio2yaml - contains fields “related” and “relatedstring”. In principle, these could be appended to CSL "note", if citeproc can handle citations in the bibliography ... } @Article{doody, author = {Doody, Terrence}, title = {Hemingway's Style and {Jake}'s Narration}, year = 1974, volume = 4, number = 3, pages = {212-225}, hyphenation = {american}, related = {matuz:doody}, relatedstring= {\autocap{e}xcerpt in}, journal = {The Journal of Narrative Technique}, annotation = {An article entry cited as an excerpt from a collection entry. Note the format of the related and relatedstring fields}, } @Collection{matuz:doody, editor = {Matuz, Roger}, title = {Contemporary Literary Criticism}, year = 1990, volume = 61, publisher = {Gale}, location = {Detroit}, pages = {204-208}, hyphenation = {american}, annotation = {A collection entry providing the excerpt information for the doody entry. Note the format of the pages field}, } ^D --- nocite: "[@*]" references: - annote: An article entry cited as an excerpt from a collection entry. Note the format of the related and relatedstring fields author: - family: Doody given: Terrence container-title: The Journal of Narrative Technique id: doody issue: 3 issued: 1974 language: en-US page: 212-225 title: Hemingway's style and Jake's narration type: article-journal volume: 4 - annote: A collection entry providing the excerpt information for the doody entry. Note the format of the pages field editor: - family: Matuz given: Roger id: "matuz:doody" issued: 1990 language: en-US page: 204-208 publisher: Gale publisher-place: Detroit title: Contemporary literary criticism type: book volume: 61 --- ``` ================================================ FILE: test/command/biblatex-edtf-date.md ================================================ ``` % pandoc -f biblatex -t markdown -s Note that current CSL doesn't give us a way to distinguish between /open and /unknown, so item3-3 and item3-4 get parsed similarly. That should change in CSL 1.1, and then this test should be revised. @article{item3-3, date={1998/unknown}} @article{item3-4, date={1999/open}} @article{item3-10, date={2004-04-05T14:34:00}} @article{item5-1, date={0000}} @article{item5-2, date={-0876}} @article{item5-3, date={-0877/-0866}} @article{item5-5, date={-0343-02}} @article{item5-8, date={1723~}} @article{item5-9, date={1723?}} @article{item5-10, date={1723?~}} @article{item5-11, date={2004-22}} @article{item5-12, date={2004-24}} @article{item5-13, date={20uu}} @article{item5-14, date={y-123456789}} ^D --- nocite: "[@*]" references: - id: item3-3 issued: 1998/ type: article-journal - id: item3-4 issued: 1999/ type: article-journal - id: item3-10 issued: 2004-04-05 type: article-journal - id: item5-1 type: article-journal - id: item5-2 issued: "-0876" type: article-journal - id: item5-3 issued: "-0877/-0866" type: article-journal - id: item5-5 issued: "-0343-02" type: article-journal - id: item5-8 issued: 1723\~ type: article-journal - id: item5-9 issued: 1723 type: article-journal - id: item5-10 issued: 1723\~ type: article-journal - id: item5-11 issued: 2004-22 type: article-journal - id: item5-12 issued: 2004-24 type: article-journal - id: item5-13 issued: 2000/2099 type: article-journal - id: item5-14 issued: y-123456789 type: article-journal --- ``` ================================================ FILE: test/command/biblatex-escapedquotes.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{From jgm/pandoc#1568. Double quotes escaped using {"}.} @ARTICLE{Koff2009-gn, title = "Pan-Canadian evaluation of irreversible compression ratios ({"}lossy{"} compression) for development of national guidelines", author = "Koff, David and Bak, Peter and Brownrigg, Paul and Hosseinzadeh, Danoush and Khademi, April and Kiss, Alex and Lepanto, Luigi and Michalak, Tracy and Shulman, Harry and Volkening, Andrew", affiliation = "Sunnybrook Health Sciences Centre, 2075 Bayview Ave., Toronto, ON, M4N 3M5, Canada. dkoffmcmaster.ca", journal = "Journal of digital imaging", } ^D --- nocite: "[@*]" references: - author: - family: Koff given: David - family: Bak given: Peter - family: Brownrigg given: Paul - family: Hosseinzadeh given: Danoush - family: Khademi given: April - family: Kiss given: Alex - family: Lepanto given: Luigi - family: Michalak given: Tracy - family: Shulman given: Harry - family: Volkening given: Andrew container-title: Journal of digital imaging id: Koff2009-gn title: Pan-canadian evaluation of irreversible compression ratios (\"lossy\" compression) for development of national guidelines type: article-journal --- ``` ================================================ FILE: test/command/biblatex-formatting.md ================================================ ``` % pandoc -f biblatex -t markdown -s @article{item1, Title = {The Title: \textit{italics}, \textbf{bold}, \textsubscript{subscript}, \textsuperscript{superscript}, \textsc{small-caps}} } ^D --- nocite: "[@*]" references: - id: item1 title: "The title: *Italics*, **bold**, ~subscript~, ^superscript^, [small-caps]{.smallcaps}" title-short: The title type: article-journal --- ``` ================================================ FILE: test/command/biblatex-gaonkar-in.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Gaonkar 2001) Gaonkar, Dilip Parameshwar. 2001. “On Alternative Modernities.” In *Alternative Modernities*, edited by Dilip Parameshwar Gaonkar, 1–23. Durham; London: Duke University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Gaonkar, 2001) Gaonkar, D. P. (2001). On alternative modernities. In D. P. Gaonkar (Ed.), *Alternative modernities* (pp. 1–23). Durham; London: Duke University Press. } @InCollection{gaonkar:in, author = {Gaonkar, Dilip Parameshwar}, editor = {Gaonkar, Dilip Parameshwar}, title = {On Alternative Modernities}, date = 2001, booktitle = {Alternative Modernities}, publisher = {Duke University Press}, location = {Durham and London}, isbn = {0-822-32714-7}, pages = {1-23}, } ^D --- nocite: "[@*]" references: - author: - family: Gaonkar given: Dilip Parameshwar container-title: Alternative modernities editor: - family: Gaonkar given: Dilip Parameshwar id: "gaonkar:in" isbn: 0-822-32714-7 issued: 2001 page: 1-23 publisher: Duke University Press publisher-place: Durham; London title: On alternative modernities type: chapter --- ``` ================================================ FILE: test/command/biblatex-gaonkar.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Gaonkar 2001) Gaonkar, Dilip Parameshwar, ed. 2001. *Alternative Modernities*. Durham; London: Duke University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Gaonkar, 2001) Gaonkar, D. P. (Ed.). (2001). *Alternative modernities*. Durham; London: Duke University Press. } @Collection{gaonkar, editor = {Gaonkar, Dilip Parameshwar}, title = {Alternative Modernities}, date = 2001, publisher = {Duke University Press}, location = {Durham and London}, isbn = {0-822-32714-7}, hyphenation = {american}, annotation = {This is a collection entry. Note the format of the location field in the database file as well as the isbn field}, } ^D --- nocite: "[@*]" references: - annote: This is a collection entry. Note the format of the location field in the database file as well as the isbn field editor: - family: Gaonkar given: Dilip Parameshwar id: gaonkar isbn: 0-822-32714-7 issued: 2001 language: en-US publisher: Duke University Press publisher-place: Durham; London title: Alternative modernities type: book --- ``` ================================================ FILE: test/command/biblatex-geer.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Geer 1985) Geer, Ingrid de. 1985. “Earl, Saint, Bishop, Skald – and Music: The Orkney Earldom of the Twelfth Century. A Musicological Study.” PhD thesis, Uppsala: Uppsala Universitet. Formatted with pandoc and apa.csl, 2013-10-23: (Geer, 1985) Geer, I. de. (1985). *Earl, saint, bishop, skald – and music: The Orkney earldom of the twelfth century. A musicological study* (PhD thesis). Uppsala Universitet, Uppsala. } @Thesis{geer, author = {de Geer, Ingrid}, title = {Earl, Saint, Bishop, Skald~-- and Music}, type = {phdthesis}, institution = {Uppsala Universitet}, date = 1985, subtitle = {The {Orkney} Earldom of the Twelfth Century. {A} Musicological Study}, location = {Uppsala}, options = {useprefix=false}, hyphenation = {british}, annotation = {This is a typical thesis entry for a PhD thesis. Note the type field in the database file which uses a localization key. Also note the format of the printed name and compare the useprefix option in the options field as well as vangennep}, } ^D --- nocite: "[@*]" references: - annote: This is a typical thesis entry for a PhD thesis. Note the type field in the database file which uses a localization key. Also note the format of the printed name and compare the useprefix option in the options field as well as vangennep author: - dropping-particle: de family: Geer given: Ingrid genre: PhD thesis id: geer issued: 1985 language: en-GB publisher: Uppsala Universitet publisher-place: Uppsala title: "Earl, saint, bishop, skald -- and music: The Orkney earldom of the twelfth century. A musicological study" title-short: Earl, saint, bishop, skald -- and music type: thesis --- ``` ================================================ FILE: test/command/biblatex-gerhardt.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Gerhardt 2000) Gerhardt, Michael J. 2000. *The Federal Appointments Process: A Constitutional and Historical Analysis*. Durham; London: Duke University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Gerhardt, 2000) Gerhardt, M. J. (2000). *The federal appointments process: A constitutional and historical analysis*. Durham; London: Duke University Press. } @Book{gerhardt, author = {Gerhardt, Michael J.}, title = {The Federal Appointments Process}, date = 2000, publisher = {Duke University Press}, location = {Durham and London}, hyphenation = {american}, sorttitle = {Federal Appointments Process}, indextitle = {Federal Appointments Process, The}, subtitle = {A Constitutional and Historical Analysis}, shorttitle = {Federal Appointments Process}, annotation = {This is a book entry. Note the format of the location field as well as the sorttitle and indextitle fields}, } ^D --- nocite: "[@*]" references: - annote: This is a book entry. Note the format of the location field as well as the sorttitle and indextitle fields author: - family: Gerhardt given: Michael J. id: gerhardt issued: 2000 language: en-US publisher: Duke University Press publisher-place: Durham; London title: "The federal appointments process: A constitutional and historical analysis" title-short: Federal appointments process type: book --- ``` ================================================ FILE: test/command/biblatex-gillies.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Gillies 1933) Gillies, Alexander. 1933. “Herder and the Preparation of Goethe’s Idea of World Literature.” *Publications of the English Goethe Society, New Series* 9: 46–67. Formatted with pandoc and apa.csl, 2013-10-23: (Gillies, 1933) Gillies, A. (1933). Herder and the preparation of Goethe’s idea of world literature. *Publications of the English Goethe Society, new series*, *9*, 46–67. NOTES: - biblio2yaml - "new series" is not pretty, but it’s the best we can do at the moment, given the limitations of CSL. } @Article{gillies, author = {Gillies, Alexander}, title = {Herder and the Preparation of {Goethe}'s Idea of World Literature}, journaltitle = {Publications of the English Goethe Society}, date = 1933, series = {newseries}, volume = 9, pages = {46-67}, hyphenation = {british}, annotation = {An article entry with a series and a volume field. Note that format of the series field in the database file}, } ^D --- nocite: "[@*]" references: - annote: An article entry with a series and a volume field. Note that format of the series field in the database file author: - family: Gillies given: Alexander collection-title: New series container-title: Publications of the English Goethe Society id: gillies issued: 1933 language: en-GB page: 46-67 title: Herder and the preparation of Goethe's idea of world literature type: article-journal volume: 9 --- ``` ================================================ FILE: test/command/biblatex-glashow.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Glashow 1961) Glashow, Sheldon. 1961. “Partial Symmetries of Weak Interactions.” *Nucl. Phys.* 22: 579–588. Formatted with pandoc and apa.csl, 2013-10-23: (Glashow, 1961) Glashow, S. (1961). Partial symmetries of weak interactions. *Nucl. Phys.*, *22*, 579–588. } @Article{glashow, author = {Glashow, Sheldon}, title = {Partial Symmetries of Weak Interactions}, journaltitle = {Nucl.~Phys.}, date = 1961, volume = 22, pages = {579-588}, } ^D --- nocite: "[@*]" references: - author: - family: Glashow given: Sheldon container-title: Nucl. Phys. id: glashow issued: 1961 page: 579-588 title: Partial symmetries of weak interactions type: article-journal volume: 22 --- ``` ================================================ FILE: test/command/biblatex-gonzalez.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Gonzalez 2001) Gonzalez, Ray. 2001. *The Ghost of John Wayne and Other Stories*. Tucson: The University of Arizona Press. Formatted with pandoc and apa.csl, 2013-10-23: (Gonzalez, 2001) Gonzalez, R. (2001). *The ghost of John Wayne and other stories*. Tucson: The University of Arizona Press. } @Book{gonzalez, author = {Gonzalez, Ray}, title = {The Ghost of {John Wayne} and Other Stories}, date = 2001, publisher = {The University of Arizona Press}, location = {Tucson}, isbn = {0-816-52066-6}, hyphenation = {american}, sorttitle = {Ghost of John Wayne and Other Stories}, indextitle = {Ghost of {John Wayne} and Other Stories, The}, shorttitle = {Ghost of {John Wayne}}, annotation = {A collection of short stories. This is a book entry. Note the sorttitle and indextitle fields in the database file. There's also an isbn field}, } ^D --- nocite: "[@*]" references: - annote: A collection of short stories. This is a book entry. Note the sorttitle and indextitle fields in the database file. There's also an isbn field author: - family: Gonzalez given: Ray id: gonzalez isbn: 0-816-52066-6 issued: 2001 language: en-US publisher: The University of Arizona Press publisher-place: Tucson title: The ghost of John Wayne and other stories title-short: Ghost of John Wayne type: book --- ``` ================================================ FILE: test/command/biblatex-hammond.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Hammond 1997) Hammond, Christopher. 1997. *The Basics of Crystallography and Diffraction*. Oxford: International Union of Crystallography; Oxford University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Hammond, 1997) Hammond, C. (1997). *The basics of crystallography and diffraction*. Oxford: International Union of Crystallography; Oxford University Press. } @Book{hammond, author = {Hammond, Christopher}, title = {The basics of crystallography and diffraction}, date = 1997, publisher = {International Union of Crystallography and Oxford University Press}, location = {Oxford}, hyphenation = {british}, sorttitle = {Basics of crystallography and diffraction}, indextitle = {Basics of crystallography and diffraction, The}, shorttitle = {Crystallography and diffraction}, annotation = {A book entry. Note the sorttitle and indextitle fields as well as the format of the publisher field}, } ^D --- nocite: "[@*]" references: - annote: A book entry. Note the sorttitle and indextitle fields as well as the format of the publisher field author: - family: Hammond given: Christopher id: hammond issued: 1997 language: en-GB publisher: International Union of Crystallography; Oxford University Press publisher-place: Oxford title: The basics of crystallography and diffraction title-short: Crystallography and diffraction type: book --- ``` ================================================ FILE: test/command/biblatex-herrmann.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Herrmann et al. 2006) Herrmann, Wolfgang A., Karl Öfele, Sabine K. Schneider, Eberhardt Herdtweck, and Stephan D. Hoffmann. 2006. “A Carbocyclic Carbene as an Efficient Catalyst Ligand for C–C Coupling Reactions.” *Angew. Chem. Int. Ed.* 45 (23): 3859–3862. Formatted with pandoc and apa.csl, 2013-10-23: (Herrmann, Öfele, Schneider, Herdtweck, & Hoffmann, 2006) Herrmann, W. A., Öfele, K., Schneider, S. K., Herdtweck, E., & Hoffmann, S. D. (2006). A carbocyclic carbene as an efficient catalyst ligand for C–C coupling reactions. *Angew. Chem. Int. Ed.*, *45*(23), 3859–3862. } @string{ anch-ie = {Angew.~Chem. Int.~Ed.} } @Article{herrmann, author = {Herrmann, Wolfgang A. and {\"O}fele, Karl and Schneider, Sabine K. and Herdtweck, Eberhardt and Hoffmann, Stephan D.}, title = {A carbocyclic carbene as an efficient catalyst ligand for {C--C} coupling reactions}, journaltitle = anch-ie, date = 2006, volume = 45, number = 23, pages = {3859-3862}, indextitle = {Carbocyclic carbene as an efficient catalyst, A}, } ^D --- nocite: "[@*]" references: - author: - family: Herrmann given: Wolfgang A. - family: Öfele given: Karl - family: Schneider given: Sabine K. - family: Herdtweck given: Eberhardt - family: Hoffmann given: Stephan D. container-title: Angew. Chem. Int. Ed. id: herrmann issue: 23 issued: 2006 page: 3859-3862 title: A carbocyclic carbene as an efficient catalyst ligand for C--C coupling reactions type: article-journal volume: 45 --- ``` ================================================ FILE: test/command/biblatex-hyman.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Hyman 1981) Hyman, Arthur. 1981. “Aristotle’s Theory of the Intellect and Its Interpretation by Averroes.” In *Studies in Aristotle*, edited by Dominic J. O’Meara, 161–191. Studies in Philosophy and the History of Philosophy 9. Washington, D.C.: The Catholic University of America Press. Formatted with pandoc and apa.csl, 2013-10-23: (Hyman, 1981) Hyman, A. (1981). Aristotle’s theory of the intellect and its interpretation by Averroes. In D. J. O’Meara (Ed.), *Studies in Aristotle* (pp. 161–191). Washington, D.C.: The Catholic University of America Press. } @InCollection{hyman, author = {Arthur Hyman}, editor = {O'Meara, Dominic J.}, title = {Aristotle's Theory of the Intellect and its Interpretation by {Averroes}}, date = 1981, booktitle = {Studies in {Aristotle}}, series = {Studies in Philosophy and the History of Philosophy}, number = 9, publisher = {The Catholic University of America Press}, location = {Washington, D.C.}, pages = {161-191}, keywords = {secondary}, hyphenation = {american}, indextitle = {Aristotle's Theory of the Intellect}, shorttitle = {Aristotle's Theory of the Intellect}, annotation = {An incollection entry with a series and number field}, } ^D --- nocite: "[@*]" references: - annote: An incollection entry with a series and number field author: - family: Hyman given: Arthur collection-number: 9 collection-title: Studies in philosophy and the history of philosophy container-title: Studies in Aristotle editor: - family: O'Meara given: Dominic J. id: hyman issued: 1981 keyword: secondary language: en-US page: 161-191 publisher: The Catholic University of America Press publisher-place: Washington, D.C. title: Aristotle's theory of the intellect and its interpretation by Averroes title-short: Aristotle's theory of the intellect type: chapter --- ``` ================================================ FILE: test/command/biblatex-iliad.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Homer 2004) Homer. 2004. *Die Ilias*. Translated by Wolfgang Schadewaldt. 3rd ed. Düsseldorf; Zürich: Artemis & Winkler. Formatted with pandoc and apa.csl, 2013-10-23: (Homer, 2004) Homer. (2004). *Die Ilias*. (W. Schadewaldt, trans.) (3rd ed.). Düsseldorf; Zürich: Artemis & Winkler. } @Book{iliad, author = {Homer}, title = {Die Ilias}, date = 2004, translator = {Schadewaldt, Wolfgang}, introduction = {Latacz, Joachim}, edition = 3, publisher = {Artemis \& Winkler}, location = {D{\"u}sseldorf and Z{\"u}rich}, hyphenation = {german}, sorttitle = {Ilias}, indextitle = {Ilias, Die}, shorttitle = {Ilias}, annotation = {A German translation of the \emph{Iliad}. Note the translator and introduction fields and the format of the location field in the database file. Also note the sorttitle and indextitle fields}, } ^D --- nocite: "[@*]" references: - annote: A German translation of the *Iliad*. Note the translator and introduction fields and the format of the location field in the database file. Also note the sorttitle and indextitle fields author: - family: Homer edition: 3 id: iliad issued: 2004 language: de-DE publisher: Artemis & Winkler publisher-place: Düsseldorf; Zürich title: Die Ilias title-short: Ilias translator: - family: Schadewaldt given: Wolfgang type: book --- ``` ================================================ FILE: test/command/biblatex-inbook-title-booktitle-maintitle-series-2.md ================================================ ``` % pandoc -f biblatex -t markdown -s @inbook{item1, Author = {Author, Al}, Bookauthor = {Bookauthor, Bob}, Booksubtitle = {Booksubtitle}, Booktitle = {Booktitle}, Booktitleaddon = {Booktitleaddon}, Date = {2011}, Hyphenation = {french}, Location = {Location}, Mainsubtitle = {Mainsubtitle}, Maintitle = {Maintitle}, Maintitleaddon = {Maintitleaddon}, Number = {3}, Publisher = {Publisher}, Series = {Series}, Subtitle = {Subtitle}, Title = {Title of the ``inbook'' Entry}, Titleaddon = {Titleaddon}, Volume = {4}} ^D --- nocite: "[@*]" references: - author: - family: Author given: Al collection-number: 3 collection-title: Series container-author: - family: Bookauthor given: Bob container-title: "Maintitle: Mainsubtitle. Maintitleaddon" id: item1 issued: 2011 language: fr-FR publisher: Publisher publisher-place: Location title: "Title of the \"inbook\" Entry: Subtitle. Titleaddon" type: chapter volume: 4 volume-title: "Booktitle: Booksubtitle. Booktitleaddon" --- ``` ================================================ FILE: test/command/biblatex-inbook-title-booktitle-maintitle-series.md ================================================ ``` % pandoc -f biblatex -t markdown -s @inbook{item1, Author = {Author, Al}, Booksubtitle = {Booksubtitle}, Booktitle = {Booktitle}, Booktitleaddon = {Booktitleaddon}, Date = {2011}, Hyphenation = {french}, Location = {Location}, Mainsubtitle = {Mainsubtitle}, Maintitle = {Maintitle}, Maintitleaddon = {Maintitleaddon}, Number = {3}, Publisher = {Publisher}, Series = {Series}, Subtitle = {Subtitle}, Title = {Title of the ``inbook'' Entry}, Titleaddon = {Titleaddon}, Volume = {4}} ^D --- nocite: "[@*]" references: - author: - family: Author given: Al collection-number: 3 collection-title: Series container-title: "Maintitle: Mainsubtitle. Maintitleaddon" id: item1 issued: 2011 language: fr-FR publisher: Publisher publisher-place: Location title: "Title of the \"inbook\" Entry: Subtitle. Titleaddon" type: chapter volume: 4 volume-title: "Booktitle: Booksubtitle. Booktitleaddon" --- ``` ================================================ FILE: test/command/biblatex-inbook.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib TODO / citeproc: in biblatex "inbook" entries, citeproc should suppress bookauthor = CSL container-author if identical with author. -- See annotation in kant:kpv. } @string{dtv = {Deutscher Taschenbuch-Verlag}} @inbook{kant:kpv, Annotation = {An edition of Kant's \emph{Collected Works}, volume five. This is an inbook entry which explicitly refers to the \emph{Critique of Practical Reason} only, not to the entire fifth volume. Note the author and bookauthor fields in the database file. By default, the bookauthor is omitted if the values of the author and bookauthor fields are identical}, Author = {Kant, Immanuel}, Bookauthor = {Kant, Immanuel}, Booktitle = {Kritik der praktischen Vernunft. Kritik der Urtheilskraft}, Date = 1968, Hyphenation = {german}, Location = {Berlin}, Maintitle = {Kants Werke. Akademie Textausgabe}, Pages = {1-163}, Publisher = {Walter de Gruyter}, Shorthand = {KpV}, Shorttitle = {Kritik der praktischen Vernunft}, Title = {Kritik der praktischen Vernunft}, Volume = 5} @inbook{kant:ku, Annotation = {An edition of Kant's \emph{Collected Works}, volume five. This is an inbook entry which explicitly refers to the \emph{Critique of Judgment} only, not to the entire fifth volume}, Author = {Kant, Immanuel}, Bookauthor = {Kant, Immanuel}, Booktitle = {Kritik der praktischen Vernunft. Kritik der Urtheilskraft}, Date = 1968, Hyphenation = {german}, Location = {Berlin}, Maintitle = {Kants Werke. Akademie Textausgabe}, Pages = {165-485}, Publisher = {Walter de Gruyter}, Shorthand = {KU}, Title = {Kritik der Urtheilskraft}, Volume = 5} @inbook{nietzsche:historie, Annotation = {A single essay from the critical edition of Nietzsche's works. This inbook entry explicitly refers to an essay found in the first volume. Note the title, booktitle, and maintitle fields. Also note the sorttitle and sortyear fields. We want this entry to be listed after the entry referring to the entire first volume}, Author = {Nietzsche, Friedrich}, Bookauthor = {Nietzsche, Friedrich}, Booktitle = {Die Geburt der Tragödie. Unzeitgemäße Betrachtungen I--IV. Nachgelassene Schriften 1870--1973}, Date = 1988, Editor = {Colli, Giorgio and Montinari, Mazzino}, Hyphenation = {german}, Indexsorttitle = {Vom Nutzen und Nachtheil der Historie fur das Leben}, Indextitle = {Vom Nutzen und Nachtheil der Historie für das Leben}, Location = {München and Berlin and New York}, Mainsubtitle = {Kritische Studienausgabe}, Maintitle = {Sämtliche Werke}, Pages = {243-334}, Publisher = dtv # { and Walter de Gruyter}, Shorttitle = {Vom Nutzen und Nachtheil der Historie}, Sorttitle = {Werke-01-243}, Sortyear = {1988-2}, Subtitle = {Vom Nutzen und Nachtheil der Historie für das Leben}, Title = {Unzeitgemässe Betrachtungen. Zweites Stück}, Volume = 1} ^D --- nocite: "[@*]" references: - annote: An edition of Kant's *Collected Works*, volume five. This is an inbook entry which explicitly refers to the *Critique of Practical Reason* only, not to the entire fifth volume. Note the author and bookauthor fields in the database file. By default, the bookauthor is omitted if the values of the author and bookauthor fields are identical author: - family: Kant given: Immanuel container-author: - family: Kant given: Immanuel container-title: Kants Werke. Akademie Textausgabe id: "kant:kpv" issued: 1968 language: de-DE page: 1-163 publisher: Walter de Gruyter publisher-place: Berlin title: Kritik der praktischen Vernunft title-short: Kritik der praktischen Vernunft type: chapter volume: 5 volume-title: Kritik der praktischen Vernunft. Kritik der Urtheilskraft - annote: An edition of Kant's *Collected Works*, volume five. This is an inbook entry which explicitly refers to the *Critique of Judgment* only, not to the entire fifth volume author: - family: Kant given: Immanuel container-author: - family: Kant given: Immanuel container-title: Kants Werke. Akademie Textausgabe id: "kant:ku" issued: 1968 language: de-DE page: 165-485 publisher: Walter de Gruyter publisher-place: Berlin title: Kritik der Urtheilskraft type: chapter volume: 5 volume-title: Kritik der praktischen Vernunft. Kritik der Urtheilskraft - annote: A single essay from the critical edition of Nietzsche's works. This inbook entry explicitly refers to an essay found in the first volume. Note the title, booktitle, and maintitle fields. Also note the sorttitle and sortyear fields. We want this entry to be listed after the entry referring to the entire first volume author: - family: Nietzsche given: Friedrich container-author: - family: Nietzsche given: Friedrich container-title: "Sämtliche Werke: Kritische Studienausgabe" editor: - family: Colli given: Giorgio - family: Montinari given: Mazzino id: "nietzsche:historie" issued: 1988 language: de-DE page: 243-334 publisher: Deutscher Taschenbuch-Verlag; Walter de Gruyter publisher-place: München; Berlin; New York title: "Unzeitgemässe Betrachtungen. Zweites Stück: Vom Nutzen und Nachtheil der Historie für das Leben" title-short: Vom Nutzen und Nachtheil der Historie type: chapter volume: 1 volume-title: Die Geburt der Tragödie. Unzeitgemäße Betrachtungen I--IV. Nachgelassene Schriften 1870--1973 --- ``` ================================================ FILE: test/command/biblatex-incollection-2.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib} @string{hup = {Harvard University Press}} @incollection{westfahl:space, Annotation = {A cross-referenced article from a collection. This is an incollection entry with a crossref field. Note the subtitle and indextitle fields}, Author = {Westfahl, Gary}, Crossref = {westfahl:frontier}, Hyphenation = {american}, Indextitle = {True Frontier, The}, Pages = {55-65}, Subtitle = {Confronting and Avoiding the Realities of Space in {American} Science Fiction Films}, Title = {The True Frontier}} @incollection{gaonkar:in, Author = {Gaonkar, Dilip Parameshwar}, Booktitle = {Alternative Modernities}, Date = 2001, Editor = {Gaonkar, Dilip Parameshwar}, Isbn = {0-822-32714-7}, Location = {Durham and London}, Pages = {1-23}, Publisher = {Duke University Press}, Title = {On Alternative Modernities}} @collection{westfahl:frontier, Annotation = {This is a collection entry. Note the format of the location field as well as the subtitle and booksubtitle fields}, Booksubtitle = {The Frontier Theme in Science Fiction}, Booktitle = {Space and Beyond}, Date = 2000, Editor = {Westfahl, Gary}, Hyphenation = {american}, Location = {Westport, Conn. and London}, Publisher = {Greenwood}, Subtitle = {The Frontier Theme in Science Fiction}, Title = {Space and Beyond}} ^D --- nocite: "[@*]" references: - annote: A cross-referenced article from a collection. This is an incollection entry with a crossref field. Note the subtitle and indextitle fields author: - family: Westfahl given: Gary container-title: "Space and beyond: The frontier theme in science fiction" editor: - family: Westfahl given: Gary id: "westfahl:space" issued: 2000 language: en-US page: 55-65 publisher: Greenwood publisher-place: Westport, Conn.; London title: "The true frontier: Confronting and avoiding the realities of space in American science fiction films" title-short: The true frontier type: chapter - author: - family: Gaonkar given: Dilip Parameshwar container-title: Alternative modernities editor: - family: Gaonkar given: Dilip Parameshwar id: "gaonkar:in" isbn: 0-822-32714-7 issued: 2001 page: 1-23 publisher: Duke University Press publisher-place: Durham; London title: On alternative modernities type: chapter - annote: This is a collection entry. Note the format of the location field as well as the subtitle and booksubtitle fields editor: - family: Westfahl given: Gary id: "westfahl:frontier" issued: 2000 language: en-US publisher: Greenwood publisher-place: Westport, Conn.; London title: "Space and beyond: The frontier theme in science fiction" title-short: Space and beyond type: book --- ``` ================================================ FILE: test/command/biblatex-incollection.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib} @string{hup = {Harvard University Press}} @incollection{brandt, Annotation = {An incollection entry with a series and a number. Note the format of the printed name and compare the useprefix option in the options field as well as vangennep. Also note the indextitle, and indexsorttitle fields}, Author = {von Brandt, Ahasver and Hoffmann, Erich}, Booktitle = {Europa im Hoch- und Spätmittelalter}, Date = 1987, Editor = {Seibt, Ferdinand}, Hyphenation = {german}, Indexsorttitle = {Nordischen Lander von der Mitte des 11. Jahrhunderts bis 1448}, Indextitle = {Nordischen Länder von der Mitte des 11.~Jahrhunderts bis 1448, Die}, Location = {Stuttgart}, Number = 2, Options = {useprefix=false}, Pages = {884-917}, Publisher = {Klett-Cotta}, Series = {Handbuch der europäischen Geschichte}, Shorttitle = {Die nordischen Länder}, Title = {Die nordischen Länder von der Mitte des 11.~Jahrhunderts bis 1448}} @incollection{hyman, Annotation = {An incollection entry with a series and number field}, Author = {Hyman, Arthur}, Booktitle = {Studies in {Aristotle}}, Date = 1981, Editor = {O'Meara, Dominic J.}, Hyphenation = {american}, Indextitle = {Aristotle's Theory of the Intellect}, Keywords = {secondary}, Location = {Washington, D.C.}, Number = 9, Pages = {161-191}, Publisher = {The Catholic University of America Press}, Series = {Studies in Philosophy and the History of Philosophy}, Shorttitle = {Aristotle's Theory of the Intellect}, Title = {Aristotle's Theory of the Intellect and its Interpretation by {Averroes}}} @incollection{pines, Annotation = {A typical incollection entry. Note the indextitle field}, Author = {Pines, Shlomo}, Booktitle = {Studies in Medieval {Jewish} History and Literature}, Date = 1979, Editor = {Twersky, Isadore}, Hyphenation = {american}, Indextitle = {Limitations of Human Knowledge According to Al-Farabi, ibn Bajja, and Maimonides, The}, Keywords = {secondary}, Location = {Cambridge, Mass.}, Pages = {82-109}, Publisher = hup, Shorttitle = {Limitations of Human Knowledge}, Title = {The Limitations of Human Knowledge According to {Al-Farabi}, {ibn Bajja}, and {Maimonides}}} ^D --- nocite: "[@*]" references: - annote: An incollection entry with a series and a number. Note the format of the printed name and compare the useprefix option in the options field as well as vangennep. Also note the indextitle, and indexsorttitle fields author: - dropping-particle: von family: Brandt given: Ahasver - family: Hoffmann given: Erich collection-number: 2 collection-title: Handbuch der europäischen Geschichte container-title: Europa im Hoch- und Spätmittelalter editor: - family: Seibt given: Ferdinand id: brandt issued: 1987 language: de-DE page: 884-917 publisher: Klett-Cotta publisher-place: Stuttgart title: Die nordischen Länder von der Mitte des 11. Jahrhunderts bis 1448 title-short: Die nordischen Länder type: chapter - annote: An incollection entry with a series and number field author: - family: Hyman given: Arthur collection-number: 9 collection-title: Studies in philosophy and the history of philosophy container-title: Studies in Aristotle editor: - family: O'Meara given: Dominic J. id: hyman issued: 1981 keyword: secondary language: en-US page: 161-191 publisher: The Catholic University of America Press publisher-place: Washington, D.C. title: Aristotle's theory of the intellect and its interpretation by Averroes title-short: Aristotle's theory of the intellect type: chapter - annote: A typical incollection entry. Note the indextitle field author: - family: Pines given: Shlomo container-title: Studies in medieval Jewish history and literature editor: - family: Twersky given: Isadore id: pines issued: 1979 keyword: secondary language: en-US page: 82-109 publisher: Harvard University Press publisher-place: Cambridge, Mass. title: The limitations of human knowledge according to Al-Farabi, [ibn Bajja]{.nocase}, and Maimonides title-short: Limitations of human knowledge type: chapter --- ``` ================================================ FILE: test/command/biblatex-inproceedings.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib} @string{cup = {Cambridge University Press}} @inproceedings{moraux, Annotation = {This is a typical inproceedings entry. Note the booksubtitle, shorttitle, indextitle, and indexsorttitle fields. Also note the eventdate field.}, Author = {Moraux, Paul}, Booktitle = {Aristotle on Mind and the Senses}, Booktitleaddon = {Proceedings of the Seventh Symposium Aristotelicum}, Date = 1979, Editor = {Lloyd, G. E. R. and Owen, G. E. L.}, Eventdate = 1975, Hyphenation = {french}, Indexsorttitle = {De Anima dans la tradition grecque}, Indextitle = {\emph{De Anima} dans la tradition grècque, Le}, Keywords = {secondary}, Location = {Cambridge}, Pages = {281-324}, Publisher = cup, Shorttitle = {\emph{De Anima} dans la tradition grècque}, Subtitle = {Quelques aspects de l'interpretation du traité, de Theophraste à Themistius}, Title = {Le \emph{De Anima} dans la tradition grècque}} @inproceedings{salam, Author = {Salam, Abdus}, Booksubtitle = {Relativistic groups and analyticity}, Booktitle = {Elementary particle theory}, Booktitleaddon = {Proceedings of the Eighth {Nobel} Symposium}, Date = 1968, Editor = {Svartholm, Nils}, Eventdate = {1968-05-19/1968-05-25}, Location = {Stockholm}, Pages = {367-377}, Publisher = {Almquist \& Wiksell}, Title = {Weak and Electromagnetic Interactions}, Venue = {Aspenäsgarden, Lerum}} ^D --- nocite: "[@*]" references: - annote: This is a typical inproceedings entry. Note the booksubtitle, shorttitle, indextitle, and indexsorttitle fields. Also note the eventdate field. author: - family: Moraux given: Paul container-title: Aristotle on Mind and the Senses. Proceedings of the Seventh Symposium Aristotelicum editor: - family: Lloyd given: G. E. R. - family: Owen given: G. E. L. event-date: 1975 id: moraux issued: 1979 keyword: secondary language: fr-FR page: 281-324 publisher: Cambridge University Press publisher-place: Cambridge title: "Le *De Anima* dans la tradition grècque: Quelques aspects de l'interpretation du traité, de Theophraste à Themistius" title-short: "*De Anima* dans la tradition grècque" type: paper-conference - author: - family: Salam given: Abdus container-title: "Elementary particle theory: Relativistic groups and analyticity. Proceedings of the eighth Nobel symposium" editor: - family: Svartholm given: Nils event-date: 1968-05-19/1968-05-25 event-place: Aspenäsgarden, Lerum id: salam issued: 1968 page: 367-377 publisher: Almquist & Wiksell publisher-place: Stockholm title: Weak and electromagnetic interactions type: paper-conference --- ``` ================================================ FILE: test/command/biblatex-issue288.md ================================================ ``` % pandoc -f biblatex -t markdown -s @thesis{Leavitt_2016, location = {{Los Angeles, CA}}, title = {Upvoting the News: {{Breaking}} News Aggregation, Crowd Collaboration, and Algorithm-Driven Attention on Reddit.Com}, timestamp = {2017-04-06T14:13:22Z}, langid = {english}, institution = {{University of Southern California}}, type = {Dissertation}, author = {Leavitt, Alex}, date = {2016-08}, } ^D --- nocite: "[@*]" references: - author: - family: Leavitt given: Alex genre: Dissertation id: Leavitt_2016 issued: 2016-08 language: en-US publisher: University of Southern California publisher-place: Los Angeles, CA title: "Upvoting the news: Breaking news aggregation, crowd collaboration, and algorithm-driven attention on reddit.com" title-short: Upvoting the news type: thesis --- ``` ================================================ FILE: test/command/biblatex-itzhaki.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Itzhaki 1996) Itzhaki, Nissan. 1996. “Some Remarks on ’t Hooft’s S-matrix for Black Holes” (version 1). March 11. Formatted with pandoc and apa.csl, 2013-10-23: (Itzhaki, 1996) Itzhaki, N. (1996, March 11). Some remarks on ’t Hooft’s S-matrix for black holes. NOTES: - biblio2yaml: - eprinttype = {arxiv}, eprint = {hep-th/9603067}, should be converted to a url: http://arxiv.org/abs/hep-th/9603067 (prefix http://arxiv.org/abs/ seems to work for all arxiv material) - citeproc: - obtaining correct case of "'t Hooft's" in title is possible but awkward: '{t Hooft's} works; {'t Hooft}'s or '{t Hooft}'s do not } @Online{itzhaki, author = {Itzhaki, Nissan}, title = {Some remarks on '{t Hooft's} {S}-matrix for black holes}, date = {1996-03-11}, version = 1, hyphenation = {american}, eprinttype = {arxiv}, eprint = {hep-th/9603067}, annotation = {An online reference from arXiv. Note the eprint and eprinttype fields. Also note that the arXiv reference is transformed into a clickable link if hyperref support has been enabled}, abstract = {We discuss the limitations of 't Hooft's proposal for the black hole S-matrix. We find that the validity of the S-matrix implies violation of the semi-classical approximation at scales large compared to the Planck scale. We also show that the effect of the centrifugal barrier on the S-matrix is crucial even for large transverse distances.}, } ^D --- nocite: "[@*]" references: - abstract: We discuss the limitations of 't Hooft's proposal for the black hole S-matrix. We find that the validity of the S-matrix implies violation of the semi-classical approximation at scales large compared to the Planck scale. We also show that the effect of the centrifugal barrier on the S-matrix is crucial even for large transverse distances. annote: An online reference from arXiv. Note the eprint and eprinttype fields. Also note that the arXiv reference is transformed into a clickable link if hyperref support has been enabled author: - family: Itzhaki given: Nissan id: itzhaki issued: 1996-03-11 language: en-US title: Some remarks on '[t Hooft's]{.nocase} S-matrix for black holes type: webpage url: "https://arxiv.org/abs/hep-th/9603067" version: 1 --- ``` ================================================ FILE: test/command/biblatex-jaffe.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Jaffé 1885–1888) Jaffé, Philipp, ed. 1885–1888. *Regesta Pontificum Romanorum ab condita ecclesia ad annum post Christum natum \mcxcviii\*. 2nd ed. 2. Leipzig. Formatted with pandoc and apa.csl, 2013-10-23: (Jaffé, 1885–1888) Jaffé, P. (Ed.). (1885–1888). *Regesta Pontificum Romanorum ab condita ecclesia ad annum post Christum natum \mcxcviii\* (2nd ed., 1-2). Leipzig. NOTES: - biblatex conversion: - hyphenation = {latin} - citeproc: - "vols." is missing - works in Zotero - This does not show up in the tests from the citeproc test suite that currently fail. - "\ needs to be fixed. - maybe add markdown syntax ^^small caps^^ ? - in pandoc "plain" output, small caps could be converted to uppercase chars: "MCXCVIII" would definitely look better here. } @Collection{jaffe, editor = {Jaff{\'e}, Philipp}, title = {Regesta Pontificum Romanorum ab condita ecclesia ad annum post Christum natum \textsc{mcxcviii}}, date = {1885/1888}, editora = {Loewenfeld, Samuel and Kaltenbrunner, Ferdinand and Ewald, Paul}, edition = 2, volumes = 2, location = {Leipzig}, editoratype = {redactor}, indextitle = {Regesta Pontificum Romanorum}, shorttitle = {Regesta Pontificum Romanorum}, annotation = {A collection entry with edition and volumes fields. Note the editora and editoratype fields}, hyphenation = {latin}, } ^D --- nocite: "[@*]" references: - annote: A collection entry with edition and volumes fields. Note the editora and editoratype fields edition: 2 editor: - family: Jaffé given: Philipp id: jaffe issued: 1885/1888 language: la number-of-volumes: 2 publisher-place: Leipzig title: Regesta Pontificum Romanorum ab condita ecclesia ad annum post Christum natum [mcxcviii]{.smallcaps} title-short: Regesta Pontificum Romanorum type: book --- ``` ================================================ FILE: test/command/biblatex-jcg.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (“Semantic 3D Media and Content” 2011) “Semantic 3D Media and Content.” 2011. *Computers and Graphics* 35 (4). Formatted with pandoc and apa.csl, 2013-10-23: (“Semantic 3D media and content,” 2011) Semantic 3D media and content. (2011). *Computers and Graphics*, *35*(4). NOTES: - output looks OK even if indistinguishable from article without page numbers } @Periodical{jcg, title = {Computers and Graphics}, year = 2011, issuetitle = {Semantic {3D} Media and Content}, volume = 35, number = 4, issn = {0097-8493}, annotation = {This is a periodical entry with an issn field.}, } ^D --- nocite: "[@*]" references: - annote: This is a periodical entry with an issn field. container-title: Computers and Graphics id: jcg issn: 0097-8493 issue: 4 issued: 2011 title: Semantic 3D media and content type: article-journal volume: 35 --- ``` ================================================ FILE: test/command/biblatex-kant-kpv.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Kant 1968) Kant, Immanuel. 1968. “Kritik der praktischen Vernunft.” In *Kants Werke. Akademie Textausgabe*, by Immanuel Kant, 5:1–163. Berlin: Walter de Gruyter. Formatted with pandoc and apa.csl, 2013-10-23: (Kant, 1968) Kant, I. (1968). Kritik der praktischen Vernunft. In *Kants Werke. Akademie Textausgabe* (Vol. 5, pp. 1–163). Berlin: Walter de Gruyter. NOTES: - citeproc - support for the not yet official "volume-title" is missing - csl style file - if author and container-author are identical, container-author should be suppressed (apparently csl style file issue; zotero shows same behaviour) } @InBook{kant:kpv, title = {Kritik der praktischen Vernunft}, date = 1968, author = {Kant, Immanuel}, booktitle = {Kritik der praktischen Vernunft. Kritik der Urtheilskraft}, bookauthor = {Kant, Immanuel}, maintitle = {Kants Werke. Akademie Textausgabe}, volume = 5, publisher = {Walter de Gruyter}, location = {Berlin}, pages = {1-163}, shorthand = {KpV}, hyphenation = {german}, shorttitle = {Kritik der praktischen Vernunft}, annotation = {An edition of Kant's \emph{Collected Works}, volume five. This is an inbook entry which explicitly refers to the \emph{Critique of Practical Reason} only, not to the entire fifth volume. Note the author and bookauthor fields in the database file. By default, the bookauthor is omitted if the values of the author and bookauthor fields are identical}, } ^D --- nocite: "[@*]" references: - annote: An edition of Kant's *Collected Works*, volume five. This is an inbook entry which explicitly refers to the *Critique of Practical Reason* only, not to the entire fifth volume. Note the author and bookauthor fields in the database file. By default, the bookauthor is omitted if the values of the author and bookauthor fields are identical author: - family: Kant given: Immanuel container-author: - family: Kant given: Immanuel container-title: Kants Werke. Akademie Textausgabe id: "kant:kpv" issued: 1968 language: de-DE page: 1-163 publisher: Walter de Gruyter publisher-place: Berlin title: Kritik der praktischen Vernunft title-short: Kritik der praktischen Vernunft type: chapter volume: 5 volume-title: Kritik der praktischen Vernunft. Kritik der Urtheilskraft --- ``` ================================================ FILE: test/command/biblatex-kant-ku.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Kant 1968) Kant, Immanuel. 1968. “Kritik der Urtheilskraft.” In *Kants Werke. Akademie Textausgabe*, by Immanuel Kant, 5:165–485. Berlin: Walter de Gruyter. Formatted with pandoc and apa.csl, 2013-10-23: (Kant, 1968) Kant, I. (1968). Kritik der Urtheilskraft. In *Kants Werke. Akademie Textausgabe* (Vol. 5, pp. 165–485). Berlin: Walter de Gruyter. NOTES: - citeproc - support for the not yet official "volume-title" is missing - CSL style file - if author and container-author are identical, container-author should be suppressed (apparently csl style file issue; zotero shows same behaviour) } @InBook{kant:ku, title = {Kritik der Urtheilskraft}, date = 1968, author = {Kant, Immanuel}, booktitle = {Kritik der praktischen Vernunft. Kritik der Urtheilskraft}, bookauthor = {Kant, Immanuel}, maintitle = {Kants Werke. Akademie Textausgabe}, volume = 5, publisher = {Walter de Gruyter}, location = {Berlin}, pages = {165-485}, shorthand = {KU}, hyphenation = {german}, annotation = {An edition of Kant's \emph{Collected Works}, volume five. This is an inbook entry which explicitly refers to the \emph{Critique of Judgment} only, not to the entire fifth volume}, } ^D --- nocite: "[@*]" references: - annote: An edition of Kant's *Collected Works*, volume five. This is an inbook entry which explicitly refers to the *Critique of Judgment* only, not to the entire fifth volume author: - family: Kant given: Immanuel container-author: - family: Kant given: Immanuel container-title: Kants Werke. Akademie Textausgabe id: "kant:ku" issued: 1968 language: de-DE page: 165-485 publisher: Walter de Gruyter publisher-place: Berlin title: Kritik der Urtheilskraft type: chapter volume: 5 volume-title: Kritik der praktischen Vernunft. Kritik der Urtheilskraft --- ``` ================================================ FILE: test/command/biblatex-kastenholz.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Kastenholz and Hünenberger 2006) Kastenholz, M. A., and Philippe H. Hünenberger. 2006. “Computation of Methodologyindependent Ionic Solvation Free Energies from Molecular Simulations: I. the Electrostatic Potential in Molecular Liquids.” *J. Chem. Phys.* 124. doi:[10.1063/1.2172593](https://doi.org/10.1063/1.2172593 "10.1063/1.2172593"). Formatted with pandoc and apa.csl, 2013-10-23: (Kastenholz & Hünenberger, 2006) Kastenholz, M. A., & Hünenberger, P. H. (2006). Computation of methodologyindependent ionic solvation free energies from molecular simulations: I. the electrostatic potential in molecular liquids. *J. Chem. Phys.*, *124*. doi:[10.1063/1.2172593](https://doi.org/10.1063/1.2172593 "10.1063/1.2172593") NOTES: - biblio2xaml - fix conversion of "\hyphen” - the string "doi:" should not appear as part of the content of the "doi" field } @string{ jchph = {J.~Chem. Phys.} } @Article{kastenholz, author = {Kastenholz, M. A. and H{\"u}nenberger, Philippe H.}, title = {Computation of methodology\hyphen independent ionic solvation free energies from molecular simulations}, journaltitle = jchph, date = 2006, subtitle = {I. {The} electrostatic potential in molecular liquids}, volume = 124, eid = 124106, doi = {10.1063/1.2172593}, hyphenation = {american}, indextitle = {Computation of ionic solvation free energies}, annotation = {An article entry with an eid and a doi field. Note that the \textsc{doi} is transformed into a clickable link if hyperref support has been enabled}, abstract = {The computation of ionic solvation free energies from atomistic simulations is a surprisingly difficult problem that has found no satisfactory solution for more than 15 years. The reason is that the charging free energies evaluated from such simulations are affected by very large errors. One of these is related to the choice of a specific convention for summing up the contributions of solvent charges to the electrostatic potential in the ionic cavity, namely, on the basis of point charges within entire solvent molecules (M scheme) or on the basis of individual point charges (P scheme). The use of an inappropriate convention may lead to a charge-independent offset in the calculated potential, which depends on the details of the summation scheme, on the quadrupole-moment trace of the solvent molecule, and on the approximate form used to represent electrostatic interactions in the system. However, whether the M or P scheme (if any) represents the appropriate convention is still a matter of on-going debate. The goal of the present article is to settle this long-standing controversy by carefully analyzing (both analytically and numerically) the properties of the electrostatic potential in molecular liquids (and inside cavities within them).}, } ^D --- nocite: "[@*]" references: - abstract: The computation of ionic solvation free energies from atomistic simulations is a surprisingly difficult problem that has found no satisfactory solution for more than 15 years. The reason is that the charging free energies evaluated from such simulations are affected by very large errors. One of these is related to the choice of a specific convention for summing up the contributions of solvent charges to the electrostatic potential in the ionic cavity, namely, on the basis of point charges within entire solvent molecules (M scheme) or on the basis of individual point charges (P scheme). The use of an inappropriate convention may lead to a charge-independent offset in the calculated potential, which depends on the details of the summation scheme, on the quadrupole-moment trace of the solvent molecule, and on the approximate form used to represent electrostatic interactions in the system. However, whether the M or P scheme (if any) represents the appropriate convention is still a matter of on-going debate. The goal of the present article is to settle this long-standing controversy by carefully analyzing (both analytically and numerically) the properties of the electrostatic potential in molecular liquids (and inside cavities within them). annote: An article entry with an eid and a doi field. Note that the [doi]{.smallcaps} is transformed into a clickable link if hyperref support has been enabled author: - family: Kastenholz given: M. A. - family: Hünenberger given: Philippe H. container-title: J. Chem. Phys. doi: 10.1063/1.2172593 id: kastenholz issued: 2006 language: en-US title: "Computation of methodology-independent ionic solvation free energies from molecular simulations: I. The electrostatic potential in molecular liquids" title-short: Computation of methodology-independent ionic solvation free energies from molecular simulations type: article-journal volume: 124 --- ``` ================================================ FILE: test/command/biblatex-knuth-ct-a.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Knuth 1984) Knuth, Donald E. 1984. *Computers & Typesetting*. Vol. A. Reading, Mass.: Addison-Wesley. Formatted with pandoc and apa.csl, 2013-10-23: (Knuth, 1984) Knuth, D. E. (1984). *Computers & typesetting* (Vol. A). Reading, Mass.: Addison-Wesley. NOTES: - volume-title currently not implemented by chicago-author-date.csl and apa.csl. } @Book{knuth:ct:a, author = {Knuth, Donald E.}, title = {The {\TeX} book}, date = 1984, maintitle = {Computers \& Typesetting}, volume = {A}, publisher = {Addison-Wesley}, location = {Reading, Mass.}, hyphenation = {american}, sortyear = {1984-1}, sorttitle = {Computers & Typesetting A}, indexsorttitle= {The TeXbook}, indextitle = {\protect\TeX book, The}, shorttitle = {\TeX book}, annotation = {The first volume of a five-volume book. Note the sorttitle and sortyear fields. We want this volume to be listed after the entry referring to the entire five-volume set. Also note the indextitle and indexsorttitle fields. Indexing packages that don't generate robust index entries require some control sequences to be protected from expansion}, } ^D --- nocite: "[@*]" references: - annote: The first volume of a five-volume book. Note the sorttitle and sortyear fields. We want this volume to be listed after the entry referring to the entire five-volume set. Also note the indextitle and indexsorttitle fields. Indexing packages that don't generate robust index entries require some control sequences to be protected from expansion author: - family: Knuth given: Donald E. id: "knuth:ct:a" issued: 1984 language: en-US publisher: Addison-Wesley publisher-place: Reading, Mass. title: Computers & typesetting type: book volume: A volume-title: The TeX book --- ``` ================================================ FILE: test/command/biblatex-knuth-ct-b.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2015-03-08: (Knuth 1986) Knuth, Donald E. 1986. *Computers & Typesetting*. Vol. B. Reading, Mass.: Addison-Wesley. Formatted with pandoc and apa.csl, 2015-03-08: (Knuth, 1986) Knuth, D. E. (1986). *Computers & typesetting* (Vol. B). Reading, Mass.: Addison-Wesley. NOTES: - volume-title currently not implemented by chicago-author-date.csl and apa.csl. } @Book{knuth:ct:b, author = {Knuth, Donald E.}, title = {\TeX: {T}he Program}, date = 1986, maintitle = {Computers \& Typesetting}, volume = {B}, publisher = {Addison-Wesley}, location = {Reading, Mass.}, hyphenation = {american}, sortyear = {1986-1}, sorttitle = {Computers & Typesetting B}, indexsorttitle= {TeX: The Program}, shorttitle = {\TeX}, annotation = {The second volume of a five-volume book. Note the sorttitle and sortyear fields. Also note the indexsorttitle field}, } ^D --- nocite: "[@*]" references: - annote: The second volume of a five-volume book. Note the sorttitle and sortyear fields. Also note the indexsorttitle field author: - family: Knuth given: Donald E. id: "knuth:ct:b" issued: 1986 language: en-US publisher: Addison-Wesley publisher-place: Reading, Mass. title: Computers & typesetting title-short: TeX type: book volume: B volume-title: "TeX: The program" --- ``` ================================================ FILE: test/command/biblatex-knuth-ct-c.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2015-03-08: (Knuth 1986) Knuth, Donald E. 1986. *Computers & Typesetting*. Vol. C. Reading, Mass.: Addison-Wesley. Formatted with pandoc and apa.csl, 2015-03-08: (Knuth, 1986) Knuth, D. E. (1986). *Computers & typesetting* (Vol. C). Reading, Mass.: Addison-Wesley. NOTES: - volume-title currently not implemented by chicago-author-date.csl and apa.csl. } @Book{knuth:ct:c, author = {Knuth, Donald E.}, title = {The {METAFONTbook}}, date = 1986, maintitle = {Computers \& Typesetting}, volume = {C}, publisher = {Addison-Wesley}, location = {Reading, Mass.}, hyphenation = {american}, sortyear = {1986-2}, sorttitle = {Computers & Typesetting C}, indextitle = {METAFONTbook, The}, shorttitle = {{METAFONTbook}}, annotation = {The third volume of a five-volume book. Note the sorttitle and sortyear fields as well as the indextitle field}, } ^D --- nocite: "[@*]" references: - annote: The third volume of a five-volume book. Note the sorttitle and sortyear fields as well as the indextitle field author: - family: Knuth given: Donald E. id: "knuth:ct:c" issued: 1986 language: en-US publisher: Addison-Wesley publisher-place: Reading, Mass. title: Computers & typesetting type: book volume: C volume-title: The METAFONTbook --- ``` ================================================ FILE: test/command/biblatex-knuth-ct-d.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2015-03-08: (Knuth 1986) Knuth, Donald E. 1986. *Computers & Typesetting*. Vol. D. Reading, Mass.: Addison-Wesley. Formatted with pandoc and apa.csl, 2015-03-08: (Knuth, 1986) Knuth, D. E. (1986). *Computers & typesetting* (Vol. D). Reading, Mass.: Addison-Wesley. NOTES: - biblio2yaml - Should letters following a colon, such as the "T" in "{{METAFONT}: {T}he Program}" be protected by default? -- I'm not sure ... - volume-title currently not implemented by chicago-author-date.csl and apa.csl. } @Book{knuth:ct:d, author = {Knuth, Donald E.}, title = {{METAFONT}: {T}he Program}, date = 1986, maintitle = {Computers \& Typesetting}, volume = {D}, publisher = {Addison-Wesley}, location = {Reading, Mass.}, hyphenation = {american}, sortyear = {1986-3}, sorttitle = {Computers & Typesetting D}, shorttitle = {{METAFONT}}, annotation = {The fourth volume of a five-volume book. Note the sorttitle and sortyear fields}, } ^D --- nocite: "[@*]" references: - annote: The fourth volume of a five-volume book. Note the sorttitle and sortyear fields author: - family: Knuth given: Donald E. id: "knuth:ct:d" issued: 1986 language: en-US publisher: Addison-Wesley publisher-place: Reading, Mass. title: Computers & typesetting title-short: METAFONT type: book volume: D volume-title: "METAFONT: The program" --- ``` ================================================ FILE: test/command/biblatex-knuth-ct-e.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2015-03-08: (Knuth 1986) Knuth, Donald E. 1986. *Computers & Typesetting*. Vol. E. Reading, Mass.: Addison-Wesley. Formatted with pandoc and apa.csl, 2015-03-08: (Knuth, 1986) Knuth, D. E. (1986). *Computers & typesetting* (Vol. E). Reading, Mass.: Addison-Wesley. NOTES: - volume-title currently not implemented by chicago-author-date.csl and apa.csl. } @Book{knuth:ct:e, author = {Knuth, Donald E.}, title = {Computer Modern Typefaces}, date = 1986, maintitle = {Computers \& Typesetting}, volume = {E}, publisher = {Addison-Wesley}, location = {Reading, Mass.}, hyphenation = {american}, sortyear = {1986-4}, sorttitle = {Computers & Typesetting E}, annotation = {The fifth volume of a five-volume book. Note the sorttitle and sortyear fields}, } ^D --- nocite: "[@*]" references: - annote: The fifth volume of a five-volume book. Note the sorttitle and sortyear fields author: - family: Knuth given: Donald E. id: "knuth:ct:e" issued: 1986 language: en-US publisher: Addison-Wesley publisher-place: Reading, Mass. title: Computers & typesetting type: book volume: E volume-title: Computer modern typefaces --- ``` ================================================ FILE: test/command/biblatex-knuth-ct-related.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Knuth 1984–1986) Knuth, Donald E. 1984–1986. *Computers & Typesetting*. 5. Reading, Mass.: Addison-Wesley. Formatted with pandoc and apa.csl, 2013-10-23: (Knuth, 1984–1986) Knuth, D. E. (1984–1986). *Computers & typesetting* (1-5). Reading, Mass.: Addison-Wesley. NOTES: - biblio2yaml - related = {...}, relatedtype = {multivolume}, -- no counterpart in CSL - citeproc - term "vols." missing } @Book{knuth:ct:related, author = {Knuth, Donald E.}, title = {Computers \& Typesetting}, date = {1984/1986}, volumes = 5, publisher = {Addison-Wesley}, location = {Reading, Mass.}, hyphenation = {american}, sortyear = {1984-0}, sorttitle = {Computers & Typesetting}, indexsorttitle= {Computers & Typesetting}, related = {knuth:ct:a,knuth:ct:b,knuth:ct:c,knuth:ct:d,knuth:ct:e}, relatedtype = {multivolume}, annotation = {A five-volume book cited as a whole and related to its individual volumes. Note the related and relatedtype fields}, } ^D --- nocite: "[@*]" references: - annote: A five-volume book cited as a whole and related to its individual volumes. Note the related and relatedtype fields author: - family: Knuth given: Donald E. id: "knuth:ct:related" issued: 1984/1986 language: en-US number-of-volumes: 5 publisher: Addison-Wesley publisher-place: Reading, Mass. title: Computers & typesetting type: book --- ``` ================================================ FILE: test/command/biblatex-knuth-ct.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Knuth 1984–1986) Knuth, Donald E. 1984–1986. *Computers & Typesetting*. 5. Reading, Mass.: Addison-Wesley. Formatted with pandoc and apa.csl, 2013-10-23: (Knuth, 1984–1986) Knuth, D. E. (1984–1986). *Computers & typesetting* (1-5). Reading, Mass.: Addison-Wesley. NOTES: - citeproc - term "vols." is missing } @Book{knuth:ct, author = {Knuth, Donald E.}, title = {Computers \& Typesetting}, date = {1984/1986}, volumes = 5, publisher = {Addison-Wesley}, location = {Reading, Mass.}, hyphenation = {american}, sortyear = {1984-0}, sorttitle = {Computers & Typesetting}, indexsorttitle= {Computers & Typesetting}, annotation = {A five-volume book cited as a whole. This is a book entry, note the volumes field}, } ^D --- nocite: "[@*]" references: - annote: A five-volume book cited as a whole. This is a book entry, note the volumes field author: - family: Knuth given: Donald E. id: "knuth:ct" issued: 1984/1986 language: en-US number-of-volumes: 5 publisher: Addison-Wesley publisher-place: Reading, Mass. title: Computers & typesetting type: book --- ``` ================================================ FILE: test/command/biblatex-kowalik.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Kowalik and Isard 1995) Kowalik, F., and M. Isard. 1995. “Estimateur d’un défaut de fonctionnement d’un modulateur en quadrature et étage de modulation l’utilisant.” French patent request. Formatted with pandoc and apa.csl, 2013-10-23: (Kowalik & Isard, 1995) Kowalik, F., & Isard, M. (1995, January 11). Estimateur d’un défaut de fonctionnement d’un modulateur en quadrature et étage de modulation l’utilisant. French patent request. } @Patent{kowalik, author = {Kowalik, F. and Isard, M.}, title = {Estimateur d'un d{\'e}faut de fonctionnement d'un modulateur en quadrature et {\'e}tage de modulation l'utilisant}, number = 9500261, date = {1995-01-11}, type = {patreqfr}, hyphenation = {french}, indextitle = {Estimateur d'un d{\'e}faut de fonctionnement}, annotation = {This is a patent entry for a French patent request with a full date. The number is given in the number field. Note the format of the type and date fields in the database file. Compare almendro, laufenberg, and sorace}, } ^D --- nocite: "[@*]" references: - annote: This is a patent entry for a French patent request with a full date. The number is given in the number field. Note the format of the type and date fields in the database file. Compare almendro, laufenberg, and sorace author: - family: Kowalik given: F. - family: Isard given: M. genre: French patent request id: kowalik issued: 1995-01-11 language: fr-FR number: 9500261 title: Estimateur d'un défaut de fonctionnement d'un modulateur en quadrature et étage de modulation l'utilisant type: patent --- ``` ================================================ FILE: test/command/biblatex-kullback-related.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Kullback 1997) Kullback, Solomon. 1997. *Information Theory and Statistics*. New York: Dover Publications. Formatted with pandoc and apa.csl, 2013-10-23: (Kullback, 1997) Kullback, S. (1997). *Information theory and statistics*. New York: Dover Publications. NOTES: - related = {kullback}, relatedtype = {origpubin}, -- not possible in CSL } @Book{kullback:related, author = {Kullback, Solomon}, title = {Information Theory and Statistics}, year = 1997, publisher = {Dover Publications}, location = {New York}, hyphenation = {american}, related = {kullback}, relatedtype = {origpubin}, annotation = {A reprint of the kullback entry. Note the format of the related and relatedtype fields}, } ^D --- nocite: "[@*]" references: - annote: A reprint of the kullback entry. Note the format of the related and relatedtype fields author: - family: Kullback given: Solomon id: "kullback:related" issued: 1997 language: en-US publisher: Dover Publications publisher-place: New York title: Information theory and statistics type: book --- ``` ================================================ FILE: test/command/biblatex-kullback-reprint.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Kullback 1997) Kullback, Solomon. 1997. *Information Theory and Statistics*. New York: Dover Publications. Formatted with pandoc and apa.csl, 2013-10-23: (Kullback, 1997) Kullback, S. (1997). *Information theory and statistics*. New York: Dover Publications. NOTES: Formatted with chicago-author-date-TEST-20131018.csl (Kullback [1959] 1997) Kullback, Solomon. (1959) 1997. *Information Theory and Statistics*. New York: Dover Publications. } @Book{kullback:reprint, author = {Kullback, Solomon}, title = {Information Theory and Statistics}, year = 1997, publisher = {Dover Publications}, location = {New York}, origyear = 1959, origpublisher= {John Wiley \& Sons}, hyphenation = {american}, annotation = {A reprint of the kullback entry. Note the format of origyear and origpublisher. These fields are not used by the standard bibliography styles}, } ^D --- nocite: "[@*]" references: - annote: A reprint of the kullback entry. Note the format of origyear and origpublisher. These fields are not used by the standard bibliography styles author: - family: Kullback given: Solomon id: "kullback:reprint" issued: 1997 language: en-US original-date: 1959 original-publisher: John Wiley & Sons publisher: Dover Publications publisher-place: New York title: Information theory and statistics type: book --- ``` ================================================ FILE: test/command/biblatex-kullback.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Kullback 1959) Kullback, Solomon. 1959. *Information Theory and Statistics*. New York: John Wiley & Sons. Formatted with pandoc and apa.csl, 2013-10-23: (Kullback, 1959) Kullback, S. (1959). *Information theory and statistics*. New York: John Wiley & Sons. } @Book{kullback, author = {Kullback, Solomon}, title = {Information Theory and Statistics}, year = 1959, publisher = {John Wiley \& Sons}, location = {New York}, hyphenation = {american}, } ^D --- nocite: "[@*]" references: - author: - family: Kullback given: Solomon id: kullback issued: 1959 language: en-US publisher: John Wiley & Sons publisher-place: New York title: Information theory and statistics type: book --- ``` ================================================ FILE: test/command/biblatex-laufenberg.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Laufenberg et al. 2006) Laufenberg, Xaver, Dominique Eynius, Helmut Suelzle, Stephan Usbeck, Matthias Spaeth, Miriam Neuser-Hoffmann, Christian Myrzik, et al. 2006. “Elektrische Einrichtung und Betriebsverfahren.” European patent. Formatted with pandoc and apa.csl, 2013-10-23: (Laufenberg et al., 2006) Laufenberg, X., Eynius, D., Suelzle, H., Usbeck, S., Spaeth, M., Neuser-Hoffmann, M., … Ebner, N. (2006, September 13). Elektrische Einrichtung und Betriebsverfahren. European patent. NOTES: - biblio2yaml - Is there any equivalent of "holder" in CSL? } @Patent{laufenberg, author = {Laufenberg, Xaver and Eynius, Dominique and Suelzle, Helmut and Usbeck, Stephan and Spaeth, Matthias and Neuser-Hoffmann, Miriam and Myrzik, Christian and Schmid, Manfred and Nietfeld, Franz and Thiel, Alexander and Braun, Harald and Ebner, Norbert}, title = {Elektrische Einrichtung und Betriebsverfahren}, number = 1700367, date = {2006-09-13}, holder = {{Robert Bosch GmbH} and {Daimler Chrysler AG} and {Bayerische Motoren Werke AG}}, type = {patenteu}, hyphenation = {german}, annotation = {This is a patent entry with a holder field. Note the format of the type and location fields in the database file. Compare almendro, sorace, and kowalik}, abstract = {The invention relates to an electric device comprising a generator, in particular for use in the vehicle electric system of a motor vehicle and a controller for controlling the generator voltage. The device is equipped with a control zone, in which the voltage is controlled and zones, in which the torque is controlled. The invention also relates to methods for operating a device of this type.}, file = {http://v3.espacenet.com/textdoc?IDX=EP1700367}, } ^D --- nocite: "[@*]" references: - abstract: The invention relates to an electric device comprising a generator, in particular for use in the vehicle electric system of a motor vehicle and a controller for controlling the generator voltage. The device is equipped with a control zone, in which the voltage is controlled and zones, in which the torque is controlled. The invention also relates to methods for operating a device of this type. annote: This is a patent entry with a holder field. Note the format of the type and location fields in the database file. Compare almendro, sorace, and kowalik author: - family: Laufenberg given: Xaver - family: Eynius given: Dominique - family: Suelzle given: Helmut - family: Usbeck given: Stephan - family: Spaeth given: Matthias - family: Neuser-Hoffmann given: Miriam - family: Myrzik given: Christian - family: Schmid given: Manfred - family: Nietfeld given: Franz - family: Thiel given: Alexander - family: Braun given: Harald - family: Ebner given: Norbert genre: European patent id: laufenberg issued: 2006-09-13 language: de-DE number: 1700367 title: Elektrische Einrichtung und Betriebsverfahren type: patent --- ``` ================================================ FILE: test/command/biblatex-loh.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Loh 1992) Loh, Nin C. 1992. “High-resolution Micromachined Interferometric Accelerometer.” Master’s thesis, Cambridge, Mass.: Massachusetts Institute of Technology. Formatted with pandoc and apa.csl, 2013-10-23: (Loh, 1992) Loh, N. C. (1992). *High-resolution micromachined interferometric accelerometer* (Master’s thesis). Massachusetts Institute of Technology, Cambridge, Mass. NOTES: - biblio2yaml - At some point, actual localization of "localization keys" will have to be implemented } @Thesis{loh, author = {Loh, Nin C.}, title = {High-Resolution Micromachined Interferometric Accelerometer}, type = {mathesis}, institution = {Massachusetts Institute of Technology}, date = 1992, location = {Cambridge, Mass.}, hyphenation = {american}, annotation = {This is a typical thesis entry for an MA thesis. Note the type field in the database file which uses a localization key}, } ^D --- nocite: "[@*]" references: - annote: This is a typical thesis entry for an MA thesis. Note the type field in the database file which uses a localization key author: - family: Loh given: Nin C. genre: Master's thesis id: loh issued: 1992 language: en-US publisher: Massachusetts Institute of Technology publisher-place: Cambridge, Mass. title: High-resolution micromachined interferometric accelerometer type: thesis --- ``` ================================================ FILE: test/command/biblatex-malinowski.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Malinowski 1972) Malinowski, Bronisław. 1972. *Argonauts of the Western Pacific: An Account of Native Enterprise and Adventure in the Archipelagoes of Melanesian New Guinea*. 8th ed. London: Routledge and Kegan Paul. Formatted with pandoc and apa.csl, 2013-10-23: (Malinowski, 1972) Malinowski, B. (1972). *Argonauts of the Western Pacific: An account of native enterprise and adventure in the Archipelagoes of Melanesian New Guinea* (8th ed.). London: Routledge and Kegan Paul. } @Book{malinowski, author = {Malinowski, Bronis{\l}aw}, title = {Argonauts of the {Western Pacific}}, date = 1972, edition = 8, publisher = {Routledge {and} Kegan Paul}, location = {London}, hyphenation = {british}, subtitle = {An account of native enterprise and adventure in the {Archipelagoes of Melanesian New Guinea}}, shorttitle = {Argonauts}, annotation = {This is a book entry. Note the format of the publisher and edition fields as well as the subtitle field}, } ^D --- nocite: "[@*]" references: - annote: This is a book entry. Note the format of the publisher and edition fields as well as the subtitle field author: - family: Malinowski given: Bronisław edition: 8 id: malinowski issued: 1972 language: en-GB publisher: Routledge and Kegan Paul publisher-place: London title: "Argonauts of the Western Pacific: An account of native enterprise and adventure in the [Archipelagoes of Melanesian New Guinea]{.nocase}" title-short: Argonauts type: book --- ``` ================================================ FILE: test/command/biblatex-manual.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib} @Manual{cms, title = {The {Chicago} Manual of Style}, date = 2003, subtitle = {The Essential Guide for Writers, Editors, and Publishers}, edition = 15, publisher = {University of Chicago Press}, location = {Chicago, Ill.}, isbn = {0-226-10403-6}, label = {CMS}, hyphenation = {american}, sorttitle = {Chicago Manual of Style}, indextitle = {Chicago Manual of Style, The}, shorttitle = {Chicago Manual of Style}, annotation = {This is a manual entry without an author or editor. Note the label field in the database file which is provided for author-year citation styles. Also note the sorttitle and indextitle fields. By default, all entries without an author or editor are alphabetized by title but we want this entry to be alphabetized under \enquote*{C} rather than \enquote*{T}. There's also an isbn field}, } ^D --- nocite: "[@*]" references: - annote: This is a manual entry without an author or editor. Note the label field in the database file which is provided for author-year citation styles. Also note the sorttitle and indextitle fields. By default, all entries without an author or editor are alphabetized by title but we want this entry to be alphabetized under 'C' rather than 'T'. There's also an isbn field edition: 15 id: cms isbn: 0-226-10403-6 issued: 2003 language: en-US publisher: University of Chicago Press publisher-place: Chicago, Ill. title: "The Chicago manual of style: The essential guide for writers, editors, and publishers" title-short: Chicago manual of style type: book --- ``` ================================================ FILE: test/command/biblatex-markey.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Markey 2005) Markey, Nicolas. 2005. “Tame the BeaST: The B to X of BibTeX” (version 1.3). October 16. . Formatted with pandoc and apa.csl, 2013-10-23: (Markey, 2005) Markey, N. (2005, October 16). Tame the BeaST: The B to X of BibTeX. Retrieved October 01, 2006, from } @Online{markey, author = {Markey, Nicolas}, title = {Tame the {BeaST}}, date = {2005-10-16}, url = {http://tug.ctan.org/tex-archive/info/bibtex/tamethebeast/ttb_en.pdf}, subtitle = {The {B} to {X} of {BibTeX}}, version = {1.3}, urldate = {2006-10-01}, hyphenation = {american}, sorttitle = {Tame the Beast}, annotation = {An online entry for a tutorial. Note the format of the date field (yyyy-mm-dd) in the database file.}, } ^D --- nocite: "[@*]" references: - accessed: 2006-10-01 annote: An online entry for a tutorial. Note the format of the date field (yyyy-mm-dd) in the database file. author: - family: Markey given: Nicolas id: markey issued: 2005-10-16 language: en-US title: "Tame the BeaST: The B to X of BibTeX" title-short: Tame the BeaST type: webpage url: "http://tug.ctan.org/tex-archive/info/bibtex/tamethebeast/ttb_en.pdf" version: 1.3 --- ``` ================================================ FILE: test/command/biblatex-maron.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Maron 2000) Maron, Monika. 2000. *Animal Triste*. Translated by Brigitte Goldstein. Lincoln: University of Nebraska Press. Formatted with pandoc and apa.csl, 2013-10-23: (Maron, 2000) Maron, M. (2000). *Animal triste*. (B. Goldstein, Trans.). Lincoln: University of Nebraska Press. NOTES: - origlanguage concatenated with translator, e.g. “translated from the German by …” not possible in CSL } @Book{maron, author = {Maron, Monika}, title = {Animal Triste}, date = 2000, translator = {Brigitte Goldstein}, origlanguage = {german}, publisher = {University of Nebraska Press}, location = {Lincoln}, hyphenation = {american}, shorttitle = {Animal Triste}, annotation = {An English translation of a German novel with a French title. In other words: a book entry with a translator field. Note the origlanguage field which is concatenated with the translator}, } ^D --- nocite: "[@*]" references: - annote: "An English translation of a German novel with a French title. In other words: a book entry with a translator field. Note the origlanguage field which is concatenated with the translator" author: - family: Maron given: Monika id: maron issued: 2000 language: en-US publisher: University of Nebraska Press publisher-place: Lincoln title: Animal triste title-short: Animal triste translator: - family: Goldstein given: Brigitte type: book --- ``` ================================================ FILE: test/command/biblatex-massa.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Massa 2004) Massa, Werner. 2004. *Crystal Structure Determination*. 2nd ed. Berlin: Spinger. Formatted with pandoc and apa.csl, 2013-10-23: (Massa, 2004) Massa, W. (2004). *Crystal structure determination* (2nd ed.). Berlin: Spinger. } @Book{massa, author = {Werner Massa}, title = {Crystal structure determination}, date = 2004, edition = 2, publisher = {Spinger}, location = {Berlin}, hyphenation = {british}, annotation = {A book entry with an edition field}, } ^D --- nocite: "[@*]" references: - annote: A book entry with an edition field author: - family: Massa given: Werner edition: 2 id: massa issued: 2004 language: en-GB publisher: Spinger publisher-place: Berlin title: Crystal structure determination type: book --- ``` ================================================ FILE: test/command/biblatex-moore-related.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Moore 1998) Moore, Gordon E. 1998. “Cramming More Components onto Integrated Circuits.” *Proceedings of the IEEE* 86 (1): 82–85. Formatted with pandoc and apa.csl, 2013-10-23: (Moore, 1998) Moore, G. E. (1998). Cramming more components onto integrated circuits. *Proceedings of the IEEE*, *86*(1), 82–85. NOTES: - "related = {moore}, relatedtype = {reprintfrom}," – no equivalent implemented in CSL } @Article{moore:related, author = {Moore, Gordon E.}, title = {Cramming more components onto integrated circuits}, journaltitle = {Proceedings of the {IEEE}}, year = 1998, volume = 86, number = 1, pages = {82-85}, hyphenation = {american}, related = {moore}, relatedtype = {reprintfrom}, annotation = {A reprint of Moore's law. Note the related and relatedtype fields}, } ^D --- nocite: "[@*]" references: - annote: A reprint of Moore's law. Note the related and relatedtype fields author: - family: Moore given: Gordon E. container-title: Proceedings of the IEEE id: "moore:related" issue: 1 issued: 1998 language: en-US page: 82-85 title: Cramming more components onto integrated circuits type: article-journal volume: 86 --- ``` ================================================ FILE: test/command/biblatex-moore.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Moore 1965) Moore, Gordon E. 1965. “Cramming More Components onto Integrated Circuits.” *Electronics* 38 (8): 114–117. Formatted with pandoc and apa.csl, 2013-10-23: (Moore, 1965) Moore, G. E. (1965). Cramming more components onto integrated circuits. *Electronics*, *38*(8), 114–117. } @Article{moore, author = {Moore, Gordon E.}, title = {Cramming more components onto integrated circuits}, journaltitle = {Electronics}, year = 1965, volume = 38, number = 8, pages = {114-117}, hyphenation = {american}, } ^D --- nocite: "[@*]" references: - author: - family: Moore given: Gordon E. container-title: Electronics id: moore issue: 8 issued: 1965 language: en-US page: 114-117 title: Cramming more components onto integrated circuits type: article-journal volume: 38 --- ``` ================================================ FILE: test/command/biblatex-moraux.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Moraux 1979) Moraux, Paul. 1979. “Le *De Anima* dans la tradition grècque: Quelques aspects de l’interpretation du traité, de Theophraste à Themistius.” In *Aristotle on Mind and the Senses. Proceedings of the Seventh Symposium Aristotelicum*, edited by G. E. R. Lloyd and G. E. L. Owen, 281–324. Cambridge: Cambridge University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Moraux, 1979) Moraux, P. (1979). Le *De Anima* dans la tradition grècque: Quelques aspects de l’interpretation du traité, de Theophraste à Themistius. In G. E. R. Lloyd & G. E. L. Owen (eds.), *Aristotle on Mind and the Senses. Proceedings of the Seventh Symposium Aristotelicum* (pp. 281–324). Cambridge: Cambridge University Press. NOTES: - Since case (conversion) can only be specified per entry, not per field, for apa.csl the case of container-title would have to be adjusted manually. } @string{ cup = {Cambridge University Press} } @InProceedings{moraux, author = {Moraux, Paul}, editor = {Lloyd, G. E. R. and Owen, G. E. L.}, title = {Le \emph{De Anima} dans la tradition gr{\`e}cque}, date = 1979, booktitle = {Aristotle on Mind and the Senses}, subtitle = {Quelques aspects de l'interpretation du trait{\'e}, de Theophraste {\`a} Themistius}, booktitleaddon= {Proceedings of the Seventh Symposium Aristotelicum}, eventdate = 1975, publisher = cup, location = {Cambridge}, pages = {281-324}, keywords = {secondary}, hyphenation = {french}, indexsorttitle= {De Anima dans la tradition grecque}, indextitle = {\emph{De Anima} dans la tradition gr{\`e}cque, Le}, shorttitle = {\emph{De Anima} dans la tradition gr{\`e}cque}, annotation = {This is a typical inproceedings entry. Note the booksubtitle, shorttitle, indextitle, and indexsorttitle fields. Also note the eventdate field.}, } ^D --- nocite: "[@*]" references: - annote: This is a typical inproceedings entry. Note the booksubtitle, shorttitle, indextitle, and indexsorttitle fields. Also note the eventdate field. author: - family: Moraux given: Paul container-title: Aristotle on Mind and the Senses. Proceedings of the Seventh Symposium Aristotelicum editor: - family: Lloyd given: G. E. R. - family: Owen given: G. E. L. event-date: 1975 id: moraux issued: 1979 keyword: secondary language: fr-FR page: 281-324 publisher: Cambridge University Press publisher-place: Cambridge title: "Le *De Anima* dans la tradition grècque: Quelques aspects de l'interpretation du traité, de Theophraste à Themistius" title-short: "*De Anima* dans la tradition grècque" type: paper-conference --- ``` ================================================ FILE: test/command/biblatex-murray.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Hostetler et al. 1998) Hostetler, Michael J., Julia E. Wingate, Chuan-Jian Zhong, Jay E. Harris, Richard W. Vachet, Michael R. Clark, J. David Londono, et al. 1998. “Alkanethiolate Gold Cluster Molecules with Core Diameters from 1.5 to 5.2 nm: Core and Monolayer Properties as a Function of Core Size.” *Langmuir* 14 (1): 17–30. Formatted with pandoc and apa.csl, 2013-10-23: (Hostetler et al., 1998) Hostetler, M. J., Wingate, J. E., Zhong, C.-J., Harris, J. E., Vachet, R. W., Clark, M. R., … Murray, R. W. (1998). Alkanethiolate gold cluster molecules with core diameters from 1.5 to 5.2 nm: Core and monolayer properties as a function of core size. *Langmuir*, *14*(1), 17–30. } @Article{murray, author = {Hostetler, Michael J. and Wingate, Julia E. and Zhong, Chuan-Jian and Harris, Jay E. and Vachet, Richard W. and Clark, Michael R. and Londono, J. David and Green, Stephen J. and Stokes, Jennifer J. and Wignall, George D. and Glish, Gary L. and Porter, Marc D. and Evans, Neal D. and Murray, Royce W.}, title = {Alkanethiolate gold cluster molecules with core diameters from 1.5 to 5.2~{nm}}, journaltitle = {Langmuir}, date = 1998, subtitle = {Core and monolayer properties as a function of core size}, volume = 14, number = 1, pages = {17-30}, hyphenation = {american}, indextitle = {Alkanethiolate gold cluster molecules}, shorttitle = {Alkanethiolate gold cluster molecules}, annotation = {An article entry with \arabic{author} authors. By default, long author and editor lists are automatically truncated. This is configurable}, } ^D --- nocite: "[@*]" references: - annote: An article entry with `\arabic{author}`{=latex} authors. By default, long author and editor lists are automatically truncated. This is configurable author: - family: Hostetler given: Michael J. - family: Wingate given: Julia E. - family: Zhong given: Chuan-Jian - family: Harris given: Jay E. - family: Vachet given: Richard W. - family: Clark given: Michael R. - family: Londono given: J. David - family: Green given: Stephen J. - family: Stokes given: Jennifer J. - family: Wignall given: George D. - family: Glish given: Gary L. - family: Porter given: Marc D. - family: Evans given: Neal D. - family: Murray given: Royce W. container-title: Langmuir id: murray issue: 1 issued: 1998 language: en-US page: 17-30 title: "Alkanethiolate gold cluster molecules with core diameters from 1.5 to 5.2 [nm]{.nocase}: Core and monolayer properties as a function of core size" title-short: Alkanethiolate gold cluster molecules type: article-journal volume: 14 --- ``` ================================================ FILE: test/command/biblatex-nietzsche-historie.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Nietzsche 1988) Nietzsche, Friedrich. 1988. “Unzeitgemässe Betrachtungen. Zweites Stück: Vom Nutzen und Nachtheil der Historie für das Leben.” In *Sämtliche Werke: Kritische Studienausgabe*, by Friedrich Nietzsche, edited by Giorgio Colli and Mazzino Montinari, 1:243–334. München; Berlin; New York: Deutscher Taschenbuch-Verlag; Walter de Gruyter. Formatted with pandoc and apa.csl, 2013-10-23: (Nietzsche, 1988) Nietzsche, F. (1988). Unzeitgemässe Betrachtungen. Zweites Stück: Vom Nutzen und Nachtheil der Historie für das Leben. In G. Colli & M. Montinari (eds.), *Sämtliche Werke: Kritische Studienausgabe* (Vol. 1, pp. 243–334). München; Berlin; New York: Deutscher Taschenbuch-Verlag; Walter de Gruyter. } @string{ dtv = {Deutscher Taschenbuch-Verlag} } @InBook{nietzsche:historie, title = {Unzeitgem{\"a}sse Betrachtungen. Zweites St{\"u}ck}, date = 1988, author = {Nietzsche, Friedrich}, booktitle = {Die Geburt der Trag{\"o}die. Unzeitgem{\"a}{\ss}e Betrachtungen I--IV. Nachgelassene Schriften 1870--1973}, bookauthor = {Nietzsche, Friedrich}, editor = {Colli, Giorgio and Montinari, Mazzino}, subtitle = {Vom Nutzen und Nachtheil der Historie f{\"u}r das Leben}, maintitle = {S{\"a}mtliche Werke}, mainsubtitle = {Kritische Studienausgabe}, volume = 1, publisher = dtv # { and Walter de Gruyter}, location = {M{\"u}nchen and Berlin and New York}, pages = {243-334}, hyphenation = {german}, sortyear = {1988-2}, sorttitle = {Werke-01-243}, indexsorttitle= {Vom Nutzen und Nachtheil der Historie fur das Leben}, indextitle = {Vom Nutzen und Nachtheil der Historie f{\"u}r das Leben}, shorttitle = {Vom Nutzen und Nachtheil der Historie}, annotation = {A single essay from the critical edition of Nietzsche's works. This inbook entry explicitly refers to an essay found in the first volume. Note the title, booktitle, and maintitle fields. Also note the sorttitle and sortyear fields. We want this entry to be listed after the entry referring to the entire first volume}, } ^D --- nocite: "[@*]" references: - annote: A single essay from the critical edition of Nietzsche's works. This inbook entry explicitly refers to an essay found in the first volume. Note the title, booktitle, and maintitle fields. Also note the sorttitle and sortyear fields. We want this entry to be listed after the entry referring to the entire first volume author: - family: Nietzsche given: Friedrich container-author: - family: Nietzsche given: Friedrich container-title: "Sämtliche Werke: Kritische Studienausgabe" editor: - family: Colli given: Giorgio - family: Montinari given: Mazzino id: "nietzsche:historie" issued: 1988 language: de-DE page: 243-334 publisher: Deutscher Taschenbuch-Verlag; Walter de Gruyter publisher-place: München; Berlin; New York title: "Unzeitgemässe Betrachtungen. Zweites Stück: Vom Nutzen und Nachtheil der Historie für das Leben" title-short: Vom Nutzen und Nachtheil der Historie type: chapter volume: 1 volume-title: Die Geburt der Tragödie. Unzeitgemäße Betrachtungen I--IV. Nachgelassene Schriften 1870--1973 --- ``` ================================================ FILE: test/command/biblatex-nietzsche-ksa.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Nietzsche 1988) Nietzsche, Friedrich. 1988. *Sämtliche Werke: Kritische Studienausgabe*. Edited by Giorgio Colli and Mazzino Montinari. 2nd ed. 15. München; Berlin; New York: Deutscher Taschenbuch-Verlag; Walter de Gruyter. Formatted with pandoc and apa.csl, 2013-10-23: (Nietzsche, 1988) Nietzsche, F. (1988). *Sämtliche Werke: Kritische Studienausgabe*. (G. Colli & M. Montinari, eds.) (2nd ed., 1-15). München; Berlin; New York: Deutscher Taschenbuch-Verlag; Walter de Gruyter. NOTES: - biblio2yaml - term "vols." missing } @string{ dtv = {Deutscher Taschenbuch-Verlag} } @Book{nietzsche:ksa, author = {Nietzsche, Friedrich}, title = {S{\"a}mtliche Werke}, date = 1988, editor = {Colli, Giorgio and Montinari, Mazzino}, edition = 2, volumes = 15, publisher = dtv # { and Walter de Gruyter}, location = {M{\"u}nchen and Berlin and New York}, hyphenation = {german}, sortyear = {1988-0}, sorttitle = {Werke-00-000}, indexsorttitle= {Samtliche Werke}, subtitle = {Kritische Studienausgabe}, annotation = {The critical edition of Nietzsche's works. This is a book entry referring to a 15-volume work as a whole. Note the volumes field and the format of the publisher and location fields in the database file. Also note the sorttitle and sortyear fields which are used to fine-tune the sorting order of the bibliography. We want this item listed first in the bibliography}, } ^D --- nocite: "[@*]" references: - annote: The critical edition of Nietzsche's works. This is a book entry referring to a 15-volume work as a whole. Note the volumes field and the format of the publisher and location fields in the database file. Also note the sorttitle and sortyear fields which are used to fine-tune the sorting order of the bibliography. We want this item listed first in the bibliography author: - family: Nietzsche given: Friedrich edition: 2 editor: - family: Colli given: Giorgio - family: Montinari given: Mazzino id: "nietzsche:ksa" issued: 1988 language: de-DE number-of-volumes: 15 publisher: Deutscher Taschenbuch-Verlag; Walter de Gruyter publisher-place: München; Berlin; New York title: "Sämtliche Werke: Kritische Studienausgabe" title-short: Sämtliche Werke type: book --- ``` ================================================ FILE: test/command/biblatex-nietzsche-ksa1.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2015-03-08: (Nietzsche 1988) Nietzsche, Friedrich. 1988. *Sämtliche Werke: Kritische Studienausgabe*. Edited by Giorgio Colli and Mazzino Montinari. 2nd ed. Vol. 1. München; Berlin; New York: Deutscher Taschenbuch-Verlag; Walter de Gruyter. Formatted with pandoc and apa.csl, 2015-03-08: (Nietzsche, 1988) Nietzsche, F. (1988). *Sämtliche Werke: Kritische Studienausgabe*. (G. Colli & M. Montinari, eds., F. Nietzsche) (2nd ed., Vol. 1). München; Berlin; New York: Deutscher Taschenbuch-Verlag; Walter de Gruyter. NOTES: - volume-title currently not implemented by chicago-author-date.csl and apa.csl. } @string{ dtv = {Deutscher Taschenbuch-Verlag} } @Book{nietzsche:ksa1, author = {Nietzsche, Friedrich}, title = {Die Geburt der Trag{\"o}die. Unzeitgem{\"a}{\ss}e Betrachtungen I--IV. Nachgelassene Schriften 1870--1973}, date = 1988, editor = {Colli, Giorgio and Montinari, Mazzino}, maintitle = {S{\"a}mtliche Werke}, mainsubtitle = {Kritische Studienausgabe}, volume = 1, edition = 2, publisher = dtv # { and Walter de Gruyter}, location = {M{\"u}nchen and Berlin and New York}, hyphenation = {german}, sortyear = {1988-1}, sorttitle = {Werke-01-000}, indexsorttitle= {Samtliche Werke I}, bookauthor = {Nietzsche, Friedrich}, indextitle = {S{\"a}mtliche Werke I}, shorttitle = {S{\"a}mtliche Werke I}, annotation = {A single volume from the critical edition of Nietzsche's works. This book entry explicitly refers to the first volume only. Note the title and maintitle fields. Also note the sorttitle and sortyear fields. We want this entry to be listed after the entry referring to the entire edition}, } ^D --- nocite: "[@*]" references: - annote: A single volume from the critical edition of Nietzsche's works. This book entry explicitly refers to the first volume only. Note the title and maintitle fields. Also note the sorttitle and sortyear fields. We want this entry to be listed after the entry referring to the entire edition author: - family: Nietzsche given: Friedrich container-author: - family: Nietzsche given: Friedrich edition: 2 editor: - family: Colli given: Giorgio - family: Montinari given: Mazzino id: "nietzsche:ksa1" issued: 1988 language: de-DE publisher: Deutscher Taschenbuch-Verlag; Walter de Gruyter publisher-place: München; Berlin; New York title: "Sämtliche Werke: Kritische Studienausgabe" type: book volume: 1 volume-title: Die Geburt der Tragödie. Unzeitgemäße Betrachtungen I--IV. Nachgelassene Schriften 1870--1973 --- ``` ================================================ FILE: test/command/biblatex-online.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib} @online{markey, Annotation = {An online entry for a tutorial. Note the format of the date field (yyyy-mm-dd) in the database file.}, Author = {Markey, Nicolas}, Date = {2005-10-16}, Hyphenation = {american}, Sorttitle = {Tame the Beast}, Subtitle = {The {B} to {X} of {BibTeX}}, Title = {Tame the {BeaST}}, Url = {http://tug.ctan.org/tex-archive/info/bibtex/tamethebeast/ttb_en.pdf}, Urldate = {2006-10-01}, Version = {1.3}, } @online{CTAN, Annotation = {This is an online entry. The \textsc{url}, which is given in the url field, is transformed into a clickable link if hyperref support has been enabled. Note the format of the urldate field (yyyy-mm-dd) in the database file. Also note the label field which may be used as a fallback by citation styles which need an author and\slash or a year}, Date = 2006, Hyphenation = {american}, Label = {CTAN}, Subtitle = {The {Comprehensive TeX Archive Network}}, Title = {{CTAN}}, Url = {http://www.ctan.org}, Urldate = {2006-10-01}, } ^D --- nocite: "[@*]" references: - accessed: 2006-10-01 annote: An online entry for a tutorial. Note the format of the date field (yyyy-mm-dd) in the database file. author: - family: Markey given: Nicolas id: markey issued: 2005-10-16 language: en-US title: "Tame the BeaST: The B to X of BibTeX" title-short: Tame the BeaST type: webpage url: "http://tug.ctan.org/tex-archive/info/bibtex/tamethebeast/ttb_en.pdf" version: 1.3 - accessed: 2006-10-01 annote: This is an online entry. The [url]{.smallcaps}, which is given in the url field, is transformed into a clickable link if hyperref support has been enabled. Note the format of the urldate field (yyyy-mm-dd) in the database file. Also note the label field which may be used as a fallback by citation styles which need an author and/or a year id: CTAN issued: 2006 language: en-US title: "CTAN: The Comprehensive TeX Archive Network" title-short: CTAN type: webpage url: "http://www.ctan.org" --- ``` ================================================ FILE: test/command/biblatex-options-url-false-doi-false.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Entry contains url and doi fields; these should be discarded since the options field specifies url=false, doi=false. Exception: As in standard biblatex, in online entries url should never be discarded, even if options contains url=false. } @article{item1, Author = {Author, Andy}, Date = {2012}, Doi = {1234/5678.90}, Journal = {Journal}, Options = {url=false, doi=false}, Title = {Title, Any Entry Type Except online}, Url = {http://foo.bar} } @online{item2, Author = {Author, Andy}, Date = {2012}, Doi = {1234/5678.90}, Journal = {Journal}, Options = {url=false, doi=false}, Title = {Title, Entry Type online}, Url = {http://foo.bar} } ^D --- nocite: "[@*]" references: - author: - family: Author given: Andy container-title: Journal id: item1 issued: 2012 title: Title, any entry type except online type: article-journal - author: - family: Author given: Andy container-title: Journal id: item2 issued: 2012 title: Title, entry type online type: webpage url: "http://foo.bar" --- ``` ================================================ FILE: test/command/biblatex-padhye.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Padhye, Firoiu, and Towsley 1999) Padhye, Jitendra, Victor Firoiu, and Don Towsley. 1999. “A Stochastic Model of TCP Reno Congestion Avoidance and Control.” Technical report 99-02. Amherst, Mass.: University of Massachusetts. Formatted with pandoc and apa.csl, 2013-10-23: (Padhye, Firoiu, & Towsley, 1999) Padhye, J., Firoiu, V., & Towsley, D. (1999). *A stochastic model of TCP Reno congestion avoidance and control* (technical report No. 99-02). Amherst, Mass.: University of Massachusetts. } @Report{padhye, author = {Padhye, Jitendra and Firoiu, Victor and Towsley, Don}, title = {A Stochastic Model of {TCP Reno} Congestion Avoidance and Control}, type = {techreport}, institution = {University of Massachusetts}, date = 1999, number = {99-02}, location = {Amherst, Mass.}, hyphenation = {american}, sorttitle = {A Stochastic Model of TCP Reno Congestion Avoidance and Control}, indextitle = {Stochastic Model of {TCP Reno} Congestion Avoidance and Control, A}, annotation = {This is a report entry for a technical report. Note the format of the type field in the database file which uses a localization key. The number of the report is given in the number field. Also note the sorttitle and indextitle fields}, abstract = {The steady state performance of a bulk transfer TCP flow (i.e., a flow with a large amount of data to send, such as FTP transfers) may be characterized by three quantities. The first is the send rate, which is the amount of data sent by the sender in unit time. The second is the throughput, which is the amount of data received by the receiver in unit time. Note that the throughput will always be less than or equal to the send rate due to losses. Finally, the number of non-duplicate packets received by the receiver in unit time gives us the goodput of the connection. The goodput is always less than or equal to the throughput, since the receiver may receive two copies of the same packet due to retransmissions by the sender. In a previous paper, we presented a simple model for predicting the steady state send rate of a bulk transfer TCP flow as a function of loss rate and round trip time. In this paper, we extend that work in two ways. First, we analyze the performance of bulk transfer TCP flows using more precise, stochastic analysis. Second, we build upon the previous analysis to provide both an approximate formula as well as a more accurate stochastic model for the steady state throughput of a bulk transfer TCP flow.}, file = {ftp://gaia.cs.umass.edu/pub/Padhey99-markov.ps}, } ^D --- nocite: "[@*]" references: - abstract: The steady state performance of a bulk transfer TCP flow (i.e., a flow with a large amount of data to send, such as FTP transfers) may be characterized by three quantities. The first is the send rate, which is the amount of data sent by the sender in unit time. The second is the throughput, which is the amount of data received by the receiver in unit time. Note that the throughput will always be less than or equal to the send rate due to losses. Finally, the number of non-duplicate packets received by the receiver in unit time gives us the goodput of the connection. The goodput is always less than or equal to the throughput, since the receiver may receive two copies of the same packet due to retransmissions by the sender. In a previous paper, we presented a simple model for predicting the steady state send rate of a bulk transfer TCP flow as a function of loss rate and round trip time. In this paper, we extend that work in two ways. First, we analyze the performance of bulk transfer TCP flows using more precise, stochastic analysis. Second, we build upon the previous analysis to provide both an approximate formula as well as a more accurate stochastic model for the steady state throughput of a bulk transfer TCP flow. annote: This is a report entry for a technical report. Note the format of the type field in the database file which uses a localization key. The number of the report is given in the number field. Also note the sorttitle and indextitle fields author: - family: Padhye given: Jitendra - family: Firoiu given: Victor - family: Towsley given: Don genre: technical report id: padhye issued: 1999 language: en-US number: 99-02 publisher: University of Massachusetts publisher-place: Amherst, Mass. title: A stochastic model of TCP Reno congestion avoidance and control type: report --- ``` ================================================ FILE: test/command/biblatex-patent.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{adapted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib TODO: Is there a CSL counterpart for the biblatex field "holder"?} @patent{almendro, Annotation = {This is a patent entry with a location field. The number is given in the number field. Note the format of the location field in the database file. Compare laufenberg, sorace, and kowalik}, Author = {Almendro, José L. and Martín, Jacinto and Sánchez, Alberto and Nozal, Fernando}, Date = 1998, Hyphenation = {german}, Location = {countryfr and countryuk and countryde}, Number = {EU-29702195U}, Title = {Elektromagnetisches Signalhorn}} @patent{kowalik, Annotation = {This is a patent entry for a French patent request with a full date. The number is given in the number field. Note the format of the type and date fields in the database file. Compare almendro, laufenberg, and sorace}, Author = {Kowalik, F. and Isard, M.}, Date = {1995-01-11}, Hyphenation = {french}, Indextitle = {Estimateur d'un défaut de fonctionnement}, Number = 9500261, Title = {Estimateur d'un défaut de fonctionnement d'un modulateur en quadrature et étage de modulation l'utilisant}, Type = {patreqfr}} @patent{laufenberg, Annotation = {This is a patent entry with a holder field. Note the format of the type and location fields in the database file. Compare almendro, sorace, and kowalik}, Author = {Laufenberg, Xaver and Eynius, Dominique and Suelzle, Helmut and Usbeck, Stephan and Spaeth, Matthias and Neuser-Hoffmann, Miriam and Myrzik, Christian and Schmid, Manfred and Nietfeld, Franz and Thiel, Alexander and Braun, Harald and Ebner, Norbert}, Date = {2006-09-13}, File = {http://v3.espacenet.com/textdoc?IDX=EP1700367}, Holder = {{Robert Bosch GmbH} and {Daimler Chrysler AG} and {Bayerische Motoren Werke AG}}, Hyphenation = {german}, Number = 1700367, Title = {Elektrische Einrichtung und Betriebsverfahren}, Type = {patenteu}, Abstract = {The invention relates to an electric device comprising a generator, in particular for use in the vehicle electric system of a motor vehicle and a controller for controlling the generator voltage. The device is equipped with a control zone, in which the voltage is controlled and zones, in which the torque is controlled. The invention also relates to methods for operating a device of this type.}} @patent{sorace, Annotation = {This is a patent entry with a holder field. Note the format of the type and date fields in the database file. Compare almendro, laufenberg, and kowalik}, Author = {Sorace, Ronald E. and Reinhardt, Victor S. and Vaughn, Steven A.}, Date = {1997-09-16}, Date-Modified = {2013-10-16 13:44:15 +0000}, Holder = {{Hughes Aircraft Company}}, Hyphenation = {american}, Number = 5668842, Title = {High-Speed Digital-to-{RF} Converter}, Type = {patentus}} ^D --- nocite: "[@*]" references: - annote: This is a patent entry with a location field. The number is given in the number field. Note the format of the location field in the database file. Compare laufenberg, sorace, and kowalik author: - family: Almendro given: José L. - family: Martín given: Jacinto - family: Sánchez given: Alberto - family: Nozal given: Fernando id: almendro issued: 1998 jurisdiction: France; United Kingdom; Germany language: de-DE number: EU-29702195U title: Elektromagnetisches Signalhorn type: patent - annote: This is a patent entry for a French patent request with a full date. The number is given in the number field. Note the format of the type and date fields in the database file. Compare almendro, laufenberg, and sorace author: - family: Kowalik given: F. - family: Isard given: M. genre: French patent request id: kowalik issued: 1995-01-11 language: fr-FR number: 9500261 title: Estimateur d'un défaut de fonctionnement d'un modulateur en quadrature et étage de modulation l'utilisant type: patent - abstract: The invention relates to an electric device comprising a generator, in particular for use in the vehicle electric system of a motor vehicle and a controller for controlling the generator voltage. The device is equipped with a control zone, in which the voltage is controlled and zones, in which the torque is controlled. The invention also relates to methods for operating a device of this type. annote: This is a patent entry with a holder field. Note the format of the type and location fields in the database file. Compare almendro, sorace, and kowalik author: - family: Laufenberg given: Xaver - family: Eynius given: Dominique - family: Suelzle given: Helmut - family: Usbeck given: Stephan - family: Spaeth given: Matthias - family: Neuser-Hoffmann given: Miriam - family: Myrzik given: Christian - family: Schmid given: Manfred - family: Nietfeld given: Franz - family: Thiel given: Alexander - family: Braun given: Harald - family: Ebner given: Norbert genre: European patent id: laufenberg issued: 2006-09-13 language: de-DE number: 1700367 title: Elektrische Einrichtung und Betriebsverfahren type: patent - annote: This is a patent entry with a holder field. Note the format of the type and date fields in the database file. Compare almendro, laufenberg, and kowalik author: - family: Sorace given: Ronald E. - family: Reinhardt given: Victor S. - family: Vaughn given: Steven A. genre: U.S. patent id: sorace issued: 1997-09-16 language: en-US number: 5668842 title: High-speed digital-to-RF converter type: patent --- ``` ================================================ FILE: test/command/biblatex-periodical.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{excerpt from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib} @periodical{jcg, Annotation = {This is a periodical entry with an issn field.}, Issn = {0097-8493}, Issuetitle = {Semantic {3D} Media and Content}, Number = 4, Title = {Computers and Graphics}, Volume = 35, Year = 2011} ^D --- nocite: "[@*]" references: - annote: This is a periodical entry with an issn field. container-title: Computers and Graphics id: jcg issn: 0097-8493 issue: 4 issued: 2011 title: Semantic 3D media and content type: article-journal volume: 35 --- ``` ================================================ FILE: test/command/biblatex-piccato.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Piccato 2001) Piccato, Pablo. 2001. *City of Suspects: Crime in Mexico City, 1900–1931*. Durham; London: Duke University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Piccato, 2001) Piccato, P. (2001). *City of suspects: Crime in Mexico City, 1900–1931*. Durham; London: Duke University Press. } @Book{piccato, author = {Piccato, Pablo}, title = {City of Suspects}, date = 2001, publisher = {Duke University Press}, location = {Durham and London}, hyphenation = {american}, subtitle = {Crime in {Mexico City}, 1900--1931}, shorttitle = {City of Suspects}, annotation = {This is a book entry. Note the format of the location field in the database file}, } ^D --- nocite: "[@*]" references: - annote: This is a book entry. Note the format of the location field in the database file author: - family: Piccato given: Pablo id: piccato issued: 2001 language: en-US publisher: Duke University Press publisher-place: Durham; London title: "City of suspects: Crime in Mexico City, 1900--1931" title-short: City of suspects type: book --- ``` ================================================ FILE: test/command/biblatex-pines.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Pines 1979) Pines, Shlomo. 1979. “The Limitations of Human Knowledge According to Al-Farabi, ibn Bajja, and Maimonides.” In *Studies in Medieval Jewish History and Literature*, edited by Isadore Twersky, 82–109. Cambridge, Mass.: Harvard University Press. Formatted with pandoc and apa.csl, 2013-10-23: (Pines, 1979) Pines, S. (1979). The limitations of human knowledge according to Al-Farabi, ibn Bajja, and Maimonides. In I. Twersky (Ed.), *Studies in medieval Jewish history and literature* (pp. 82–109). Cambridge, Mass.: Harvard University Press. } @string{ hup = {Harvard University Press} } @InCollection{pines, author = {Pines, Shlomo}, editor = {Twersky, Isadore}, title = {The Limitations of Human Knowledge According to {Al-Farabi}, {ibn Bajja}, and {Maimonides}}, date = 1979, booktitle = {Studies in Medieval {Jewish} History and Literature}, publisher = hup, location = {Cambridge, Mass.}, pages = {82-109}, keywords = {secondary}, hyphenation = {american}, indextitle = {Limitations of Human Knowledge According to {Al-Farabi}, {ibn Bajja}, and {Maimonides}, The}, shorttitle = {Limitations of Human Knowledge}, annotation = {A typical incollection entry. Note the indextitle field}, } ^D --- nocite: "[@*]" references: - annote: A typical incollection entry. Note the indextitle field author: - family: Pines given: Shlomo container-title: Studies in medieval Jewish history and literature editor: - family: Twersky given: Isadore id: pines issued: 1979 keyword: secondary language: en-US page: 82-109 publisher: Harvard University Press publisher-place: Cambridge, Mass. title: The limitations of human knowledge according to Al-Farabi, [ibn Bajja]{.nocase}, and Maimonides title-short: Limitations of human knowledge type: chapter --- ``` ================================================ FILE: test/command/biblatex-quotes.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{excerpt from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib \mkbibquote{} should be replaced by a matching set of quotation marks that can be used by citeproc for quote substitution and flipflopping. English smart double quotation marks seem best, as they cannot be confused with apostrophes. \enquote{}, \enquote*{} should be replaced by a matching set of quotation marks, too: “foo”, ‘bar’. } @string{pup = {Princeton University Press}} @book{nussbaum, Annotation = {A book entry. Note the sorttitle and indexsorttitle fields and the markup of the quotes in the database file}, Author = {Nussbaum, Martha}, Date = 1978, Hyphenation = {american}, Indexsorttitle = {Aristotle's De Motu Animalium}, Keywords = {secondary}, Location = {Princeton}, Publisher = pup, Sorttitle = {Aristotle's De Motu Animalium}, Title = {Aristotle's \mkbibquote{De Motu Animalium}}} ^D --- nocite: "[@*]" references: - annote: A book entry. Note the sorttitle and indexsorttitle fields and the markup of the quotes in the database file author: - family: Nussbaum given: Martha id: nussbaum issued: 1978 keyword: secondary language: en-US publisher: Princeton University Press publisher-place: Princeton title: Aristotle's "De Motu Animalium" type: book --- ``` ================================================ FILE: test/command/biblatex-reese.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Reese 1958) Reese, Trevor R. 1958. “Georgia in Anglo-Spanish Diplomacy, 1736-1739.” *William and Mary Quarterly, 3* 15: 168–190. Formatted with pandoc and apa.csl, 2013-10-23: (Reese, 1958) Reese, T. R. (1958). Georgia in Anglo-Spanish diplomacy, 1736-1739. *William and Mary Quarterly, 3*, *15*, 168–190. NOTES: - biblio2yaml - series field: still not entirely satisfactory. Could we map this to some existing CSL variable, and have the CSL styles handle this? "edition", maybe ?? } @Article{reese, author = {Reese, Trevor R.}, title = {Georgia in {Anglo-Spanish} Diplomacy, 1736-1739}, journaltitle = {William and Mary Quarterly}, date = 1958, series = 3, volume = 15, pages = {168-190}, hyphenation = {american}, annotation = {An article entry with a series and a volume field. Note the format of the series. If the value of the series field is an integer, this number is printed as an ordinal and the string \enquote*{series} is appended automatically}, } ^D --- nocite: "[@*]" references: - annote: An article entry with a series and a volume field. Note the format of the series. If the value of the series field is an integer, this number is printed as an ordinal and the string 'series' is appended automatically author: - family: Reese given: Trevor R. collection-title: 3rd series container-title: William and Mary Quarterly id: reese issued: 1958 language: en-US page: 168-190 title: Georgia in Anglo-Spanish diplomacy, 1736-1739 type: article-journal volume: 15 --- ``` ================================================ FILE: test/command/biblatex-report.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{excerpt from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib TODO: Where to map "file" field? } @report{chiu, Annotation = {This is a report entry for a research report. Note the format of the type field in the database file which uses a localization key. The number of the report is given in the number field. Also note the sorttitle and indextitle fields}, Author = {Chiu, Willy W. and Chow, We Min}, Date = 1978, Hyphenation = {american}, Indextitle = {Hybrid Hierarchical Model, A}, Institution = {IBM}, Number = {RC-6947}, Sorttitle = {Hybrid Hierarchical Model of a Multiple Virtual Storage (MVS) Operating System}, Title = {A Hybrid Hierarchical Model of a {Multiple Virtual Storage} ({MVS}) Operating System}, Type = {resreport}} @report{padhye, Annotation = {This is a report entry for a technical report. Note the format of the type field in the database file which uses a localization key. The number of the report is given in the number field. Also note the sorttitle and indextitle fields}, Author = {Padhye, Jitendra and Firoiu, Victor and Towsley, Don}, Date = 1999, File = {ftp://gaia.cs.umass.edu/pub/Padhey99-markov.ps}, Hyphenation = {american}, Indextitle = {Stochastic Model of TCP Reno Congestion Avoidance and Control, A}, Institution = {University of Massachusetts}, Location = {Amherst, Mass.}, Number = {99-02}, Sorttitle = {A Stochastic Model of TCP Reno Congestion Avoidance and Control}, Title = {A Stochastic Model of {TCP Reno} Congestion Avoidance and Control}, Type = {techreport}, Abstract = {The steady state performance of a bulk transfer TCP flow (i.e., a flow with a large amount of data to send, such as FTP transfers) may be characterized by three quantities. The first is the send rate, which is the amount of data sent by the sender in unit time. The second is the throughput, which is the amount of data received by the receiver in unit time. Note that the throughput will always be less than or equal to the send rate due to losses. Finally, the number of non-duplicate packets received by the receiver in unit time gives us the goodput of the connection. The goodput is always less than or equal to the throughput, since the receiver may receive two copies of the same packet due to retransmissions by the sender. In a previous paper, we presented a simple model for predicting the steady state send rate of a bulk transfer TCP flow as a function of loss rate and round trip time. In this paper, we extend that work in two ways. First, we analyze the performance of bulk transfer TCP flows using more precise, stochastic analysis. Second, we build upon the previous analysis to provide both an approximate formula as well as a more accurate stochastic model for the steady state throughput of a bulk transfer TCP flow.}} ^D --- nocite: "[@*]" references: - annote: This is a report entry for a research report. Note the format of the type field in the database file which uses a localization key. The number of the report is given in the number field. Also note the sorttitle and indextitle fields author: - family: Chiu given: Willy W. - family: Chow given: We Min genre: research report id: chiu issued: 1978 language: en-US number: RC-6947 publisher: IBM title: A hybrid hierarchical model of a Multiple Virtual Storage (MVS) operating system type: report - abstract: The steady state performance of a bulk transfer TCP flow (i.e., a flow with a large amount of data to send, such as FTP transfers) may be characterized by three quantities. The first is the send rate, which is the amount of data sent by the sender in unit time. The second is the throughput, which is the amount of data received by the receiver in unit time. Note that the throughput will always be less than or equal to the send rate due to losses. Finally, the number of non-duplicate packets received by the receiver in unit time gives us the goodput of the connection. The goodput is always less than or equal to the throughput, since the receiver may receive two copies of the same packet due to retransmissions by the sender. In a previous paper, we presented a simple model for predicting the steady state send rate of a bulk transfer TCP flow as a function of loss rate and round trip time. In this paper, we extend that work in two ways. First, we analyze the performance of bulk transfer TCP flows using more precise, stochastic analysis. Second, we build upon the previous analysis to provide both an approximate formula as well as a more accurate stochastic model for the steady state throughput of a bulk transfer TCP flow. annote: This is a report entry for a technical report. Note the format of the type field in the database file which uses a localization key. The number of the report is given in the number field. Also note the sorttitle and indextitle fields author: - family: Padhye given: Jitendra - family: Firoiu given: Victor - family: Towsley given: Don genre: technical report id: padhye issued: 1999 language: en-US number: 99-02 publisher: University of Massachusetts publisher-place: Amherst, Mass. title: A stochastic model of TCP Reno congestion avoidance and control type: report --- ``` ================================================ FILE: test/command/biblatex-salam.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Salam 1968) Salam, Abdus. 1968. “Weak and Electromagnetic Interactions.” In *Elementary Particle Theory: Relativistic Groups and Analyticity. Proceedings of the Eighth Nobel Symposium*, edited by Nils Svartholm, 367–377. Stockholm: Almquist & Wiksell. Formatted with pandoc and apa.csl, 2013-10-23: (Salam, 1968) Salam, A. (1968). Weak and electromagnetic interactions. In N. Svartholm (Ed.), *Elementary particle theory: Relativistic groups and analyticity. Proceedings of the eighth Nobel symposium* (pp. 367–377). Stockholm: Almquist & Wiksell. } @InProceedings{salam, author = {Salam, Abdus}, editor = {Svartholm, Nils}, title = {Weak and Electromagnetic Interactions}, date = 1968, booktitle = {Elementary particle theory}, booksubtitle = {Relativistic groups and analyticity}, booktitleaddon= {Proceedings of the Eighth {Nobel} Symposium}, eventdate = {1968-05-19/1968-05-25}, venue = {Aspen{\"a}sgarden, Lerum}, publisher = {Almquist \& Wiksell}, location = {Stockholm}, pages = {367-377}, } ^D --- nocite: "[@*]" references: - author: - family: Salam given: Abdus container-title: "Elementary particle theory: Relativistic groups and analyticity. Proceedings of the eighth Nobel symposium" editor: - family: Svartholm given: Nils event-date: 1968-05-19/1968-05-25 event-place: Aspenäsgarden, Lerum id: salam issued: 1968 page: 367-377 publisher: Almquist & Wiksell publisher-place: Stockholm title: Weak and electromagnetic interactions type: paper-conference --- ``` ================================================ FILE: test/command/biblatex-sarfraz.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Sarfraz and Razzak 2002) Sarfraz, M., and M. F. A. Razzak. 2002. “Technical Section: An Algorithm for Automatic Capturing of the Font Outlines.” *Computers and Graphics* 26 (5): 795–804. Formatted with pandoc and apa.csl, 2013-10-23: (Sarfraz & Razzak, 2002) Sarfraz, M., & Razzak, M. F. A. (2002). Technical section: An algorithm for automatic capturing of the font outlines. *Computers and Graphics*, *26*(5), 795–804. } @Article{sarfraz, author = {M. Sarfraz and M. F. A. Razzak}, title = {Technical section: {An} algorithm for automatic capturing of the font outlines}, year = 2002, volume = 26, number = 5, pages = {795-804}, issn = {0097-8493}, journal = {Computers and Graphics}, annotation = {An article entry with an issn field}, } ^D --- nocite: "[@*]" references: - annote: An article entry with an issn field author: - family: Sarfraz given: M. - family: Razzak given: M. F. A. container-title: Computers and Graphics id: sarfraz issn: 0097-8493 issue: 5 issued: 2002 page: 795-804 title: "Technical section: An algorithm for automatic capturing of the font outlines" title-short: Technical section type: article-journal volume: 26 --- ``` ================================================ FILE: test/command/biblatex-shore.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Shore 1991) Shore, Bradd. 1991. “Twice-born, Once Conceived: Meaning Construction and Cultural Cognition.” *American Anthropologist, New Series* 93 (1) (March): 9–27. Formatted with pandoc and apa.csl, 2013-10-23: (Shore, 1991) Shore, B. (1991). Twice-born, once conceived: Meaning construction and cultural cognition. *American Anthropologist, new series*, *93*(1), 9–27. } @Article{shore, author = {Shore, Bradd}, title = {Twice-Born, Once Conceived}, journaltitle = {American Anthropologist}, date = {1991-03}, subtitle = {Meaning Construction and Cultural Cognition}, series = {newseries}, volume = 93, number = 1, pages = {9-27}, annotation = {An article entry with series, volume, and number fields. Note the format of the series which is a localization key}, } ^D --- nocite: "[@*]" references: - annote: An article entry with series, volume, and number fields. Note the format of the series which is a localization key author: - family: Shore given: Bradd collection-title: New series container-title: American Anthropologist id: shore issue: 1 issued: 1991-03 page: 9-27 title: "Twice-born, once conceived: Meaning construction and cultural cognition" title-short: Twice-born, once conceived type: article-journal volume: 93 --- ``` ================================================ FILE: test/command/biblatex-sigfridsson.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Sigfridsson and Ryde 1998) Sigfridsson, Emma, and Ulf Ryde. 1998. “Comparison of Methods for Deriving Atomic Charges from the Electrostatic Potential and Moments.” *Journal of Computational Chemistry* 19 (4): 377–395. doi:[10.1002/(SICI)1096-987X(199803)19:4\<377::AID-JCC1\>3.0.CO;2-P](https://doi.org/10.1002/(SICI)1096-987X(199803)19:4<377::AID-JCC1>3.0.CO;2-P "10.1002/(SICI)1096-987X(199803)19:4<377::AID-JCC1>3.0.CO;2-P"). Formatted with pandoc and apa.csl, 2013-10-23: (Sigfridsson & Ryde, 1998) Sigfridsson, E., & Ryde, U. (1998). Comparison of methods for deriving atomic charges from the electrostatic potential and moments. *Journal of Computational Chemistry*, *19*(4), 377–395. doi:[10.1002/(SICI)1096-987X(199803)19:4\<377::AID-JCC1\>3.0.CO;2-P](https://doi.org/10.1002/(SICI)1096-987X(199803)19:4<377::AID-JCC1>3.0.CO;2-P "10.1002/(SICI)1096-987X(199803)19:4<377::AID-JCC1>3.0.CO;2-P") NOTES: - biblio2xaml - the string "doi:" should not appear as part of the content of the "doi" field } @Article{sigfridsson, author = {Sigfridsson, Emma and Ryde, Ulf}, title = {Comparison of methods for deriving atomic charges from the electrostatic potential and moments}, journaltitle = {Journal of Computational Chemistry}, date = 1998, volume = 19, number = 4, pages = {377-395}, doi = {10.1002/(SICI)1096-987X(199803)19:4<377::AID-JCC1>3.0.CO;2-P}, hyphenation = {american}, indextitle = {Methods for deriving atomic charges}, annotation = {An article entry with volume, number, and doi fields. Note that the \textsc{doi} is transformed into a clickable link if hyperref support has been enabled}, abstract = {Four methods for deriving partial atomic charges from the quantum chemical electrostatic potential (CHELP, CHELPG, Merz-Kollman, and RESP) have been compared and critically evaluated. It is shown that charges strongly depend on how and where the potential points are selected. Two alternative methods are suggested to avoid the arbitrariness in the point-selection schemes and van der Waals exclusion radii: CHELP-BOW, which also estimates the charges from the electrostatic potential, but with potential points that are Boltzmann-weighted after their occurrence in actual simulations using the energy function of the program in which the charges will be used, and CHELMO, which estimates the charges directly from the electrostatic multipole moments. Different criteria for the quality of the charges are discussed.}, } ^D --- nocite: "[@*]" references: - abstract: "Four methods for deriving partial atomic charges from the quantum chemical electrostatic potential (CHELP, CHELPG, Merz-Kollman, and RESP) have been compared and critically evaluated. It is shown that charges strongly depend on how and where the potential points are selected. Two alternative methods are suggested to avoid the arbitrariness in the point-selection schemes and van der Waals exclusion radii: CHELP-BOW, which also estimates the charges from the electrostatic potential, but with potential points that are Boltzmann-weighted after their occurrence in actual simulations using the energy function of the program in which the charges will be used, and CHELMO, which estimates the charges directly from the electrostatic multipole moments. Different criteria for the quality of the charges are discussed." annote: An article entry with volume, number, and doi fields. Note that the [doi]{.smallcaps} is transformed into a clickable link if hyperref support has been enabled author: - family: Sigfridsson given: Emma - family: Ryde given: Ulf container-title: Journal of Computational Chemistry doi: "10.1002/(SICI)1096-987X(199803)19:4\\<377::AID-JCC1\\>3.0.CO;2-P" id: sigfridsson issue: 4 issued: 1998 language: en-US page: 377-395 title: Comparison of methods for deriving atomic charges from the electrostatic potential and moments type: article-journal volume: 19 --- ``` ================================================ FILE: test/command/biblatex-sorace.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Sorace, Reinhardt, and Vaughn 1997) Sorace, Ronald E., Victor S. Reinhardt, and Steven A. Vaughn. 1997. “High-speed Digital-to-RF Converter.” U.S. patent. Formatted with pandoc and apa.csl, 2013-10-23: (Sorace, Reinhardt, & Vaughn, 1997) Sorace, R. E., Reinhardt, V. S., & Vaughn, S. A. (1997, September 16). High-speed digital-to-RF converter. U.S. patent. } @Patent{sorace, author = {Sorace, Ronald E. and Reinhardt, Victor S. and Vaughn, Steven A.}, title = {High-Speed Digital-to-{RF} Converter}, number = 5668842, date = {1997-09-16}, holder = {{Hughes Aircraft Company}}, type = {patentus}, hyphenation = {american}, annotation = {This is a patent entry with a holder field. Note the format of the type and date fields in the database file. Compare almendro, laufenberg, and kowalik}, } ^D --- nocite: "[@*]" references: - annote: This is a patent entry with a holder field. Note the format of the type and date fields in the database file. Compare almendro, laufenberg, and kowalik author: - family: Sorace given: Ronald E. - family: Reinhardt given: Victor S. - family: Vaughn given: Steven A. genre: U.S. patent id: sorace issued: 1997-09-16 language: en-US number: 5668842 title: High-speed digital-to-RF converter type: patent --- ``` ================================================ FILE: test/command/biblatex-spiegelberg.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Spiegelberg 1969) Spiegelberg, Herbert. 1969. ““Intention” und “Intentionalität” in der Scholastik, bei Brentano und Husserl.” *Studia Philosophica* 29: 189–216. Formatted with pandoc and apa.csl, 2013-10-23: (Spiegelberg, 1969) Spiegelberg, H. (1969). “Intention” und “Intentionalität” in der Scholastik, bei Brentano und Husserl. *Studia Philosophica*, *29*, 189–216. NOTES: - citeproc - flipflopping of quotes incorrect } @Article{spiegelberg, author = {Spiegelberg, Herbert}, title = {\mkbibquote{Intention} und \mkbibquote{Intentionalit{\"a}t} in der Scholastik, bei Brentano und Husserl}, journaltitle = {Studia Philosophica}, date = 1969, volume = 29, pages = {189-216}, hyphenation = {german}, sorttitle = {Intention und Intentionalitat in der Scholastik, bei Brentano und Husserl}, indexsorttitle= {Intention und Intentionalitat in der Scholastik, bei Brentano und Husserl}, shorttitle = {Intention und Intentionalit{\"a}t}, annotation = {An article entry. Note the sorttitle and indexsorttitle fields and the markup of the quotes in the database file}, } ^D --- nocite: "[@*]" references: - annote: An article entry. Note the sorttitle and indexsorttitle fields and the markup of the quotes in the database file author: - family: Spiegelberg given: Herbert container-title: Studia Philosophica id: spiegelberg issued: 1969 language: de-DE page: 189-216 title: "\"Intention\" und \"Intentionalität\" in der Scholastik, bei Brentano und Husserl" title-short: Intention und Intentionalität type: article-journal volume: 29 --- ``` ================================================ FILE: test/command/biblatex-springer.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Springer 1950) Springer, Otto. 1950. “Mediaeval Pilgrim Routes from Scandinavia to Rome.” *Mediaeval Studies* 12: 92–122. Formatted with pandoc and apa.csl, 2013-10-23: (Springer, 1950) Springer, O. (1950). Mediaeval pilgrim routes from Scandinavia to Rome. *Mediaeval Studies*, *12*, 92–122. } @Article{springer, author = {Springer, Otto}, title = {Mediaeval Pilgrim Routes from {Scandinavia} to {Rome}}, journaltitle = {Mediaeval Studies}, date = 1950, volume = 12, pages = {92-122}, hyphenation = {british}, shorttitle = {Mediaeval Pilgrim Routes}, annotation = {A plain article entry}, } ^D --- nocite: "[@*]" references: - annote: A plain article entry author: - family: Springer given: Otto container-title: Mediaeval Studies id: springer issued: 1950 language: en-GB page: 92-122 title: Mediaeval pilgrim routes from Scandinavia to Rome title-short: Mediaeval pilgrim routes type: article-journal volume: 12 --- ``` ================================================ FILE: test/command/biblatex-strings.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{excerpt from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib} @string{anch-ie = {Angew.~Chem. Int.~Ed.}} @article{herrmann, Author = {Herrmann, Wolfgang A. and Öfele, Karl and Schneider, Sabine K. and Herdtweck, Eberhardt and Hoffmann, Stephan D.}, Date = 2006, Hyphenation = {english}, Indextitle = {Carbocyclic carbene as an efficient catalyst, A}, Journaltitle = anch-ie, Number = 23, Pages = {3859-3862}, Title = {A Carbocyclic Carbene as an Efficient Catalyst Ligand for {C--C} Coupling Reactions}, Volume = 45} ^D --- nocite: "[@*]" references: - author: - family: Herrmann given: Wolfgang A. - family: Öfele given: Karl - family: Schneider given: Sabine K. - family: Herdtweck given: Eberhardt - family: Hoffmann given: Stephan D. container-title: Angew. Chem. Int. Ed. id: herrmann issue: 23 issued: 2006 language: en-US page: 3859-3862 title: A carbocyclic carbene as an efficient catalyst ligand for C--C coupling reactions type: article-journal volume: 45 --- ``` ================================================ FILE: test/command/biblatex-test-case-conversion.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ - bibtex and biblatex - expect titles in title case - styles use titles as is, or convert them to sentence case - strings wrapped {} are not converted - all CSL styles at and - expect titles in sentence case - styles use titles as is, or convert them to title case - except for (hardcoded) list of stop words, see - citeproc-js (MLZ only?) also recognizes a markup syntax for suppressing title-case changes on a range of text (see ): - `lowercase` - Proposal: - When converting to yaml, convert English titles to sentence case, - for all strings wrapped in {} where {} is not part of a latex command, ... - ... when starting with an uppercase letter: suppress conversion, remove the {} - ... when starting with a lowercase letter ("nm", "iPod"): suppress conversion, replace the {} with - Note: Camel case ("iPod") needs to be protected in bibtex/biblatex anyway; the only "extension" (wrt bibtex/biblatex specs) we'd be introducing is wrapping lowercase-only strings in {}, something that is never necessary on the latex side but won't break anything there either. - citeproc-hs/pandoc-citeproc should be modified to honour this new syntax and suppress conversion to title case for strings wrapped in ``. - Expected output, using one of the title-case CSL styles, here chicago-author-date.csl: Author, Ann. 2013. “A Title, in English, with a Proper Name and an ACRONYM and a camelCase Word and Some Units, 400 nm, 3 cm, and a Quote, *Alea iacta est*.” *Journal*. } @article{item1, Author = {Author, Ann}, Date = {2013}, Hyphenation = {english}, Journaltitle = {Journal}, Title = {A Title, in {English}, with a {Proper Name} and an {ACRONYM} and a {camelCase} Word and Some Units, 400~{nm}, 3~{cm}, and a Quote, \textit{{Alea} {iacta est}}} } ^D --- nocite: "[@*]" references: - author: - family: Author given: Ann container-title: Journal id: item1 issued: 2013 language: en-US title: A title, in English, with a Proper Name and an ACRONYM and a [camelCase]{.nocase} word and some units, 400 [nm]{.nocase}, 3 [cm]{.nocase}, and a quote, *Alea [iacta est]{.nocase}* type: article-journal --- ``` ================================================ FILE: test/command/biblatex-textnormal.md ================================================ ``` % pandoc -f biblatex -t markdown -s @book{item1, Title = {The Title \textnormal{of this book}}, } ^D --- nocite: "[@*]" references: - id: item1 title: The title [of this book]{.nodecor} type: book --- ``` ================================================ FILE: test/command/biblatex-thesis.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{excerpted from http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/biblatex-examples.bib TODO: Uppercase letters following hyphens need to be converted to lowercase, too (e.g., "r" in "High-Resolution". -- Same for citeproc when doing title-case conversion!) } @thesis{geer, Annotation = {This is a typical thesis entry for a PhD thesis. Note the type field in the database file which uses a localization key. Also note the format of the printed name and compare the useprefix option in the options field as well as vangennep}, Author = {de Geer, Ingrid}, Date = 1985, Hyphenation = {british}, Institution = {Uppsala Universitet}, Location = {Uppsala}, Options = {useprefix=false}, Subtitle = {The {Orkney} Earldom of the Twelfth Century. {A} Musicological Study}, Title = {Earl, Saint, Bishop, Skald~-- and Music}, Type = {phdthesis}} @thesis{loh, Annotation = {This is a typical thesis entry for an MA thesis. Note the type field in the database file which uses a localization key}, Author = {Loh, Nin C.}, Date = 1992, Hyphenation = {american}, Institution = {Massachusetts Institute of Technology}, Location = {Cambridge, Mass.}, Title = {High-Resolution Micromachined Interferometric Accelerometer}, Type = {mathesis}} ^D --- nocite: "[@*]" references: - annote: This is a typical thesis entry for a PhD thesis. Note the type field in the database file which uses a localization key. Also note the format of the printed name and compare the useprefix option in the options field as well as vangennep author: - dropping-particle: de family: Geer given: Ingrid genre: PhD thesis id: geer issued: 1985 language: en-GB publisher: Uppsala Universitet publisher-place: Uppsala title: "Earl, saint, bishop, skald -- and music: The Orkney earldom of the twelfth century. A musicological study" title-short: Earl, saint, bishop, skald -- and music type: thesis - annote: This is a typical thesis entry for an MA thesis. Note the type field in the database file which uses a localization key author: - family: Loh given: Nin C. genre: Master's thesis id: loh issued: 1992 language: en-US publisher: Massachusetts Institute of Technology publisher-place: Cambridge, Mass. title: High-resolution micromachined interferometric accelerometer type: thesis --- ``` ================================================ FILE: test/command/biblatex-title-and-shorttitle.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ TODO: Slight inconsistency: When a biblatex “title” field contains a colon, the part before the colon is mapped to CSL “title-short”. When there’s a biblatex “title” and a “subtitle” field, CSL “title-short” is not set, though it would make at least as much sense to map “title” (without “subtitle”) to CSL “title-short” in this case. CSL “container-title-short” could also be set - from biblatex “shortjournal” - for inbook, incollection etc. from the “shorttitle” field of the cross-referenced book, collection etc. entry (see item5, item6) ... but it might not really be worth it, “container-title-short” not being used once in my sample of 70+ CSL styles. } @book{item4, Shorttitle = {The Shorttitle}, Subtitle = {And a Subtitle, in Two Separate Fields; plus a Separate “Shorttitle” Field}, Title = {The Title: With a Colon in the “Title” Field}} @book{item3, Subtitle = {And a Subtitle, in two separate fields}, Title = {The Title: With a Colon in the “title” field}} @book{item2, Subtitle = {The Subtitle, In Two Separate fields}, Title = {The Title}} @book{item1, Title = {The Title: And the Subtitle, all in the “title” Field}} @inbook{item5, Title = {The inbook Title: And the Subtitle, all in the “title” Field}, Crossref = {item6}} @book{item6, Title = {The Title: And the Subtitle, all in the “title” Field}, Shorttitle = {The Shorttitle}, } ^D --- nocite: "[@*]" references: - id: item4 title: "The title: With a colon in the \"title\" field: And a subtitle, in two separate fields; plus a separate \"shorttitle\" field" title-short: The shorttitle type: book - id: item3 title: "The title: With a colon in the \"title\" field: And a subtitle, in two separate fields" title-short: The title type: book - id: item2 title: "The title: The subtitle, in two separate fields" title-short: The title type: book - id: item1 title: "The title: And the subtitle, all in the \"title\" field" title-short: The title type: book - container-title: "The title: And the subtitle, all in the \"title\" field" id: item5 title: "The inbook title: And the subtitle, all in the \"title\" field" title-short: The inbook title type: chapter - id: item6 title: "The title: And the subtitle, all in the \"title\" field" title-short: The shorttitle type: book --- ``` ================================================ FILE: test/command/biblatex-vangennep-related.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (van Gennep 1909) van Gennep, Arnold. 1909. *Les rites de passage*. Paris: Nourry. Formatted with pandoc and apa.csl, 2013-10-23: (van Gennep, 1909) van Gennep, A. (1909). *Les rites de passage*. Paris: Nourry. NOTES: - biblio2yaml - "related = {vizedom:related}, relatedtype = {bytranslator}": no equivalent implemented in CSL - "options = {useprefix}," is shorthand for "options = {useprefix=true}," } @Book{vangennep:related, author = {van Gennep, Arnold}, title = {Les rites de passage}, date = 1909, publisher = {Nourry}, location = {Paris}, options = {useprefix}, hyphenation = {french}, related = {vizedom:related}, relatedtype = {bytranslator}, sorttitle = {Rites de passage}, indextitle = {Rites de passage, Les}, shorttitle = {Rites de passage}, annotation = {A variant of the vangennep entry related to its translation. Note the format of the related and relatedtype fields}, } ^D --- nocite: "[@*]" references: - annote: A variant of the vangennep entry related to its translation. Note the format of the related and relatedtype fields author: - family: Gennep given: Arnold non-dropping-particle: van id: "vangennep:related" issued: 1909 language: fr-FR publisher: Nourry publisher-place: Paris title: Les rites de passage title-short: Rites de passage type: book --- ``` ================================================ FILE: test/command/biblatex-vangennep-trans.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (van Gennep 1960) van Gennep, Arnold. 1960. *The Rites of Passage*. Translated by Monika B. Vizedom and Gabrielle L. Caffee. University of Chicago Press. Formatted with pandoc and apa.csl, 2013-10-23: (van Gennep, 1960) van Gennep, A. (1960). *The rites of passage*. (M. B. Vizedom & G. L. Caffee, Trans.). University of Chicago Press. } @Book{vangennep:trans, author = {van Gennep, Arnold}, title = {The Rites of Passage}, year = 1960, translator = {Vizedom, Monika B. and Caffee, Gabrielle L.}, language = {english}, origlanguage = {french}, publisher = {University of Chicago Press}, options = {useprefix}, indextitle = {Rites of Passage, The}, sorttitle = {Rites of Passage}, shorttitle = {Rites of Passage}, hyphenation = {american}, annotation = {A translation of the vangennep entry. Note the translator and origlanguage fields. Compare with the vangennep:related entry.}, } ^D --- nocite: "[@*]" references: - annote: "A translation of the vangennep entry. Note the translator and origlanguage fields. Compare with the vangennep:related entry." author: - family: Gennep given: Arnold non-dropping-particle: van id: "vangennep:trans" issued: 1960 language: en-US publisher: University of Chicago Press title: The rites of passage title-short: Rites of passage translator: - family: Vizedom given: Monika B. - family: Caffee given: Gabrielle L. type: book --- ``` ================================================ FILE: test/command/biblatex-vangennep.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (van Gennep 1909) van Gennep, Arnold. 1909. *Les rites de passage*. Paris: Nourry. Formatted with pandoc and apa.csl, 2013-10-23: (van Gennep, 1909) van Gennep, A. (1909). *Les rites de passage*. Paris: Nourry. } @Book{vangennep, author = {van Gennep, Arnold}, title = {Les rites de passage}, date = 1909, publisher = {Nourry}, location = {Paris}, options = {useprefix}, hyphenation = {french}, sorttitle = {Rites de passage}, indextitle = {Rites de passage, Les}, shorttitle = {Rites de passage}, annotation = {A book entry. Note the format of the printed name and compare the useprefix option in the options field as well as brandt and geer}, } ^D --- nocite: "[@*]" references: - annote: A book entry. Note the format of the printed name and compare the useprefix option in the options field as well as brandt and geer author: - family: Gennep given: Arnold non-dropping-particle: van id: vangennep issued: 1909 language: fr-FR publisher: Nourry publisher-place: Paris title: Les rites de passage title-short: Rites de passage type: book --- ``` ================================================ FILE: test/command/biblatex-vazques-de-parga-mvbook.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Vázques de Parga, Lacarra, and Uría Ríu 1993) Vázques de Parga, Luis, José María Lacarra, and Juan Uría Ríu. 1993. *Las Peregrinaciones a Santiago de Compostela*. 3. Pamplona: Iberdrola. Formatted with pandoc and apa.csl, 2013-10-23: (Vázques de Parga, Lacarra, & Uría Ríu, 1993) Vázques de Parga, L., Lacarra, J. M., & Uría Ríu, J. (1993). *Las Peregrinaciones a Santiago de Compostela* (1-3). Pamplona: Iberdrola. NOTES: - citeproc - term "vols." missing } @mvbook{vazques-de-parga, author = {V{\'a}zques{ de }Parga, Luis and Lacarra, Jos{\'e} Mar{\'i}a and Ur{\'i}a R{\'i}u, Juan}, title = {Las Peregrinaciones a Santiago de Compostela}, date = 1993, volumes = 3, note = {Ed. facs. de la realizada en 1948--49}, publisher = {Iberdrola}, location = {Pamplona}, hyphenation = {spanish}, sorttitle = {Peregrinaciones a Santiago de Compostela}, indextitle = {Peregrinaciones a Santiago de Compostela, Las}, shorttitle = {Peregrinaciones}, annotation = {A multivolume book cited as a whole. This is a book entry with volumes, note, sorttitle, and indextitle fields}, } ^D --- nocite: "[@*]" references: - annote: A multivolume book cited as a whole. This is a book entry with volumes, note, sorttitle, and indextitle fields author: - family: Vázques de Parga given: Luis - family: Lacarra given: José María - family: Uría Ríu given: Juan id: vazques-de-parga issued: 1993 language: es-ES note: Ed. facs. de la realizada en 1948--49 number-of-volumes: 3 publisher: Iberdrola publisher-place: Pamplona title: Las Peregrinaciones a Santiago de Compostela title-short: Peregrinaciones type: book --- ``` ================================================ FILE: test/command/biblatex-vazques-de-parga.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Vázques de Parga, Lacarra, and Uría Ríu 1993) Vázques de Parga, Luis, José María Lacarra, and Juan Uría Ríu. 1993. *Las Peregrinaciones a Santiago de Compostela*. 3. Pamplona: Iberdrola. Formatted with pandoc and apa.csl, 2013-10-23: (Vázques de Parga, Lacarra, & Uría Ríu, 1993) Vázques de Parga, L., Lacarra, J. M., & Uría Ríu, J. (1993). *Las Peregrinaciones a Santiago de Compostela* (1-3). Pamplona: Iberdrola. NOTES: - citeproc - term "vols." missing } @Book{vazques-de-parga, author = {V{\'a}zques{ de }Parga, Luis and Lacarra, Jos{\'e} Mar{\'i}a and Ur{\'i}a R{\'i}u, Juan}, title = {Las Peregrinaciones a Santiago de Compostela}, date = 1993, volumes = 3, note = {Ed. facs. de la realizada en 1948--49}, publisher = {Iberdrola}, location = {Pamplona}, hyphenation = {spanish}, sorttitle = {Peregrinaciones a Santiago de Compostela}, indextitle = {Peregrinaciones a Santiago de Compostela, Las}, shorttitle = {Peregrinaciones}, annotation = {A multivolume book cited as a whole. This is a book entry with volumes, note, sorttitle, and indextitle fields}, } ^D --- nocite: "[@*]" references: - annote: A multivolume book cited as a whole. This is a book entry with volumes, note, sorttitle, and indextitle fields author: - family: Vázques de Parga given: Luis - family: Lacarra given: José María - family: Uría Ríu given: Juan id: vazques-de-parga issued: 1993 language: es-ES note: Ed. facs. de la realizada en 1948--49 number-of-volumes: 3 publisher: Iberdrola publisher-place: Pamplona title: Las Peregrinaciones a Santiago de Compostela title-short: Peregrinaciones type: book --- ``` ================================================ FILE: test/command/biblatex-video.md ================================================ ``` % pandoc -f biblatex -t markdown -s @video{x1,title={blah}} @movie{x2,title={blah}} ^D --- nocite: "[@*]" references: - id: x1 title: Blah type: motion_picture - id: x2 title: Blah type: motion_picture --- ``` ================================================ FILE: test/command/biblatex-vizedom-related.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Vizedom and Caffee 1960) Vizedom, Monika B., and Gabrielle L. Caffee, trans. 1960. *The Rites of Passage*. University of Chicago Press. Formatted with pandoc and apa.csl, 2013-10-23: (Vizedom & Caffee, 1960) Vizedom, M. B., & Caffee, G. L. (Trans.). (1960). *The rites of passage*. University of Chicago Press. NOTES: - biblio2yaml - "related = {vangennep}, relatedtype = {translationof}": no equivalent implemented in CSL } @Book{vizedom:related, title = {The Rites of Passage}, year = 1960, translator = {Vizedom, Monika B. and Caffee, Gabrielle L.}, language = {english}, publisher = {University of Chicago Press}, hyphenation = {american}, options = {usetranslator}, related = {vangennep}, relatedtype = {translationof}, indextitle = {Rites of Passage, The}, sorttitle = {Rites of Passage}, shorttitle = {Rites of Passage}, annotation = {A translated work from vangennep. Note the format of the related and relatedtype fields}, } ^D --- nocite: "[@*]" references: - annote: A translated work from vangennep. Note the format of the related and relatedtype fields id: "vizedom:related" issued: 1960 language: en-US publisher: University of Chicago Press title: The rites of passage title-short: Rites of passage translator: - family: Vizedom given: Monika B. - family: Caffee given: Gabrielle L. type: book --- ``` ================================================ FILE: test/command/biblatex-wassenberg.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Wassenberg and Sanders 2010) Wassenberg, Jan, and Peter Sanders. 2010. “Faster Radix Sort via Virtual Memory and Write-combining” (version 1). August 17. Formatted with pandoc and apa.csl, 2013-10-23: (Wassenberg & Sanders, 2010) Wassenberg, J., & Sanders, P. (2010, August 17). Faster radix sort via virtual memory and write-combining. NOTES: - biblio2yaml - "eprinttype = {arxiv}, eprintclass = {cs.DS}, eprint = {1008.2849v1}" should be used to reconstruct a Url: http://arxiv.org/abs/1008.2849v1 ("cs.DS" does not seem to be essential) } @Online{wassenberg, author = {Wassenberg, Jan and Sanders, Peter}, title = {Faster Radix Sort via Virtual Memory and Write-Combining}, date = {2010-08-17}, version = 1, hyphenation = {american}, eprinttype = {arxiv}, eprintclass = {cs.DS}, eprint = {1008.2849v1}, annotation = {A recent online reference from arXiv using the new (April 2007 onward) identifier format. Note the eprint, eprinttype, and eprintclass fields. Also note that the arXiv reference is transformed into a clickable link if hyperref support has been enabled}, abstract = {Sorting algorithms are the deciding factor for the performance of common operations such as removal of duplicates or database sort-merge joins. This work focuses on 32-bit integer keys, optionally paired with a 32-bit value. We present a fast radix sorting algorithm that builds upon a microarchitecture-aware variant of counting sort}, } ^D --- nocite: "[@*]" references: - abstract: Sorting algorithms are the deciding factor for the performance of common operations such as removal of duplicates or database sort-merge joins. This work focuses on 32-bit integer keys, optionally paired with a 32-bit value. We present a fast radix sorting algorithm that builds upon a microarchitecture-aware variant of counting sort annote: A recent online reference from arXiv using the new (April 2007 onward) identifier format. Note the eprint, eprinttype, and eprintclass fields. Also note that the arXiv reference is transformed into a clickable link if hyperref support has been enabled author: - family: Wassenberg given: Jan - family: Sanders given: Peter id: wassenberg issued: 2010-08-17 language: en-US title: Faster radix sort via virtual memory and write-combining type: webpage url: "https://arxiv.org/abs/1008.2849v1" version: 1 --- ``` ================================================ FILE: test/command/biblatex-weinberg.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Weinberg 1967) Weinberg, Steven. 1967. “A Model of Leptons.” *Phys. Rev. Lett.* 19: 1264–1266. Formatted with pandoc and apa.csl, 2013-10-23: (Weinberg, 1967) Weinberg, S. (1967). A model of leptons. *Phys. Rev. Lett.*, *19*, 1264–1266. } @Article{weinberg, author = {Weinberg, Steven}, title = {A Model of Leptons}, journaltitle = {Phys.~Rev.~Lett.}, date = 1967, volume = 19, pages = {1264-1266}, } ^D --- nocite: "[@*]" references: - author: - family: Weinberg given: Steven container-title: Phys. Rev. Lett. id: weinberg issued: 1967 page: 1264-1266 title: A model of leptons type: article-journal volume: 19 --- ``` ================================================ FILE: test/command/biblatex-westfahl-frontier.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Westfahl 2000) Westfahl, Gary, ed. 2000. *Space and Beyond: The Frontier Theme in Science Fiction*. Westport, Conn.; London: Greenwood. Formatted with pandoc and apa.csl, 2013-10-23: (Westfahl, 2000) Westfahl, G. (Ed.). (2000). *Space and beyond: The frontier theme in science fiction*. Westport, Conn.; London: Greenwood. } @Collection{westfahl:frontier, editor = {Westfahl, Gary}, title = {Space and Beyond}, date = 2000, subtitle = {The Frontier Theme in Science Fiction}, publisher = {Greenwood}, location = {Westport, Conn. and London}, hyphenation = {american}, booktitle = {Space and Beyond}, booksubtitle = {The Frontier Theme in Science Fiction}, annotation = {This is a collection entry. Note the format of the location field as well as the subtitle and booksubtitle fields}, } ^D --- nocite: "[@*]" references: - annote: This is a collection entry. Note the format of the location field as well as the subtitle and booksubtitle fields editor: - family: Westfahl given: Gary id: "westfahl:frontier" issued: 2000 language: en-US publisher: Greenwood publisher-place: Westport, Conn.; London title: "Space and beyond: The frontier theme in science fiction" title-short: Space and beyond type: book --- ``` ================================================ FILE: test/command/biblatex-westfahl-space.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Westfahl 2000a) (Westfahl 2000b) Westfahl, Gary. 2000a. “The True Frontier: Confronting and Avoiding the Realities of Space in American Science Fiction Films.” In *Space and Beyond: The Frontier Theme in Science Fiction*, edited by Gary Westfahl, 55–65. Westport, Conn.; London: Greenwood. ———, ed. 2000b. *Space and Beyond: The Frontier Theme in Science Fiction*. Westport, Conn.; London: Greenwood. Formatted with pandoc and apa.csl, 2013-10-23: (Westfahl, 2000) (Westfahl, 2000) Westfahl, G. (2000). The true frontier: Confronting and avoiding the realities of space in American science fiction films. In G. Westfahl (Ed.), *Space and beyond: The frontier theme in science fiction* (pp. 55–65). Westport, Conn.; London: Greenwood. Westfahl, G. (Ed.). (2000). *Space and beyond: The frontier theme in science fiction*. Westport, Conn.; London: Greenwood. } @InCollection{westfahl:space, author = {Westfahl, Gary}, title = {The True Frontier}, subtitle = {Confronting and Avoiding the Realities of Space in {American} Science Fiction Films}, pages = {55-65}, crossref = {westfahl:frontier}, hyphenation = {american}, indextitle = {True Frontier, The}, annotation = {A cross-referenced article from a collection. This is an incollection entry with a crossref field. Note the subtitle and indextitle fields}, } @Collection{westfahl:frontier, editor = {Westfahl, Gary}, title = {Space and Beyond}, date = 2000, subtitle = {The Frontier Theme in Science Fiction}, publisher = {Greenwood}, location = {Westport, Conn. and London}, hyphenation = {american}, booktitle = {Space and Beyond}, booksubtitle = {The Frontier Theme in Science Fiction}, annotation = {This is a collection entry. Note the format of the location field as well as the subtitle and booksubtitle fields}, } ^D --- nocite: "[@*]" references: - annote: A cross-referenced article from a collection. This is an incollection entry with a crossref field. Note the subtitle and indextitle fields author: - family: Westfahl given: Gary container-title: "Space and beyond: The frontier theme in science fiction" editor: - family: Westfahl given: Gary id: "westfahl:space" issued: 2000 language: en-US page: 55-65 publisher: Greenwood publisher-place: Westport, Conn.; London title: "The true frontier: Confronting and avoiding the realities of space in American science fiction films" title-short: The true frontier type: chapter - annote: This is a collection entry. Note the format of the location field as well as the subtitle and booksubtitle fields editor: - family: Westfahl given: Gary id: "westfahl:frontier" issued: 2000 language: en-US publisher: Greenwood publisher-place: Westport, Conn.; London title: "Space and beyond: The frontier theme in science fiction" title-short: Space and beyond type: book --- ``` ================================================ FILE: test/command/biblatex-wilde.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Wilde 1899) Wilde, Oscar. 1899. *The Importance of Being Earnest: A Trivial Comedy for Serious People*. English and American Drama of the Nineteenth Century. Leonard Smithers and Company. Formatted with pandoc and apa.csl, 2013-10-23: (Wilde, 1899) Wilde, O. (1899). *The importance of being earnest: A trivial comedy for serious people*. Leonard Smithers and Company. NOTES: - biblio2yaml - From "eprint = {4HIWAAAAYAAJ}, eprinttype = {googlebooks}", a url could be reconstructed, shortest form: http://books.google.com?id=4HIWAAAAYAAJ } @Book{wilde, author = {Wilde, Oscar}, title = {The Importance of Being Earnest: {A} Trivial Comedy for Serious People}, year = 1899, series = {English and American drama of the Nineteenth Century}, publisher = {Leonard Smithers {and} Company}, eprint = {4HIWAAAAYAAJ}, eprinttype = {googlebooks}, annotation = {A book with eprint and eprinttype fields.}, } ^D --- nocite: "[@*]" references: - annote: A book with eprint and eprinttype fields. author: - family: Wilde given: Oscar collection-title: English and american drama of the nineteenth century id: wilde issued: 1899 publisher: Leonard Smithers and Company title: "The importance of being earnest: A trivial comedy for serious people" title-short: The importance of being earnest type: book url: "https://books.google.com?id=4HIWAAAAYAAJ" --- ``` ================================================ FILE: test/command/biblatex-worman.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Worman 2002) Worman, Nancy. 2002. *The Cast of Character: Style in Greek Literature*. Austin: University of Texas Press. Formatted with pandoc and apa.csl, 2013-10-23: (Worman, 2002) Worman, N. (2002). *The cast of character: Style in Greek literature*. Austin: University of Texas Press. } @Book{worman, author = {Worman, Nancy}, title = {The Cast of Character}, date = 2002, publisher = {University of Texas Press}, location = {Austin}, hyphenation = {american}, sorttitle = {Cast of Character}, indextitle = {Cast of Character, The}, subtitle = {Style in {Greek} Literature}, shorttitle = {Cast of Character}, annotation = {A book entry. Note the sorttitle and indextitle fields}, } ^D --- nocite: "[@*]" references: - annote: A book entry. Note the sorttitle and indextitle fields author: - family: Worman given: Nancy id: worman issued: 2002 language: en-US publisher: University of Texas Press publisher-place: Austin title: "The cast of character: Style in Greek literature" title-short: Cast of character type: book --- ``` ================================================ FILE: test/command/biblatex-yoon.md ================================================ ``` % pandoc -f biblatex -t markdown -s @comment{ Adapted from biblatex-example.bib Formatted with pandoc and chicago-author-date.csl, 2013-10-23: (Yoon et al. 2006) Yoon, Myeong S., Dowook Ryu, Jeongryul Kim, and Kyo Han Ahn. 2006. “Palladium Pincer Complexes with Reduced Bond Angle Strain: Efficient Catalysts for the Heck Reaction.” *Organometallics* 25 (10): 2409–2411. Formatted with pandoc and apa.csl, 2013-10-23: (Yoon, Ryu, Kim, & Ahn, 2006) Yoon, M. S., Ryu, D., Kim, J., & Ahn, K. H. (2006). Palladium pincer complexes with reduced bond angle strain: Efficient catalysts for the Heck reaction. *Organometallics*, *25*(10), 2409–2411. } @Article{yoon, author = {Yoon, Myeong S. and Ryu, Dowook and Kim, Jeongryul and Ahn, Kyo Han}, title = {Palladium pincer complexes with reduced bond angle strain: efficient catalysts for the {Heck} reaction}, journaltitle = {Organometallics}, date = 2006, volume = 25, number = 10, pages = {2409-2411}, indextitle = {Palladium pincer complexes}, } ^D --- nocite: "[@*]" references: - author: - family: Yoon given: Myeong S. - family: Ryu given: Dowook - family: Kim given: Jeongryul - family: Ahn given: Kyo Han container-title: Organometallics id: yoon issue: 10 issued: 2006 page: 2409-2411 title: "Palladium pincer complexes with reduced bond angle strain: Efficient catalysts for the Heck reaction" title-short: Palladium pincer complexes with reduced bond angle strain type: article-journal volume: 25 --- ``` ================================================ FILE: test/command/biblio.bib ================================================ @Book{item1, author="John Doe", title="First Book", year="2005", address="Cambridge", publisher="Cambridge University Press" } @Article{item2, author="John Doe", title="Article", year="2006", journal="Journal of Generic Studies", volume="6", pages="33-34" } @InCollection{пункт3, author="John Doe and Jenny Roe", title="Why Water Is Wet", booktitle="Third Book", editor="Sam Smith", publisher="Oxford University Press", address="Oxford", year="2007" } ================================================ FILE: test/command/bibtex-basic.md ================================================ ``` % pandoc -f biblatex -t markdown -s @Book{item1, author="John Doe", title="First Book", year="2005", address="Cambridge", publisher="Cambridge University Press" } @Article{item2, author="John Doe", title="Article", year="2006", journal="Journal of Generic Studies", volume="6", pages="33-34" } @InCollection{пункт3, author="John Doe and Jenny Roe", title="Why Water Is Wet", booktitle="Third Book", editor="Sam Smith", publisher="Oxford University Press", address="Oxford", year="2007" } ^D --- nocite: "[@*]" references: - author: - family: Doe given: John id: item1 issued: 2005 publisher: Cambridge University Press publisher-place: Cambridge title: First book type: book - author: - family: Doe given: John container-title: Journal of Generic Studies id: item2 issued: 2006 page: 33-34 title: Article type: article-journal volume: 6 - author: - family: Doe given: John - family: Roe given: Jenny container-title: Third book editor: - family: Smith given: Sam id: пункт3 issued: 2007 publisher: Oxford University Press publisher-place: Oxford title: Why water is wet type: chapter --- ``` ================================================ FILE: test/command/bioethics.csl ================================================ ================================================ FILE: test/command/bits-book-meta.md ================================================ ``` % pandoc -f jats -t native -s handbook-648 The NCBI Handbook McEntyre Jo Ostell Jim National Center for Biotechnology Information (NCBI), National Library of Medicine, National Institutes of Health, Bethesda, MD 20892-6510 11 2002 National Center for Biotechnology Information (NCBI), National Library of Medicine, National Institutes of Health Bethesda, MD 1 ^D Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "Jo" , Space , Str "McEntyre" ] ] ) , ( "date" , MetaInlines [ Str "2002-11" ] ) , ( "institute" , MetaList [ MetaInlines [ Str "National" , Space , Str "Center" , Space , Str "for" , Space , Str "Biotechnology" , Space , Str "Information" , Space , Str "(NCBI)," , Space , Str "National" , Space , Str "Library" , Space , Str "of" , Space , Str "Medicine," , Space , Str "National" , Space , Str "Institutes" , Space , Str "of" , Space , Str "Health," , Space , Str "Bethesda," , Space , Str "MD" , Space , Str "20892-6510" ] ] ) , ( "title" , MetaInlines [ Str "The" , Space , Str "NCBI" , Space , Str "Handbook" ] ) ] } [] ``` ================================================ FILE: test/command/bits-book-part-wrapper-meta.md ================================================ ``` % pandoc -f jats -t native -s Balisage Series on Markup Technologies

    The Balisage Series on Markup Technologies is an occasional series...

    Proceedings of Balisage: The Markup Conference 2013

    Balisage is a peer-reviewed conference...

    ^D Pandoc Meta { unMeta = fromList [ ( "abstract" , MetaBlocks [ Para [ Str "Balisage" , Space , Str "is" , Space , Str "a" , Space , Str "peer-reviewed" , Space , Str "conference..." ] ] ) , ( "title" , MetaInlines [ Str "Proceedings" , Space , Str "of" , Space , Str "Balisage:" , Space , Str "The" , Space , Str "Markup" , Space , Str "Conference" , SoftBreak , Str "2013" ] ) ] } [ Para [ Str "The" , Space , Emph [ Str "Balisage" , Space , Str "Series" , Space , Str "on" , Space , Str "Markup" , Space , Str "Technologies" ] , SoftBreak , Str "is" , Space , Str "an" , Space , Str "occasional" , Space , Str "series..." ] ] ``` ================================================ FILE: test/command/bits-book-part-wrapper.md ================================================ ``` % pandoc -f jats -t native Balisage Series on Markup Technologies

    The Balisage Series on Markup Technologies is an occasional series...

    Proceedings of Balisage: The Markup Conference 2013

    Balisage is a peer-reviewed conference...

    The Databases History

    Initially, GenBank was built and maintained at Los Alamos National Laboratory.

    Back matter of book part References Olson M Hood L Cantor C Botstein D A common language for physical mapping of the human genome Science 1989 245 4925 1434 1435 2781285
    ^D [ Para [ Str "The" , Space , Emph [ Str "Balisage" , Space , Str "Series" , Space , Str "on" , Space , Str "Markup" , Space , Str "Technologies" ] , SoftBreak , Str "is" , Space , Str "an" , Space , Str "occasional" , Space , Str "series..." ] , Header 2 ( "bid.3" , [] , [] ) [ Str "History" ] , Para [ Str "Initially," , Space , Str "GenBank" , Space , Str "was" , Space , Str "built" , Space , Str "and" , Space , Str "maintained" , Space , Str "at" , Space , Str "Los" , Space , Str "Alamos" , SoftBreak , Str "National" , Space , Str "Laboratory." ] , Header 2 ( "" , [] , [] ) [ Str "Back" , Space , Str "matter" , Space , Str "of" , Space , Str "book" , Space , Str "part" ] , Header 1 ( "" , [] , [] ) [ Str "References" ] , Div ( "refs" , [] , [] ) [] ] ``` ================================================ FILE: test/command/bits-book.md ================================================ ``` % pandoc -f jats -t native About this book The NCBI Handbook

    Bioinformatics consists of a computational approach to biomedical information management and analysis.

    The Databases History

    Initially, GenBank was built and maintained at Los Alamos National Laboratory.

    Back matter of book part References Olson M Hood L Cantor C Botstein D A common language for physical mapping of the human genome Science 1989 245 4925 1434 1435 2781285
    Acknowledgments

    We gratefully acknowledge the work of Vladimir Soussov, as well as the entire NCBI Entrez team...

    ^D [ Header 2 ( "" , [] , [] ) [ Str "The" , Space , Str "NCBI" , Space , Str "Handbook" ] , Para [ Str "Bioinformatics" , Space , Str "consists" , Space , Str "of" , Space , Str "a" , Space , Str "computational" , Space , Str "approach" , SoftBreak , Str "to" , Space , Str "biomedical" , Space , Str "information" , Space , Str "management" , Space , Str "and" , Space , Str "analysis." ] , Header 2 ( "bid.3" , [] , [] ) [ Str "History" ] , Para [ Str "Initially," , Space , Str "GenBank" , Space , Str "was" , Space , Str "built" , Space , Str "and" , Space , Str "maintained" , Space , Str "at" , Space , Str "Los" , Space , Str "Alamos" , SoftBreak , Str "National" , Space , Str "Laboratory." ] , Header 2 ( "" , [] , [] ) [ Str "Back" , Space , Str "matter" , Space , Str "of" , Space , Str "book" , Space , Str "part" ] , Header 1 ( "" , [] , [] ) [ Str "References" ] , Div ( "refs" , [] , [] ) [] , Header 2 ( "bid.394" , [] , [] ) [ Str "Acknowledgments" ] , Para [ Str "We" , Space , Str "gratefully" , Space , Str "acknowledge" , Space , Str "the" , Space , Str "work" , Space , Str "of" , Space , Str "Vladimir" , Space , Str "Soussov," , SoftBreak , Str "as" , Space , Str "well" , Space , Str "as" , Space , Str "the" , Space , Str "entire" , Space , Str "NCBI" , Space , Str "Entrez" , Space , Str "team..." ] ] ``` ================================================ FILE: test/command/bits-index-elements.md ================================================ ``` % pandoc -f jats -t native Index group

    Content of index group

    Index

    Content of index

    N

    Content of index div

    Navy . Armed forces . Necessary and proper clause, congressional power Newsgathering as commerce
    ^D [ Header 1 ( "" , [] , [] ) [ Str "Index" , Space , Str "group" ] , Para [ Str "Content" , Space , Str "of" , Space , Str "index" , Space , Str "group" ] , Header 2 ( "" , [] , [] ) [ Str "Index" ] , Para [ Str "Content" , Space , Str "of" , Space , Str "index" ] , Header 3 ( "" , [] , [] ) [ Str "N" ] , Para [ Str "Content" , Space , Str "of" , Space , Str "index" , Space , Str "div" ] , Plain [ Str "Navy" ] , Plain [ Str "." ] , Plain [ Str "Armed" , Space , Str "forces" ] , Plain [ Str "." ] , Plain [ Str "Necessary" , Space , Str "and" , Space , Str "proper" , Space , Str "clause," , Space , Str "congressional" , Space , Str "power" ] , Plain [ Str "Newsgathering" , Space , Str "as" , Space , Str "commerce" ] ] ``` ================================================ FILE: test/command/bits-legend.md ================================================ ``` % pandoc -f jats -t native Field of Application Key I

    input

    O

    output

    ...
    ^D [ Figure ( "fig_A.1" , [] , [] ) (Caption Nothing [ Plain [ Str "Field" , Space , Str "of" , Space , Str "Application" ] ]) [ Header 1 ( "" , [] , [] ) [ Str "Key" ] , DefinitionList [ ( [ Str "I" ] , [ [ Para [ Str "input" ] ] ] ) , ( [ Str "O" ] , [ [ Para [ Str "output" ] ] ] ) ] , Para [ Image ( "" , [] , [] ) [] ( "1234" , "" ) ] ] ] ``` ================================================ FILE: test/command/bits-named-boook-parts.md ================================================ ``` % pandoc -f jats -t native

    This is the dedication text.

    ^D [ Header 1 ( "" , [] , [] ) [ Str "Dedication" ] , Para [ Str "This" , Space , Str "is" , Space , Str "the" , Space , Str "dedication" , Space , Str "text." ] ] ``` ``` % pandoc -f jats -t native

    This is the foreword text.

    ^D [ Header 1 ( "" , [] , [] ) [ Str "Foreword" ] , Para [ Str "This" , Space , Str "is" , Space , Str "the" , Space , Str "foreword" , Space , Str "text." ] ] ``` ``` % pandoc -f jats -t native

    This is the preface text.

    ^D [ Header 1 ( "" , [] , [] ) [ Str "Preface" ] , Para [ Str "This" , Space , Str "is" , Space , Str "the" , Space , Str "preface" , Space , Str "text." ] ] ``` ================================================ FILE: test/command/bits-title-display-as.md ================================================ ``` % pandoc -f jats -t native THE EUROPEAN UNION EXPLAINED ^D [ Header 1 ( "" , [] , [] ) [ Str "THE" , Space , Str "EUROPEAN" , Space , Str "UNION" , Space , Str "EXPLAINED" ] ] ``` ``` % pandoc -f jats -t native THE EUROPEAN UNION EXPLAINED ^D [ Header 3 ( "" , [] , [] ) [ Str "THE" , Space , Str "EUROPEAN" , Space , Str "UNION" , Space , Str "EXPLAINED" ] ] ``` ``` % pandoc -f jats -t native The European Parliament

    Members of the European Parliament (MEPs) are directly elected by EU citizens.

    Composition of the European Parliament

    The seats in the European Parliament are allocated among the Member States.

    Composition of the European Parliament - II

    Most MEPs are associated with a national political party in their home country.

    ^D [ Header 1 ( "" , [] , [] ) [ Str "The" , Space , Str "European" , Space , Str "Parliament" ] , Para [ Str "Members" , Space , Str "of" , Space , Str "the" , Space , Str "European" , Space , Str "Parliament" , Space , Str "(MEPs)" , Space , Str "are" , Space , Str "directly" , Space , Str "elected" , Space , Str "by" , Space , Str "EU" , Space , Str "citizens." ] , Header 3 ( "" , [] , [] ) [ Str "Composition" , Space , Str "of" , Space , Str "the" , Space , Str "European" , Space , Str "Parliament" ] , Para [ Str "The" , Space , Str "seats" , Space , Str "in" , Space , Str "the" , Space , Str "European" , Space , Str "Parliament" , Space , Str "are" , Space , Str "allocated" , Space , Str "among" , Space , Str "the" , Space , Str "Member" , Space , Str "States." ] , Header 2 ( "" , [] , [] ) [ Str "Composition" , Space , Str "of" , Space , Str "the" , Space , Str "European" , Space , Str "Parliament" , Space , Str "-" , Space , Str "II" ] , Para [ Str "Most" , Space , Str "MEPs" , Space , Str "are" , Space , Str "associated" , Space , Str "with" , Space , Str "a" , Space , Str "national" , Space , Str "political" , Space , Str "party" , Space , Str "in" , Space , Str "their" , Space , Str "home" , Space , Str "country." ] ] ``` ================================================ FILE: test/command/bits-title-supress.md ================================================ ``` % pandoc -f jats -t native The European Parliament

    Members of the European Parliament (MEPs) are directly elected by EU citizens.

    ^D [ Header 1 ( "" , [] , [] ) [ Str "The" , Space , Str "European" , Space , Str "Parliament" ] , Para [ Str "Members" , Space , Str "of" , Space , Str "the" , Space , Str "European" , Space , Str "Parliament" , Space , Str "(MEPs)" , Space , Str "are" , Space , Str "directly" , Space , Str "elected" , Space , Str "by" , Space , Str "EU" , Space , Str "citizens." ] ] ``` ``` % pandoc -f jats -t native The European Parliament

    Members of the European Parliament (MEPs) are directly elected by EU citizens.

    ^D [ Header 1 ( "" , [] , [] ) [ Str "The" , Space , Str "European" , Space , Str "Parliament" ] , Para [ Str "Members" , Space , Str "of" , Space , Str "the" , Space , Str "European" , Space , Str "Parliament" , Space , Str "(MEPs)" , Space , Str "are" , Space , Str "directly" , Space , Str "elected" , Space , Str "by" , Space , Str "EU" , Space , Str "citizens." ] ] ``` ``` % pandoc -f jats -t native The European Parliament

    Members of the European Parliament (MEPs) are directly elected by EU citizens.

    ^D [ Para [ Str "Members" , Space , Str "of" , Space , Str "the" , Space , Str "European" , Space , Str "Parliament" , Space , Str "(MEPs)" , Space , Str "are" , Space , Str "directly" , Space , Str "elected" , Space , Str "by" , Space , Str "EU" , Space , Str "citizens." ] ] ``` ================================================ FILE: test/command/bits-toc-elements.md ================================================ ``` % pandoc -f jats -t native TOC group

    Content of toc group

    TOC

    Content of TOC

    Mental Health Services Introduction Mental Health of the Population
    ^D [ Header 1 ( "" , [] , [] ) [ Str "TOC" , Space , Str "group" ] , Para [ Str "Content" , Space , Str "of" , Space , Str "toc" , Space , Str "group" ] , Header 2 ( "" , [] , [] ) [ Str "TOC" ] , Para [ Str "Content" , Space , Str "of" , Space , Str "TOC" ] , Header 3 ( "" , [] , [] ) [ Str "Mental" , Space , Str "Health" , Space , Str "Services" ] , Header 4 ( "" , [] , [] ) [ Str "Introduction" ] , Header 4 ( "" , [] , [] ) [ Str "Mental" , Space , Str "Health" , Space , Str "of" , Space , Str "the" , Space , Str "Population" ] ] ``` ================================================ FILE: test/command/chap1/text.md ================================================ # Chapter one A spider: ![spider](spider.png) Another spider: ![another spider][refspider] The moon: ![moon](../../lalune.jpg) Link to [spider picture](spider.png). URL left alone: [manual](https://pandoc.org/MANUAL.html). Absolute path left alone: [absolute](/foo/bar/baz.png). Link to fragment: [chapter two](#chapter-two). Empty path: [empty](). ================================================ FILE: test/command/chap2/text.md ================================================ # Chapter two A spider: ![spider](spider.png) [refspider]: spider.png ================================================ FILE: test/command/chicago-annotated-bibliography.csl ================================================ ================================================ FILE: test/command/chicago-author-date-with-original-date-and-status.csl ================================================ ================================================ FILE: test/command/chicago-fullnote-bibliography.csl ================================================ ================================================ FILE: test/command/chicago-note-bibliography.csl ================================================ ================================================ FILE: test/command/chinese-gb7714-2005-numeric.csl ================================================ ================================================ FILE: test/command/cite-in-inline-note.md ================================================ ``` % pandoc -t native foo^[bar [@doe]] ^D [ Para [ Str "foo" , Note [ Para [ Str "bar" , Space , Cite [ Citation { citationId = "doe" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 1 , citationHash = 0 } ] [ Str "[@doe]" ] ] ] ] ] ``` ================================================ FILE: test/command/citeproc-17.md ================================================ ``` % pandoc -t csljson --- references: - id: foo type: book title: "Hi & Low" ... ^D [ { "id": "foo", "title": "Hi & Low", "type": "book" } ] ``` ================================================ FILE: test/command/citeproc-20.md ================================================ ``` % pandoc --citeproc -t plain Lorem ipsum dolor sit amet^[Consectetur adipisicing elit: «sed do eiusmod tempor incididunt» [@doe_1989, 15].]. --- csl: command/chicago-fullnote-bibliography.csl suppress-bibliography: true references: - id: doe_1989 author: - family: Doe given: John issued: - year: 1989 publisher: ABC publisher-place: New York title: Tempor type: book ... ^D Lorem ipsum dolor sit amet[1]. [1] Consectetur adipisicing elit: «sed do eiusmod tempor incididunt» (John Doe, Tempor (New York: ABC, 1989), 15). ``` ================================================ FILE: test/command/citeproc-7a.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/chicago-fullnote-bibliography.csl references: - id: test title: Test author: family: Doe given: John --- Test [@test, 12]. Test [@test, 12]. Test.^[asdfasdf] Test [@test, 12]. ^D Test.[^1] Test.[^2] Test.[^3] Test.[^4] :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-test .csl-entry} Doe, John. "Test," n.d. ::: :::: [^1]: John Doe, "Test," n.d., 12. [^2]: Doe, 12. [^3]: asdfasdf [^4]: Doe, "Test," 12. ``` ================================================ FILE: test/command/citeproc-7b.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/bioethics.csl references: - id: test title: Test author: family: Doe given: John --- Irrelevant.^[note] Test [@test, 12]. Test.^[asdfasdf] Test [@test, 12]. ^D Irrelevant.[^1] Test.[^2] Test.[^3] Test.[^4] [^1]: note [^2]: J. Doe. ***Test***: 12. [^3]: asdfasdf [^4]: Doe (cited n. 2) : 12. ``` ================================================ FILE: test/command/citeproc-87.md ================================================ ``` % pandoc --citeproc -Mlang=it-IT -t markdown-citations Foo [@a 50: «Disse: "bar"»]. «Disse: "baz"» --- suppress-bibliography: true references: - id: a author: - literal: Aristotele title: Metafisica type: book ... ^D Foo (Aristotele, s.d., 50: «Disse: "bar"»). «Disse: "baz"» ``` The Quoted is passed to citeproc as a Span ("",["csl-quoted"],[]) so that flipflopping and localization occur. ``` % pandoc -C -t plain -Mlang=en --csl command/le-tapuscrit-note.csl --- references: - id: a author: - literal: Aristotele title: Metafisica et "Physica" type: article-journal ... Foo [@a 50]. ^D Foo.[1] ARISTOTELE, “Metafisica et ‘Physica’.” [1] Aristotele, “Metafisica et ‘Physica’,” p. 50. ``` ``` % pandoc -C -t plain -Mlang=it --csl command/le-tapuscrit-note.csl --- references: - id: a author: - literal: Aristotele title: Metafisica et "Physica" type: article-journal ... Foo [@a 50]. ^D Foo.[1] ARISTOTELE, «Metafisica et “Physica”». [1] Aristotele, «Metafisica et “Physica”», p. 50. ``` ================================================ FILE: test/command/citeproc-author-in-text-suffix.md ================================================ ``` % pandoc -t native @a [p. 33; @b] ^D [ Para [ Cite [ Citation { citationId = "a" , citationPrefix = [] , citationSuffix = [ Str "p.\160\&33" ] , citationMode = AuthorInText , citationNoteNum = 1 , citationHash = 0 } , Citation { citationId = "b" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 1 , citationHash = 0 } ] [ Str "@a" , Space , Str "[p." , Space , Str "33;" , Space , Str "@b]" ] ] ] ``` ================================================ FILE: test/command/csv.md ================================================ ``` % pandoc -f csv -t native Fruit,Price,Quantity Apple,25 cents,33 """Navel"" Orange","35 cents",22 ,,45 ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Fruit" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Price" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Quantity" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apple" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "25" , Space , Str "cents" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "33" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "\"Navel\"" , Space , Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "35" , Space , Str "cents" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "22" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "45" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/defaults-inheritance-1.md ================================================ ``` % pandoc -d command/defaults3 # Header ^D # Header ``` ================================================ FILE: test/command/defaults-inheritance-2.md ================================================ ``` % pandoc -d command/defaults6 2>&1 ^D Error: Circular defaults file reference in 'command/defaults7.yaml' => 63 ``` ================================================ FILE: test/command/defaults-inheritance-3.md ================================================ ``` % pandoc -d command/defaults8

    Header

    ^D # Header ``` ================================================ FILE: test/command/defaults1.yaml ================================================ include-in-header: command/A.txt ================================================ FILE: test/command/defaults2.yaml ================================================ include-in-header: - command/B.txt - command/C.txt ================================================ FILE: test/command/defaults3.yaml ================================================ defaults: - command/defaults4 - command/defaults5 to: markdown ================================================ FILE: test/command/defaults4.yaml ================================================ from: html defaults: - command/defaults5 ================================================ FILE: test/command/defaults5.yaml ================================================ from: markdown to: html ================================================ FILE: test/command/defaults6.yaml ================================================ defaults: - command/defaults7 ================================================ FILE: test/command/defaults7.yaml ================================================ defaults: - command/defaults6 ================================================ FILE: test/command/defaults8.yaml ================================================ from: html defaults: command/defaults9 ================================================ FILE: test/command/defaults9.yaml ================================================ to: markdown ================================================ FILE: test/command/din-1505-2.csl ================================================ ================================================ FILE: test/command/docbook-bibliography.md ================================================ ``` % pandoc -f docbook -t native --quiet Document References [1] First reference [2] Second reference [3] Third reference ^D [ Header 1 ( "" , [] , [] ) [ Str "Document" , Space , Str "References" ] , Para [ Span ( "refTheFirst" , [] , [] ) [] , Str "[1]" , Space , Str "First" , Space , Str "reference" ] , Para [ Span ( "refTheSecond" , [] , [] ) [] , Str "[2]" , Space , Str "Second" , Space , Str "reference" ] , Para [ Span ( "refTheThird" , [] , [] ) [] , Str "[3]" , Space , Str "Third" , Space , Str "reference" ] ] ``` ================================================ FILE: test/command/dokuwiki-quote.md ================================================ ``` % pandoc -f dokuwiki -t native > some code >> ok > then > more ^D [ BlockQuote [ CodeBlock ( "" , [] , [] ) "some\ncode\n" , BlockQuote [ Plain [ Str "ok" ] ] , Plain [ Str "then" , LineBreak , Str "more" ] ] ] ``` ================================================ FILE: test/command/dots.md ================================================ ``` % pandoc -f latex -t native \dots \ldots \vdots ^D [ Para [ Str "\8230" ] , Para [ Str "\8230" ] , Para [ Str "\8942" ] ] ``` ================================================ FILE: test/command/duplicate_attributes.md ================================================ ``` % pandoc [span]{.foobar style="color:blue" class="zip" style="color:red"} ^D 2> [WARNING] Ignoring duplicate attribute style="color:red".

    span

    ``` ================================================ FILE: test/command/emoji.md ================================================ ``` % pandoc -t markdown+emoji -f markdown+emoji :smile: ^D :smile: ``` ``` % pandoc -t markdown-emoji -f markdown+emoji :smile: ^D 😄 ``` ``` % pandoc -t gfm -f markdown+emoji :smile: ^D :smile: ``` ``` % pandoc -t gfm-emoji -f markdown+emoji :smile: ^D 😄 ``` ================================================ FILE: test/command/empty_paragraphs.md ================================================ ``` % pandoc -f native -t docx -o - | pandoc -f docx -t native [Para [Str "hi"], Para [], Para [], Para [Str "lo"]] ^D [ Para [ Str "hi" ] , Para [ Str "lo" ] ] ``` ``` % pandoc -f native -t docx+empty_paragraphs -o - | pandoc -f docx -t native [Para [Str "hi"], Para [], Para [], Para [Str "lo"]] ^D [ Para [ Str "hi" ] , Para [ Str "lo" ] ] ``` ``` % pandoc -f native -t docx -o - | pandoc -f docx+empty_paragraphs -t native [Para [Str "hi"], Para [], Para [], Para [Str "lo"]] ^D [ Para [ Str "hi" ] , Para [ Str "lo" ] ] ``` ``` % pandoc -f native -t docx+empty_paragraphs -o - | pandoc -f docx+empty_paragraphs -t native [Para [Str "hi"], Para [], Para [], Para [Str "lo"]] ^D [ Para [ Str "hi" ] , Para [] , Para [] , Para [ Str "lo" ] ] ``` ``` % pandoc -f native -t html5 [Para [Str "hi"], Para [], Para [], Para [Str "lo"]] ^D

    hi

    lo

    ``` ``` % pandoc -f native -t html5+empty_paragraphs [Para [Str "hi"], Para [], Para [], Para [Str "lo"]] ^D

    hi

    lo

    ``` ``` % pandoc -f html+empty_paragraphs -t native

    hi

    lo

    ^D [ Para [ Str "hi" ] , Para [] , Para [] , Para [ Str "lo" ] ] ``` ``` % pandoc -f html -t native

    hi

    lo

    ^D [ Para [ Str "hi" ] , Para [ Str "lo" ] ] ``` ``` % pandoc -f native -t opendocument+empty_paragraphs [Para [Str "hi"], Para [], Para [], Para [Str "lo"]] ^D hi lo ``` ``` % pandoc -f native -t opendocument [Para [Str "hi"], Para [], Para [], Para [Str "lo"]] ^D hi lo ``` ================================================ FILE: test/command/figures-context.md ================================================ # Figure with one image, caption and label ``` % pandoc -t context -f html
    The Mandrill, a photo used in image processing tests.
    ^D \startplacefigure[title={\quotation{The Mandrill}, a photo used in image processing tests.}] {\externalfigure[mandrill.jpg]} \stopplacefigure ``` # Nested figures ``` % pandoc -t context -f html
    The Mandrill is a commonly used test image.
    Another test image. This one is called peppers.
    Signal processing test images.
    ^D \startplacefigure[reference=test-images,title={Signal processing test images.}] \startfloatcombination \startplacefigure[reference=mandrill,title={\quotation{The Mandrill} is a commonly used test image.}] {\externalfigure[../testing/mandrill.jpg]} \stopplacefigure \startplacefigure[reference=peppers,title={Another test image. This one is called \quotation{peppers}.}] {\externalfigure[../testing/peppers.webp]} \stopplacefigure \stopfloatcombination \stopplacefigure ``` ================================================ FILE: test/command/figures-fb2.md ================================================ ``` % pandoc -f native -t fb2 [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Str "content"]]] ^D unrecognisedpandoc<p />

    content

    ``` ================================================ FILE: test/command/figures-haddock.md ================================================ ``` % pandoc -f native -t haddock [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Str "content"]]] ^D content ``` ================================================ FILE: test/command/figures-html.md ================================================ # Writer HTML5 figure with caption and content. ``` % pandoc -f native -t html5 [Figure ("fig-id",[],[]) (Caption Nothing [Plain [Str "caption"]]) [Para [Str "content"]]] ^D

    content

    caption
    ``` HTML5 figure with NO caption and content. ``` % pandoc -f native -t html5 [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Str "content"]]] ^D

    content

    ``` HTML4 figure with caption and content. ``` % pandoc -f native -t html4 [Figure ("fig-id",[],[]) (Caption Nothing [Plain [Str "caption"]]) [Para [Str "content"]]] ^D

    content

    caption
    ``` HTML4 figure with NO caption and content. ``` % pandoc -f native -t html4 [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Str "content"]]] ^D

    content

    ``` # Reader Figure with caption and multiple elements. ``` % pandoc -f html -t native
    • ITEM
    CAP2
    ^D [ Figure ( "" , [ "important" ] , [] ) (Caption Nothing [ Plain [ Str "CAP2" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "../media/rId25.jpg" , "" ) ] , BulletList [ [ Plain [ Str "ITEM" ] ] ] ] ] ``` Figure without caption. ``` % pandoc -f html -t native
    • ITEM
    ^D [ Figure ( "" , [ "important" ] , [] ) (Caption Nothing []) [ Plain [ Image ( "" , [] , [] ) [] ( "../media/rId25.jpg" , "" ) ] , BulletList [ [ Plain [ Str "ITEM" ] ] ] ] ] ``` ================================================ FILE: test/command/figures-jats.md ================================================ Figure float with caption at the figure level. ``` % pandoc -f native -t jats [Figure ("fig-id",[],[]) (Caption Nothing [Para [Str "Caption"]]) [Para [Str "Text"], Para [Image ("fig-id-2",[],[]) [] ("foo.png", "fig:")]]] ^D

    Caption

    Text

    ``` Figure float with caption and alt text. ``` % pandoc -f native -t jats [Figure ("fig-id",[],[]) (Caption Nothing [Para [Str "Caption"]]) [Para [Str "Text"], Para [Image ("fig-id-2",[],[]) [Str "alt"] ("foo.png", "fig:")]]] ^D

    Caption

    Text

    alt
    ``` ================================================ FILE: test/command/figures-jira.md ================================================ A figure with title ``` % pandoc -f native -t jira [Figure ("fig-id",[],[("title","This is the title")]) (Caption Nothing []) []] ^D {panel:title=This is the title} {anchor:fig-id} {panel} ``` ================================================ FILE: test/command/figures-latex.md ================================================ # Figure with one image, caption and label ``` % pandoc -f latex -t native \begin{document} \begin{figure} \includegraphics{../../media/rId25.jpg} \caption{CAP} \label{LAB} \end{figure} \end{document} ^D [ Figure ( "LAB" , [] , [] ) (Caption Nothing [ Plain [ Str "CAP" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "../../media/rId25.jpg" , "" ) ] ] ] ``` # Nested figures ``` % pandoc -f latex -t native \begin{figure} \begin{subfigure}[b]{0.5\textwidth} \begin{subfigure}[b]{0.5\textwidth} \centering \includegraphics{test/media/rId25.jpg} \caption{CAP1.1} \end{subfigure} \begin{subfigure}[b]{0.5\textwidth} \centering \includegraphics{test/media/rId25.jpg} \caption{CAP1.2} \end{subfigure} \caption{CAP1} \label{fig:inner1} \end{subfigure} \begin{subfigure}[b]{0.5\textwidth} \includegraphics{test/media/rId25.jpg} \caption{CAP2} \label{fig:inner2} \end{subfigure} \caption{CAP} \label{fig:outer} \end{figure} ^D [ Figure ( "fig:outer" , [] , [] ) (Caption Nothing [ Plain [ Str "CAP" ] ]) [ Figure ( "fig:inner1" , [] , [] ) (Caption Nothing [ Plain [ Str "CAP1" ] ]) [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "CAP1.1" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "test/media/rId25.jpg" , "" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "CAP1.2" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "test/media/rId25.jpg" , "" ) ] ] ] , Figure ( "fig:inner2" , [] , [] ) (Caption Nothing [ Plain [ Str "CAP2" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "test/media/rId25.jpg" , "" ) ] ] ] ] ``` ================================================ FILE: test/command/figures-markdown.md ================================================ Figure float with caption at the figure level. ``` % pandoc -f native -t markdown [Figure ("fig-id",[],[]) (Caption Nothing [Para [Str "Caption"]]) [Para [Image ("",[],[]) [] ("foo.png", "fig:")]]] ^D

    Caption

    ``` ================================================ FILE: test/command/figures-mediawiki.md ================================================ Figure float with caption at the figure level. ``` % pandoc -f native -t mediawiki [Figure ("fig-id",[],[]) (Caption Nothing [Para [Str "Caption"]]) [Para [Image ("",[],[]) [] ("foo.png", "fig:")]]] ^D
    [[File:foo.png|thumb|none]]
    ``` ================================================ FILE: test/command/figures-org.md ================================================ ``` % pandoc -f native -t org [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Str "content"]]] ^D <> content ``` ``` % pandoc -f native -t org [Figure ("",[],[]) (Caption Nothing []) [Para [Str "content"]]] ^D content ``` ================================================ FILE: test/command/figures-rst.md ================================================ Figure float with caption at the figure level. ``` % pandoc -f native -t rst [Figure ("fig-id",[],[]) (Caption Nothing [Para [Str "Caption"]]) [Para [Image ("",[],[]) [] ("foo.png", "fig:")]]] ^D .. figure:: foo.png name: fig-id :alt: fig: Caption ``` ================================================ FILE: test/command/figures-texinfo.md ================================================ Figure float with caption at the figure level. ``` % pandoc -f native -t texinfo [Figure ("fig-id",[],[]) (Caption Nothing [Para [Str "Caption"]]) [Para [Image ("",[],[]) [] ("foo.png", "fig:")]]] ^D @node Top @top Top @float Figure @image{foo,,,,png} @caption{Caption} @end float ``` Float that has no caption and doesn't contain a `SimpleFigure` ``` % pandoc -f native -t texinfo [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Image ("",[],[]) [] ("foo.png", "")]]] ^D @node Top @top Top @float @image{foo,,,,png} @end float ``` Table float with caption at the figure level. ``` % pandoc -f native -t texinfo [Figure ("fig-id",[],[]) (Caption Nothing [Para [Str "Caption"]]) [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Fruit"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Price"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Quantity"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Apple"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "25",Space,Str "cents"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "33"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "\"Navel\"",Space,Str "Orange"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "35",Space,Str "cents"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "22"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "45"]]]])] (TableFoot ("",[],[]) [])]] ^D @node Top @top Top @float Table @multitable {"Navel" Orange} {35 cents} {Quantity} @headitem Fruit @tab Price @tab Quantity @item Apple @tab 25 cents @tab 33 @item "Navel" Orange @tab 35 cents @tab 22 @item 45 @end multitable @caption{Caption} @end float ``` Float the isn't a table nor a figure. ``` % pandoc -f native -t texinfo [Figure ("fig-id",[],[]) (Caption Nothing [Para[ Str "Caption"]]) [Para [Str "Content"]]] ^D @node Top @top Top @float Content @caption{Caption} @end float ``` ================================================ FILE: test/command/figures-textile.md ================================================ ``` % pandoc -f native -t textile [Figure ("fig-id",[],[]) (Caption Nothing [Para [Str "Caption"]]) [Para [Image ("",[],[]) [] ("foo.png", "")]]] ^D
    Caption
    !foo.png!
    ``` ``` % pandoc -f native -t textile [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Image ("",[],[]) [] ("foo.png", "")]]] ^D
    !foo.png!
    ``` ================================================ FILE: test/command/figures-xwiki.md ================================================ ``` % pandoc -f native -t xwiki [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Str "content"]]] ^D ((( {{id name="fig-id" /}}content ))) ``` ================================================ FILE: test/command/figures-zimwiki.md ================================================ ``` % pandoc -f native -t zimwiki [Figure ("fig-id",[],[]) (Caption Nothing []) [Para [Str "content"]]] ^D content ``` ================================================ FILE: test/command/file1.txt ================================================ # Zed [foo]: bar [foo] and [Zed](#zed) and [other Zed](command/file2.txt#zed) and [other file](command/file2.txt) and [foreign Zed](c.md#zed) ================================================ FILE: test/command/file2.txt ================================================ ## Zed [foo]: baz [foo] ================================================ FILE: test/command/gfm.md ================================================ gfm tests: ``` % pandoc -f gfm -t native | Fruit | Price | | ----- | ----: | | apple | 0.13 | | orange|1.12| ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Fruit" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Price" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "apple" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0.13" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1.12" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f gfm -t native ~~stricken out~~ ^D [ Para [ Strikeout [ Str "stricken" , Space , Str "out" ] ] ] ``` ``` % pandoc -f gfm -t native # Header ## Header # -foo-bar_baz ^D [ Header 1 ( "header" , [] , [] ) [ Str "Header" ] , Header 2 ( "header-1" , [] , [] ) [ Str "Header" ] , Header 1 ( "-foo-bar_baz" , [] , [] ) [ Str "-foo-bar_baz" ] ] ``` ``` % pandoc -f gfm -t native My:thumbsup:emoji:heart: ^D [ Para [ Str "My" , Span ( "" , [ "emoji" ] , [ ( "data-emoji" , "thumbsup" ) ] ) [ Str "\128077" ] , Str "emoji" , Span ( "" , [ "emoji" ] , [ ( "data-emoji" , "heart" ) ] ) [ Str "\10084\65039" ] ] ] ``` ``` % pandoc -f gfm -t native "hi" ^D [ Para [ Str "\"hi\"" ] ] ``` ``` % pandoc -f gfm+smart -t native "hi" ^D [ Para [ Quoted DoubleQuote [ Str "hi" ] ] ] ``` ``` % pandoc -t gfm -f native [Table ("",[],[]) (Caption Nothing [Plain [Str "The",Space,Str "caption."]]) [(AlignDefault,ColWidthDefault) ,(AlignRight,ColWidthDefault)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Fruit"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Price"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "apple"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "0.13"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "orange"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1.12"]]]])] (TableFoot ("",[],[]) [])] ^D | Fruit | Price | |--------|------:| | apple | 0.13 | | orange | 1.12 | The caption. ``` ``` % pandoc -f gfm-smart -t gfm+smart “hi” ^D "hi" ``` ``` % pandoc -f gfm+smart -t gfm-smart "hi" ^D “hi” ``` ``` % pandoc -f gfm+smart -t gfm+smart "hi" ^D "hi" ``` ``` % pandoc -f gfm+hard_line_breaks -t native hi hi ^D [ Para [ Str "hi" , LineBreak , Str "hi" ] ] ``` ``` % pandoc -f gfm -t native - [ ] foo - [x] bar ^D [ BulletList [ [ Plain [ Str "\9744" , Space , Str "foo" ] ] , [ Plain [ Str "\9746" , Space , Str "bar" ] ] ] ] ``` ``` % pandoc -f gfm-task_lists -t native - [ ] foo - [x] bar ^D [ BulletList [ [ Plain [ Str "[" , Space , Str "]" , Space , Str "foo" ] ] , [ Plain [ Str "[x]" , Space , Str "bar" ] ] ] ] ``` ``` % pandoc -f gfm -t gfm - [ ] foo - [x] bar ^D - [ ] foo - [x] bar ``` ================================================ FILE: test/command/harvard-university-of-kent.csl ================================================ ================================================ FILE: test/command/hspace.md ================================================ `\hspace` and `\vspace` should count as both block and inline. Here they need to be inline: ``` % pandoc -f markdown+raw_tex -t native \begin{figure} \includegraphics{lalune.jpg} \caption{lalune \hspace{2em} \vspace{1em} bloo} \end{figure} ^D [ RawBlock (Format "tex") "\\begin{figure}\n\\includegraphics{lalune.jpg}\n\\caption{lalune \\hspace{2em} \\vspace{1em} bloo}\n\\end{figure}" ] ``` Here block: ``` % pandoc -f markdown+raw_tex -t native \begin{tabular}[t]{cc|c} \(P\) & \(Q\) & \(P\wedge Q\)\\ \hline T & T &\\ T & F &\\ F & T &\\ F & F &\\ \end{tabular} \hspace{1em} \begin{tabular}[t]{cc|c} \(P\) & \(Q\) & \(P\vee Q\)\\ \hline T & T &\\ T & F &\\ F & T &\\ F & F &\\ \end{tabular} ^D [ RawBlock (Format "tex") "\\begin{tabular}[t]{cc|c}\n\\(P\\) & \\(Q\\) & \\(P\\wedge Q\\)\\\\\n\\hline\nT & T &\\\\\nT & F &\\\\\nF & T &\\\\\nF & F &\\\\\n\\end{tabular}\n\\hspace{1em}\n\\begin{tabular}[t]{cc|c}\n\\(P\\) & \\(Q\\) & \\(P\\vee Q\\)\\\\\n\\hline\nT & T &\\\\\nT & F &\\\\\nF & T &\\\\\nF & F &\\\\\n\\end{tabular}" ] ``` ``` % pandoc -f markdown+raw_tex -t native hi\hspace{1em}there ^D [ Para [ Str "hi" , RawInline (Format "tex") "\\hspace{1em}" , Str "there" ] ] ``` ``` % pandoc -f markdown+raw_tex -t native hi \hspace{1em} there ^D [ Para [ Str "hi" ] , RawBlock (Format "tex") "\\hspace{1em}" , Para [ Str "there" ] ] ``` ================================================ FILE: test/command/html-read-figure.md ================================================ ``` % pandoc -f html -t native
    bar
    ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "bar" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "foo.png" , "voyage" ) ] ] ] ``` ``` % pandoc -f html -t native
    bar
    ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "bar" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "foo.png" , "voyage" ) ] ] ] ``` ``` % pandoc -f html -t native
    ^D [ Figure ( "" , [] , [] ) (Caption Nothing []) [ Plain [ Image ( "" , [] , [] ) [] ( "foo.png" , "voyage" ) ] ] ] ``` ``` % pandoc -f html -t native

    bar
    ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "bar" ] ]) [ Para [ Image ( "" , [] , [] ) [] ( "foo.png" , "voyage" ) ] ] ] ``` ``` % pandoc -f html -t native
    this is ignored
    bar baz
    ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "bar" , Space , Strong [ Str "baz" ] ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "this" , Space , Str "is" , Space , Str "ignored" ] ( "foo.png" , "voyage" ) ] ] ] ``` ================================================ FILE: test/command/html-trim-definition-list-terms.md ================================================ ``` % pandoc -f html -t native
    foo bar
    baz
    test
    ^D [ DefinitionList [ ( [ Str "foo" , SoftBreak , Str "bar" , LineBreak , Str "baz" ] , [ [ Plain [ Str "test" ] ] ] ) ] ] ``` ================================================ FILE: test/command/html-writer-a-in-a.md ================================================ a is not allowed inside a in HTML, so we remove links in link text: ``` % pandoc -f native -t html [ Link ("",[],[]) [Link ("",[],[]) [Str "Lo"] ("url2","") ] ("url","") ] ^D Lo ``` ================================================ FILE: test/command/hyphenat.md ================================================ ``` % pandoc -f latex -t native electromagnetic\hyp{}endioscopy ^D [ Para [ Str "electromagnetic-endioscopy" ] ] ``` ``` % pandoc -f latex -t native C\colonhyp\bshyp{}Windows\bshyp ^D [ Para [ Str "C:\173\\\173Windows\\\173" ] ] ``` ``` % pandoc -f latex -t native \fshyp{}usr\fshyp{}share\fshyp ^D [ Para [ Str "/\173usr/\173share/\173" ] ] ``` ``` % pandoc -f latex -t native \fshyp{}home\fshyp{}schrieveslaach\fshyp\dothyp{}m2 ^D [ Para [ Str "/\173home/\173schrieveslaach/\173.\173m2" ] ] ``` ``` % pandoc -f latex -t native \nohyphens{Pneumonoultramicroscopicsilicovolcanoconiosis} ^D [ Para [ Str "Pneumonoultramicroscopicsilicovolcanoconiosis" ] ] ``` ``` % pandoc -f latex -t native \textnhtt{Pneumonoultramicroscopicsilicovolcanoconiosis} ^D [ Para [ Code ( "" , [] , [] ) "Pneumonoultramicroscopicsilicovolcanoconiosis" ] ] ``` ``` % pandoc -f latex -t native \nhttfamily{Pneumonoultramicroscopicsilicovolcanoconiosis} ^D [ Para [ Code ( "" , [] , [] ) "Pneumonoultramicroscopicsilicovolcanoconiosis" ] ] ``` ================================================ FILE: test/command/ieee.csl ================================================ ================================================ FILE: test/command/ifstrequal.md ================================================ ``` % pandoc -f latex -t native \ifstrequal{a}{b}{yes}{\emph{no}} \newcommand{\h}[1]{\ifstrequal{#1}{a}{\'a}{#1}} \h{a} \h{b} ^D [ Para [ Emph [ Str "no" ] , SoftBreak , Str "\225" , SoftBreak , Str "b" ] ] ``` ================================================ FILE: test/command/indented-fences.md ================================================ ````` % pandoc -t native ```haskell let x = y in y ``` ^D [ CodeBlock ( "" , [ "haskell" ] , [] ) "let x = y\nin y" ] ````` ````` % pandoc -t native ~~~ {.haskell} let x = y in y + y + y ~~~ ^D [ CodeBlock ( "" , [ "haskell" ] , [] ) " let x = y\nin y +\ny +\ny" ] ````` ================================================ FILE: test/command/input-with-endinput.md ================================================ ``` % pandoc --from=latex -t native \begin{document} Visible \include{command/bar-endinput} Visible \end{document} ^D [ Para [ Str "Visible" ] , Para [ Emph [ Str "hi" , Space , Str "there" ] ] , Para [ Str "Visible" ] ] ``` ================================================ FILE: test/command/issue160.csl ================================================ ================================================ FILE: test/command/issue437.csl ================================================ ================================================ FILE: test/command/issue58.csl ================================================ ================================================ FILE: test/command/jabberwocky.md ================================================ ``` % pandoc -t plain -f markdown | 'Twas brillig, and the slithy toves | Did gyre and gimble in the wabe: | All mimsy were the borogoves, | And the mome raths outgrabe. | | "Beware the Jabberwock, my son! | The jaws that bite, the claws that catch! | Beware the Jubjub bird, and shun | The frumious Bandersnatch!" ^D ’Twas brillig, and the slithy toves       Did gyre and gimble in the wabe: All mimsy were the borogoves,       And the mome raths outgrabe. “Beware the Jabberwock, my son!       The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun       The frumious Bandersnatch!” ``` ================================================ FILE: test/command/jats-figure-alt-text.md ================================================ ``` % pandoc -f jats -t native

    bar

    alternative-decription baz
    ^D [ Figure ( "fig-1" , [] , [] ) (Caption Nothing [ Plain [ Str "bar" ] ]) [ Plain [ Str "alternative-decription" ] , Para [ Image ( "" , [] , [] ) [ Str "baz" ] ( "foo.png" , "" ) ] ] ] ``` ================================================ FILE: test/command/latex-center.md ================================================ # `\begin{center}` ``` % pandoc -f latex -t native \begin{center} Hello \end{center} ^D [ Div ( "" , [ "center" ] , [] ) [ Para [ Str "Hello" ] ] ] ``` ================================================ FILE: test/command/latex-color.md ================================================ # `\textcolor{}{}` ``` % pandoc -f latex -t native Hello \textcolor{red}{World} ^D [ Para [ Str "Hello" , Space , Span ( "" , [] , [ ( "style" , "color: red" ) ] ) [ Str "World" ] ] ] ``` ``` % pandoc -f latex -t native \textcolor{red}{Hello} World ^D [ Para [ Span ( "" , [] , [ ( "style" , "color: red" ) ] ) [ Str "Hello" ] , Space , Str "World" ] ] ``` ``` % pandoc -f latex -t native Hello \textcolor{blue}{\textbf{World}} ^D [ Para [ Str "Hello" , Space , Span ( "" , [] , [ ( "style" , "color: blue" ) ] ) [ Strong [ Str "World" ] ] ] ] ``` ``` % pandoc -f latex -t native Hello \textcolor{blue}{\textbf{World}}. ^D [ Para [ Str "Hello" , Space , Span ( "" , [] , [ ( "style" , "color: blue" ) ] ) [ Strong [ Str "World" ] ] , Str "." ] ] ``` ``` % pandoc -f latex -t native \textcolor{orange}{ \begin{itemize} \item Item 1 \item Item 2 \end{itemize} } ^D [ Div ( "" , [] , [ ( "style" , "color: orange" ) ] ) [ BulletList [ [ Para [ Str "Item" , Space , Str "1" ] ] , [ Para [ Str "Item" , Space , Str "2" ] ] ] ] ] ``` ``` % pandoc -f latex -t native \textcolor{blue}{ \begin{itemize} \item Item 1 \item Item 2 \end{itemize} } some more text ^D [ Div ( "" , [] , [ ( "style" , "color: blue" ) ] ) [ BulletList [ [ Para [ Str "Item" , Space , Str "1" ] ] , [ Para [ Str "Item" , Space , Str "2" ] ] ] ] , Para [ Str "some" , Space , Str "more" , Space , Str "text" ] ] ``` # `\colorbox{}{}` ``` % pandoc -f latex -t native Hello \colorbox{red}{World} ^D [ Para [ Str "Hello" , Space , Span ( "" , [] , [ ( "style" , "background-color: red" ) ] ) [ Str "World" ] ] ] ``` ``` % pandoc -f latex -t native \colorbox{red}{Hello} World ^D [ Para [ Span ( "" , [] , [ ( "style" , "background-color: red" ) ] ) [ Str "Hello" ] , Space , Str "World" ] ] ``` ``` % pandoc -f latex -t native Hello \colorbox{blue}{\textbf{World}} ^D [ Para [ Str "Hello" , Space , Span ( "" , [] , [ ( "style" , "background-color: blue" ) ] ) [ Strong [ Str "World" ] ] ] ] ``` ``` % pandoc -f latex -t native Hello \colorbox{blue}{\textbf{World}}. ^D [ Para [ Str "Hello" , Space , Span ( "" , [] , [ ( "style" , "background-color: blue" ) ] ) [ Strong [ Str "World" ] ] , Str "." ] ] ``` ``` % pandoc -f latex -t native \colorbox{orange}{ \begin{minipage}{\textwidth} \begin{itemize} \item Item 1 \item Item 2 \end{itemize} \end{minipage} } ^D [ Div ( "" , [] , [ ( "style" , "background-color: orange" ) ] ) [ Div ( "" , [ "minipage" ] , [] ) [ BulletList [ [ Para [ Str "Item" , Space , Str "1" ] ] , [ Para [ Str "Item" , Space , Str "2" ] ] ] ] ] ] ``` ``` % pandoc -f latex -t native \colorbox{blue}{ \begin{minipage}{\textwidth} \begin{itemize} \item Item 1 \item Item 2 \end{itemize} \end{minipage} } some more text ^D [ Div ( "" , [] , [ ( "style" , "background-color: blue" ) ] ) [ Div ( "" , [ "minipage" ] , [] ) [ BulletList [ [ Para [ Str "Item" , Space , Str "1" ] ] , [ Para [ Str "Item" , Space , Str "2" ] ] ] ] ] , Para [ Str "some" , Space , Str "more" , Space , Str "text" ] ] ``` ================================================ FILE: test/command/latex-command-comment.md ================================================ ``` % pandoc -f latex -t native \emph% {hi} ^D [ Para [ Emph [ Str "hi" ] ] ] ``` ================================================ FILE: test/command/latex-fontawesome.md ================================================ ``` % pandoc -f latex -t native Check: \faCheck ^D [ Para [ Str "Check:" , Space , Str "\10003" ] ] ``` ``` % pandoc -f latex -t native Close: \faClose ^D [ Para [ Str "Close:" , Space , Str "\10007" ] ] ``` ================================================ FILE: test/command/latex-math-trailing-space.md ================================================ Test that trailing spaces before \end{equation} don't create spurious paragraph breaks: ``` % pandoc -f latex -t latex \begin{equation} a \end{equation} ^D \begin{equation} a \end{equation} ``` Same for align environment: ``` % pandoc -f latex -t latex \begin{align} x &= y \\ \end{align} ^D \begin{align} x &= y \\ \end{align} ``` Test with multiple trailing spaces: ``` % pandoc -f latex -t latex \begin{equation} a + b \end{equation} ^D \begin{equation} a + b \end{equation} ``` ================================================ FILE: test/command/latex-tabular-column-specs.md ================================================ See https://groups.google.com/forum/#!topic/pandoc-discuss/_VXtqihCyDU. ``` % pandoc -f latex -t native \begin{tabular}{>{$}l<{$}>{$}l<{$} >{$}l<{$}} \toprule & f1 & f2 \\ \midrule e & 0.5 & 4 \\ f & 0.5 & 5,5 \\ \bottomrule \end{tabular} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "f1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "f2" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "0.5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "4" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "f" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "0.5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Math InlineMath "5,5" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/le-tapuscrit-note.csl ================================================ ================================================ FILE: test/command/lettrine.md ================================================ ``` % pandoc -f latex -t native \lettrine{A}{category} is \lettrine[lhang=0.17]{A}{category} is ^D [ Para [ Span ( "" , [ "lettrine" ] , [] ) [ Str "A" ] , SmallCaps [ Str "category" ] , Space , Str "is" ] , Para [ Span ( "" , [ "lettrine" ] , [] ) [ Str "A" ] , SmallCaps [ Str "category" ] , Space , Str "is" ] ] ``` ================================================ FILE: test/command/lists-inside-definition.md ================================================ This inserts an empty `\item[]` when a list occurs at the beginning of a definition list definition; otherwise the list may start on the line with the label, which looks terrible. See https://tex.stackexchange.com/questions/192480/force-itemize-inside-description-onto-a-new-line ``` % pandoc -t latex Definition : 1. list 2. list ^D \begin{description} \tightlist \item[Definition] \hfill \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item list \item list \end{enumerate} \end{description} ``` ``` % pandoc -t latex Definition : Foo 1. list 2. list ^D \begin{description} \tightlist \item[Definition] Foo \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item list \item list \end{enumerate} \end{description} ``` ``` % pandoc -t latex Definition : - list - list ^D \begin{description} \tightlist \item[Definition] \hfill \begin{itemize} \tightlist \item list \item list \end{itemize} \end{description} ``` ================================================ FILE: test/command/locators.csl ================================================ ================================================ FILE: test/command/lstlisting.md ================================================ ``` % pandoc -f latex -t native \begin{lstlisting}[language=Java, caption={Java Example}, label=lst:Hello-World] public class World { public static void main(String[] args) { System.out.println("Hello World"); } } \end{lstlisting} ^D [ CodeBlock ( "lst:Hello-World" , [ "java" ] , [ ( "language" , "Java" ) , ( "caption" , "Java Example" ) , ( "label" , "lst:Hello-World" ) ] ) "public class World {\n public static void main(String[] args) {\n System.out.println(\"Hello World\");\n }\n}" ] ``` ``` % pandoc -f latex -t native \begin{lstlisting}[language=Java, escapechar=|, caption={Java Example}, label=lst:Hello-World] public class World { public static void main(String[] args) { System.out.println("Hello World"); } } \end{lstlisting} ^D [ CodeBlock ( "lst:Hello-World" , [ "java" ] , [ ( "language" , "Java" ) , ( "escapechar" , "|" ) , ( "caption" , "Java Example" ) , ( "label" , "lst:Hello-World" ) ] ) "public class World {\n public static void main(String[] args) {\n System.out.println(\"Hello World\");\n }\n}" ] ``` ================================================ FILE: test/command/macro-defs-in-preamble.md ================================================ ``` % pandoc -s -f latex-latex_macros -t native \documentclass[11pt]{article} \newcommand{\vara}{\alpha} \newcommand{\varb}{b} \begin{document} $\vara \varb$ \end{document} ^D Pandoc Meta { unMeta = fromList [] } [ RawBlock (Format "latex") "\\newcommand{\\vara}{\\alpha}" , RawBlock (Format "latex") "\\newcommand{\\varb}{b}" , Para [ Math InlineMath "\\vara \\varb" ] ] ``` ================================================ FILE: test/command/macros.md ================================================ ``` % pandoc -f markdown+latex_macros -t markdown+raw_tex-raw_attribute \newcommand{\my}{\phi} $\my+\my$ ^D \newcommand{\my}{\phi} $\phi+\phi$ ``` ``` % pandoc -f markdown-latex_macros -t markdown+raw_tex-raw_attribute \newcommand{\my}{\phi} $\my+\my$ ^D \newcommand{\my}{\phi} $\my+\my$ ``` `\let` macros should be expanded at point of definition, while `\newcommand` macros should be expanded at point of use: ``` % pandoc -f latex -t latex \let\a\b \def\b{\emph{ouk}} \a a ^D a̱ ``` ``` % pandoc -f latex -t latex \newcommand{\a}{\b} \newcommand{\b}{\emph{ouk}} \a a ^D \emph{ouk}a ``` ``` % pandoc -f latex -t latex \def\BDpos{} \def\BDneg{-} \def\beq{\begin{align}} \def\eeq{\end{align}} \def\e#1{\emph{#1}} \def\f#1#2{\emph{#1--#2}} $5\BDneg 6\BDpos 7$ \beq x &= y\\ \eeq \e{hi} \f{hi}{ok} ^D \(5-67\) \begin{align} x &= y\\ \end{align} \emph{hi} \emph{hi--ok} ``` ``` % pandoc -f markdown+latex_macros -t markdown+raw_tex-raw_attribute \newcommand{\my}{\phi} \begin{equation} \my+\my \end{equation} ^D \newcommand{\my}{\phi} \begin{equation} \phi+\phi \end{equation} ``` ``` % pandoc -f markdown-latex_macros -t markdown+raw_tex-raw_attribute \newcommand{\my}{\phi} \begin{equation} \my+\my \end{equation} ^D \newcommand{\my}{\phi} \begin{equation} \my+\my \end{equation} ``` ``` % pandoc -f markdown+latex_macros -t markdown+raw_tex-raw_attribute \newcommand{\my}{\emph{a}} \my ^D \newcommand{\my}{\emph{a}} \emph{a} ``` ``` % pandoc -f latex -t plain \def\bar{hello} \let\fooi\bar \def\fooii{\bar} \fooi +\fooii \def\bar{goodbye} \fooi +\fooii ^D hello+hello hello+goodbye ``` ``` % pandoc -f latex -t plain \def\txt{a} \def\foo{\txt} \let\bar\foo \bar % -> a \def\txt{b} \bar % -> b \def\foo{OH} \bar % -> b ^D a b b ``` ``` % pandoc -f latex -t plain \def\aaa{aaa} \def\bbb{x\aaa} \edef\ccc{y\aaa} \def\aaa{AAA} \bbb \ccc ^D xAAAyaaa ``` ``` % pandoc -f latex -t plain \gdef\aaa{aaa} \gdef\bbb{x\aaa} \xdef\ccc{y\aaa} \gdef\aaa{AAA} \bbb \ccc ^D xAAAyaaa ``` ================================================ FILE: test/command/make-section-column-divs.md ================================================ For reference see https://groups.google.com/d/msgid/pandoc-discuss/cef24253-7731-417c-a1ee-48153c4344bf%40googlegroups.com ``` % pandoc ::: columns ::: column # A a ::: ::: column # B b ::: ::: ^D

    A

    a

    B

    b

    ``` ================================================ FILE: test/command/man-defines.md ================================================ ``` % pandoc -f man -t plain .de test ok .. .test .br \A'test' .br \A'xyz' ^D ok 1 0 ``` ================================================ FILE: test/command/md-abbrevs.md ================================================ Pandoc recognizes an abbreviation and inserts a nonbreaking space (among other things, this prevents a sentence-ending space from being inserted in LaTeX output). ``` % pandoc -t native Mr. Bob ^D [ Para [ Str "Mr.\160Bob" ] ] ``` If you don't want this to happen you can escape the period: ``` % pandoc -t native Hi Mr\. Bob ^D [ Para [ Str "Hi" , Space , Str "Mr." , Space , Str "Bob" ] ] ``` ================================================ FILE: test/command/mdoc-An.md ================================================ ``` % pandoc -f mdoc -t plain .Sh DESCRIPTION .An Emily Brontë , .An Anne Brontë .Pp .An -split .An Charlotte Brontë .An Bramwell Brontë ^D DESCRIPTION Emily Brontë, Anne Brontë Charlotte Brontë Bramwell Brontë ``` ================================================ FILE: test/command/mdoc-Bd-unfilled.md ================================================ ``` % pandoc -f mdoc -t plain .Bd -unfilled ’Twas brillig, and the slithy toves Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe. “Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!” .Ed ^D ’Twas brillig, and the slithy toves Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe. “Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!” ``` ================================================ FILE: test/command/mdoc-Bl-column.md ================================================ ``` % pandoc -f mdoc -t djot .Bl -column a b .It a Ta .Sy b b .Ta c .Pp .It c Ta d Ta \& .It Em e .Ta f .Ta g .It h Ta Ta j .It Pq a Ta b Ta c .El ^D | a | *b* b | c | | c | d | | | _e_ | f | g | | h | | j | | (a) | b | c | ``` ================================================ FILE: test/command/mdoc-Bl-tag.md ================================================ ``` % pandoc -f mdoc -t native .Bl -tag .It one one .It Xo two .Xc two .It Em three Xo three .Xc three .It Po one two .Bq three .Pc four .El ^D [ DefinitionList [ ( [ Str "one" ] , [ [ Para [ Str "one" ] ] ] ) , ( [ Str "two" ] , [ [ Para [ Str "two" ] ] ] ) , ( [ Emph [ Str "three" ] , Space , Str "three" ] , [ [ Para [ Str "three" ] ] ] ) , ( [ Str "(one" , Space , Str "two" , Space , Str "[three])" ] , [ [ Para [ Str "four" ] ] ] ) ] ] ``` ================================================ FILE: test/command/mediawiki_behavior_switches.md ================================================ ``` % pandoc -f mediawiki -t native ____INDEX____ __NOINDEX__ __noindex__ __FOOBAR__ ^D [ Para [ Str "____" , SoftBreak , Str "__noindex__" , SoftBreak , Str "__FOOBAR__" ] ] ``` ================================================ FILE: test/command/mmd-metadata.md ================================================ ``` % pandoc -f markdown_mmd -t markdown -s Title: Blah blah blah Author: Doo de Doo Base Header Level: 1 Bibliography: Pubs.bib Lang: en-GB body ^D --- author: Doo de Doo baseheaderlevel: 1 bibliography: Pubs.bib lang: en-GB title: Blah blah blah --- body ``` ================================================ FILE: test/command/modern-humanities-research-association.csl ================================================ ================================================ FILE: test/command/multiple-metadata-blocks.md ================================================ If multiple blocks define a field, the first is used. ``` % pandoc -s -t native --- foo: bar ... --- foo: bim ... ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaInlines [ Str "bim" ] ) ] } [] ``` ================================================ FILE: test/command/nested-spanlike.md ================================================ ``` % pandoc -f markdown -t html [test]{.foo .underline #bar .smallcaps .kbd} ^D

    test

    ``` ================================================ FILE: test/command/nested-table-to-asciidoc-6942.md ================================================ A table within a table should be converted into a table within table ``` % pandoc -f html -t asciidoc NestedTables
    a1 a2
    b
    c d
    ^D [width="100%",cols="50%,50%",] |=== a| [cols=",",] !=== !a1 !a2 !=== |b |c |d |=== ``` A table within a table within a table cannot be converted because asciidoc only supports two levels of tables. The table on level 3 is thus converted to level 2 and a warning is produced ``` % pandoc -f html -t asciidoc --verbose NestedTables
    a1
    1 2
    b
    c d
    ^D 2> [INFO] Not rendering Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.5),(AlignDefault,ColWidth 0.5)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "a1"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2"]]]]] (TableFoot ("",[],[]) [])]]]] (TableFoot ("",[],[]) []) [width="100%",cols="50%,50%",] |=== a| [width="100%",cols="50%,50%",] !=== !a1 ! !=== |b |c |d |=== ``` ================================================ FILE: test/command/newif.md ================================================ ``` % pandoc -f latex -t plain \iftrue should print \iftrue should print \else should not print A \fi \else should not print B \fi \iffalse should not print C \else \iftrue should print \else should not print D \fi \fi \newif\ifepub \ifepub should not print E \fi \epubtrue \ifepub should print \else should not print F \fi \epubfalse \ifepub should not print G \else should print \fi ^D should print should print should print should print should print ``` ================================================ FILE: test/command/oscola.csl ================================================ ================================================ FILE: test/command/pandoc-citeproc-118.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - director: family: Hitchcock given: Alfred id: nbn issued: year: 1959 language: 'en-US' publisher: 'Metro-Goldwyn-Mayer' publisher-place: USA title: North by Northwest type: motion_picture --- [@nbn] is a spy thriller film. ^D (Hitchcock 1959) is a spy thriller film. :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-nbn .csl-entry} Hitchcock, Alfred, dir. 1959. *North by Northwest*. Metro-Goldwyn-Mayer. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-119.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- bibliography: 'command/averroes.bib' csl: command/apa.csl --- @averroes/bland; @averroes/hannes; @averroes/hercz References {#references .unnumbered} ========== ^D Averroes (1982); Averroes (1892); Averroes (1869) # References {#references .unnumbered} :::::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0" line-spacing="2"} ::: {#ref-averroes/hercz .csl-entry} Averroes. (1869). *Drei Abhandlungen über die Conjunction des separaten Intellects mit dem Menschen: Von Averroes (Vater und Sohn), aus dem Arabischen übersetzt von Samuel Ibn Tibbon*. (J. Hercz, Ed. & Trans.). Berlin: S. Hermann. ::: ::: {#ref-averroes/hannes .csl-entry} Averroes. (1892). *Des Averroës Abhandlung: "Über die Möglichkeit der Conjunktion" oder "Über den materiellen Intellekt"*. (L. Hannes, Ed. & Trans.). Halle an der Saale: C. A. Kaemmerer. ::: ::: {#ref-averroes/bland .csl-entry} Averroes. (1982). *The epistle on the possibility of conjunction with the active intellect by Ibn Rushd with the commentary of Moses Narboni*. (K. P. Bland, Ed. & Trans.). New York: Jewish Theological Seminary of America. ::: :::::: ``` ================================================ FILE: test/command/pandoc-citeproc-13.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-note-bibliography.csl' references: - author: family: Author given: - Ann container-title: Journal id: item1 issued: - year: 2011 title: Title type: 'article-newspaper' --- Foo [@item1]. ^D Foo.[^1] :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-item1 .csl-entry} Author, Ann. "Title." *Journal*, 2011. ::: :::: [^1]: Author, "Title." ``` ================================================ FILE: test/command/pandoc-citeproc-136.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - id: stanze type: book issued: - year: 1547 title: Stanze in lode della donna brutta publisher-place: Florence language: it-IT ... @stanze is an anoynymous work. ^D *Stanze in lode della donna brutta* (1547) is an anoynymous work. :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-stanze .csl-entry} *Stanze in lode della donna brutta*. 1547. Florence. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-14.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: family: Pelikan given: Jaroslav container-title: 'The Christian tradition: A history of the development of doctrine' id: CTv1c2 issued: - year: 1971 language: 'en-US' page: '34-56' publisher: University of Chicago Press publisher-place: Chicago title: Chapter two type: chapter volume: 1 volume-title: 'The emergence of the Catholic tradition (100--600)' - author: family: Pelikan given: Jaroslav container-title: 'The Christian tradition: A history of the development of doctrine' id: CTv1 issued: - year: 1971 language: 'en-US' publisher: University of Chicago Press publisher-place: Chicago title: 'The emergence of the Catholic tradition (100--600)' type: book volume: 1 - author: family: Pelikan given: Jaroslav id: CT issued: - year: 1971 language: 'en-US' publisher: University of Chicago Press publisher-place: Chicago title: 'The Christian tradition: A history of the development of doctrine' type: book --- Foo [@CT, 1:12]. Bar [@CTv1, 12]. Baz [@CTv1c2, 12]. References {#references .unnumbered} ========== ^D Foo (Pelikan 1971b, 1:12). Bar (Pelikan 1971c, 12). Baz (Pelikan 1971a, 12). # References {#references .unnumbered} :::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-CTv1c2 .csl-entry} Pelikan, Jaroslav. 1971a. "Chapter Two." In *The Emergence of the Catholic Tradition (100--600)*, vol. 1 of *The Christian Tradition: A History of the Development of Doctrine*. University of Chicago Press. ::: ::: {#ref-CT .csl-entry} Pelikan, Jaroslav. 1971b. *The Christian Tradition: A History of the Development of Doctrine*. University of Chicago Press. ::: ::: {#ref-CTv1 .csl-entry} Pelikan, Jaroslav. 1971c. *The Emergence of the Catholic Tradition (100--600)*. In *The Christian Tradition: A History of the Development of Doctrine*, vol. 1. University of Chicago Press. ::: :::::: ``` ================================================ FILE: test/command/pandoc-citeproc-152.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/apa.csl references: - URL: 'http://geekfeminism.wikia.com/wiki/Geek_Feminism' accessed: day: 10 month: 4 year: 2013 container-title: Geek Feminism custom2: ok.mm id: Feminism2013gf issued: day: 10 month: 4 year: 2013 keyword: gender title: Geek Feminism type: webpage - URL: 'http://geekfeminism.wikia.com/wiki/Category:Communities' accessed: day: 19 month: 10 year: 2011 container-title: Geek Feminism custom2: gender.mm id: Feminism2011ces issued: day: 14 month: 8 year: 2011 keyword: gender title: Communities type: 'entry-encyclopedia' --- Test ==== I have two citations [@Feminism2013gf; @Feminism2011ces]. References ========== ^D # Test I have two citations ("Communities," 2011; "Geek Feminism," 2013). # References {#references .unnumbered} ::::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0" line-spacing="2"} ::: {#ref-Feminism2011ces .csl-entry} Communities. (2011, August 14). In *Geek Feminism*. Retrieved from ::: ::: {#ref-Feminism2013gf .csl-entry} Geek Feminism. (2013, April 10). *Geek Feminism*. Retrieved April 10, 2013, from ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-160.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/issue160.csl references: - author: - family: Doe given: Jane citation-label: Jane11 id: item1 issued: year: 2011 title: A book type: book --- No citation-label ----------------- Foo [@item1]. Expected -------- > Foo \[Jane11\]. > > \[Jane11\] Jane Doe. A book. 2011. ^D ## No citation-label Foo \[Jane11\]. ## Expected > Foo \[Jane11\]. > > \[Jane11\] Jane Doe. A book. 2011. :::: {#refs .references .csl-bib-body} ::: {#ref-item1 .csl-entry} \[Jane11\] Jane Doe. A book. 2011. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-175.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Doe given: Jane container-title: A magazine id: item1 issued: - month: 1 year: 2011 - month: 2 year: 2011 page: '33-44' title: A title type: 'article-magazine' --- Missing en-dash between months ------------------------------ Foo [@item1]. Expected -------- > Doe, Jane. 2011. "A Title." *A Magazine*, January--February. ^D ## Missing en-dash between months Foo (Doe 2011). ## Expected > Doe, Jane. 2011. "A Title." *A Magazine*, January--February. :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} Doe, Jane. 2011. "A Title." *A Magazine*, January--February, 33--44. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-197.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-fullnote-bibliography.csl' nocite: '@test' references: - editor: - family: Abelard given: Peter id: test issued: date-parts: - - 1989 publisher: Clarendon Press publisher-place: Oxford title: Test type: book --- This is a test [@test]. ^D This is a test.[^1] :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-test .csl-entry} Abelard, Peter, ed. *Test*. Oxford: Clarendon Press, 1989. ::: :::: [^1]: Peter Abelard, ed., *Test* (Oxford: Clarendon Press, 1989). ``` ================================================ FILE: test/command/pandoc-citeproc-213.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/harvard-university-of-kent.csl' references: - author: - family: Doe given: Ann collection-title: The collection title dimensions: 789 pp. id: doe1 issued: - year: 1999 publisher: The publisher title: Title type: book --- Foo [@doe1]. References {#references .unnumbered} ========== ^D Foo (Doe 1999). # References {#references .unnumbered} :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-doe1 .csl-entry} Doe, A. (1999). *Title*. The collection title. The publisher. 789 pp. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-25.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Author given: Al id: item1 issued: date-parts: - - 1998 title: 'foo bar baz: bazbaz foo' type: 'article-journal' --- Foo [@item1]. References {#references .unnumbered} ========== ^D Foo (Author 1998). # References {#references .unnumbered} :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} Author, Al. 1998. *Foo Bar Baz: Bazbaz Foo*. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-250.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- link-citations: true references: - author: family: Doe id: doe title: Title --- [@doe] ^D ([Doe, n.d.](#ref-doe)) :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-doe .csl-entry} Doe. n.d. *Title*. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-27.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/science.csl references: - author: - family: AuthorOne given: Joe - family: AuthorTwo given: Jill container-title: Some Journal id: AuthorOne2014 issue: X issued: date-parts: - - 2014 page: 'XXXX-YYYY' title: Sample Title type: 'article-journal' volume: XX --- Minimal example =============== Here is some text that needs a citation [@AuthorOne2014]. ^D # Minimal example Here is some text that needs a citation (*1*). :::: {#refs .references .csl-bib-body} ::: {#ref-AuthorOne2014 .csl-entry} [1. ]{.csl-left-margin}[J. AuthorOne, J. AuthorTwo, *Some Journal*, in press.]{.csl-right-inline} ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-292.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/sage-harvard.csl' references: - author: family: Doe id: doe issued: year: 2007 type: article - author: family: Zoe id: zoe issued: - year: 2009 type: article - author: family: Roe id: roe issued: - year: 2007 type: article --- [@zoe; @roe; see for comparison @doe, p. 3] ^D (Roe, 2007; Zoe, 2009; see for comparison Doe, 2007: 3) :::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-doe .csl-entry} Doe (2007). ::: ::: {#ref-roe .csl-entry} Roe (2007). ::: ::: {#ref-zoe .csl-entry} Zoe (2009). ::: :::::: ``` ================================================ FILE: test/command/pandoc-citeproc-301.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - id: test title: Essays presented to N.R. Ker (On Art) - id: test2 title: '*Test:* An experiment: An abridgement' --- @test; @test2 ^D *Essays Presented to N.R. Ker (On Art)* (n.d.); **Test:* An Experiment: An Abridgement* (n.d.) ::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-test .csl-entry} *Essays Presented to N.R. Ker (On Art)*. n.d. ::: ::: {#ref-test2 .csl-entry} **Test:* An Experiment: An Abridgement*. n.d. ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-307.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- lang: 'fr-FR' references: - author: - family: Bazin given: André container-title: Cahiers du cinéma id: bazin_cybernetique_1954 issue: 36 issued: date-parts: - - 1954 - 6 page: '22-27' title: 'La Cybernétique d''André Cayatte' type: 'article-journal' --- Bonjour[@bazin_cybernetique_1954] ! ^D Bonjour(Bazin 1954) ! :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-bazin_cybernetique_1954 .csl-entry} Bazin, André. 1954. « La Cybernétique d'André Cayatte ». *Cahiers du cinéma*, nᵒ 36 (juin): 22‑27. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-31.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/oscola.csl references: - DOI: 10.1086/504343 author: - family: Olson given: Hope A. container-title: Library Quarterly id: item1 issue: 1 issued: date-parts: - - 2006 page: '19-35' title: 'Codes, costs, and critiques: The organization of information in *Library Quarterly*, 1931--2004' title-short: 'Codes, costs, and critiques' type: 'article-magazine' volume: 76 - id: item2 title: Second title --- Foo [@item1]. Bar [@item2]. Baz [@item1]. ^D Foo.[^1] Bar.[^2] Baz.[^3] ::::: {#refs .references .csl-bib-body} ::: {#ref-item1 .csl-entry} Olson HA, '[Codes, Costs, and Critiques: The Organization of Information in *Library Quarterly*, 1931--2004](https://doi.org/10.1086/504343)' (2006) 76 *Library Quarterly* 19 ::: ::: {#ref-item2 .csl-entry} 'Second Title' ::: ::::: [^1]: Hope A Olson, 'Codes, Costs, and Critiques: The Organization of Information in *Library Quarterly*, 1931--2004' (2006) 76 *Library Quarterly* 19. [^2]: 'Second Title'. [^3]: Olson (n 1). ``` ================================================ FILE: test/command/pandoc-citeproc-312.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/apa.csl nocite: '@*' references: - author: - literal: NN id: 'Y' issued: - year: 1950 title: 'Date: Year' title-short: Date type: webpage - author: - literal: NN id: Y/Y issued: - year: 1951 - year: 1952 title: 'Date range: Year' title-short: Date range type: webpage - author: - literal: NN id: YM issued: - month: 1 year: 1953 title: 'Date: Year+month' title-short: Date type: webpage - author: - literal: NN id: YM/YM issued: - month: 1 year: 1954 - month: 2 year: 1955 title: 'Date range: Year+month' title-short: Date range type: webpage - author: - literal: NN id: YM/YM_same issued: - month: 1 year: 1956 - month: 2 year: 1956 title: 'Date range: Year+month, same year' title-short: Date range type: webpage - author: - literal: NN id: YMD issued: - day: 15 month: 1 year: 1958 title: 'Date: Year+month+day' title-short: Date type: webpage - author: - literal: NN id: YMD/YMD issued: - day: 15 month: 1 year: 1959 - day: 16 month: 2 year: 1960 title: 'Date range: Year+month+day' title-short: Date range type: webpage - author: - literal: NN id: YMD/YMD_same issued: - day: 15 month: 1 year: 1961 - day: 16 month: 1 year: 1962 title: 'Date range: Year+month+day, same month' title-short: Date range type: webpage - author: - literal: NN id: YS issued: - season: 3 year: 1963 title: 'Date: Year+season' title-short: Date type: webpage - author: - literal: NN id: YS/YS issued: - season: 1 year: 1964 - season: 4 year: 1965 title: 'Date range: Year+season' title-short: Date range type: webpage - author: - literal: NN id: YS/YS_same issued: - season: 2 year: 1966 - season: 4 year: 1966 title: 'Date range: Year+season, same year' title-short: Date range type: webpage --- ^D :::::::::::::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0" line-spacing="2"} ::: {#ref-Y .csl-entry} NN. (1950). Date: Year. ::: ::: {#ref-Y/Y .csl-entry} NN. (1951--1952). Date range: Year. ::: ::: {#ref-YM .csl-entry} NN. (1953, January). Date: Year+month. ::: ::: {#ref-YM/YM .csl-entry} NN. (1954--1955, January--February). Date range: Year+month. ::: ::: {#ref-YM/YM_same .csl-entry} NN. (1956, January--February). Date range: Year+month, same year. ::: ::: {#ref-YMD .csl-entry} NN. (1958, January 15). Date: Year+month+day. ::: ::: {#ref-YMD/YMD .csl-entry} NN. (1959--1960, January 15--February 16). Date range: Year+month+day. ::: ::: {#ref-YMD/YMD_same .csl-entry} NN. (1961--1962, January 15--16). Date range: Year+month+day, same month. ::: ::: {#ref-YS .csl-entry} NN. (1963, Autumn). Date: Year+season. ::: ::: {#ref-YS/YS .csl-entry} NN. (1964--1965, Spring--Winter). Date range: Year+season. ::: ::: {#ref-YS/YS_same .csl-entry} NN. (1966, Summer--Winter). Date range: Year+season, same year. ::: :::::::::::::: ``` ================================================ FILE: test/command/pandoc-citeproc-320.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/apa.csl references: - author: - family: Benjamin given: A. S. id: itemA1 - author: - family: Yaakov given: D. non-dropping-particle: ben id: itemA2 - author: - family: Brown given: J. R. id: itemA3 - author: - family: Browning given: A. R. id: itemA4 - author: - family: Girard given: 'J.-B.' id: itemA5 - author: - family: 'Girard-Perregaux' given: A. S. id: itemA6 - author: - family: Ibn Abdulaziz given: T. id: itemA7 - author: - family: Ibn Nidal given: A. K. M. id: itemA8 - author: - family: López given: M. E. id: itemA9 - author: - family: López de Molina given: G. id: itemA10 - author: - family: Singh given: Y. id: itemA11 - author: - family: Singh Siddhu given: N. id: itemA12 - author: - family: Villafuerte given: S. A. id: itemA13 - author: - family: 'Villa-Lobos' given: J. id: itemA14 - author: - family: Macalister given: Donald id: itemB1 - author: - family: MacAlister given: Paul id: itemB2 - author: - family: Macauley given: Catharine id: itemB3 - author: - family: Macmillan given: Harold id: itemB4 - author: - family: Madison given: James id: itemB5 - author: - family: McAllister given: Ward id: itemB6 - author: - family: McAuley given: Catherine id: itemB7 - author: - family: McMillan given: Edwin M. id: itemB8 - author: - family: 'Sainte-Beuve' given: 'Charles-Augustin' id: itemC1 - author: - family: 'Saint-Gaudens' given: Augustus id: itemC2 - author: - family: 'Saint-Saëns' given: Camille id: itemC3 - author: - dropping-particle: de family: San Martin given: José id: itemC4 - author: - family: St. Denis given: Ruth id: itemC5 - author: - family: St. Laurent given: Louis Stephen id: itemC6 --- Foo [@itemA1; @itemA2; @itemA3; @itemA4; @itemA5; @itemA6; @itemA7; @itemA8; @itemA9; @itemA10; @itemA11; @itemA12; @itemA13; @itemA14]. Foo [@itemB1; @itemB2; @itemB3; @itemB4; @itemB5; @itemB6; @itemB7; @itemB8]. Foo [@itemC1; @itemC2; @itemC3; @itemC4; @itemC5; @itemC6]. ^D Foo (ben Yaakov, n.d.; Benjamin, n.d.; Brown, n.d.; Browning, n.d.; Girard, n.d.; Girard-Perregaux, n.d.; Ibn Abdulaziz, n.d.; Ibn Nidal, n.d.; López de Molina, n.d.; López, n.d.; Singh Siddhu, n.d.; Singh, n.d.; Villafuerte, n.d.; Villa-Lobos, n.d.). Foo (Macalister, n.d.; MacAlister, n.d.; Macauley, n.d.; Macmillan, n.d.; Madison, n.d.; McAllister, n.d.; McAuley, n.d.; McMillan, n.d.). Foo (Sainte-Beuve, n.d.; Saint-Gaudens, n.d.; Saint-Saëns, n.d.; San Martin, n.d.; St. Denis, n.d.; St. Laurent, n.d.). ::::::::::::::::::::::::::::::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0" line-spacing="2"} ::: {#ref-itemA2 .csl-entry} ben Yaakov, D. (n.d.). ::: ::: {#ref-itemA1 .csl-entry} Benjamin, A. S. (n.d.). ::: ::: {#ref-itemA3 .csl-entry} Brown, J. R. (n.d.). ::: ::: {#ref-itemA4 .csl-entry} Browning, A. R. (n.d.). ::: ::: {#ref-itemA5 .csl-entry} Girard, J.-B. (n.d.). ::: ::: {#ref-itemA6 .csl-entry} Girard-Perregaux, A. S. (n.d.). ::: ::: {#ref-itemA7 .csl-entry} Ibn Abdulaziz, T. (n.d.). ::: ::: {#ref-itemA8 .csl-entry} Ibn Nidal, A. K. M. (n.d.). ::: ::: {#ref-itemA10 .csl-entry} López de Molina, G. (n.d.). ::: ::: {#ref-itemA9 .csl-entry} López, M. E. (n.d.). ::: ::: {#ref-itemB1 .csl-entry} Macalister, D. (n.d.). ::: ::: {#ref-itemB2 .csl-entry} MacAlister, P. (n.d.). ::: ::: {#ref-itemB3 .csl-entry} Macauley, C. (n.d.). ::: ::: {#ref-itemB4 .csl-entry} Macmillan, H. (n.d.). ::: ::: {#ref-itemB5 .csl-entry} Madison, J. (n.d.). ::: ::: {#ref-itemB6 .csl-entry} McAllister, W. (n.d.). ::: ::: {#ref-itemB7 .csl-entry} McAuley, C. (n.d.). ::: ::: {#ref-itemB8 .csl-entry} McMillan, E. M. (n.d.). ::: ::: {#ref-itemC1 .csl-entry} Sainte-Beuve, C.-A. (n.d.). ::: ::: {#ref-itemC2 .csl-entry} Saint-Gaudens, A. (n.d.). ::: ::: {#ref-itemC3 .csl-entry} Saint-Saëns, C. (n.d.). ::: ::: {#ref-itemC4 .csl-entry} San Martin, J. de. (n.d.). ::: ::: {#ref-itemA12 .csl-entry} Singh Siddhu, N. (n.d.). ::: ::: {#ref-itemA11 .csl-entry} Singh, Y. (n.d.). ::: ::: {#ref-itemC5 .csl-entry} St. Denis, R. (n.d.). ::: ::: {#ref-itemC6 .csl-entry} St. Laurent, L. S. (n.d.). ::: ::: {#ref-itemA13 .csl-entry} Villafuerte, S. A. (n.d.). ::: ::: {#ref-itemA14 .csl-entry} Villa-Lobos, J. (n.d.). ::: ::::::::::::::::::::::::::::::: ``` ================================================ FILE: test/command/pandoc-citeproc-320a.md ================================================ ``` % pandoc --citeproc -t plain --- references: - author: - family: ʾUdhrī given: Jamīl non-dropping-particle: 'al-' issued: 2000 title: hamza id: item1 - author: - family: ʿUdhrī given: Jamīl non-dropping-particle: 'al-' issued: 2000 title: ayn id: item2 - author: - family: "'Udhrī" given: Jamīl non-dropping-particle: 'al-' issued: 2000 title: straight apostrophe id: item3 - author: - family: '‘Udhrī' given: Jamīl non-dropping-particle: 'al-' issued: 2000 title: inverted curly apostrophe = opening single curly quote (for ayn) id: item4 - author: - family: '’Udhrī' given: Jamīl non-dropping-particle: 'al-' issued: 2000 title: curly apostrophe = closing single curly quote (for hamza) id: item5 - author: - family: Uch given: Ann id: item6 issued: 2000 - author: - family: Uebel given: Joe id: item7 issued: 2000 - author: - family: Zzz given: Zoe id: item8 issued: 2000 --- Foo [@item1; @item2; @item3; @item4; @item5; @item6; @item7; @item8]. ^D Foo (al-ʾUdhrī 2000; al-ʿUdhrī 2000; al-’Udhrī 2000b, 2000a; al-‘Udhrī 2000; Uch 2000; Uebel 2000; Zzz 2000). Uch, Ann. 2000. ‘Udhrī, Jamīl al-. 2000. Inverted Curly Apostrophe = Opening Single Curly Quote (for Ayn). ʿUdhrī, Jamīl al-. 2000. Ayn. ’Udhrī, Jamīl al-. 2000a. Curly Apostrophe = Closing Single Curly Quote (for Hamza). ʾUdhrī, Jamīl al-. 2000. Hamza. ’Udhrī, Jamīl al-. 2000b. Straight Apostrophe. Uebel, Joe. 2000. Zzz, Zoe. 2000. ``` ================================================ FILE: test/command/pandoc-citeproc-322.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/annales.csl references: - author: - family: Timmory given: François container-title: 'L''Écran français' id: timmory\_\_justice_1950 issue: 272 issued: - day: 25 month: 9 year: 1950 language: 'fr-FR' page: 12 title: '*Justice est faite* : soyons justes' type: 'article-journal' --- Foo [@timmory__justice_1950]. ^D Foo.[^1] :::: {#refs .references .csl-bib-body} ::: {#ref-timmory__justice_1950 .csl-entry} François [Timmory]{.smallcaps}, « *Justice est faite* : soyons justes », *L'Écran français*, 1950, nᵒ 272, p. 12. ::: :::: [^1]: François [Timmory]{.smallcaps}, « *Justice est faite* : soyons justes », *L'Écran français*, 1950, nᵒ 272, p. 12. ``` ================================================ FILE: test/command/pandoc-citeproc-325.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Smith given: John id: item1 type: book - author: - family: Smith given: John id: item2 type: book --- [@item1; @item2] ^D (Smith, n.d.-a, n.d.-b) ::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} Smith, John. n.d.-a. ::: ::: {#ref-item2 .csl-entry} Smith, John. n.d.-b. ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-327.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chinese-gb7714-2005-numeric.csl' references: - ISSN: '1003-1111' URL: 'http://kns.cnki.net/kns/detail/detail.aspx?QueryID=4&CurRec=4&recid=&FileName=CHAN201706006&DbName=CJFDLAST2018&DbCode=CJFQ&yx=Y&pr=&URLID=21.1110.S.20171129.1725.006' abstract: '为了解辽东湾海域底栖动物次级生产力水平和时空分布特点,根据2007年的4月和10月辽东湾海域大型底栖动物调查数据,运用Brey经验公式计算大型底栖动物次级生产力和P/B值,并结合海洋生物指数方法对辽东湾海域进行生境适宜性评价。分析结果表明,该海域大型底栖动物年平均次级生产力为5.59g/(m\~2·年),年平均P/B值为3.16,辽东湾大型底栖动物次级生产力空间分布呈现出自沿岸向中部递增的趋势,P/B值空间分布与底栖动物次级生产力相反,呈现出自沿岸向中部递减的趋势,群落组成中以个体小、生活史短、代谢快的底栖动物为主。' accessed: - day: 27 month: 3 year: 2018 author: - family: 李 given: 轶平 - family: 于 given: 旭光 - family: 孙 given: 明 - family: 郭 given: 栋 - family: 段 given: 妍 - family: 董 given: 婧 container-title: 水产科学 id: LiLiaoDongWanHaiYuDiQiDongWuCiJiShengChanLiYanJiuJiShengJingGuaYiXingPingJie2017 issue: 06 issued: - year: 2017 keyword: '次级生产力,大型底栖动物,海洋生物指数方法,AMBI,macrozoobenthos,P/B value,P/B值,secondary productivity' language: 中文; page: '728-734' title: 辽东湾海域底栖动物次级生产力研究及生境适宜性评价 type: 'article-journal' --- I referenced something here [@LiLiaoDongWanHaiYuDiQiDongWuCiJiShengChanLiYanJiuJiShengJingGuaYiXingPingJie2017] ^D I referenced something here^\[1\]^ :::: {#refs .references .csl-bib-body entry-spacing="0"} ::: {#ref-LiLiaoDongWanHaiYuDiQiDongWuCiJiShengChanLiYanJiuJiShengJingGuaYiXingPingJie2017 .csl-entry} [\[1\] ]{.csl-left-margin}[李轶平, 于旭光, 孙明, 等. [辽东湾海域底栖动物次级生产力研究及生境适宜性评价](http://kns.cnki.net/kns/detail/detail.aspx?QueryID=4&CurRec=4&recid=&FileName=CHAN201706006&DbName=CJFDLAST2018&DbCode=CJFQ&yx=Y&pr=&URLID=21.1110.S.20171129.1725.006)\[J\]. 水产科学, 2017(6): 728~734.]{.csl-right-inline} ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-338.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/din-1505-2.csl' lang: de nocite: '@*' pagetitle: Citation references: - ISBN: '978-3-642-32078-1' author: - family: Wolfinger given: Christine edition: '11., vollst. überarb. Aufl.' id: 'item-1' issued: - year: 2013 keyword: UNIX; LINUX number-of-pages: 'XVIII, 529 S. : Ill., graph. Darst.' publisher: Springer Vieweg publisher-place: 'Berlin \[u.a.\]' title: 'Keine Angst vor Linux, Unix: ein Lehrbuch für Linux- und Unix-Anwender' type: book --- ^D :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item-1 .csl-entry} [Wolfinger, Christine]{.smallcaps}: *Keine Angst vor Linux, Unix: ein Lehrbuch für Linux- und Unix-Anwender*. 11., vollst. überarb. Aufl. Aufl. Berlin \[u.a.\] : Springer Vieweg, 2013 --- ISBN 978-3-642-32078-1 ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-351.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/zeitschrift-fur-kunstgeschichte.csl' references: - editor: - family: Nietzsche given: Friedrich id: Nie72 issued: - year: 1872 title: Die geburt type: book --- @Nie72 ^D Friedrich Nietzsche (ed.)[^1] :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-Nie72 .csl-entry} Nietzsche, Friedrich (ed.), *Die geburt*, 1872. ::: :::: [^1]: *Die geburt*, 1872. ``` ================================================ FILE: test/command/pandoc-citeproc-356.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Alice id: foo issued: - year: 2042 other-ids: - bar - doz type: book --- [@bar] ^D 2> [WARNING] Citeproc: citation bar not found (**bar?**) ``` ================================================ FILE: test/command/pandoc-citeproc-360.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-fullnote-bibliography.csl' references: - author: - family: 'L''Estrange' given: Michael - family: Merchant given: Stephen id: lestrange2017 issued: - day: 18 month: 7 year: 2017 language: 'en-US' title: 2017 Independent Intelligence Review title-short: Independent Intelligence Review type: report --- [@lestrange2017] ^D [^1] :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-lestrange2017 .csl-entry} L'Estrange, Michael, and Stephen Merchant. "2017 Independent Intelligence Review," July 18, 2017. ::: :::: [^1]: Michael L'Estrange and Stephen Merchant, "2017 Independent Intelligence Review," July 18, 2017. ``` ================================================ FILE: test/command/pandoc-citeproc-361.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/locators.csl references: - id: citekey title: Title type: 'article-journal' - id: other title: Other type: 'article-journal' suppress-bibliography: true --- Content[@citekey] Content[@citekey, 1] Content[@citekey, 2] Content[@citekey, 2] Content[@citekey 2] Content[@citekey, p. 2] Content[@citekey p. 2] Content[@citekey] Content[@other] Content[@citekey, 3] Content[@citekey, 3] ^D Content[^1] Content[^2] Content[^3] Content[^4] Content[^5] Content[^6] Content[^7] Content[^8] Content[^9] Content[^10] Content[^11] [^1]: Title. [^2]: Ibid-with-locator {1}. [^3]: Ibid-with-locator {2}. [^4]: Ibid. [^5]: Ibid. [^6]: Ibid. [^7]: Ibid. [^8]: Subsequent. [^9]: Other. [^10]: Subsequent {3}. [^11]: Ibid. ``` ================================================ FILE: test/command/pandoc-citeproc-365.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/le-tapuscrit-note.csl' references: - ISBN: '978-2-912573-52-0' author: - family: Le Gras given: Gwénaëlle call-number: 'Tolbiac - Rez de Jardin - Littérature et art - Magasin - 2010-82178' collection-title: 'Jeux d''acteurs' id: legras_michel_2010 issued: - year: 2010 language: fre number-of-pages: 128 publisher: Scope publisher-place: Paris source: 'BnF Catalogue général (http://catalogue.bnf.fr)' title: 'Michel Simon : l''art de la disgrâce' title-short: Michel Simon type: book --- Foo [@legras_michel_2010]. ^D Foo.[^1] :::: {#refs .references .csl-bib-body} ::: {#ref-legras_michel_2010 .csl-entry} [Le Gras]{.smallcaps} Gwénaëlle, *Michel Simon : l'art de la disgrâce*, Paris, Scope (coll. « Jeux d'acteurs »), 2010, 128 p. ::: :::: [^1]: Gwénaëlle Le Gras, *Michel Simon : l'art de la disgrâce*, Paris, Scope, 2010, 128 p. ``` ================================================ FILE: test/command/pandoc-citeproc-371.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Doe given: Jane id: item1 status: in press title: Title one type: book - author: - family: Doe given: Jane id: item2 issued: - year: 2018 title: Title two type: book --- Foo [@item2; @item1]. References {#references .unnumbered} ========== ^D Foo (Doe 2018, in press). # References {#references .unnumbered} ::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item2 .csl-entry} Doe, Jane. 2018. *Title Two*. ::: ::: {#ref-item1 .csl-entry} Doe, Jane. In press. *Title One*. ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-38.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Doe given: Ann - family: Doe given: Ben - family: Roe given: Ron id: a issued: date-parts: - - 2007 title: Title type: 'article-journal' --- @a ^D Doe et al. (2007) :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-a .csl-entry} Doe, Ann, Ben Doe, and Ron Roe. 2007. *Title*. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-386.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/advanced-optical-materials.csl' references: - DOI: 10.1038/lsa.2012.20 ISSN: '2047-7538' author: - family: Ding given: K. - family: Ning given: C. Z. container-title: Light Sci. Appl. id: ding_metallic_2012 issue: 7 issued: - month: 7 year: 2012 page: 'e20-e20' title: 'Metallic subwavelength-cavity semiconductor nanolasers' type: 'article-journal' volume: 1 --- @ding_metallic_2012 ^D ^\[1\]^ :::: {#refs .references .csl-bib-body entry-spacing="0" line-spacing="2"} ::: {#ref-ding_metallic_2012 .csl-entry} [\[1\] ]{.csl-left-margin}[[K. Ding, C. Z. Ning, *Light Sci. Appl.* **2012**, *1*, e20](https://doi.org/10.1038/lsa.2012.20).]{.csl-right-inline} ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-392.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/vancouver.csl references: - author: - family: James given: M.R.C.E.L. id: james - author: - family: MacFarlane given: J. G. id: macfarlane --- @james; @macfarlane ^D (1); (2) ::::: {#refs .references .csl-bib-body} ::: {#ref-james .csl-entry} [1. ]{.csl-left-margin}[James MRCEL. ]{.csl-right-inline} ::: ::: {#ref-macfarlane .csl-entry} [2. ]{.csl-left-margin}[MacFarlane JG. ]{.csl-right-inline} ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-399.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/style399.csl references: - author: - family: One id: one - author: - family: Two id: two - author: - family: Three id: three - author: - family: Four id: four - author: - family: Five id: five - author: - family: Six id: six - author: - family: Seven id: seven - author: - family: Eight id: eight - author: - family: Nine id: nine - author: - family: Ten id: ten - author: - family: Eleven id: eleven --- Inline citation [@one; @two; @three; @four; @five; @six; @seven; @eight; @nine; @ten; @eleven]. ^D Inline citation \[1,2,3,4,5,6,7,8,9,10,11\]. ``` ================================================ FILE: test/command/pandoc-citeproc-401.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - id: haslanger2012SocialConstructionDebunking type: chapter author: - family: Haslanger given: Sally issued: - year: 2012 original-date: - year: 2003 title: 'Social Construction: The ‘Debunking’ Project' title-short: Social Construction container-title: 'Resisting Reality: Social Construction and Social Critique' publisher: Oxford University Press publisher-place: Oxford event-place: Oxford page: 113–138 ISBN: 978-0-19-989262-4 - id: haslanger2012FeminismMetaphysicsNegotiating type: chapter author: - family: Haslanger given: Sally issued: - year: 2012 original-date: - year: 2000 title: 'Feminism in Metaphysics: Negotiating the Natural' container-title: 'Resisting Reality: Social Construction and Social Critique' publisher: Oxford University Press publisher-place: Oxford event-place: Oxford page: 139–157 ISBN: 978-0-19-989262-4 language: en ... Haslanger [-@haslanger2012SocialConstructionDebunking; @haslanger2012FeminismMetaphysicsNegotiating] says... ^D Haslanger (\[2003\] 2012, \[2000\] 2012) says... ::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-haslanger2012FeminismMetaphysicsNegotiating .csl-entry} Haslanger, Sally. (2000) 2012. "Feminism in Metaphysics: Negotiating the Natural." In *Resisting Reality: Social Construction and Social Critique*. Oxford University Press. ::: ::: {#ref-haslanger2012SocialConstructionDebunking .csl-entry} Haslanger, Sally. (2003) 2012. "Social Construction: The 'Debunking' Project." In *Resisting Reality: Social Construction and Social Critique*. Oxford University Press. ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-408.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - id: smith1 type: article-journal author: - family: Smith given: Mary issued: - year: 2019 title: Foo - id: smithsmith type: article-journal author: - family: Smith given: Mary - family: Smith given: John issued: - year: 2019 title: Foo bar ... [@smithsmith; @smith1] ^D (Smith and Smith 2019; Smith 2019) ::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-smith1 .csl-entry} Smith, Mary. 2019. *Foo*. ::: ::: {#ref-smithsmith .csl-entry} Smith, Mary, and John Smith. 2019. *Foo Bar*. ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-416.md ================================================ ``` % pandoc --citeproc -t markdown-citations Blah blah [@item1; @item2; @item3]. # References {-} --- title: The Title references: - id: item1 type: article-newspaper author: - family: Doe given: J. issued: - year: 2010 month: 12 day: 13 title: The title - id: item2 type: article-newspaper author: - family: Doe given: J. issued: - year: 2007 month: 12 day: 12 - year: 2007 month: 12 day: 13 title: The title - id: item3 type: article-newspaper author: - family: Doe given: J. issued: - year: 2008 title: The title ... ^D Blah blah (Doe 2010, 2007, 2008). # References {#references .unnumbered} :::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item2 .csl-entry} Doe, J. 2007. *The Title*. December 12--13. ::: ::: {#ref-item3 .csl-entry} Doe, J. 2008. *The Title*. ::: ::: {#ref-item1 .csl-entry} Doe, J. 2010. *The Title*. December 13. ::: :::::: ``` ================================================ FILE: test/command/pandoc-citeproc-437.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/issue437.csl references: - author: - family: Smith given: John id: hirt2009 issued: - year: 2009 publisher: Publishing House publisher-place: Lausanne title: Some Book type: book --- > Here is a quote. [@hirt2009] ^D > Here is a quote.[^1] [^1]: John Smith, Some Book, Lausanne, 2009 ``` ================================================ FILE: test/command/pandoc-citeproc-47.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Doe given: A. id: doe issued: date-parts: - - 2000 title: Title type: book - author: - family: Doe given: A. - family: Poe given: A. id: doepoe issued: date-parts: - - 2000 title: Title type: book - editor: - family: Doe given: A. id: 'doe-ed' issued: date-parts: - - 2000 title: Title type: book - author: - family: Doe given: A. - family: Loe given: A. - family: Toe given: A. id: doeloetoe issued: date-parts: - - 2000 title: Title type: book --- Foo [@doe]. Bar [@doepoe]. Foo [@doe-ed]. Bar [@doeloetoe]. Expected output: > Doe, A. 2000a. Title. > > ---------, ed. 2000b. Title. > > Doe, A., A. Loe, and A. Toe. 2000. Title. > > Doe, A., and A. Poe. 2000. Title. (See CMoS, 16e, 15.16, "Single author versus several authors---reference list order": "Successive entries by two or more authors in which only the first author's name is the same are alphabetized according to the coauthors' last names (regardless of how many coauthors there are)." and 15.18, "The 3-em dash with edited, translated, or compiled works": "The chronological order is maintained, regardless of the added abbreviation. \[ed., trans., comp., or whatever\]" References {#references .unnumbered} ========== ^D Foo (Doe 2000a). Bar (Doe and Poe 2000). Foo (Doe 2000b). Bar (Doe et al. 2000). Expected output: > Doe, A. 2000a. Title. > > ---------, ed. 2000b. Title. > > Doe, A., A. Loe, and A. Toe. 2000. Title. > > Doe, A., and A. Poe. 2000. Title. (See CMoS, 16e, 15.16, "Single author versus several authors---reference list order": "Successive entries by two or more authors in which only the first author's name is the same are alphabetized according to the coauthors' last names (regardless of how many coauthors there are)." and 15.18, "The 3-em dash with edited, translated, or compiled works": "The chronological order is maintained, regardless of the added abbreviation. \[ed., trans., comp., or whatever\]" # References {#references .unnumbered} ::::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-doe .csl-entry} Doe, A. 2000a. *Title*. ::: ::: {#ref-doe-ed .csl-entry} Doe, A., ed. 2000b. *Title*. ::: ::: {#ref-doeloetoe .csl-entry} Doe, A., A. Loe, and A. Toe. 2000. *Title*. ::: ::: {#ref-doepoe .csl-entry} Doe, A., and A. Poe. 2000. *Title*. ::: ::::::: ``` ================================================ FILE: test/command/pandoc-citeproc-51.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Doe given: John container-title: Journal of Something id: item1 issued: date-parts: - - 1987 - - 1988 page: '12-34' title: The title type: 'article-journal' volume: 3 - author: - family: Roe given: Ron container-title: Journal of Something id: item2 issued: date-parts: - - 1987 page: '12-34' title: The title type: 'article-journal' volume: 4 --- @item1; @item2 ^D Doe (1987--1988); Roe (1987) ::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} Doe, John. 1987--1988. "The Title." *Journal of Something* 3: 12--34. ::: ::: {#ref-item2 .csl-entry} Roe, Ron. 1987. "The Title." *Journal of Something* 4: 12--34. ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-53.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/archeologie-medievale.csl references: - title: Work A id: a issued: date-parts: - - 2000 author: - given: John family: Doe type: book - title: Work B id: b issued: date-parts: - - 1990 author: - given: Jane family: Roe type: book --- @a @a @b @b @a @a @b @b ^D Doe[^1] Doe[^2] Roe[^3] Roe[^4] Doe[^5] Doe[^6] Roe[^7] Roe[^8] ::::: {#refs .references .csl-bib-body} ::: {#ref-a .csl-entry} [[Doe J.]{.smallcaps} ]{.csl-block} [2000, *Work A*,.]{.csl-left-margin} ::: ::: {#ref-b .csl-entry} [[Roe J.]{.smallcaps} ]{.csl-block} [1990, *Work B*,.]{.csl-left-margin} ::: ::::: [^1]: 2000 [^2]: *Ibid.* [^3]: 1990 [^4]: *Ibid.* [^5]: 2000 [^6]: *Ibid.* [^7]: 1990 [^8]: *Ibid.* ``` ================================================ FILE: test/command/pandoc-citeproc-57.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-author-date-with-original-date-and-status.csl' references: - author: - family: Faraday given: Carry container-title: Seven Trips beyond the Asteroid Belt editor: - family: Oring given: James id: 'Faraday-forthcoming' publisher: Launch Press publisher-place: 'Cape Canaveral, FL' status: forthcoming title: Protean photography type: chapter --- [@Faraday-forthcoming] References ========== ^D (Faraday, forthcoming) # References {#references .unnumbered} :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-Faraday-forthcoming .csl-entry} Faraday, Carry. Forthcoming. "Protean Photography." In *Seven Trips Beyond the Asteroid Belt*, edited by James Oring. Cape Canaveral, FL: Launch Press. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-58.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/issue58.csl references: - id: stanze issued: date-parts: - - 1547 language: 'it-IT' publisher-place: Florence title: Stanze in lode della donna brutta type: book --- In this item, the title replaces the (unknown) author (see 14.79) [@stanze, p. 12]. References ========== ^D In this item, the title replaces the (unknown) author (see 14.79) (*Stanze in lode della donna brutta* 1547, 12). # References {#references .unnumbered} :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-stanze .csl-entry} *Stanze in lode della donna brutta*. 1547. Florence. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-61.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/modern-humanities-research-association.csl' references: - author: - family: Doe given: John id: doe issued: date-parts: - - 1985 publisher: Publisher title: Title type: book - author: - family: Roe given: Rob id: roe issued: date-parts: - - 1985 publisher: Publisher title: Title type: book --- Text ==== Foo [@doe, VIII, 89] Foo [@roe, III, 89] Foo [@doe, LVIII, 89] Foo [@roe, MVIII, 89] Foo [@doe, CL, 89] References ========== ^D # Text Foo[^1] Foo[^2] Foo[^3] Foo[^4] Foo[^5] # References {#references .unnumbered} ::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-doe .csl-entry} Doe, John, *Title* (Publisher, 1985) ::: ::: {#ref-roe .csl-entry} Roe, Rob, *Title* (Publisher, 1985) ::: ::::: [^1]: John Doe, *Title* (Publisher, 1985), VIII, 89. [^2]: Rob Roe, *Title* (Publisher, 1985), III, 89. [^3]: Doe, LVIII, 89. [^4]: Roe, MVIII, 89. [^5]: Doe, CL, 89. ``` ================================================ FILE: test/command/pandoc-citeproc-64.md ================================================ ``` % pandoc --citeproc -t markdown-citations --csl command/chicago-fullnote-bibliography.csl --- bibliography: - command/biblio.bib nocite: '[@*]' --- ^D :::::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-item2 .csl-entry} Doe, John. "Article." *Journal of Generic Studies* 6 (2006): 33--34. ::: ::: {#ref-item1 .csl-entry} ---------. *First Book*. Cambridge: Cambridge University Press, 2005. ::: ::: {#ref-пункт3 .csl-entry} Doe, John, and Jenny Roe. "Why Water Is Wet." In *Third Book*, edited by Sam Smith. Oxford: Oxford University Press, 2007. ::: :::::: ``` ================================================ FILE: test/command/pandoc-citeproc-65.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - ISBN: 3406493556 author: - family: Stotz given: Peter call-number: 'PA25 PA2616 .H24 Abt. 2, T. 5, Bd. 2, etc' collection-number: 2.5 collection-title: Handbuch der Altertumswissenschaft event-place: Munich first-reference-note-number: 1 id: 'stotz:1996handbuch' issued: literal: 1996_2004 language: de number-of-volumes: 5 publisher: Beck publisher-place: Munich source: Library of Congress ISBN title: Handbuch zur lateinischen Sprache des Mittelalters title-short: Handbuch type: book --- [@stotz:1996handbuch] ^D (Stotz 1996--2004) :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-stotz:1996handbuch .csl-entry} Stotz, Peter. 1996--2004. *Handbuch zur lateinischen Sprache des Mittelalters*. 5 vols. Handbuch der Altertumswissenschaft, 2.5. Beck. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-68.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-fullnote-bibliography.csl' references: - ISBN: 0888441088 author: - family: Goering given: Joseph call-number: BV4009 .W55 1992 collection-number: 108 collection-title: Studies and Texts event-place: Toronto first-reference-note-number: 1 id: 'goering:1992william' issued: date-parts: - - 1992 publisher: Pontifical Institute of Mediaeval Studies publisher-place: Toronto source: toroprod.library.utoronto.ca Library Catalog title: 'William de Montibus (c. 1140--1213): The Schools and the Literature of Pastoral Care' title-short: William de Montibus type: book --- \... a prose commentary [the text of fol. 9r is printed in @goering:1992william, pp. 501--3]. \... a collection of verses with a formal prose commentary [excerpts from this text were previously printed in @goering:1992william, p. 508--14; it was also briefly described in @goering:1992william, pp. 141--42] \... and finally a note starting with a citation [@goering:1992william, pp. 141-42]. ^D \... a prose commentary.[^1] \... a collection of verses with a formal prose commentary[^2] \... and finally a note starting with a citation.[^3] :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-goering:1992william .csl-entry} Goering, Joseph. *William de Montibus (c. 1140--1213): The Schools and the Literature of Pastoral Care*. Studies and Texts 108. Toronto: Pontifical Institute of Mediaeval Studies, 1992. ::: :::: [^1]: The text of fol. 9r is printed in Joseph Goering, *William de Montibus (c. 1140--1213): The Schools and the Literature of Pastoral Care*, Studies and Texts 108 (Toronto: Pontifical Institute of Mediaeval Studies, 1992), 501--3. [^2]: Excerpts from this text were previously printed in Goering, 508--14; it was also briefly described in Goering, 141--42. [^3]: Goering, *William de Montibus*, 141--42. ``` ================================================ FILE: test/command/pandoc-citeproc-7.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: family: Author given: - Ann container-title: Journal id: item1 issued: - day: 24 month: 9 year: 2011 - day: 26 month: 9 year: 2011 title: Title type: 'article-magazine' --- @item1 ^D Author (2011) :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} Author, Ann. 2011. "Title." *Journal*, September 24--26. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-70.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - ISBN: 9782503531465 author: - family: 'Dinkova-Bruun' given: Greti call-number: CB351 .F564 2009 collection-number: 50 collection-title: Textes et études du moyen âge container-title: 'Florilegium mediaevale: Études offertes à Jacqueline Hamesse à l''occasion de son éméritat' editor: - family: Meirinhos given: José Francisco - family: Weijers given: Olga event-place: 'Louvain-la-Neuve' first-reference-note-number: 1 id: 'bruun:2009samuel' issued: date-parts: - - 2009 language: fr page: '155--174' publisher: 'Fédération Internationale des Instituts d''Études Médiévales' publisher-place: 'Louvain-la-Neuve' source: Library of Congress ISBN title: 'Samuel Presbyter and the Glosses to His Versification of Psalm 1: An Anti-Church Invective?' title-short: Samuel Presbyter type: chapter - ISSN: '0362-1529' author: - family: Thorndike given: Lynn container-title: Traditio first-reference-note-number: 1 id: 'thorndike:1955unde' issued: date-parts: - - 1955 language: la note: 'ArticleType: research-article / Full publication date: 1955 / Copyright © 1955 Fordham University' page: '163--193' source: JSTOR title: Unde versus type: 'article-journal' volume: 11 --- [@thorndike:1955unde; @bruun:2009samuel] ^D (Thorndike 1955; Dinkova-Bruun 2009) ::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-bruun:2009samuel .csl-entry} Dinkova-Bruun, Greti. 2009. "Samuel Presbyter and the Glosses to His Versification of Psalm 1: An Anti-Church Invective?" In *Florilegium mediaevale: Études offertes à Jacqueline Hamesse à l'occasion de son éméritat*, edited by José Francisco Meirinhos and Olga Weijers. Textes et études du moyen âge 50. Fédération Internationale des Instituts d'Études Médiévales. ::: ::: {#ref-thorndike:1955unde .csl-entry} Thorndike, Lynn. 1955. "Unde versus." *Traditio* 11: 163--93. ::: ::::: ``` ================================================ FILE: test/command/pandoc-citeproc-75.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/apa.csl references: - author: - family: Doe given: John id: test issued: date-parts: - - 2006 title: Test type: 'article-journal' volume: 81 --- [@test, p. 6] [@test, chap. 6] [@test, n. 6] [@test, pp. 34-36, 38-39] [@test, sec. 3] [@test, p.3] [@test, 33-35, 38-39] [@test, 14] [@test bk. VI] [@test, no. 6] [@test, nos. 6 and 7] ^D (Doe, 2006, p. 6) (Doe, 2006, Chapter 6) (Doe, 2006, n. 6) (Doe, 2006, pp. 34--36, 38--39) (Doe, 2006, sec. 3) (Doe, 2006, p. 3) (Doe, 2006, pp. 33--35, 38--39) (Doe, 2006, p. 14) (Doe, 2006, bk. VI) (Doe, 2006, no. 6) (Doe, 2006, no. 6 and 7) :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0" line-spacing="2"} ::: {#ref-test .csl-entry} Doe, J. (2006). Test, *81*. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-76.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: - family: Author given: Al id: item1 issued: date-parts: - - 1998 title: 'foo bar baz: bazbaz bar foo' type: 'article-journal' - author: - family: Author given: Al id: item2 issued: date-parts: - - 1998 title: 'foo bar baz: the bazbaz bar foo' type: 'article-journal' - author: - family: Author given: Al id: item3 issued: date-parts: - - 1998 title: 'foo bar baz: a bazbaz bar foo' type: 'article-journal' - author: - family: Author given: Al id: item4 issued: date-parts: - - 1998 title: 'foo bar baz: an abazbaz bar foo' type: 'article-journal' --- @item1, @item2, @item3, @item4 ^D Author (1998c), Author (1998d), Author (1998a), Author (1998b) ::::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item3 .csl-entry} Author, Al. 1998a. *Foo Bar Baz: A Bazbaz Bar Foo*. ::: ::: {#ref-item4 .csl-entry} Author, Al. 1998b. *Foo Bar Baz: An Abazbaz Bar Foo*. ::: ::: {#ref-item1 .csl-entry} Author, Al. 1998c. *Foo Bar Baz: Bazbaz Bar Foo*. ::: ::: {#ref-item2 .csl-entry} Author, Al. 1998d. *Foo Bar Baz: The Bazbaz Bar Foo*. ::: ::::::: ``` ================================================ FILE: test/command/pandoc-citeproc-77.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-fullnote-bibliography.csl' references: - author: - family: Doe given: 'John, III' parse-names: true id: item1 type: book - author: - family: van Gogh given: Vincent parse-names: true id: item2 type: book - author: - family: Humboldt given: Alexander von parse-names: true id: item3 type: book - author: - family: Bennett given: 'Frank G.,! Jr.' parse-names: true id: item4 type: book - author: - family: Dumboldt given: 'Ezekiel, III' parse-names: true id: item5 type: book --- [@item1; @item2; @item3; @item4; @item5] ^D [^1] :::::::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-item4 .csl-entry} Bennett, Frank G., Jr., n.d. ::: ::: {#ref-item1 .csl-entry} Doe, John, III, n.d. ::: ::: {#ref-item5 .csl-entry} Dumboldt, Ezekiel, III, n.d. ::: ::: {#ref-item2 .csl-entry} Gogh, Vincent van, n.d. ::: ::: {#ref-item3 .csl-entry} Humboldt, Alexander von, n.d. ::: :::::::: [^1]: John Doe III, n.d.; Vincent van Gogh, n.d.; Alexander von Humboldt, n.d.; Frank G. Bennett, Jr., n.d.; Ezekiel Dumboldt III, n.d. ``` ================================================ FILE: test/command/pandoc-citeproc-82.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-annotated-bibliography.csl' references: - URL: 'https://www.worldcat.org/' accessed: date-parts: - - 2014 - 9 - 19 author: - literal: OCLC first-reference-note-number: 1 id: OCLC_i1099 title: WorldCat type: webpage --- Title ===== Some text.[^1] [^1]: Comment regarding text, supported by citation [@OCLC_i1099]. ^D # Title Some text.[^1] :::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-OCLC_i1099 .csl-entry} OCLC. "WorldCat." Accessed September 19, 2014. . ::: :::: [^1]: Comment regarding text, supported by citation (OCLC, "WorldCat"). ``` ================================================ FILE: test/command/pandoc-citeproc-87.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - URL: 'http://www.example.com' author: - family: Doe given: John container-title: The Web Site id: item1 issued: date-parts: - - 2006 - 10 - 26 title: Title type: webpage - URL: 'http://www.example.com' author: - family: Doe given: John container-title: The Web Site id: item2 issued: date-parts: - - 2006 - 10 - 26 - - 2006 - 11 - 27 title: Title type: webpage - URL: 'http://www.example.com' author: - family: Doe given: John container-title: The Web Site id: item2a issued: date-parts: - - 2006 - 10 - - 2006 - 11 title: Title type: webpage - URL: 'http://www.example.com' author: - family: Doe given: John container-title: The Web Site id: item2b issued: date-parts: - - 2006 - 12 - 31 - - 2007 - 1 - 1 title: Title type: webpage - URL: 'http://www.example.com' author: - family: Doe given: John container-title: The Newspaper id: item3 issued: date-parts: - - 2006 - 10 - 26 - - 2006 - 11 - 27 title: Title type: 'article-newspaper' - URL: 'http://www.example.com' author: - family: Doe given: John container-title: The Newspaper id: item3b issued: date-parts: - - 2006 - 10 - - 2006 - 11 title: Title type: 'article-newspaper' --- @item1 -- webpage, date @item2 -- webpage, date range @item2a -- webpage, date range YM @item2b -- webpage, date range across years @item3 -- article-newspaper @item3b -- article-newspaper YM References ========== ^D Doe (2006a) -- webpage, date Doe (2006c) -- webpage, date range Doe (2006b) -- webpage, date range YM Doe (2006--2007) -- webpage, date range across years Doe (2006e) -- article-newspaper Doe (2006d) -- article-newspaper YM # References {#references .unnumbered} ::::::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} Doe, John. 2006a. "Title." The Web Site, October 26. . ::: ::: {#ref-item2a .csl-entry} Doe, John. 2006b. "Title." The Web Site, October--November. . ::: ::: {#ref-item2 .csl-entry} Doe, John. 2006c. "Title." The Web Site, October 26--November 27. . ::: ::: {#ref-item3b .csl-entry} Doe, John. 2006d. "Title." *The Newspaper*, October--November. . ::: ::: {#ref-item3 .csl-entry} Doe, John. 2006e. "Title." *The Newspaper*, October 26--November 27. . ::: ::: {#ref-item2b .csl-entry} Doe, John. 2006--2007. "Title." The Web Site, December 31--January 1. . ::: ::::::::: ``` ================================================ FILE: test/command/pandoc-citeproc-chicago-author-date.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- bibliography: command/biblio.bib link-citations: true --- Pandoc with citeproc-hs ======================= [@nonexistent] @nonexistent @item1 says blah. @item1 [p. 30] says blah. @item1 [p. 30, with suffix] says blah. @item1 [-@item2 p. 30; see also @пункт3] says blah. In a note.[^1] A citation group [see @item1 chap. 3; also @пункт3 p. 34-35]. Another one [see @item1 p. 34-35]. And another one in a note.[^2] Citation with a suffix and locator [@item1 pp. 33, 35-37, and nowhere else]. Citation with suffix only [@item1 and nowhere else]. Now some modifiers.[^3] With some markup [*see* @item1 p. **32**]. References {#references .unnumbered} ========== [^1]: @пункт3 [p. 12] and a citation without locators [@пункт3]. [^2]: Some citations [see @item1 chap. 3; @пункт3; @item2]. [^3]: Like a citation without author: [-@item1], and now Doe with a locator [-@item2 p. 44]. ^D 2> [WARNING] Citeproc: citation nonexistent not found # Pandoc with citeproc-hs ([**nonexistent?**](#ref-nonexistent)) ([**nonexistent?**](#ref-nonexistent)) Doe ([2005](#ref-item1)) says blah. Doe ([2005, 30](#ref-item1)) says blah. Doe ([2005, 30](#ref-item1), with suffix) says blah. Doe ([2005](#ref-item1); [2006, 30](#ref-item2); see also [Doe and Roe 2007](#ref-пункт3)) says blah. In a note.[^1] A citation group (see [Doe 2005, chap. 3](#ref-item1); also [Doe and Roe 2007, 34--35](#ref-пункт3)). Another one (see [Doe 2005, 34--35](#ref-item1)). And another one in a note.[^2] Citation with a suffix and locator ([Doe 2005, 33, 35--37](#ref-item1), and nowhere else). Citation with suffix only ([Doe 2005](#ref-item1) and nowhere else). Now some modifiers.[^3] With some markup (*see* [Doe 2005, 32](#ref-item1)). # References {#references .unnumbered} :::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} Doe, John. 2005. *First Book*. Cambridge University Press. ::: ::: {#ref-item2 .csl-entry} Doe, John. 2006. "Article." *Journal of Generic Studies* 6: 33--34. ::: ::: {#ref-пункт3 .csl-entry} Doe, John, and Jenny Roe. 2007. "Why Water Is Wet." In *Third Book*, edited by Sam Smith. Oxford University Press. ::: :::::: [^1]: Doe and Roe ([2007, 12](#ref-пункт3)) and a citation without locators ([Doe and Roe 2007](#ref-пункт3)). [^2]: Some citations (see [Doe 2005, chap. 3](#ref-item1); [2006](#ref-item2); [Doe and Roe 2007](#ref-пункт3)). [^3]: Like a citation without author: ([2005](#ref-item1)), and now Doe with a locator ([2006, 44](#ref-item2)). ``` ================================================ FILE: test/command/pandoc-citeproc-chicago-fullnote-bibliography.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- bibliography: command/biblio.bib csl: 'command/chicago-fullnote-bibliography.csl' link-citations: true --- Pandoc with citeproc-hs ======================= [@nonexistent] @nonexistent @item1 says blah. @item1 [p. 30] says blah. @item1 [p. 30, with suffix] says blah. @item1 [-@item2 p. 30; see also @пункт3] says blah. In a note.[^1] A citation group [see @item1 chap. 3; also @пункт3 p. 34-35]. Another one [see @item1 p. 34-35]. And another one in a note.[^2] Citation with a suffix and locator [@item1 pp. 33, 35-37, and nowhere else]. Citation with suffix only [@item1 and nowhere else]. Now some modifiers.[^3] With some markup [*see* @item1 p. **32**]. References {#references .unnumbered} ========== [^1]: @пункт3 [p. 12] and a citation without locators [@пункт3]. [^2]: Some citations [see @item1 chap. 3; @пункт3; @item2]. [^3]: Like a citation without author: [-@item1], and again [-@item1], and now Doe with a locator [-@item2 p. 44]. ^D 2> [WARNING] Citeproc: citation nonexistent not found # Pandoc with citeproc-hs [^1] [^2] John Doe[^3] says blah. Doe[^4] says blah. Doe[^5] says blah. Doe[^6] says blah. In a note.[^7] A citation group.[^8] Another one.[^9] And another one in a note.[^10] Citation with a suffix and locator.[^11] Citation with suffix only.[^12] Now some modifiers.[^13] With some markup.[^14] # References {#references .unnumbered} :::::: {#refs .references .csl-bib-body .hanging-indent entry-spacing="0"} ::: {#ref-item2 .csl-entry} Doe, John. "Article." *Journal of Generic Studies* 6 (2006): 33--34. ::: ::: {#ref-item1 .csl-entry} ---------. *First Book*. Cambridge: Cambridge University Press, 2005. ::: ::: {#ref-пункт3 .csl-entry} Doe, John, and Jenny Roe. "Why Water Is Wet." In *Third Book*, edited by Sam Smith. Oxford: Oxford University Press, 2007. ::: :::::: [^1]: [**Nonexistent?**](#ref-nonexistent) [^2]: [**Nonexistent?**](#ref-nonexistent) [^3]: [*First Book* (Cambridge: Cambridge University Press, 2005)](#ref-item1). [^4]: [30](#ref-item1). [^5]: [30](#ref-item1), with suffix. [^6]: [*First Book*](#ref-item1); ["Article," *Journal of Generic Studies* 6 (2006): 30](#ref-item2); see also [John Doe and Jenny Roe, "Why Water Is Wet," in *Third Book*, ed. Sam Smith (Oxford: Oxford University Press, 2007)](#ref-пункт3). [^7]: Doe and Roe, ["Why Water Is Wet," 12](#ref-пункт3) and a citation without locators ([Doe and Roe, "Why Water Is Wet"](#ref-пункт3)). [^8]: See [Doe, *First Book*, chap. 3](#ref-item1); also [Doe and Roe, "Why Water Is Wet," 34--35](#ref-пункт3). [^9]: See [Doe, *First Book*, 34--35](#ref-item1). [^10]: Some citations (see [Doe, chap. 3](#ref-item1); [Doe and Roe, "Why Water Is Wet"](#ref-пункт3); [Doe, "Article"](#ref-item2)). [^11]: [Doe, *First Book*, 33, 35--37](#ref-item1), and nowhere else. [^12]: [Doe, *First Book*](#ref-item1) and nowhere else. [^13]: Like a citation without author: (), and again (), and now Doe with a locator (["Article," 44](#ref-item2)). [^14]: *See* [Doe, *First Book*, 32](#ref-item1). ``` ================================================ FILE: test/command/pandoc-citeproc-ieee.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- bibliography: command/biblio.bib csl: command/ieee.csl link-citations: true --- Pandoc with citeproc-hs ======================= [@nonexistent] @nonexistent @item1 says blah. @item1 [p. 30] says blah. @item1 [p. 30, with suffix] says blah. @item1 [-@item2 p. 30; see also @пункт3] says blah. In a note.[^1] A citation group [see @item1 chap. 3; also @пункт3 p. 34-35]. Another one [see @item1 p. 34-35]. And another one in a note.[^2] Citation with a suffix and locator [@item1 pp. 33, 35-37, and nowhere else]. Citation with suffix only [@item1 and nowhere else]. Now some modifiers.[^3] With some markup [*see* @item1 p. **32**]. References {#references .unnumbered} ========== [^1]: @пункт3 [p. 12] and a citation without locators [@пункт3]. [^2]: Some citations [see @item1 chap. 3; @пункт3; @item2]. [^3]: Like a citation without author: [-@item1], and now Doe with a locator [-@item2 p. 44]. ^D 2> [WARNING] Citeproc: citation nonexistent not found # Pandoc with citeproc-hs [**nonexistent?**](#ref-nonexistent) [**nonexistent?**](#ref-nonexistent) [\[1\]](#ref-item1) says blah. [\[1, p. 30\]](#ref-item1) says blah. [\[1, p. 30\]](#ref-item1), with suffix says blah. [\[1\]](#ref-item1), [\[2, p. 30\]](#ref-item2), see also [\[3\]](#ref-пункт3) says blah. In a note.[^1] A citation group see [\[1, Ch. 3\]](#ref-item1), also [\[3, pp. 34--35\]](#ref-пункт3). Another one see [\[1, pp. 34--35\]](#ref-item1). And another one in a note.[^2] Citation with a suffix and locator [\[1, pp. 33, 35--37\]](#ref-item1), and nowhere else. Citation with suffix only [\[1\]](#ref-item1) and nowhere else. Now some modifiers.[^3] With some markup *see* [\[1, p. 32\]](#ref-item1). # References {#references .unnumbered} :::::: {#refs .references .csl-bib-body entry-spacing="0"} ::: {#ref-item1 .csl-entry} [\[1\] ]{.csl-left-margin}[J. Doe, *First book*. Cambridge: Cambridge University Press, 2005.]{.csl-right-inline} ::: ::: {#ref-item2 .csl-entry} [\[2\] ]{.csl-left-margin}[J. Doe, "Article," *Journal of Generic Studies*, vol. 6, pp. 33--34, 2006.]{.csl-right-inline} ::: ::: {#ref-пункт3 .csl-entry} [\[3\] ]{.csl-left-margin}[J. Doe and J. Roe, "Why water is wet," in *Third book*, S. Smith, Ed. Oxford: Oxford University Press, 2007.]{.csl-right-inline} ::: :::::: [^1]: [\[3, p. 12\]](#ref-пункт3) and a citation without locators [\[3\]](#ref-пункт3). [^2]: Some citations see [\[1, Ch. 3\]](#ref-item1), [\[2\]](#ref-item2), [\[3\]](#ref-пункт3). [^3]: Like a citation without author: [\[1\]](#ref-item1), and now Doe with a locator [\[2, p. 44\]](#ref-item2). ``` ================================================ FILE: test/command/pandoc-citeproc-locators-delimited.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/locators.csl references: - id: citekey title: Title type: 'article-journal' suppress-bibliography: true --- See . Standard page range[@citekey {35-89, 102}] Alphanumeric[@citekey {abcdefg1234}] Kitchen sink[@citekey, {123(4)a-8(\[a\]12.398{8})}] Empty braces inside[@citekey, {{}}] Label specified[@citekey {p. a}] Should it work outside? No. [@citekey, p. {(a)}] Empty locator [@citekey, {}] Empty locator to force suffix[@citekey {} 123-35 numbers are suffix] Suffix generally [@citekey {123-35} numbers not, but text is suffix] With preceding comma[@citekey, {p. VI}] No commas before label[@citekey, {, p. (p. is not recognised)}] Trim white space[@citekey, { p. 9 }] Without delimiters[@citekey, suffix] With rendering label[@citekey {ss IV div 4 s 128L(7)(a)(i)-(iv), 129(5), 130(b)}] The text is apparently NOT verbatim; it is lightly processed as page numbers. [@citekey {no comma, no label, no nothing}] AGLC-style page \[para\] [@citekey {584 \[78\]}] Unbalanced curly { breaks the parse[@citekey {p. suffix{suffix}suffix] Unbalanced curly } ends early[@citekey {green}suffix}suffix] ^D See . Standard page range[^1] Alphanumeric[^2] Kitchen sink[^3] Empty braces inside[^4] Label specified[^5] Should it work outside? No.[^6] Empty locator[^7] Empty locator to force suffix[^8] Suffix generally[^9] With preceding comma[^10] No commas before label[^11] Trim white space[^12] Without delimiters[^13] With rendering label[^14] The text is apparently NOT verbatim; it is lightly processed as page numbers.[^15] AGLC-style page \[para\][^16] Unbalanced curly { breaks the parse[^17] Unbalanced curly } ends early[^18] [^1]: Title {35--89, 102}. [^2]: Ibid-with-locator {abcdefg1234}. [^3]: Ibid-with-locator {123(4)a--8(\[a\]12.398{8})}. [^4]: Ibid-with-locator {{}}. [^5]: Ibid-with-locator {a}. [^6]: Subsequent, p. {(a)}. [^7]: Ibid. [^8]: Ibid 123-35 numbers are suffix. [^9]: Ibid-with-locator {123--35} numbers not, but text is suffix. [^10]: Ibid-with-locator {VI}. [^11]: Ibid-with-locator {, p. (p. is not recognised)}. [^12]: Ibid-with-locator {9}. [^13]: Subsequent, suffix. [^14]: Ibid-with-locator ss {IV div 4 s 128L(7)(a)(i)--(iv), 129(5), 130(b)}. [^15]: Ibid-with-locator {no comma, no label, no nothing}. [^16]: Ibid-with-locator {584 \[78\]}. [^17]: Subsequent {p. suffix{suffix}suffix. [^18]: Ibid-with-locator {green}suffix}suffix. ``` ================================================ FILE: test/command/pandoc-citeproc-locators-integrated.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: command/locators.csl references: - id: citekey title: Title type: 'article-journal' suppress-bibliography: true --- See . [@citekey, 89, and suffix] [@citekey, 89, perfect Ibid with suffix] [@citekey, 123-79, and suffix] [@citekey, xi, will be entirely suffix] [@citekey, p. xi, gives you a (page) locator xi] [@citekey, pp. VII, 89, gives you a (pages) locator VII, 89] [@citekey, p. VI, VII, VIII-IX, explicit romans] [@citekey \[89\]] [@citekey, p. \[89\]] [@citekey and nothing else] [@citekey, 123(4)\[5\]6, and suffix] [@citekey, 3(a), 4.4.8, \[7.6\], 7A(2)(a)(i)-(iv)] [@citekey, 4B.2a.i(3.4), and suffix] [@citekey, IV.2A, and suffix] [@citekey, \[28\], and suffix] [@citekey, \[39-52\], and suffix] [@citekey, \[39\]-\[52\], and suffix] [@citekey, s 123(4)(a)(iv), and suffix] [@citekey, ss 123(4)-(6), and suffix] [@citekey, \[13\], and suffix] [@citekey, p.3, and suffix] [@citekey, (13 entirely suffix] [@citekey, p.a entirely suffix] [@citekey, s (a) entirely suffix] ^D See . [^1] [^2] [^3] [^4] [^5] [^6] [^7] [^8] [^9] [^10] [^11] [^12] [^13] [^14] [^15] [^16] [^17] [^18] [^19] [^20] [^21] [^22] [^23] [^24] [^1]: Title {89}, and suffix. [^2]: Ibid, perfect Ibid with suffix. [^3]: Ibid-with-locator {123--79}, and suffix. [^4]: Subsequent, xi, will be entirely suffix. [^5]: Ibid-with-locator {xi}, gives you a (page) locator xi. [^6]: Ibid-with-locator {VII, 89}, gives you a (pages) locator VII, 89. [^7]: Ibid-with-locator {VI, VII, VIII--IX}, explicit romans. [^8]: Ibid-with-locator {\[89\]}. [^9]: Ibid. [^10]: Subsequent and nothing else. [^11]: Ibid-with-locator {123(4)\[5\]6}, and suffix. [^12]: Ibid-with-locator {3(a), 4.4.8, \[7.6\], 7A(2)(a)(i)--(iv)}. [^13]: Ibid-with-locator {4B.2a.i(3.4)}, and suffix. [^14]: Ibid-with-locator {IV.2A}, and suffix. [^15]: Ibid-with-locator {\[28\]}, and suffix. [^16]: Ibid-with-locator {\[39--52\]}, and suffix. [^17]: Ibid-with-locator {\[39\]--\[52\]}, and suffix. [^18]: Ibid-with-locator s {123(4)(a)(iv)}, and suffix. [^19]: Ibid-with-locator ss {123(4)--(6)}, and suffix. [^20]: Ibid-with-locator {\[13\]}, and suffix. [^21]: Ibid-with-locator {3}, and suffix. [^22]: Subsequent, (13 entirely suffix. [^23]: Ibid, p.a entirely suffix. [^24]: Ibid, s (a) entirely suffix. ``` ================================================ FILE: test/command/pandoc-citeproc-move-period-inside-quote.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-fullnote-bibliography.csl' references: - DOI: 10.1038/171737a0 URL: 'http://www.nature.com/nature/journal/v171/n4356/abs/171737a0.html' accessed: day: 17 month: 6 year: 2008 author: - family: Watson given: J. D. - family: Crick given: F. H. C. container-title: Nature custom4: custom4 id: WatsonCrick1953 issue: 4356 issued: date-parts: - - 1953 - 4 - 25 language: 'en-US' note: this is a note original-date: year: 1951 page: '737-738' title: 'Molecular structure of nucleic acids: a structure for deoxyribose nucleic acid' title-short: Molecular structure of nucleic acids type: 'article-journal' volume: 171 suppress-bibliography: true --- Here is a "test citation" [@WatsonCrick1953]. Here is a test citation [@WatsonCrick1953]. ^D Here is a "test citation."[^1] Here is a test citation.[^2] [^1]: J. D. Watson and F. H. C. Crick, "Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid," *Nature* 171, no. 4356 (April 25, 1953): 737--38, . [^2]: Watson and Crick. ``` ================================================ FILE: test/command/pandoc-citeproc-no-author.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - container-title: Magazine id: item1 issued: year: 2012 title: Title A type: 'article-magazine' - container-title: Magazine id: item2 issued: year: 2012 title: Title B type: 'article-magazine' - container-title: Magazine id: item3 issued: year: 2012 title: Title C type: 'article-magazine' - container-title: Magazine id: item4 issued: year: 2012 title: Title D type: 'article-magazine' - container-title: Newspaper id: item5 issued: year: 2012 title: Title E type: 'article-magazine' - container-title: Newspaper id: item6 issued: year: 2012 title: Title F type: 'article-magazine' --- @item1 [p. 3], @item2, @item3, @item4, @item5, @item6 ^D *Magazine* (2012a, 3), *Magazine* (2012b), *Magazine* (2012c), *Magazine* (2012d), *Newspaper* (2012a), *Newspaper* (2012b) ::::::::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} *Magazine*. 2012a. "Title A." ::: ::: {#ref-item2 .csl-entry} *Magazine*. 2012b. "Title B." ::: ::: {#ref-item3 .csl-entry} *Magazine*. 2012c. "Title C." ::: ::: {#ref-item4 .csl-entry} *Magazine*. 2012d. "Title D." ::: ::: {#ref-item5 .csl-entry} *Newspaper*. 2012a. "Title E." ::: ::: {#ref-item6 .csl-entry} *Newspaper*. 2012b. "Title F." ::: ::::::::: ``` ================================================ FILE: test/command/pandoc-citeproc-number-of-volumes.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- references: - author: family: Author given: - Al id: item1 issued: year: 2013 language: 'en-US' number-of-volumes: 2 publisher: Publisher publisher-place: Location title: Title type: book --- @item1 ^D Author (2013) :::: {#refs .references .csl-bib-body .hanging-indent} ::: {#ref-item1 .csl-entry} Author, Al. 2013. *Title*. 2 vols. Publisher. ::: :::: ``` ================================================ FILE: test/command/pandoc-citeproc-page-range.md ================================================ ``` % pandoc --citeproc -t markdown-citations --- csl: 'command/chicago-fullnote-bibliography.csl' references: - URL: 'https://johnmacfarlane.net/vagueness.pdf' id: test1 - URL: 'https://pandoc.org' id: test2 - URL: 'https://johnmacfarlane.net' id: test3 suppress-bibliography: true --- Test 1 [@test1, pp. 93--101]. Test 2 [@test2, pp. 93--101]. Test 3 [@test3, pp. 93-101]. ^D Test 1.[^1] Test 2.[^2] Test 3.[^3] [^1]: N.d., 93--101, . [^2]: N.d., 93--101, . [^3]: N.d., 93--101, . ``` ================================================ FILE: test/command/parse-raw.md ================================================ ``` % pandoc -f latex+raw_tex -t markdown \emph{Hi \foo{there}} ^D *Hi `\foo{there}`{=latex}* ``` ``` % pandoc -f latex -t markdown \emph{Hi \foo{there}} ^D *Hi* ``` ``` % pandoc -f html+raw_html -t markdown Hi there ^D *Hi ``{=html}there``{=html}* ``` ``` % pandoc -f html -t markdown Hi there ^D *Hi there* ``` ================================================ FILE: test/command/pdfstandard.md ================================================ 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} ``` ================================================ FILE: test/command/refs.md ================================================ ``` % pandoc -f latex -t native Figure \ref{fig:1} ^D [ Para [ Str "Figure" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "fig:1" ) ] ) [ Str "[fig:1]" ] ( "#fig:1" , "" ) ] ] ``` ``` % pandoc -f latex -t native Figure \cref{fig:1} ^D [ Para [ Str "Figure" , Space , Link ( "" , [] , [ ( "reference-type" , "ref+label" ) , ( "reference" , "fig:1" ) ] ) [ Str "[fig:1]" ] ( "#fig:1" , "" ) ] ] ``` ``` % pandoc -f latex -t native Figure \vref{fig:1} ^D [ Para [ Str "Figure" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "fig:1" ) ] ) [ Str "[fig:1]" ] ( "#fig:1" , "" ) ] ] ``` ``` % pandoc -f latex -t native \autoref{fig:flowchart} ^D [ Para [ Link ( "" , [] , [ ( "reference-type" , "ref+label" ) , ( "reference" , "fig:flowchart" ) ] ) [ Str "[fig:flowchart]" ] ( "#fig:flowchart" , "" ) ] ] ``` ``` % pandoc -f latex -t native Accuracy~\eqref{eq:Accuracy} is the proportion, measuring true results among all results. \begin{equation} \label{eq:Accuracy} Accuracy = \frac{t_p + t_n}{t_p + f_p + f_n + t_n} \end{equation} ^D [ Para [ Str "Accuracy\160" , Link ( "" , [] , [ ( "reference-type" , "eqref" ) , ( "reference" , "eq:Accuracy" ) ] ) [ Str "[eq:Accuracy]" ] ( "#eq:Accuracy" , "" ) , Space , Str "is" , Space , Str "the" , Space , Str "proportion," , Space , Str "measuring" , Space , Str "true" , Space , Str "results" , Space , Str "among" , Space , Str "all" , Space , Str "results." ] , Para [ Math DisplayMath "\\begin{equation}\n \\label{eq:Accuracy}\n Accuracy = \\frac{t_p + t_n}{t_p + f_p + f_n + t_n}\n\\end{equation}" ] ] ``` ``` % pandoc -f latex -t native \begin{figure} \includegraphics{command/SVG_logo.svg} \caption{Logo} \label{fig:Logo} \end{figure} Figure \ref{fig:Logo} illustrated the SVG logo ^D [ Figure ( "fig:Logo" , [] , [] ) (Caption Nothing [ Plain [ Str "Logo" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "command/SVG_logo.svg" , "" ) ] ] , Para [ Str "Figure" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "fig:Logo" ) ] ) [ Str "1" ] ( "#fig:Logo" , "" ) , Space , Str "illustrated" , Space , Str "the" , Space , Str "SVG" , Space , Str "logo" ] ] ``` ``` % pandoc -f latex -t native \chapter{One} \begin{figure} \includegraphics{command/SVG_logo.svg} \caption{Logo} \label{fig:Logo} \end{figure} \begin{figure} \includegraphics{command/SVG_logo2.svg} \caption{Logo2} \label{fig:Logo2} \end{figure} \chapter{Two} \section{Subone} \begin{figure} \includegraphics{command/SVG_logo3.svg} \caption{Logo3} \label{fig:Logo3} \end{figure} Figure \ref{fig:Logo} illustrated the SVG logo Figure \ref{fig:Logo2} illustrated the SVG logo Figure \ref{fig:Logo3} illustrated the SVG logo ^D [ Header 1 ( "one" , [] , [] ) [ Str "One" ] , Figure ( "fig:Logo" , [] , [] ) (Caption Nothing [ Plain [ Str "Logo" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "command/SVG_logo.svg" , "" ) ] ] , Figure ( "fig:Logo2" , [] , [] ) (Caption Nothing [ Plain [ Str "Logo2" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "command/SVG_logo2.svg" , "" ) ] ] , Header 1 ( "two" , [] , [] ) [ Str "Two" ] , Header 2 ( "subone" , [] , [] ) [ Str "Subone" ] , Figure ( "fig:Logo3" , [] , [] ) (Caption Nothing [ Plain [ Str "Logo3" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "command/SVG_logo3.svg" , "" ) ] ] , Para [ Str "Figure" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "fig:Logo" ) ] ) [ Str "1.1" ] ( "#fig:Logo" , "" ) , Space , Str "illustrated" , Space , Str "the" , Space , Str "SVG" , Space , Str "logo" ] , Para [ Str "Figure" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "fig:Logo2" ) ] ) [ Str "1.2" ] ( "#fig:Logo2" , "" ) , Space , Str "illustrated" , Space , Str "the" , Space , Str "SVG" , Space , Str "logo" ] , Para [ Str "Figure" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "fig:Logo3" ) ] ) [ Str "2.1" ] ( "#fig:Logo3" , "" ) , Space , Str "illustrated" , Space , Str "the" , Space , Str "SVG" , Space , Str "logo" ] ] ``` ``` % pandoc -f latex -t native \label{section} Section \ref{section} ^D [ Para [ Span ( "section" , [] , [ ( "label" , "section" ) ] ) [] , Space , Str "Section" , Space , Link ( "" , [] , [ ( "reference-type" , "ref" ) , ( "reference" , "section" ) ] ) [ Str "[section]" ] ( "#section" , "" ) ] ] ``` ================================================ FILE: test/command/reset-citation-positions.md ================================================ ``` % 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. ``` ================================================ FILE: test/command/rst-links.md ================================================ ``` % pandoc -f rst `*ab*`_ .. _`*ab*`: foo ^D

    *ab*

    ``` ``` % pandoc -f rst `A B c`_ .. _A B C: foo ^D

    A B c

    ``` ================================================ FILE: test/command/rst-writer-gridtable-if-rowspans.md ================================================ ``` % pandoc -f native -t rst [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ]) ] ^D +---+---+---+ | 1 | 2 | 3 | +---+ +---+ | 1 | | 3 | +---+---+---+ | 1 | 2 | 3 | +---+---+---+ +---+---+---+ | 1 | 2 | 3 | +---+ +---+ | 1 | | 3 | +===+===+===+ | 1 | 2 | 3 | +---+---+---+ +===+===+===+ | 1 | 2 | 3 | +---+ +---+ | 1 | | 3 | +---+---+---+ | 1 | 2 | 3 | +===+===+===+ ``` ================================================ FILE: test/command/rst_block_subst.md ================================================ ``` % pandoc -f rst -t native |figure05| .. |figure05| figure:: img/dummy.png capt ^D [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "capt" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "capt" ] ( "img/dummy.png" , "" ) ] ] ] ``` ================================================ FILE: test/command/sage-harvard.csl ================================================ ================================================ FILE: test/command/science.csl ================================================ ================================================ FILE: test/command/section-divs.md ================================================ ``` % pandoc --section-divs ::: {#hi .section .level1} # Hi ::: {#there .section .level2} ## there ::: ::: ::: {#ok .section .level1} Ok == ::: ^D

    Hi

    there

    Ok

    ``` ================================================ FILE: test/command/setext-fenced-div.md ================================================ ``` % pandoc -t native ::: {.cell} --- ::: ^D [ Div ( "" , [ "cell" ] , [] ) [ HorizontalRule ] ] ``` ================================================ FILE: test/command/shift-heading-level-by.md ================================================ ``` % pandoc --shift-heading-level-by 1 -t native -s --- title: My title ... # First heading ## Second ^D Pandoc Meta { unMeta = fromList [ ( "title" , MetaInlines [ Str "My" , Space , Str "title" ] ) ] } [ Header 2 ( "first-heading" , [] , [] ) [ Str "First" , Space , Str "heading" ] , Header 3 ( "second" , [] , [] ) [ Str "Second" ] ] ``` ``` % pandoc --shift-heading-level-by -1 -t native -s --- title: Old title ... # First heading ## Second # Another top-level heading ^D Pandoc Meta { unMeta = fromList [ ( "title" , MetaInlines [ Str "First" , Space , Str "heading" ] ) ] } [ Header 1 ( "second" , [] , [] ) [ Str "Second" ] , Para [ Str "Another" , Space , Str "top-level" , Space , Str "heading" ] ] ``` ================================================ FILE: test/command/short-caption.md ================================================ ``` % pandoc -f latex -t native \begin{table} \caption[short caption]{long caption} \begin{tabular}{ll} hi & hi \\ \end{tabular} \end{table} ^D [ Table ( "" , [] , [] ) (Caption (Just [ Str "short" , Space , Str "caption" ]) [ Plain [ Str "long" , Space , Str "caption" ] ]) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "hi" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "hi" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/sloppypar.md ================================================ ``` % pandoc -f latex+raw_tex -t native \begin{sloppypar} Sequi id qui facere et incidunt ut. Et fuga ut voluptate enim qui. Odit unde magni ipsam dicta modi. Modi soluta velit est aut aut possimus. Qui et temporibus explicabo. Esse ab ut quidem. Vel qui perspiciatis quae odio consectetur alias non sed. Quo consectetur libero omnis quos eius ad vel. \end{sloppypar} ^D [ Para [ Str "Sequi" , Space , Str "id" , Space , Str "qui" , Space , Str "facere" , Space , Str "et" , Space , Str "incidunt" , Space , Str "ut." , Space , Str "Et" , Space , Str "fuga" , Space , Str "ut" , Space , Str "voluptate" , Space , Str "enim" , Space , Str "qui." , Space , Str "Odit" , Space , Str "unde" , Space , Str "magni" , Space , Str "ipsam" , Space , Str "dicta" , Space , Str "modi." , Space , Str "Modi" , Space , Str "soluta" , Space , Str "velit" , Space , Str "est" , Space , Str "aut" , Space , Str "aut" , Space , Str "possimus." ] , Para [ Str "Qui" , Space , Str "et" , Space , Str "temporibus" , Space , Str "explicabo." , Space , Str "Esse" , Space , Str "ab" , Space , Str "ut" , Space , Str "quidem." , Space , Str "Vel" , Space , Str "qui" , Space , Str "perspiciatis" , Space , Str "quae" , Space , Str "odio" , Space , Str "consectetur" , Space , Str "alias" , Space , Str "non" , Space , Str "sed." , Space , Str "Quo" , Space , Str "consectetur" , Space , Str "libero" , Space , Str "omnis" , Space , Str "quos" , Space , Str "eius" , Space , Str "ad" , Space , Str "vel." ] ] ``` ``` % pandoc -f latex -t native \begin{sloppypar} Sequi id qui facere et incidunt ut. Et fuga ut voluptate enim qui. Odit unde magni ipsam dicta modi. Modi soluta velit est aut aut possimus. Qui et temporibus explicabo. Esse ab ut quidem. Vel qui perspiciatis quae odio consectetur alias non sed. Quo consectetur libero omnis quos eius ad vel. \end{sloppypar} ^D [ Para [ Str "Sequi" , Space , Str "id" , Space , Str "qui" , Space , Str "facere" , Space , Str "et" , Space , Str "incidunt" , Space , Str "ut." , Space , Str "Et" , Space , Str "fuga" , Space , Str "ut" , Space , Str "voluptate" , Space , Str "enim" , Space , Str "qui." , Space , Str "Odit" , Space , Str "unde" , Space , Str "magni" , Space , Str "ipsam" , Space , Str "dicta" , Space , Str "modi." , Space , Str "Modi" , Space , Str "soluta" , Space , Str "velit" , Space , Str "est" , Space , Str "aut" , Space , Str "aut" , Space , Str "possimus." ] , Para [ Str "Qui" , Space , Str "et" , Space , Str "temporibus" , Space , Str "explicabo." , Space , Str "Esse" , Space , Str "ab" , Space , Str "ut" , Space , Str "quidem." , Space , Str "Vel" , Space , Str "qui" , Space , Str "perspiciatis" , Space , Str "quae" , Space , Str "odio" , Space , Str "consectetur" , Space , Str "alias" , Space , Str "non" , Space , Str "sed." , Space , Str "Quo" , Space , Str "consectetur" , Space , Str "libero" , Space , Str "omnis" , Space , Str "quos" , Space , Str "eius" , Space , Str "ad" , Space , Str "vel." ] ] ``` ================================================ FILE: test/command/smart.md ================================================ ``` % pandoc -f markdown+smart -t markdown-smart "hi"...dog's breath---cat 5--6 ^D “hi”…dog’s breath—cat 5–6 ``` ``` % pandoc -f markdown+smart -t markdown+smart "hi"...dog's breath---cat 5--6 ^D "hi"...dog's breath---cat 5--6 ``` When we render literal quotes without smart, we need to escape: ``` % pandoc -f markdown-smart \ -t markdown+smart "hi"...dog's breath---cat 5--6 ^D \"hi\"\...dog\'s breath\-\--cat 5\--6 ``` ``` % pandoc -f markdown+smart -t rst-smart "hi"...dog's breath---cat 5--6 ^D “hi”…dog’s breath—cat 5–6 ``` ``` % pandoc -f markdown+smart -t rst+smart "hi"...dog's breath---cat 5--6 ^D "hi"...dog's breath---cat 5--6 ``` ``` % pandoc -f markdown-smart -t rst+smart "hi"...dog's breath---cat 5--6 ^D \"hi\"\...dog\'s breath\-\--cat 5\--6 ``` ================================================ FILE: test/command/style399.csl ================================================ ================================================ FILE: test/command/sub-file-chapter-1.tex ================================================ \documentclass[main.tex]{subfiles} \begin{document} \section{Chapter 1} This is Chapter 1, provided in a sub file. \end{document} ================================================ FILE: test/command/sub-file-chapter-2.tex ================================================ \documentclass[main.tex]{subfiles} \begin{document} \section{Chapter 2} This is Chapter 2, provided in a second sub file. \end{document} ================================================ FILE: test/command/svg.md ================================================ ``` % pandoc -f latex -t icml \includegraphics{command/corrupt.svg} ^D 2> [WARNING] Could not determine image size for command/corrupt.svg: could not determine image type $ID/Embedded ``` ``` % pandoc -f latex -t icml \includegraphics{command/SVG_logo.svg} ^D $ID/Embedded ``` ``` % pandoc -f latex -t icml \includegraphics{command/SVG_logo-without-xml-declaration.svg} ^D $ID/Embedded ``` ``` % pandoc -f latex -t icml \includegraphics{command/inkscape-cube.svg} ^D $ID/Embedded ``` ================================================ FILE: test/command/table-with-cell-align.md ================================================ ``` % pandoc -f docbook -t native --quiet 1 2 3 4 ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f native -t opendocument --quiet [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Para [Str "1"]] ,Cell ("",[],[]) AlignLeft (RowSpan 1) (ColSpan 1) [Para [Str "2"]] ,Cell ("",[],[]) AlignRight (RowSpan 1) (ColSpan 1) [Para [Str "3"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "4"]]]])] (TableFoot ("",[],[]) [])] ^D 1 2 3 4 ``` ================================================ FILE: test/command/table-with-column-span.md ================================================ ``` % pandoc -f docbook -t native --quiet Octet no. 1 Octet no. 2 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 Code A Code B ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) , ( AlignDefault , ColWidth 6.25e-2 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 8) [ Para [ Strong [ Str "Octet" , Space , Str "no." , Space , Str "1" ] ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 8) [ Para [ Strong [ Str "Octet" , Space , Str "no." , Space , Str "2" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "16" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "15" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "14" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "13" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "11" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "10" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "9" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "8" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 8) [ Para [ Str "Code" , Space , Str "A" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 8) [ Para [ Str "Code" , Space , Str "B" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f native -t opendocument --quiet [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2) ,(AlignDefault,ColWidth 6.25e-2)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 8) [Para [Strong [Str "Octet",Space,Str "no.",Space,Str "1"]]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 8) [Para [Strong [Str "Octet",Space,Str "no.",Space,Str "2"]]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "16"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "15"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "14"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "13"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "11"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "10"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "9"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "8"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "7"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "6"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "5"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "4"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "3"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "2"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "1"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 8) [Para [Str "Code",Space,Str "A"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 8) [Para [Str "Code",Space,Str "B"]]]])] (TableFoot ("",[],[]) [])] ^D Octet no. 1 Octet no. 2 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 Code A Code B ``` ================================================ FILE: test/command/tabularx.md ================================================ ``` % pandoc -f latex -t native --quiet \begin{tabularx}{\linewidth}{|c|c|c|} \hline Column Heading 1 & Column Heading 2 & Column Heading 3 \\ \hline Cell 1.1 & Cell 1.2 & Cell 1.3 \\ \hline Cell 2.1 & Cell 2.2 & Cell 2.3 \\ \hline Cell 3.1 & Cell 3.2 & Cell 3.3 \\ \hline \end{tabularx} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f latex -t native --quiet \begin{tabularx}{\linewidth}{|X|c|p{0.25\linewidth}|} \hline Column Heading 1 & Column Heading 2 & Column Heading 3 \\ \hline Cell 1.1 & Cell 1.2 & Cell 1.3 \\ \hline Cell 2.1 & Cell 2.2 & Cell 2.3 \\ \hline Cell 3.1 & Cell 3.2 & Cell 3.3 \\ \hline \end{tabularx} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignLeft , ColWidth 0.25 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ``` % pandoc -f latex -t native --quiet \begin{tabularx}{\linewidth}{|b{0.25\linewidth}|c|m{0.25\linewidth}|} \hline Column Heading 1 & Column Heading 2 & Column Heading 3 \\ \hline Cell 1.1 & Cell 1.2 & Cell 1.3 \\ \hline Cell 2.1 & Cell 2.2 & Cell 2.3 \\ \hline Cell 3.1 & Cell 3.2 & Cell 3.3 \\ \hline \end{tabularx} ^D [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidth 0.25 ) , ( AlignCenter , ColWidthDefault ) , ( AlignLeft , ColWidth 0.25 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Heading" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1.3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2.3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3.3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ``` ================================================ FILE: test/command/tasklist.md ================================================ tests adapted from ``` % pandoc - [ ] foo - [x] bar ^D
    ``` ``` % pandoc - [x] foo - [ ] bar - [x] baz - [ ] bim ^D
    ``` custom html task list test: ``` % pandoc - [ ] unchecked - plain item - [x] checked paragraph 1. [ ] ordered unchecked 2. [] plain item 3. [x] ordered checked paragraph - [ ] list item with a second paragraph - [x] checked ^D
    • plain item

    paragraph

    1. [] plain item

    paragraph

    • second paragraph

    ``` latex task list test: ``` % pandoc -t latex - [ ] foo bar baz - [x] ok ^D \begin{itemize} \item[$\square$] foo bar baz \item[$\boxtimes$] ok \end{itemize} ``` round trip: ``` % pandoc -f markdown -t markdown - [ ] foo - [x] bar ^D - [ ] foo - [x] bar ``` ================================================ FILE: test/command/tex-group.md ================================================ ``` % pandoc -f latex -t html \newenvironment{foo}% {\emph\bgroup}% {\egroup} \begin{foo} hi \end{foo} ^D

    hi

    ``` ================================================ FILE: test/command/three.txt ================================================ 1st line. 2nd line. 3rd line. ================================================ FILE: test/command/toc.md ================================================ ``` % pandoc -s --toc -t markdown # A ## b # B ## b ::: interior # C ## cc # D ::: ::: blue # E ## e ::: ^D - [A](#a){#toc-a} - [b](#b){#toc-b} - [B](#b-1){#toc-b-1} - [b](#b-2){#toc-b-2} - [E](#e){#toc-e} - [e](#e-1){#toc-e-1} # A ## b # B ## b ::: interior # C ## cc # D ::: ::: blue # E ## e ::: ``` ================================================ FILE: test/command/translations.md ================================================ ``` % pandoc -f latex -t native -M lang=en \figurename\ 2 ^D [ Para [ Str "Figure\160\&2" ] ] ``` ``` % pandoc -f latex -t native -M lang=de-DE \figurename\ 2 ^D [ Para [ Str "Abbildung\160\&2" ] ] ``` ``` % pandoc -f latex -t native -M lang=en \setmainlanguage{german} \figurename 2 ^D [ Para [ Str "Abbildung2" ] ] ``` ``` % pandoc -f latex -t native -M lang=sr-Latn \figurename~2 \figurename. ^D [ Para [ Str "Slika\160\&2" , SoftBreak , Str "Slika." ] ] ``` ================================================ FILE: test/command/typst-hs-80.md ================================================ ``` % pandoc -f typst -t native -s #set document( title: [My Title], ) #title() ^D Pandoc Meta { unMeta = fromList [ ( "title" , MetaInlines [ Str "My" , Space , Str "Title" ] ) ] } [] ``` ``` % pandoc -f typst -t native -s #set document( title: [ignored], ) #title[My Title] ^D Pandoc Meta { unMeta = fromList [ ( "title" , MetaInlines [ Str "My" , Space , Str "Title" ] ) ] } [] ``` ``` % pandoc -f typst -t native -s #title[My Title] ^D Pandoc Meta { unMeta = fromList [ ( "title" , MetaInlines [ Str "My" , Space , Str "Title" ] ) ] } [] ``` ================================================ FILE: test/command/typst-image-alt.md ================================================ ``` % pandoc -f markdown-implicit_figures -t typst ![Alt text from inlines](image.png) ^D #box(image("image.png", alt: "Alt text from inlines")) ``` ``` % pandoc -f markdown-implicit_figures -t typst ![](image.png){alt="Explicit alt attribute"} ^D #box(image("image.png", alt: "Explicit alt attribute")) ``` ``` % pandoc -f markdown-implicit_figures -t typst ![Inlines ignored](image.png){alt="Explicit wins"} ^D #box(image("image.png", alt: "Explicit wins")) ``` ``` % pandoc -f markdown-implicit_figures -t typst ![](image.png) ^D #box(image("image.png")) ``` ``` % pandoc -f markdown-implicit_figures -t typst ![Inlines ignored for decorative](image.png){alt=""} ^D #box(image("image.png")) ``` ``` % pandoc -t typst ![Caption text](image.png){alt="Alt text describing the image"} ^D #figure(image("image.png", alt: "Alt text describing the image"), caption: [ Caption text ] ) ``` ``` % pandoc -t typst ![Caption only](image.png) ^D #figure(image("image.png", alt: "Caption only"), caption: [ Caption only ] ) ``` ``` % pandoc -f markdown -t typst ![Caption](test.png){alt="A \"quoted\" phrase and C:\\path\\file"} ^D #figure(image("test.png", alt: "A \"quoted\" phrase and C:\\path\\file"), caption: [ Caption ] ) ``` ``` % pandoc -f html -t typst A small red dot ^D #box(image(bytes((137,80,78,71,13,10,26,10)), alt: "A small red dot")) ``` ================================================ FILE: test/command/typst-images.md ================================================ ``` % pandoc -t typst ![dot](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAC4jAAAuIwF4pT92AAAASUlEQVQYGa3OWwoAIAgEQNf739k0EjVCfxKCHtMqiEh0jcWjOKBAkQjPe7MFdun/IbRdDNb05nvolzWzEx0DdozK96W1PzjNHxcgphkBs9CoHwAAAABJRU5ErkJggg==){width=1in} ^D #figure(image(bytes((137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,10,0,0,0,10,8,6,0,0,0,141,50,207,189,0,0,0,9,112,72,89,115,0,0,46,35,0,0,46,35,1,120,165,63,118,0,0,0,73,73,68,65,84,24,25,173,206,91,10,0,32,8,4,64,215,251,223,217,52,18,53,66,127,18,130,30,211,42,136,72,116,141,197,163,56,160,64,145,8,207,123,179,5,118,233,255,33,180,93,12,214,244,230,123,232,151,53,179,19,29,3,118,140,202,247,165,181,63,56,205,31,23,32,166,25,1,179,208,168,31,0,0,0,0,73,69,78,68,174,66,96,130)), width: 1in, alt: "dot"), caption: [ dot ] ) ``` ``` % pandoc -t typst ![dot](dot.png){width="1in"} ^D #figure(image("dot.png", width: 1in, alt: "dot"), caption: [ dot ] ) ``` ``` % pandoc -t typst ![chicken](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%20aria-label%3D%22Chicken%22%3E%20%3Cpath%20d%3D%22M76.5%2033.3l-8.3-6.6%204-5c-1.3-2-1.3-4.7.1-6.6%203.4-3.8%208.7-2%2010.2%202.2%204.5.7%207.5%205.4%204.4%209.4a5.7%205.7%200%2001-6.4%201.6zM73%2026.2l3%202.4%203.8-4.7c1.3%201%203.5%202%204.5.7%202.1-2.9-1.5-5.4-4.7-3.5%201.2-4.6-2.7-6.2-4.5-3.9-.7%202.2.2%203%201.7%204.2z%22%2F%3E%20%3Cpath%20d%3D%22M54%2072.3a19.1%2019.1%200%2001-19.2-17.8%2021.4%2021.4%200%20014.7-15.2c3.5-4.3%208.4-7.1%2013.7-8l3.1-.1c8.2-.9%2012.9-5.5%2013-5.6l1-1%208.2%206.5-1%201.3c-3.3%204-2.8%2011.3-2.5%2014.2a21.5%2021.5%200%2001-4.4%2017.5%2021.2%2021.2%200%2001-16.5%208.2zM70.6%2029a27%2027%200%2001-14%205.5l-3%20.2a18%2018%200%2000-11.3%206.7%2018%2018%200%2000-4%2012.8%2016%2016%200%200015.9%2014.7c5.4%200%2010.4-2.5%2014-6.9A18%2018%200%200072%2049.4c0-.8-.2-1.6-.3-2.2-.5-4-.7-10.7%202.1-15.5z%22%2F%3E%20%3Cpath%20d%3D%22M46%2087C22.6%2087%20.5%2087%20.5%2066.8c0-19%2020.4-34.6%2045.5-34.6%201.2%200%202.5%200%203.7.2%2010.1.6%209.7-.1.5%203.2a18.3%2018.3%200%2000-12%2018.6%2016%2016%200%200015.9%2014.7c5.4%200%2010.4-2.5%2014-6.9A18%2018%200%200072%2049.4c-.6-4-.4-7.4-.2-11.3a35.8%2035.8%200%200119.5%2026A11.7%2011.7%200%200187.8%2087H46zm-2.8-51.3c-22%201-39.4%2014.6-39.4%2031%200%2015.8%2016%2016.9%2042.2%2016.9h41.8c4.6%200%208.4-3.8%208.4-8.4-.4-4.8-3.2-7.7-8.1-8.4-.3-10.4-7.4-18.4-13.3-22.7.1%202%20.5%204.3.7%206.1%200%205-1.6%2010-4.8%2014A21.2%2021.2%200%200154%2072.3a19.1%2019.1%200%2001-19.3-17.8%2021.4%2021.4%200%20018.4-18.8z%22%2F%3E%20%3C%2Fsvg%3E) ^D #figure(image(bytes(" "), alt: "chicken"), caption: [ chicken ] ) ``` ================================================ FILE: test/command/typst-property-output.md ================================================ ``` % pandoc -f html -t typst foo ^D #text(fill: orange)[foo] ``` ``` % pandoc -f html -t typst foo ^D foo ``` ``` % pandoc -f html -t typst
    foo
    ^D #block(inset: 10pt)[ foo ] ``` ``` % pandoc -f html -t typst
    foo
    ^D #block[ #set text(fill: purple); foo ] ``` ``` % pandoc -f html -t typst
    foo
    ^D #block[ foo ] ``` ``` % pandoc -f html -t typst
    AB
    ^D #figure( align(center)[#table( columns: 2, align: (auto,auto,), fill: blue, [A], [B], )] , kind: table ) ``` ``` % pandoc -f html -t typst
    AB
    ^D #figure( align(center)[#set text(fill: orange); #table( columns: 2, align: (auto,auto,), [A], [B], )] , kind: table ) ``` ``` % pandoc -f html -t typst
    AB
    ^D #figure( align(center)[#table( columns: 2, align: (auto,auto,), [A], table.cell(fill: green)[B], )] , kind: table ) ``` ``` % pandoc -f html -t typst
    AB
    ^D #figure( align(center)[#table( columns: 2, align: (auto,auto,), [A], [#set text(fill: fuchsia); B], )] , kind: table ) ``` ``` % pandoc -f html -t typst
    AB
    ^D #figure( align(center)[#table( columns: 2, align: (auto,center,), [A], table.cell(align: center)[#set text(fill: maroon); B], )] , kind: table ) ``` ``` % pandoc -f html -t typst
    AB
    ^D #figure( align(center)[#table( columns: 2, align: (auto,center,), [A], table.cell(align: horizon + center)[B], )] , kind: table ) ``` ``` % pandoc -f html -t typst
    AB
    ^D #figure( align(center)[#table( columns: 2, align: (auto,auto,), [A], [B], )] , kind: table ) ``` ``` % pandoc -f html -t typst

    Paragraph before.

    A B C

    Paragraph after.

    ^D Paragraph before. #{set text(size: 3em); table( columns: 3, align: (auto,auto,auto,), [A], [B], [C], )} Paragraph after. ``` ``` % pandoc -f html -t typst

    Paragraph before.

    A B C

    Paragraph after.

    ^D Paragraph before. #figure( align(center)[#set text(size: 3em); #table( columns: 3, align: (auto,auto,auto,), [A], [B], [C], )] , kind: table ) Paragraph after. ``` ``` % pandoc -f html -t typst

    Paragraph before.

    A B C

    Paragraph after.

    ^D Paragraph before. #table( columns: 3, align: (auto,auto,auto,), [A], [B], [C], ) Paragraph after. ``` ================================================ FILE: test/command/unicode-collation.md ================================================ ``` % pandoc --citeproc -t plain --- lang: en-US csl: command/apa.csl references: - id: a1 type: book author: - family: Ubina given: A. John issued: 1985 - id: a2 type: book author: - family: Über given: Aglaia issued: 1996 - id: a3 type: book author: - family: Oñate given: José issued: 1985 - id: a4 type: book author: - family: Onush given: Frank issued: 2002 - id: a5 type: book author: - family: O'Neil given: Timothy issued: 2010 --- [@a1;@a2;@a3;@a4;@a5] ^D (O’Neil, 2010; Oñate, 1985; Onush, 2002; Über, 1996; Ubina, 1985) O’Neil, T. (2010). Oñate, J. (1985). Onush, F. (2002). Über, A. (1996). Ubina, A. J. (1985). ``` ``` % pandoc --citeproc -t plain --- lang: es csl: command/apa.csl references: - id: a1 type: book author: - family: Ubina given: A. John issued: 1985 - id: a2 type: book author: - family: Über given: Aglaia issued: 1996 - id: a3 type: book author: - family: Oñate given: José issued: 1985 - id: a4 type: book author: - family: Onush given: Frank issued: 2002 - id: a5 type: book author: - family: O'Neil given: Timothy issued: 2010 --- [@a1;@a2;@a3;@a4;@a5] ^D (O’Neil, 2010; Onush, 2002; Oñate, 1985; Über, 1996; Ubina, 1985) O’Neil, T. (2010). Onush, F. (2002). Oñate, J. (1985). Über, A. (1996). Ubina, A. J. (1985). ``` ``` % pandoc -C -t plain --- nocite: '@*' lang: fr-FR-u-kb-true references: - id: cote author: cote - id: côte author: côte - id: coté author: coté - id: côté author: côté ... ^D cote. s. d. côte. s. d. coté. s. d. côté. s. d. ``` ================================================ FILE: test/command/vancouver.csl ================================================ ================================================ FILE: test/command/vars-and-metadata.md ================================================ Variables should not leak into metadata in the Markdown writer: ``` % pandoc -t markdown -Vfoo=1 -Vbar=2 -s --- foo: x ... zib ^D --- foo: x --- zib ``` ================================================ FILE: test/command/video-audio.md ================================================ ``` % pandoc -f markdown-implicit_figures -t html ![](./test.mp4) ![Your browser does not support video.](foo/test.webm){width=300} ![](test.mp3) ![](./test.pdf) ![](./test.jpg) ^D

    ``` ================================================ FILE: test/command/wikilinks_title_after_pipe.md ================================================ # CommonMark ## Reader ``` % pandoc --from commonmark_x+wikilinks_title_after_pipe -t html --columns 90 [[https://example.org]] [[https://example.org|title]] [[name of page]] [[name of page|title]] ^D

    https://example.org

    title

    name of page

    title

    ``` ## Writer ``` % pandoc -t commonmark_x+wikilinks_title_after_pipe -f html

    https://example.org

    title

    Home

    Title

    ^D [[https://example.org]] [[https://example.org|title]] [[Home]] [[Name%20of%20page|Title]] ``` # Markdown ## Reader ``` % pandoc --from markdown+wikilinks_title_after_pipe -t html --columns 90 [[https://example.org]] [[https://example.org|title]] [[name of page]] [[name of page|title]] ^D

    https://example.org

    title

    name of page

    title

    ``` ## Writer ``` % pandoc -t markdown+wikilinks_title_after_pipe -f html

    https://example.org

    title

    Home

    Title

    ^D [[https://example.org]] [[https://example.org|title]] [[Home]] [[Name%20of%20page|Title]] ``` ================================================ FILE: test/command/wikilinks_title_before_pipe.md ================================================ # CommonMark ## Reader ``` % pandoc -f commonmark+wikilinks_title_before_pipe -t html --columns 90 [[https://example.org]] [[title|https://example.org]] [[Name of page]] [[Title|Name of page]] ^D

    https://example.org

    title

    Name of page

    Title

    ``` ## Writer ``` % pandoc -t commonmark_x+wikilinks_title_before_pipe -f html

    https://example.org

    title

    Home

    Title

    ^D [[https://example.org]] [[title|https://example.org]] [[Home]] [[Title|Name%20of%20page]] ``` ## Regular links should still work ``` % pandoc -f commonmark+wikilinks_title_before_pipe -t html [Title](Name%20of%20page) ^D

    Title

    ``` # Markdown ## Reader ``` % pandoc -f markdown+wikilinks_title_before_pipe -t html --columns 90 [[https://example.org]] [[title|https://example.org]] [[Name of page]] [[Title|Name of page]] ^D

    https://example.org

    title

    Name of page

    Title

    ``` ## Writer ``` % pandoc -t markdown+wikilinks_title_before_pipe -f html

    https://example.org

    title

    Home

    Title

    ^D [[https://example.org]] [[title|https://example.org]] [[Home]] [[Title|Name%20of%20page]] ``` ================================================ FILE: test/command/write18.md ================================================ Handle \write18{..} as raw tex: ``` % pandoc -t native \write18{git --version} ^D [ RawBlock (Format "tex") "\\write18{git --version}" ] ``` ``` % pandoc -f latex+raw_tex -t native \write18{git --version} ^D [ RawBlock (Format "latex") "\\write18{git --version}" ] ``` ================================================ FILE: test/command/yaml-metadata-blocks.md ================================================ ``` % pandoc -s -t native --- foobar_: this should be ignored foo: bar_: as should this --- ^D Pandoc Meta { unMeta = fromList [ ( "foo" , MetaMap (fromList []) ) ] } [] ``` ``` % pandoc -s -t native --- # For precedence, see multiple-metadata-blocks.md and vars-and-metadata.md # For Bools, see also 4819.md # For Multiline strings, see yaml-with-chomp.md int: 7 float: 1.5 scientific: 3.7e-5 bool: true more: False nothing: null empty: [] nested: int: 8 float: 2.5 bool: true more: False nothing: null empty: [] scientific: 3.7e-5 --- ^D Pandoc Meta { unMeta = fromList [ ( "bool" , MetaBool True ) , ( "empty" , MetaList [] ) , ( "float" , MetaInlines [ Str "1.5" ] ) , ( "int" , MetaInlines [ Str "7" ] ) , ( "more" , MetaBool False ) , ( "nested" , MetaMap (fromList [ ( "bool" , MetaBool True ) , ( "empty" , MetaList [] ) , ( "float" , MetaInlines [ Str "2.5" ] ) , ( "int" , MetaInlines [ Str "8" ] ) , ( "more" , MetaBool False ) , ( "nothing" , MetaString "" ) , ( "scientific" , MetaInlines [ Str "3.7e-5" ] ) ]) ) , ( "nothing" , MetaString "" ) , ( "scientific" , MetaInlines [ Str "3.7e-5" ] ) ] } [] ``` ``` % pandoc -s -t native --- array: - foo: bar - bool: True --- ^D Pandoc Meta { unMeta = fromList [ ( "array" , MetaList [ MetaMap (fromList [ ( "foo" , MetaInlines [ Str "bar" ] ) ]) , MetaMap (fromList [ ( "bool" , MetaBool True ) ]) ] ) ] } [] ``` ``` % pandoc -s -t native --metadata-file command/yaml-metadata.yaml --- title: document --- ^D Pandoc Meta { unMeta = fromList [ ( "other" , MetaInlines [ Emph [ Str "markdown" ] , Space , Str "value" ] ) , ( "title" , MetaInlines [ Str "document" ] ) ] } [] ``` ``` % pandoc -s -t native --metadata-file command/yaml-metadata.yaml -M title=cmdline ^D Pandoc Meta { unMeta = fromList [ ( "other" , MetaInlines [ Emph [ Str "markdown" ] , Space , Str "value" ] ) , ( "title" , MetaString "cmdline" ) ] } [] ``` ================================================ FILE: test/command/yaml-metadata.yaml ================================================ --- title: file other: _markdown_ value --- ================================================ FILE: test/command/yaml-with-chomp.md ================================================ ``` % pandoc -s -t native --- ml: |- TEST BLOCK ... ^D Pandoc Meta { unMeta = fromList [ ( "ml" , MetaInlines [ Str "TEST" , LineBreak , Str "BLOCK" ] ) ] } [] ``` ================================================ FILE: test/command/zeitschrift-fur-kunstgeschichte.csl ================================================ ================================================ FILE: test/creole-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "" , [] , [] ) [ Str "Top-level heading (1)" ] , Header 2 ( "" , [] , [] ) [ Str "This a test for creole 0.1 (2)" ] , Header 3 ( "" , [] , [] ) [ Str "This is a Subheading (3)" ] , Header 4 ( "" , [] , [] ) [ Str "Subsub (4)" ] , Header 5 ( "" , [] , [] ) [ Str "Subsubsub (5)" ] , Para [ Str "The" , Space , Str "ending" , Space , Str "equal" , Space , Str "signs" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "displayed:" ] , Header 1 ( "" , [] , [] ) [ Str "Top-level heading (1)" ] , Header 2 ( "" , [] , [] ) [ Str "This a test for creole 0.1 (2)" ] , Header 3 ( "" , [] , [] ) [ Str "This is a Subheading (3)" ] , Header 4 ( "" , [] , [] ) [ Str "Subsub (4)" ] , Header 5 ( "" , [] , [] ) [ Str "Subsubsub (5)" ] , Para [ Str "You" , Space , Str "can" , Space , Str "make" , Space , Str "things" , Space , Strong [ Str "bold" ] , Space , Str "or" , Space , Emph [ Str "italic" ] , Space , Str "or" , Space , Strong [ Emph [ Str "both" ] ] , Space , Str "or" , Space , Emph [ Strong [ Str "both" ] ] , Str "." ] , Para [ Str "Character" , Space , Str "formatting" , Space , Str "extends" , Space , Str "across" , Space , Str "line" , Space , Str "breaks:" , Space , Strong [ Str "bold," , Space , Str "this" , Space , Str "is" , Space , Str "still" , Space , Str "bold." , Space , Str "This" , Space , Str "line" , Space , Str "deliberately" , Space , Str "does" , Space , Str "not" , Space , Str "end" , Space , Str "in" , Space , Str "star-star." ] ] , Para [ Str "Not" , Space , Str "bold." , Space , Str "Character" , Space , Str "formatting" , Space , Str "does" , Space , Str "not" , Space , Str "cross" , Space , Str "paragraph" , Space , Str "boundaries." ] , Para [ Str "You" , Space , Str "can" , Space , Str "use" , Space , Link ( "" , [] , [] ) [ Str "internal links" ] ( "internal links" , "" ) , Space , Str "or" , Space , Link ( "" , [] , [] ) [ Str "external links" ] ( "http://www.wikicreole.org" , "" ) , Str "," , Space , Str "give" , Space , Str "the" , Space , Str "link" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "different" ] ( "internal links" , "" ) , Space , Str "name." ] , Para [ Str "Here's" , Space , Str "another" , Space , Str "sentence:" , Space , Str "This" , Space , Str "wisdom" , Space , Str "is" , Space , Str "taken" , Space , Str "from" , Space , Link ( "" , [] , [] ) [ Str "Ward Cunningham's" ] ( "Ward Cunningham's" , "" ) , Space , Link ( "" , [] , [] ) [ Str "Presentation at the Wikisym 06" ] ( "http://www.c2.com/doc/wikisym/WikiSym2006.pdf" , "" ) , Str "." ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "external" , Space , Str "link" , Space , Str "without" , Space , Str "a" , Space , Str "description:" , Space , Link ( "" , [] , [] ) [ Str "http://www.wikicreole.org" ] ( "http://www.wikicreole.org" , "" ) ] , Para [ Str "Be" , Space , Str "careful" , Space , Str "that" , Space , Str "italic" , Space , Str "links" , Space , Str "are" , Space , Str "rendered" , Space , Str "properly:" , Space , Emph [ Link ( "" , [] , [] ) [ Str "My Book Title" ] ( "http://my.book.example/" , "" ) ] ] , Para [ Str "Free" , Space , Str "links" , Space , Str "without" , Space , Str "braces" , Space , Str "should" , Space , Str "be" , Space , Str "rendered" , Space , Str "as" , Space , Str "well," , Space , Str "like" , Space , Link ( "" , [] , [] ) [ Str "http://www.wikicreole.org/" ] ( "http://www.wikicreole.org/" , "" ) , Space , Str "and" , Space , Link ( "" , [] , [] ) [ Str "http://www.wikicreole.org/users/~example" ] ( "http://www.wikicreole.org/users/~example" , "" ) , Str "." ] , Para [ Str "Creole1.0" , Space , Str "specifies" , Space , Str "that" , Space , Link ( "" , [] , [] ) [ Str "http://bar" ] ( "http://bar" , "" ) , Space , Str "and" , Space , Link ( "" , [] , [] ) [ Str "ftp://bar" ] ( "ftp://bar" , "" ) , Space , Str "should" , Space , Str "not" , Space , Str "render" , Space , Str "italic," , Space , Str "something" , Space , Str "like" , Space , Str "foo:" , Emph [ Str "bar" , Space , Str "should" , Space , Str "render" , Space , Str "as" , Space , Str "italic." ] ] , Para [ Str "You" , Space , Str "can" , Space , Str "use" , Space , Str "this" , Space , Str "to" , Space , Str "draw" , Space , Str "a" , Space , Str "line" , Space , Str "to" , Space , Str "separate" , Space , Str "the" , Space , Str "page:" ] , HorizontalRule , Para [ Str "You" , Space , Str "can" , Space , Str "use" , Space , Str "lists," , Space , Str "start" , Space , Str "it" , Space , Str "at" , Space , Str "the" , Space , Str "first" , Space , Str "column" , Space , Str "for" , Space , Str "now," , Space , Str "please..." ] , Para [ Str "unnumbered" , Space , Str "lists" , Space , Str "are" , Space , Str "like" ] , BulletList [ [ Plain [ Str "item" , Space , Str "a" ] ] , [ Plain [ Str "item" , Space , Str "b" ] ] , [ Plain [ Strong [ Str "bold" , Space , Str "item" , Space , Str "c" ] ] ] ] , Para [ Str "blank" , Space , Str "space" , Space , Str "is" , Space , Str "also" , Space , Str "permitted" , Space , Str "before" , Space , Str "lists" , Space , Str "like:" ] , BulletList [ [ Plain [ Str "item" , Space , Str "a" ] ] , [ Plain [ Str "item" , Space , Str "b" ] ] , [ Plain [ Str "item" , Space , Str "c" ] , BulletList [ [ Plain [ Str "item" , Space , Str "c.a" ] ] ] ] ] , Para [ Str "or" , Space , Str "you" , Space , Str "can" , Space , Str "number" , Space , Str "them" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Link ( "" , [] , [] ) [ Str "item 1" ] ( "item 1" , "" ) ] ] , [ Plain [ Str "item" , Space , Str "2" ] ] , [ Plain [ Emph [ Space , Str "italic" , Space , Str "item" , Space , Str "3" , Space ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "item" , Space , Str "3.1" ] ] , [ Plain [ Str "item" , Space , Str "3.2" ] ] ] ] ] , Para [ Str "up" , Space , Str "to" , Space , Str "five" , Space , Str "levels" ] , BulletList [ [ Plain [ Str "1" ] , BulletList [ [ Plain [ Str "2" ] , BulletList [ [ Plain [ Str "3" ] , BulletList [ [ Plain [ Str "4" ] , BulletList [ [ Plain [ Str "5" ] ] ] ] ] ] ] ] ] ] ] , BulletList [ [ Plain [ Str "You" , Space , Str "can" , Space , Str "have" , Space , Str "multiline" , Space , Str "list" , Space , Str "items" ] ] , [ Plain [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "second" , Space , Str "multiline" , Space , Str "list" , Space , Str "item" ] ] ] , Para [ Str "You" , Space , Str "can" , Space , Str "use" , Space , Str "nowiki" , Space , Str "syntax" , Space , Str "if" , Space , Str "you" , Space , Str "would" , Space , Str "like" , Space , Str "do" , Space , Str "stuff" , Space , Str "like" , Space , Str "this:" ] , CodeBlock ( "" , [] , [] ) "Guitar Chord C:\n\n||---|---|---|\n||-0-|---|---|\n||---|---|---|\n||---|-0-|---|\n||---|---|-0-|\n||---|---|---|" , Para [ Str "You" , Space , Str "can" , Space , Str "also" , Space , Str "use" , Space , Str "it" , Space , Str "inline" , Space , Str "nowiki" , Space , Code ( "" , [] , [] ) " in a sentence " , Space , Str "like" , Space , Str "this." ] , Header 1 ( "" , [] , [] ) [ Str "Escapes" ] , Para [ Str "Normal" , Space , Str "Link:" , Space , Link ( "" , [] , [] ) [ Str "http://wikicreole.org/" ] ( "http://wikicreole.org/" , "" ) , Space , Str "-" , Space , Str "now" , Space , Str "same" , Space , Str "link," , Space , Str "but" , Space , Str "escaped:" , Space , Str "http://wikicreole.org/" ] , Para [ Str "Normal" , Space , Str "asterisks:" , Space , Str "**not" , Space , Str "bold**" ] , Para [ Str "a" , Space , Str "tilde" , Space , Str "alone:" , Space , Str "~" ] , Para [ Str "a" , Space , Str "tilde" , Space , Str "escapes" , Space , Str "itself:" , Space , Str "~xxx" ] , Header 3 ( "" , [] , [] ) [ Str "Creole 0.2" ] , Para [ Str "This" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "flower" , Space , Str "with" , Space , Str "the" , Space , Str "ALT" , Space , Str "text" , Space , Str "\"this" , Space , Str "is" , Space , Str "a" , Space , Str "flower\"" , Space , Str "if" , Space , Str "your" , Space , Str "wiki" , Space , Str "supports" , Space , Str "ALT" , Space , Str "text" , Space , Str "on" , Space , Str "images:" ] , Para [ Image ( "" , [] , [] ) [ Str "here is a red flower" ] ( "Red-Flower.jpg" , "" ) ] , Header 3 ( "" , [] , [] ) [ Str "Creole 0.4" ] , Para [ Str "Tables" , Space , Str "are" , Space , Str "done" , Space , Str "like" , Space , Str "this:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "header" , Space , Str "col1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "header" , Space , Str "col2" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "you" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "can" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "also" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "align" , LineBreak , Str "it." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "You" , Space , Str "can" , Space , Str "format" , Space , Str "an" , Space , Str "address" , Space , Str "by" , Space , Str "simply" , Space , Str "forcing" , Space , Str "linebreaks:" ] , Para [ Str "My" , Space , Str "contact" , Space , Str "dates:" , LineBreak , Str "Pone:" , Space , Str "xyz" , LineBreak , Str "Fax:" , Space , Str "+45" , LineBreak , Str "Mobile:" , Space , Str "abc" ] , Header 3 ( "" , [] , [] ) [ Str "Creole 0.5" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" , Space , Str "title" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Another" , Space , Str "header" , Space , Str "title" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Code ( "" , [] , [] ) " //not italic text// " ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Code ( "" , [] , [] ) " **not bold text** " ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Emph [ Str "italic" , Space , Str "text" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Space , Str "bold" , Space , Str "text" , Space ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 3 ( "" , [] , [] ) [ Str "Creole 1.0" ] , Para [ Str "If" , Space , Str "interwiki" , Space , Str "links" , Space , Str "are" , Space , Str "setup" , Space , Str "in" , Space , Str "your" , Space , Str "wiki," , Space , Str "this" , Space , Str "links" , Space , Str "to" , Space , Str "the" , Space , Str "WikiCreole" , Space , Str "page" , Space , Str "about" , Space , Str "Creole" , Space , Str "1.0" , Space , Str "test" , Space , Str "cases:" , Space , Link ( "" , [] , [] ) [ Str "WikiCreole:Creole1.0TestCases" ] ( "WikiCreole:Creole1.0TestCases" , "" ) , Str "." ] , HorizontalRule , Para [ Str "The" , Space , Str "above" , Space , Str "test" , Space , Str "document" , Space , Str "was" , Space , Str "found" , Space , Str "on" , Space , Link ( "" , [] , [] ) [ Str "http://www.wikicreole.org/wiki/Creole1.0TestCases" ] ( "http://www.wikicreole.org/wiki/Creole1.0TestCases" , "" ) , Space , Str "and" , Space , Str "downloaded" , Space , Str "from" , Space , Link ( "" , [] , [] ) [ Str "http://www.wikicreole.org/attach/Creole1.0TestCases/creole1.0test.txt" ] ( "http://www.wikicreole.org/attach/Creole1.0TestCases/creole1.0test.txt" , "" ) , Str "." ] , Para [ Str "The" , Space , Str "Creole" , Space , Str "Wiki" , Space , Str "is" , Space , Str "licensed:" , Space , Str "Copyright" , Space , Str "(C)" , Space , Str "by" , Space , Str "the" , Space , Str "contributors." , Space , Str "Some" , Space , Str "rights" , Space , Str "reserved," , Space , Str "license" , Space , Link ( "" , [] , [] ) [ Str "https://creativecommons.org/licenses/by-sa/1.0/" ] ( "BY-SA" , "" ) , Str "." ] ] ================================================ FILE: test/creole-reader.txt ================================================ = Top-level heading (1) == This a test for creole 0.1 (2) === This is a Subheading (3) ==== Subsub (4) ===== Subsubsub (5) The ending equal signs should not be displayed: = Top-level heading (1) = == This a test for creole 0.1 (2) == === This is a Subheading (3) === ==== Subsub (4) ==== ===== Subsubsub (5) ===== You can make things **bold** or //italic// or **//both//** or //**both**//. Character formatting extends across line breaks: **bold, this is still bold. This line deliberately does not end in star-star. Not bold. Character formatting does not cross paragraph boundaries. You can use [[internal links]] or [[http://www.wikicreole.org|external links]], give the link a [[internal links|different]] name. Here's another sentence: This wisdom is taken from [[Ward Cunningham's]] [[http://www.c2.com/doc/wikisym/WikiSym2006.pdf|Presentation at the Wikisym 06]]. Here's a external link without a description: [[http://www.wikicreole.org]] Be careful that italic links are rendered properly: //[[http://my.book.example/|My Book Title]]// Free links without braces should be rendered as well, like http://www.wikicreole.org/ and http://www.wikicreole.org/users/~example. Creole1.0 specifies that http://bar and ftp://bar should not render italic, something like foo://bar should render as italic. You can use this to draw a line to separate the page: ---- You can use lists, start it at the first column for now, please... unnumbered lists are like * item a * item b * **bold item c** blank space is also permitted before lists like: * item a * item b * item c ** item c.a or you can number them # [[item 1]] # item 2 # // italic item 3 // ## item 3.1 ## item 3.2 up to five levels * 1 ** 2 *** 3 **** 4 ***** 5 * You can have multiline list items * this is a second multiline list item You can use nowiki syntax if you would like do stuff like this: {{{ Guitar Chord C: ||---|---|---| ||-0-|---|---| ||---|---|---| ||---|-0-|---| ||---|---|-0-| ||---|---|---| }}} You can also use it inline nowiki {{{ in a sentence }}} like this. = Escapes = Normal Link: http://wikicreole.org/ - now same link, but escaped: ~http://wikicreole.org/ Normal asterisks: ~**not bold~** a tilde alone: ~ a tilde escapes itself: ~~xxx === Creole 0.2 === This should be a flower with the ALT text "this is a flower" if your wiki supports ALT text on images: {{Red-Flower.jpg|here is a red flower}} === Creole 0.4 === Tables are done like this: |=header col1|=header col2| |col1|col2| |you |can | |also |align\\ it. | You can format an address by simply forcing linebreaks: My contact dates:\\ Pone: xyz\\ Fax: +45\\ Mobile: abc === Creole 0.5 === |= Header title |= Another header title | | {{{ //not italic text// }}} | {{{ **not bold text** }}} | | //italic text// | ** bold text ** | === Creole 1.0 === If interwiki links are setup in your wiki, this links to the WikiCreole page about Creole 1.0 test cases: [[WikiCreole:Creole1.0TestCases]]. ---- The above test document was found on http://www.wikicreole.org/wiki/Creole1.0TestCases and downloaded from http://www.wikicreole.org/attach/Creole1.0TestCases/creole1.0test.txt. The Creole Wiki is licensed: Copyright (C) by the contributors. Some rights reserved, license [[BY-SA|https://creativecommons.org/licenses/by-sa/1.0/]]. ================================================ FILE: test/djot-reader.djot ================================================ # Pandoc Test Suite John MacFarlane Anonymous July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. * * * * {#headers} # Headers {#level-2-with-an-embedded-link} ## Level 2 with an [embedded link](/url) {#level-3-with-emphasis} ### Level 3 with _emphasis_ {#level-4} #### Level 4 {#level-5} ##### Level 5 {#level-1} # Level 1 {#level-2-with-emphasis} ## Level 2 with _emphasis_ {#level-3} ### Level 3 with no blank line {#level-2} ## Level 2 with no blank line * * * * {#paragraphs} # Paragraphs Here's a regular paragraph. 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 a list item. Here's one with a bullet. \* criminey. There should be a hard line break\ here. * * * * {#block-quotes} # Block Quotes E-mail style: > This is a block quote. It is pretty short. > Code in a block quote: > > ``` > sub status { > print "working"; > } > ``` > > A list: > > 1. item one > 2. item two > > Nested block quotes: > > > nested > > > nested This should not be a block quote: 2 \> 1. And a following paragraph. * * * * {#code-blocks} # Code Blocks Code: ``` ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab ``` And: ``` this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ ``` * * * * {#lists} # Lists {#unordered} ## Unordered Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 {#ordered} ## Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. {#nested} ## Nested - Tab - Tab - Tab Here's another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third {#tabs-and-spaces} ## Tabs and spaces - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces {#fancy-list-markers} ## Fancy list markers (2) begins with 2 (3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items (A) a subsublist (B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. (6) Decimal start with 6 c) Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams * * * * {#definition-lists} # Definition Lists Tight using spaces: : apple red fruit : orange orange fruit : banana yellow fruit Tight using tabs: : apple red fruit : orange orange fruit : banana yellow fruit Loose: : apple red fruit : orange orange fruit : banana yellow fruit Multiple blocks with italics: : _apple_ red fruit contains seeds, crisp, pleasant to taste : _orange_ orange fruit ``` { orange code block } ``` > orange block quote Multiple definitions, tight: : apple red fruit computer : orange orange fruit bank Multiple definitions, loose: : apple red fruit computer : orange orange fruit bank Blank line after term, indented marker, alternate markers: : apple red fruit computer : orange orange fruit 1. sublist 2. sublist {#html-blocks} # HTML Blocks Simple block on one line: ::: foo ::: And nested without indentation: :::::: :::: ::: foo ::: :::: ::: bar ::: :::::: Interpreted markdown in a table: This is _emphasized_ And this is *strong* Here's a simple block: ::: foo ::: This should be a code block, though: ```
    foo
    ``` As should this: ```
    foo
    ``` Now, nested: ::::: :::: ::: foo ::: :::: ::::: This should just be an HTML comment: Multiline: Code block: ``` ``` Just plain comment, with trailing spaces on the line: Code: ```
    ``` Hr's: * * * * {#inline-markup} # Inline Markup This is _emphasized_, and so _is this_. This is *strong*, and so *is this*. An _[emphasized link](/url)_. *_This is strong and em._* So is *_this_* word. *_This is strong and em._* So is *_this_* word. This is code: `>`, `$`, `\`, `\$`, ``. {-This is _strikeout_.-} Superscripts: a^bc^d a^_hello_^ a^hello there^. Subscripts: H~2~O, H~23~O, H~many of them~O. These should not be superscripts or subscripts, because of the unescaped spaces: a\^b c\^d, a\~b c\~d. * * * * {#smart-quotes-ellipses-dashes} # Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70's? Here is some quoted '`code`' and a "[quoted link](http://example.com/?foo=1&bar=2)". Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses...and...and.... * * * * {#latex} # LaTeX - - $`2+2=4` - $`x \in y` - $`\alpha \wedge \omega` - $`223` - $`p`-Tree - Here's some display math: $$`\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}` - Here's one that has a line break in it: $`\alpha + \omega \times x^2`. These shouldn't be math: - To get the famous equation, write `$e = mc^2$`. - $22,000 is a _lot_ of money. So is $34,000. (It worked if "lot" is emphasized.) - Shoes ($20) and socks ($5). - Escaped `$`\: $73 _this should be emphasized_ 23$. Here's a LaTeX table: * * * * {#special-characters} # Special Characters Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 \< 5. 6 \> 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: ( Right paren: ) Greater-than: \> Hash: # Period: . Bang: \! Plus: + Minus: - * * * * {#links} # Links {#explicit} ## Explicit Just a [URL](/url/). [URL and title](/url/){title="title"}. [URL and title](/url/){title="title preceded by two spaces"}. [URL and title](/url/){title="title preceded by a tab"}. [URL and title](/url/){title="title with \"quotes\" in it"} [URL and title](/url/){title="title with single quotes"} [with\_underscore](/url/with_underscore) [Email link](mailto:nobody@nowhere.net) [Empty](). {#reference} ## Reference Foo [bar](/url/). With [embedded \[brackets\]](/url/). [b](/url/) by itself should be a link. Indented [once](/url). Indented [twice](/url). Indented [thrice](/url). This should \[not\]\[\] be a link. ``` [not]: /url ``` Foo [bar](/url/){title="Title with \"quotes\" inside"}. Foo [biz](/url/){title="Title with \"quote\" inside"}. {#with-ampersands} ## With ampersands Here's a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2). Here's a link with an amersand in the link text: [AT&T](http://att.com/){title="AT&T"}. Here's an [inline link](/script?foo=1&bar=2). Here's an [inline link in pointy braces](/script?foo=1&bar=2). {#autolinks} ## Autolinks With an ampersand: - In a list? - - It should. An e-mail address: > Blockquoted: Auto-links should not occur here: `` ``` or here: ``` * * * * {#images} # Images From "Voyage dans la Lune" by Georges Melies (1902): :::: ![lalune](lalune.jpg){title="Voyage dans la Lune"} {.caption} ::: lalune ::: :::: Here is a movie ![movie](movie.jpg) icon. * * * * {#footnotes} # Footnotes Here is a footnote reference,[^1] and another.[^2] This should _not_ be a footnote reference, because it contains a space.\[\^my note\] Here is an inline note.[^3] > Notes can go in quotes.[^4] 1. And in list items.[^5] This paragraph should not be part of the note, as it is not indented. [^1]: Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. [^2]: Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). ``` { } ``` If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. [^3]: This is _easier_ to type. Inline notes may contain [links](http://google.com) and `]` verbatim characters, as well as \[bracketed text\]. [^4]: In quote. [^5]: In list. ================================================ FILE: test/djot-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Div ( "Pandoc-Test-Suite" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] , Para [ Str "John" , Space , Str "MacFarlane" , SoftBreak , Str "Anonymous" ] , Para [ Str "July" , Space , Str "17," , Space , Str "2006" ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , Space , Str "John" , Space , Str "Gruber\8217s" , SoftBreak , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , HorizontalRule ] , Div ( "headers" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Headers" ] , Div ( "level-2-with-an-embedded-link" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "/url" , "" ) ] , Div ( "level-3-with-emphasis" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Div ( "level-4" , [ "section" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Div ( "level-5" , [ "section" ] , [] ) [ Header 5 ( "" , [] , [] ) [ Str "Level" , Space , Str "5" ] ] ] ] ] ] , Div ( "level-1" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Div ( "level-2-with-emphasis" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Div ( "level-3" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "Level" , Space , Str "3" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] ] ] , Div ( "level-2" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , HorizontalRule ] ] , Div ( "paragraphs" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , Space , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , Space , Str "item." , SoftBreak , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , Space , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , Space , Str "a" , Space , Str "list" , SoftBreak , Str "item." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , Space , Str "*" , Space , Str "criminey." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "hard" , Space , Str "line" , Space , Str "break" , LineBreak , Str "here." ] , HorizontalRule ] , Div ( "block-quotes" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , Space , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , BlockQuote [ Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [ "" ] , [] ) "sub status {\n print \"working\";\n}\n" , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "item" , Space , Str "one" ] ] , [ Plain [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] ] , BlockQuote [ Para [ Str "nested" ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" , Space , Str "2" , Space , Str ">" , Space , Str "1." ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , HorizontalRule ] , Div ( "code-blocks" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [ "" ] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n\nthis code block is indented by one tab\n" , Para [ Str "And:" ] , CodeBlock ( "" , [ "" ] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{\n" , HorizontalRule ] , Div ( "lists" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Lists" ] , Div ( "unordered" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] , [ Plain [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Plus" , Space , Str "1" ] ] , [ Plain [ Str "Plus" , Space , Str "2" ] ] , [ Plain [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Minus" , Space , Str "1" ] ] , [ Plain [ Str "Minus" , Space , Str "2" ] ] , [ Plain [ Str "Minus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] ] , Div ( "ordered" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second" ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "and:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "One" ] ] , [ Plain [ Str "Two" ] ] , [ Plain [ Str "Three" ] ] ] , Para [ Str "Loose" , Space , Str "using" , Space , Str "tabs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and" , Space , Str "using" , Space , Str "spaces:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog\8217s" , Space , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] ] , Div ( "nested" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "another:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing" , Space , Str "but" , Space , Str "with" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] ] , Div ( "tabs-and-spaces" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Tabs" , Space , Str "and" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] ] ] ] ] ] , Div ( "fancy-list-markers" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , OrderedList ( 2 , Decimal , TwoParens ) [ [ Para [ Str "begins" , Space , Str "with" , Space , Str "2" ] ] , [ Para [ Str "and" , Space , Str "now" , Space , Str "3" ] , Para [ Str "with" , Space , Str "a" , Space , Str "continuation" ] , OrderedList ( 4 , LowerRoman , Period ) [ [ Plain [ Str "sublist" , Space , Str "with" , Space , Str "roman" , Space , Str "numerals," , Space , Str "starting" , Space , Str "with" , Space , Str "4" ] ] , [ Plain [ Str "more" , Space , Str "items" ] , OrderedList ( 1 , UpperAlpha , TwoParens ) [ [ Plain [ Str "a" , Space , Str "subsublist" ] ] , [ Plain [ Str "a" , Space , Str "subsublist" ] ] ] ] ] ] ] , Para [ Str "Nesting:" ] , OrderedList ( 1 , UpperAlpha , Period ) [ [ Plain [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , Period ) [ [ Plain [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , TwoParens ) [ [ Plain [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , OneParen ) [ [ Plain [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "Autonumber." ] ] , [ Plain [ Str "More." ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "Nested." ] ] ] ] ] , Para [ Str "Should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "list" , Space , Str "item:" ] , Para [ Str "M.A.\160\&2007" ] , OrderedList ( 2 , UpperAlpha , Period ) [ [ Plain [ Str "Williams" ] ] ] , HorizontalRule ] ] , Div ( "definition-lists" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Definition" , Space , Str "Lists" ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "spaces:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Plain [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "tabs:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Plain [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Para [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "with" , Space , Str "italics:" ] , DefinitionList [ ( [ Emph [ Str "apple" ] ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "contains" , Space , Str "seeds," , Space , Str "crisp," , Space , Str "pleasant" , Space , Str "to" , Space , Str "taste" ] ] ] ) , ( [ Emph [ Str "orange" ] ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , CodeBlock ( "" , [ "" ] , [] ) "{ orange code block }\n" , BlockQuote [ Para [ Str "orange" , Space , Str "block" , Space , Str "quote" ] ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "tight:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , Para [ Str "bank" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , Para [ Str "bank" ] ] ] ) ] , Para [ Str "Blank" , Space , Str "line" , Space , Str "after" , Space , Str "term," , Space , Str "indented" , Space , Str "marker," , Space , Str "alternate" , Space , Str "markers:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "sublist" ] ] , [ Plain [ Str "sublist" ] ] ] ] ] ) ] ] , Div ( "html-blocks" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "HTML" , Space , Str "Blocks" ] , Para [ Str "Simple" , Space , Str "block" , Space , Str "on" , Space , Str "one" , Space , Str "line:" ] , Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] , Para [ Str "And" , Space , Str "nested" , Space , Str "without" , Space , Str "indentation:" ] , Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] ] , Div ( "" , [] , [] ) [ Para [ Str "bar" ] ] ] , Para [ Str "Interpreted" , Space , Str "markdown" , Space , Str "in" , Space , Str "a" , Space , Str "table:" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] ] , Para [ Str "And" , Space , Str "this" , Space , Str "is" , Space , Strong [ Str "strong" ] ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "simple" , Space , Str "block:" ] , Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "code" , Space , Str "block," , Space , Str "though:" ] , CodeBlock ( "" , [ "" ] , [] ) "
    \n foo\n
    \n" , Para [ Str "As" , Space , Str "should" , Space , Str "this:" ] , CodeBlock ( "" , [ "" ] , [] ) "
    foo
    \n" , Para [ Str "Now," , Space , Str "nested:" ] , Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "just" , Space , Str "be" , Space , Str "an" , Space , Str "HTML" , Space , Str "comment:" ] , Para [ Str "Multiline:" ] , Para [ Str "Code" , Space , Str "block:" ] , CodeBlock ( "" , [ "" ] , [] ) "\n" , Para [ Str "Just" , Space , Str "plain" , Space , Str "comment," , Space , Str "with" , Space , Str "trailing" , Space , Str "spaces" , Space , Str "on" , Space , Str "the" , Space , Str "line:" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [ "" ] , [] ) "
    \n" , Para [ Str "Hr\8217s:" ] , HorizontalRule ] , Div ( "inline-markup" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "An" , Space , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , Space , Str "link" ] ( "/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , Space , Code ( "" , [] , [] ) "\\`, " , Str "$" , Code ( "" , [] , [] ) ", " , Str "" , Code ( "" , [] , [] ) "." ] , Para [ Span ( "" , [ "deleted" ] , [] ) [ Str "This" , Space , Str "is" , Space , Emph [ Str "strikeout" ] , Str "." ] ] , Para [ Str "Superscripts:" , Space , Str "a" , Superscript [ Str "bc" ] , Str "d" , Space , Str "a" , Superscript [ Emph [ Str "hello" ] ] , Space , Str "a" , Superscript [ Str "hello\160there" ] , Str "." ] , Para [ Str "Subscripts:" , Space , Str "H" , Subscript [ Str "2" ] , Str "O," , Space , Str "H" , Subscript [ Str "23" ] , Str "O," , Space , Str "H" , Subscript [ Str "many\160of\160them" ] , Str "O." ] , Para [ Str "These" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "superscripts" , Space , Str "or" , Space , Str "subscripts," , Space , Str "because" , Space , Str "of" , Space , Str "the" , Space , Str "unescaped" , Space , Str "spaces:" , SoftBreak , Str "a^b" , Space , Str "c^d," , Space , Str "a~b" , Space , Str "c~d." ] , HorizontalRule ] , Div ( "smart-quotes-ellipses-dashes" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Smart" , Space , Str "quotes," , Space , Str "ellipses," , Space , Str "dashes" ] , Para [ Quoted DoubleQuote [ Str "Hello," ] , Space , Str "said" , Space , Str "the" , Space , Str "spider." , Space , Quoted DoubleQuote [ Quoted SingleQuote [ Str "Shelob" ] , Space , Str "is" , Space , Str "my" , Space , Str "name." ] ] , Para [ Quoted SingleQuote [ Str "A" ] , Str "," , Space , Quoted SingleQuote [ Str "B" ] , Str "," , Space , Str "and" , Space , Quoted SingleQuote [ Str "C" ] , Space , Str "are" , Space , Str "letters." ] , Para [ Quoted SingleQuote [ Str "Oak," ] , Space , Quoted SingleQuote [ Str "elm," ] , Space , Str "and" , Space , Quoted SingleQuote [ Str "beech" ] , Space , Str "are" , Space , Str "names" , Space , Str "of" , Space , Str "trees." , Space , Str "So" , Space , Str "is" , Space , Quoted SingleQuote [ Str "pine." ] ] , Para [ Quoted SingleQuote [ Str "He" , Space , Str "said," , Space , Quoted DoubleQuote [ Str "I" , Space , Str "want" , Space , Str "to" , Space , Str "go." ] ] , Space , Str "Were" , Space , Str "you" , Space , Str "alive" , Space , Str "in" , Space , Str "the" , Space , Str "70\8217s?" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "quoted" , Space , Quoted SingleQuote [ Code ( "" , [] , [] ) "code" ] , Space , Str "and" , Space , Str "a" , Space , Quoted DoubleQuote [ Link ( "" , [] , [] ) [ Str "quoted" , SoftBreak , Str "link" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , Str "." ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one\8212two" , Space , Str "\8212" , Space , Str "three\8212four" , Space , Str "\8212" , Space , Str "five." ] , Para [ Str "Dashes" , Space , Str "between" , Space , Str "numbers:" , Space , Str "5\8211\&7," , Space , Str "255\8211\&66," , Space , Str "1987\8211\&1999." ] , Para [ Str "Ellipses\8230and\8230and\8230." ] , HorizontalRule ] , Div ( "latex" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "LaTeX" ] , BulletList [ [] , [ Plain [ Math InlineMath "2+2=4" ] ] , [ Plain [ Math InlineMath "x \\in y" ] ] , [ Plain [ Math InlineMath "\\alpha \\wedge \\omega" ] ] , [ Plain [ Math InlineMath "223" ] ] , [ Plain [ Math InlineMath "p" , Str "-Tree" ] ] , [ Plain [ Str "Here\8217s" , Space , Str "some" , Space , Str "display" , Space , Str "math:" , SoftBreak , Math DisplayMath "\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)-f(x)}{h}" ] ] , [ Plain [ Str "Here\8217s" , Space , Str "one" , Space , Str "that" , Space , Str "has" , Space , Str "a" , Space , Str "line" , Space , Str "break" , Space , Str "in" , Space , Str "it:" , Space , Math InlineMath "\\alpha + \\omega \\times x^2" , Str "." ] ] ] , Para [ Str "These" , Space , Str "shouldn\8217t" , Space , Str "be" , Space , Str "math:" ] , BulletList [ [ Plain [ Str "To" , Space , Str "get" , Space , Str "the" , Space , Str "famous" , Space , Str "equation," , Space , Str "write" , Space , Code ( "" , [] , [] ) "$e = mc^2$" , Str "." ] ] , [ Plain [ Str "$22,000" , Space , Str "is" , Space , Str "a" , Space , Emph [ Str "lot" ] , Space , Str "of" , Space , Str "money." , Space , Str "So" , Space , Str "is" , Space , Str "$34,000." , Space , Str "(It" , Space , Str "worked" , Space , Str "if" , Space , Quoted DoubleQuote [ Str "lot" ] , Space , Str "is" , SoftBreak , Str "emphasized.)" ] ] , [ Plain [ Str "Shoes" , Space , Str "($20)" , Space , Str "and" , Space , Str "socks" , Space , Str "($5)." ] ] , [ Plain [ Str "Escaped" , Space , Code ( "" , [] , [] ) "$" , Str ":" , Space , Str "$73" , Space , Emph [ Str "this" , Space , Str "should" , Space , Str "be" , Space , Str "emphasized" ] , Space , Str "23$." ] ] ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "LaTeX" , Space , Str "table:" ] , HorizontalRule ] , Div ( "special-characters" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Plain [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Plain [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Plain [ Str "section:" , Space , Str "\167" ] ] , [ Plain [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Plain [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , HorizontalRule ] , Div ( "links" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Links" ] , Div ( "explicit" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [ ( "title" , "title" ) ] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [ ( "title" , "title preceded by two spaces" ) ] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [ ( "title" , "title preceded by a tab" ) ] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [ ( "title" , "title with \"quotes\" in it" ) ] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) ] , Para [ Link ( "" , [] , [ ( "title" , "title with single quotes" ) ] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "with_underscore" ] ( "/url/with_underscore" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Email" , Space , Str "link" ] ( "mailto:nobody@nowhere.net" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Empty" ] ( "" , "" ) , Str "." ] ] , Div ( "reference" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Reference" ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "[brackets]" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "b" ] ( "/url/" , "" ) , Space , Str "by" , Space , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "once" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "twice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "thrice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "This" , Space , Str "should" , Space , Str "[not][]" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , CodeBlock ( "" , [ "" ] , [] ) "[not]: /url\n" , Para [ Str "Foo" , Space , Link ( "" , [] , [ ( "title" , "Title with \"quotes\" inside" ) ] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [ ( "title" , "Title with \"quote\" inside" ) ] ) [ Str "biz" ] ( "/url/" , "" ) , Str "." ] ] , Div ( "with-ampersands" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , SoftBreak , Link ( "" , [] , [ ( "title" , "AT&T" ) ] ) [ Str "AT&T" ] ( "http://att.com/" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" , Space , Str "in" , Space , Str "pointy" , Space , Str "braces" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] ] , Div ( "autolinks" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Plain [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Plain [ Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Plain [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , Space , Link ( "" , [ "email" ] , [] ) [ Str "nobody@nowhere.net" ] ( "mailto:nobody@nowhere.net" , "" ) ] , BlockQuote [ Para [ Str "Blockquoted:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , Para [ Str "Auto-links" , Space , Str "should" , Space , Str "not" , Space , Str "occur" , Space , Str "here:" , Space , Code ( "" , [] , [] ) "" ] , CodeBlock ( "" , [ "" ] , [] ) "or here: \n" , HorizontalRule ] ] , Div ( "images" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Quoted DoubleQuote [ Str "Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune" ] , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Div ( "" , [] , [] ) [ Para [ Image ( "" , [] , [ ( "title" , "Voyage dans la Lune" ) ] ) [ Str "lalune" ] ( "lalune.jpg" , "" ) ] , Div ( "" , [ "caption" ] , [] ) [ Para [ Str "lalune" ] ] ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "movie" ] ( "movie.jpg" , "" ) , Space , Str "icon." ] , HorizontalRule ] , Div ( "footnotes" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Note [ Para [ Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "footnote." , Space , Str "It" , Space , Str "can" , Space , Str "go" , Space , Str "anywhere" , Space , Str "after" , Space , Str "the" , Space , Str "footnote" , Space , Str "reference." , Space , Str "It" , SoftBreak , Str "need" , Space , Str "not" , Space , Str "be" , Space , Str "placed" , Space , Str "at" , Space , Str "the" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "document." ] ] , Space , Str "and" , Space , Str "another." , Note [ Para [ Str "Here\8217s" , Space , Str "the" , Space , Str "long" , Space , Str "note." , Space , Str "This" , Space , Str "one" , Space , Str "contains" , Space , Str "multiple" , Space , Str "blocks." ] , Para [ Str "Subsequent" , Space , Str "blocks" , Space , Str "are" , Space , Str "indented" , Space , Str "to" , Space , Str "show" , Space , Str "that" , Space , Str "they" , Space , Str "belong" , Space , Str "to" , Space , Str "the" , Space , Str "footnote" , Space , Str "(as" , SoftBreak , Str "with" , Space , Str "list" , Space , Str "items)." ] , CodeBlock ( "" , [ "" ] , [] ) "{ }\n" , Para [ Str "If" , Space , Str "you" , Space , Str "want," , Space , Str "you" , Space , Str "can" , Space , Str "indent" , Space , Str "every" , Space , Str "line," , Space , Str "but" , Space , Str "you" , Space , Str "can" , Space , Str "also" , Space , Str "be" , Space , Str "lazy" , Space , Str "and" , Space , Str "just" , SoftBreak , Str "indent" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "of" , Space , Str "each" , Space , Str "block." ] ] , Space , Str "This" , Space , Str "should" , Space , Emph [ Str "not" ] , Space , Str "be" , Space , Str "a" , SoftBreak , Str "footnote" , Space , Str "reference," , Space , Str "because" , Space , Str "it" , Space , Str "contains" , Space , Str "a" , Space , Str "space.[^my" , Space , Str "note]" , Space , Str "Here" , Space , Str "is" , Space , Str "an" , Space , Str "inline" , SoftBreak , Str "note." , Note [ Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "easier" ] , Space , Str "to" , Space , Str "type." , Space , Str "Inline" , Space , Str "notes" , Space , Str "may" , Space , Str "contain" , SoftBreak , Link ( "" , [] , [] ) [ Str "links" ] ( "http://google.com" , "" ) , Space , Str "and" , Space , Code ( "" , [] , [] ) "]" , Space , Str "verbatim" , Space , Str "characters," , Space , Str "as" , Space , Str "well" , Space , Str "as" , SoftBreak , Str "[bracketed" , Space , Str "text]." ] ] ] , BlockQuote [ Para [ Str "Notes" , Space , Str "can" , Space , Str "go" , Space , Str "in" , Space , Str "quotes." , Note [ Para [ Str "In" , Space , Str "quote." ] ] ] ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "And" , Space , Str "in" , Space , Str "list" , Space , Str "items." , Note [ Para [ Str "In" , Space , Str "list." ] ] ] ] ] , Para [ Str "This" , Space , Str "paragraph" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "part" , Space , Str "of" , Space , Str "the" , Space , Str "note," , Space , Str "as" , Space , Str "it" , Space , Str "is" , Space , Str "not" , Space , Str "indented." ] ] ] ================================================ FILE: test/docbook-chapter.docbook ================================================ Test Chapter This chapter uses recursive sections.
    Like a Sect1 This section is like a Sect1.
    Like a Sect2 This section is like a Sect2.
    Like a Sect3 This section is like a Sect3.
    Like a Sect4 This section is like a Sect4.
    Like a Sect5 This section is like a Sect5.
    Would be like a Sect6 This section would be like a Sect6, if there was one.
    Would be like a Sect7 This section would be like a Sect7, if there was one.
    ================================================ FILE: test/docbook-chapter.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "" , [] , [] ) [ Str "Test" , Space , Str "Chapter" ] , Para [ Str "This" , Space , Str "chapter" , Space , Str "uses" , Space , Str "recursive" , Space , Str "sections." ] , Header 2 ( "" , [] , [] ) [ Str "Like" , Space , Str "a" , Space , Str "Sect1" ] , Para [ Str "This" , Space , Str "section" , Space , Str "is" , Space , Str "like" , Space , Str "a" , Space , Str "Sect1." ] , Header 3 ( "" , [] , [] ) [ Str "Like" , Space , Str "a" , Space , Str "Sect2" ] , Para [ Str "This" , Space , Str "section" , Space , Str "is" , Space , Str "like" , Space , Str "a" , Space , Str "Sect2." ] , Header 4 ( "" , [] , [] ) [ Str "Like" , Space , Str "a" , Space , Str "Sect3" ] , Para [ Str "This" , Space , Str "section" , Space , Str "is" , Space , Str "like" , Space , Str "a" , Space , Str "Sect3." ] , Header 5 ( "" , [] , [] ) [ Str "Like" , Space , Str "a" , Space , Str "Sect4" ] , Para [ Str "This" , Space , Str "section" , Space , Str "is" , Space , Str "like" , Space , Str "a" , Space , Str "Sect4." ] , Header 6 ( "" , [] , [] ) [ Str "Like" , Space , Str "a" , Space , Str "Sect5" ] , Para [ Str "This" , Space , Str "section" , Space , Str "is" , Space , Str "like" , Space , Str "a" , Space , Str "Sect5." ] , Header 7 ( "" , [] , [] ) [ Str "Would" , Space , Str "be" , Space , Str "like" , Space , Str "a" , Space , Str "Sect6" ] , Para [ Str "This" , Space , Str "section" , Space , Str "would" , Space , Str "be" , Space , Str "like" , Space , Str "a" , Space , Str "Sect6," , Space , Str "if" , Space , Str "there" , Space , Str "was" , Space , Str "one." ] , Header 8 ( "" , [] , [] ) [ Str "Would" , Space , Str "be" , Space , Str "like" , Space , Str "a" , Space , Str "Sect7" ] , Para [ Str "This" , Space , Str "section" , Space , Str "would" , Space , Str "be" , Space , Str "like" , Space , Str "a" , Space , Str "Sect7," , Space , Str "if" , Space , Str "there" , Space , Str "was" , Space , Str "one." ] ] ================================================ FILE: test/docbook-reader.docbook ================================================ ]>
    Pandoc Test Suite John MacFarlane Anonymous July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. Headers Level 2 with an <ulink url="/url">embedded link</ulink> Level 3 with <emphasis>emphasis</emphasis> Level 4 Level 5 Hi. Level 1 Level 2 with <emphasis>emphasis</emphasis> Level 3 with no blank line Level 4 An unnumbered section. Level 2 with no blank line Paragraphs Here’s a regular paragraph. And here’s a regular paragraph with a role. 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 a list item. Here’s one with a bullet. * criminey. Block Quotes E-mail style:
    This is a block quote. It is pretty short.
    This is a block quote with a role.
    Code in a block quote: sub status { print "working"; } sub status { print "working with line numbers"; } % ls A list: item one item two Nested block quotes:
    nested
    nested
    This should not be a block quote: 2 > 1. And a following paragraph.
    Code Blocks Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ Lists Unordered Asterisks loose: asterisk 1 asterisk 2 asterisk 3 Asterisks tight: Plus 1 Plus 2 Plus 3 Ordered First Second Third with role: First Second Third and tight: One Two Three Multiple paragraphs: Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. Item 2. Item 3. Nested Tab Tab Tab Here’s another: First Second: Fee Fie Foe Third Same thing but with paragraphs: First Second: Fee Fie Foe Third Tabs and spaces this is a list item indented with tabs this is a list item indented with spaces this is an example list item indented with tabs this is an example list item indented with spaces Fancy list markers begins with 2 and now 3 with a continuation sublist with roman numerals, starting with 4 more items a subsublist a subsublist Nesting: Upper Alpha Upper Roman. Decimal start with 6 Lower alpha with paren Autonumbering: Autonumber. More. Nested. Should not be a list item: M.A. 2007 B. Williams Callout Simple. A __letrec is equivalent to a normal Haskell &let;. &GHC; compiled the body of our list comprehension into a loop named go_s1YC. If our &case; expression matches the empty list, we return the empty list. This is reassuringly familiar. Definition Lists apple red fruit orange orange fruit banana yellow fruit Multiple blocks with italics: apple red fruit contains seeds, crisp, pleasant to taste orange orange fruit { orange code block }
    orange block quote
    Multiple definitions, loose: apple red fruit computer orange orange fruit bank Blank line after term, indented marker, alternate markers: apple red fruit computer orange orange fruit sublist sublist
    Inline Markup This is emphasized, and so is this. This is strong, and so is this. An emphasized link. This is strong and em. So is this word. This is strong and em. So is this word. So is this word with a role. So is this phrase with a role. This is code: >, $, \, \$, <html>. More code: Class and Type Referencing a man page: nix.conf5 This is strikeout. Superscripts: abcd ahello ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. Smart quotes, ellipses, dashes Hello, said the spider. Shelob is my name. A, B, and C are letters. He said, I want to go. Were you alive in the 70’s? Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. e = m c 2 1 e = m c 2 e = m c 2 Special Characters Here is some unicode: I hat: Î o umlaut: ö section: § set membership: ∈ copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - Links Explicit Just a URL. URL and title. URL and title. URL and title. URL and title URL and title with_underscore nobody@nowhere.net Empty. Reference Foo bar. Foo bar. Foo bar. With embedded [brackets]. b by itself should be a link. Indented once. Indented twice. Indented thrice. This should [not][] be a link. [not]: /url Foo bar. Foo biz. With ampersands Here’s a link with an ampersand in the URL. Here’s a link with an amersand in the link text: AT&T. Here’s an inline link. Here’s an inline link in pointy braces. Autolinks With an ampersand: http://example.com/?foo=1&bar=2 In a list? http://example.com/ It should. An e-mail address: nobody@nowhere.net
    Blockquoted: http://example.com/
    Auto-links should not occur here: <http://example.com/> or here: <http://example.com/>
    Images From Voyage dans la Lune by Georges Melies (1902):
    lalune fig caption lalune alt text shadowed by fig caption
    Here is a movie icon. And here a second movie alt text icon. And here a third movie alt text icon. lalune no figure alt text
    Footnotes Here is a footnote reference, Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. and another. Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). { <code> } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].
    Notes can go in quotes. In quote.
    And in list items. In list. This paragraph should not be part of the note, as it is not indented.
    Tables Simple table with caption: Demonstration of simple table syntax. Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1
    Simple table without caption: Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1 Simple table indented two spaces: Demonstration of simple table syntax. Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1
    Multiline table with caption: Here's the caption. It may span multiple lines. Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows.
    Table with attributes Attribute table caption
    header cell 1 header cell 2
    body cell 1 body cell 2
    Table with attributes, without caption header cell 1 header cell 2 body cell 1 body cell 2 Multiline table without caption: Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. Table without column headers: 12 12 12 12 123 123 123 123 1 1 1 1 Multiline table without column headers: First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. Multiline table with cells spanning multiple columns and rows without caption. Columns A B C A1 + B1 C1 + C2 A2 + B2 + A3 + B3 C3 An Example Procedure A Step Another Step Substeps can be nested indefinitely deep. A Final Step
    Index terms In the simplest case, index termsindex term consists of just a <primary> element, but index termmulti-level they can also consist of a <primary> and <secondary> element, and index termmulti-level3-level can even include a <tertiary> term. Index terms can also refer to other index terms: index cross referencingindex termcross referencesindex cross referencingexclusively, using the <see> tag; or index cross referencingcross referencing as a reference to related terms, using the <seealso> tag. foodbig baguette supremeNested content in index term elements is flattened. Abbreviated title Abbr. title
    ================================================ FILE: test/docbook-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "John" , Space , Str "MacFarlane" ] , MetaInlines [ Str "Anonymous" ] ] ) , ( "date" , MetaInlines [ Str "July" , Space , Str "17," , Space , Str "2006" ] ) , ( "title" , MetaInlines [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , Space , Str "John" , SoftBreak , Str "Gruber\8217s" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , Header 1 ( "headers" , [] , [ ( "role" , "sect1role" ) ] ) [ Str "Headers" ] , Header 2 ( "level-2-with-an-embedded-link" , [] , [ ( "role" , "sect2role" ) ] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "/url" , "" ) ] , Header 3 ( "level-3-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 4 ( "level-4" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Header 5 ( "level-5" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Para [ Str "Hi." ] , Header 1 ( "level-1" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Header 2 ( "level-2-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 3 ( "level-3" , [] , [] ) [ Str "Level" , Space , Str "3" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 4 ( "" , [ "unnumbered" ] , [] ) [ Str "Level" , Space , Str "4" ] , Para [ Str "An" , Space , Str "unnumbered" , Space , Str "section." ] , Header 2 ( "level-2" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , 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 , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , Space , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , SoftBreak , Str "item." , Space , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , Space , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , SoftBreak , Str "a" , Space , Str "list" , Space , Str "item." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , Space , Str "*" , Space , Str "criminey." ] , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , Space , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , 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" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" , CodeBlock ( "" , [ "numberLines" ] , [] ) "sub status {\n print \"working with line numbers\";\n}" , CodeBlock ( "" , [] , [] ) "% ls" , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "item" , Space , Str "one" ] ] , [ Para [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] ] , BlockQuote [ Para [ Str "nested" ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" , Space , Str "2" , Space , Str ">" , Space , Str "1." ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n\nthis code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Plus" , Space , Str "1" ] ] , [ Plain [ Str "Plus" , Space , Str "2" ] ] , [ Plain [ Str "Plus" , Space , Str "3" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ 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 ) [ [ Plain [ Str "One" ] ] , [ Plain [ Str "Two" ] ] , [ Plain [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog\8217s" , SoftBreak , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Header 2 ( "nested" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "another:" ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing" , Space , Str "but" , Space , Str "with" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "tabs-and-spaces" , [] , [] ) [ Str "Tabs" , Space , Str "and" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] ] ] ] ] , Header 2 ( "fancy-list-markers" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , OrderedList ( 2 , Decimal , DefaultDelim ) [ [ Para [ Str "begins" , Space , Str "with" , Space , Str "2" ] ] , [ Para [ Str "and" , Space , Str "now" , Space , Str "3" ] , Para [ Str "with" , Space , Str "a" , Space , Str "continuation" ] , OrderedList ( 4 , LowerRoman , DefaultDelim ) [ [ Para [ Str "sublist" , Space , Str "with" , Space , Str "roman" , Space , Str "numerals," , Space , Str "starting" , Space , Str "with" , Space , Str "4" ] ] , [ Para [ Str "more" , Space , Str "items" ] , OrderedList ( 1 , UpperAlpha , DefaultDelim ) [ [ Para [ Str "a" , Space , Str "subsublist" ] ] , [ Para [ Str "a" , Space , Str "subsublist" ] ] ] ] ] ] ] , Para [ Str "Nesting:" ] , OrderedList ( 1 , UpperAlpha , DefaultDelim ) [ [ Para [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , DefaultDelim ) [ [ Para [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , DefaultDelim ) [ [ Para [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , DefaultDelim ) [ [ Para [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "Autonumber." ] ] , [ Para [ Str "More." ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "Nested." ] ] ] ] ] , Para [ Str "Should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "list" , Space , Str "item:" ] , Para [ Str "M.A.\160\&2007" ] , Para [ Str "B." , Space , Str "Williams" ] , Header 2 ( "callout" , [] , [] ) [ Str "Callout" ] , Para [ Str "Simple." ] , BulletList [ [ Para [ Str "A" , Space , Code ( "" , [] , [] ) "__letrec" , Space , Str "is" , Space , Str "equivalent" , Space , Str "to" , Space , Str "a" , Space , Str "normal" , SoftBreak , Str "Haskell" , Space , Str "LET." ] ] , [ Para [ Str "GHC" , Space , Str "compiled" , Space , Str "the" , Space , Str "body" , Space , Str "of" , Space , Str "our" , Space , Str "list" , Space , Str "comprehension" , Space , Str "into" , SoftBreak , Str "a" , Space , Str "loop" , Space , Str "named" , Space , Code ( "" , [] , [] ) "go_s1YC" , Str "." ] ] , [ Para [ Str "If" , Space , Str "our" , Space , Str "CASE" , Space , Str "expression" , Space , Str "matches" , Space , Str "the" , Space , Str "empty" , Space , Str "list," , Space , Str "we" , SoftBreak , Str "return" , Space , Str "the" , Space , Str "empty" , Space , Str "list." , Space , Str "This" , Space , Str "is" , Space , Str "reassuringly" , SoftBreak , Str "familiar." ] ] ] , Header 1 ( "definition-lists" , [] , [] ) [ Str "Definition" , Space , Str "Lists" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Para [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "with" , Space , Str "italics:" ] , DefinitionList [ ( [ Emph [ Str "apple" ] ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "contains" , Space , Str "seeds," , Space , Str "crisp," , Space , Str "pleasant" , Space , Str "to" , Space , Str "taste" ] ] ] ) , ( [ Emph [ Str "orange" ] ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , CodeBlock ( "" , [] , [] ) "{ orange code block }" , BlockQuote [ Para [ Str "orange" , Space , Str "block" , Space , Str "quote" ] ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] , [ Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] , [ Para [ Str "bank" ] ] ] ) ] , Para [ Str "Blank" , Space , Str "line" , Space , Str "after" , Space , Str "term," , Space , Str "indented" , Space , Str "marker," , Space , Str "alternate" , Space , Str "markers:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] , [ Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "sublist" ] ] , [ Para [ Str "sublist" ] ] ] ] ] ) ] , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , SoftBreak , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , SoftBreak , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "An" , Space , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , Space , Str "link" ] ( "/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , SoftBreak , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , SoftBreak , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , 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" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , SoftBreak , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , SoftBreak , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Str "More" , Space , Str "code:" , Space , Code ( "" , [] , [] ) "Class" , Space , Str "and" , Space , Code ( "" , [] , [] ) "Type" ] , Para [ Str "Referencing" , Space , Str "a" , Space , Str "man" , Space , Str "page:" , Space , Code ( "" , [ "citerefentry" ] , [] ) "nix.conf(5)" ] , Para [ Strikeout [ Str "This" , Space , Str "is" , SoftBreak , Emph [ Str "strikeout" ] , Str "." ] ] , Para [ Str "Superscripts:" , Space , Str "a" , Superscript [ Str "bc" ] , Str "d" , SoftBreak , Str "a" , Superscript [ Emph [ Str "hello" ] ] , SoftBreak , Str "a" , Superscript [ Str "hello\160there" ] , Str "." ] , Para [ Str "Subscripts:" , Space , Str "H" , Subscript [ Str "2" ] , Str "O," , Space , Str "H" , Subscript [ Str "23" ] , Str "O," , SoftBreak , Str "H" , Subscript [ Str "many\160of\160them" ] , Str "O." ] , Para [ Str "These" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "superscripts" , Space , Str "or" , Space , Str "subscripts," , Space , Str "because" , Space , Str "of" , Space , Str "the" , Space , Str "unescaped" , SoftBreak , Str "spaces:" , Space , Str "a^b" , Space , Str "c^d," , Space , Str "a~b" , Space , Str "c~d." ] , Header 1 ( "smart-quotes-ellipses-dashes" , [] , [] ) [ Str "Smart" , Space , Str "quotes," , Space , Str "ellipses," , Space , Str "dashes" ] , Para [ Quoted DoubleQuote [ Str "Hello," ] , Space , Str "said" , Space , Str "the" , Space , Str "spider." , Space , Quoted DoubleQuote [ Quoted SingleQuote [ Str "Shelob" ] , Space , Str "is" , Space , Str "my" , SoftBreak , Str "name." ] ] , Para [ Quoted DoubleQuote [ Str "A" ] , Str "," , Space , Quoted DoubleQuote [ Str "B" ] , Str "," , Space , Str "and" , Space , Quoted DoubleQuote [ Str "C" ] , Space , Str "are" , Space , Str "letters." ] , Para [ Quoted DoubleQuote [ Str "He" , Space , Str "said," , Space , Quoted SingleQuote [ Str "I" , Space , Str "want" , Space , Str "to" , Space , Str "go." ] ] , Space , Str "Were" , Space , Str "you" , Space , Str "alive" , Space , Str "in" , Space , Str "the" , SoftBreak , Str "70\8217s?" ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one\8212two" , Space , Str "\8212" , Space , Str "three\8212four" , Space , Str "\8212" , Space , Str "five." ] , Para [ Str "Dashes" , Space , Str "between" , Space , Str "numbers:" , Space , Str "5\8211\&7," , Space , Str "255\8211\&66," , Space , Str "1987\8211\&1999." ] , Para [ Str "Ellipses\8230and\8230and\8230." ] , Header 1 ( "math" , [] , [] ) [] , Para [ Math DisplayMath "e = mc^{2}" , Math DisplayMath "1" , SoftBreak , Math InlineMath "e = mc^{2}" , SoftBreak , Math DisplayMath "e = mc^{2}" ] , Header 1 ( "special-characters" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Para [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Para [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Para [ Str "section:" , Space , Str "\167" ] ] , [ Para [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Para [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Header 2 ( "explicit" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "with_underscore" ] ( "/url/with_underscore" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "nobody@nowhere.net" ] ( "mailto:nobody@nowhere.net" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Empty" ] ( "" , "" ) , Str "." ] , Header 2 ( "reference" , [] , [] ) [ Str "Reference" ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "[brackets]" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "b" ] ( "/url/" , "" ) , Space , Str "by" , Space , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "once" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "twice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "thrice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "This" , Space , Str "should" , Space , Str "[not][]" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , CodeBlock ( "" , [] , [] ) "[not]: /url" , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "biz" ] ( "/url/" , "" ) , Str "." ] , Header 2 ( "with-ampersands" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , SoftBreak , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , SoftBreak , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "http://att.com/" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" , Space , Str "in" , Space , Str "pointy" , SoftBreak , Str "braces" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Header 2 ( "autolinks" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , SoftBreak , Link ( "" , [] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Para [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Para [ Link ( "" , [] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Para [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , Space , Link ( "" , [] , [] ) [ Str "nobody@nowhere.net" ] ( "mailto:nobody@nowhere.net" , "" ) ] , BlockQuote [ Para [ Str "Blockquoted:" , SoftBreak , Link ( "" , [] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , Para [ Str "Auto-links" , Space , Str "should" , Space , Str "not" , Space , Str "occur" , Space , Str "here:" , SoftBreak , Code ( "" , [] , [] ) "" ] , CodeBlock ( "" , [] , [] ) "or here: " , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Quoted DoubleQuote [ Str "Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune" ] , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "lalune" , Space , Str "fig" , Space , Str "caption" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "lalune" , Space , Str "alt" , Space , Str "text" , Space , Str "shadowed" , Space , Str "by" , Space , Str "fig" , Space , Str "caption" ] ( "lalune.jpg" , "" ) ] ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [] ( "movie.jpg" , "" ) , Space , Str "icon." , SoftBreak , Str "And" , Space , Str "here" , Space , Str "a" , Space , Str "second" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "alt" , Space , Str "text" ] ( "movie.jpg" , "" ) , Space , Str "icon." , SoftBreak , Str "And" , Space , Str "here" , Space , Str "a" , Space , Str "third" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "alt" , Space , Str "text" ] ( "movie.jpg" , "" ) , Space , Str "icon." ] , Para [ Image ( "" , [] , [] ) [ Str "lalune" , Space , Str "no" , Space , Str "figure" , Space , Str "alt" , Space , Str "text" ] ( "lalune.jpg" , "" ) ] , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Note [ Para [ Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "footnote." , Space , Str "It" , Space , Str "can" , Space , Str "go" , Space , Str "anywhere" , Space , Str "after" , Space , Str "the" , Space , Str "footnote" , Space , Str "reference." , SoftBreak , Str "It" , Space , Str "need" , Space , Str "not" , Space , Str "be" , Space , Str "placed" , Space , Str "at" , Space , Str "the" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "document." ] ] , Space , Str "and" , Space , Str "another." , Note [ Para [ Str "Here\8217s" , Space , Str "the" , Space , Str "long" , Space , Str "note." , Space , Str "This" , Space , Str "one" , Space , Str "contains" , Space , Str "multiple" , Space , Str "blocks." ] , Para [ Str "Subsequent" , Space , Str "blocks" , Space , Str "are" , Space , Str "indented" , Space , Str "to" , Space , Str "show" , Space , Str "that" , Space , Str "they" , Space , Str "belong" , Space , Str "to" , Space , Str "the" , SoftBreak , Str "footnote" , Space , Str "(as" , Space , Str "with" , Space , Str "list" , Space , Str "items)." ] , CodeBlock ( "" , [] , [] ) " { }" , Para [ Str "If" , Space , Str "you" , Space , Str "want," , Space , Str "you" , Space , Str "can" , Space , Str "indent" , Space , Str "every" , Space , Str "line," , Space , Str "but" , Space , Str "you" , Space , Str "can" , Space , Str "also" , Space , Str "be" , Space , Str "lazy" , Space , Str "and" , SoftBreak , Str "just" , Space , Str "indent" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "of" , Space , Str "each" , Space , Str "block." ] ] , Space , Str "This" , Space , Str "should" , Space , Emph [ Str "not" ] , Space , Str "be" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , SoftBreak , Str "because" , Space , Str "it" , Space , Str "contains" , Space , Str "a" , Space , Str "space.[^my" , Space , Str "note]" , Space , Str "Here" , Space , Str "is" , Space , Str "an" , Space , Str "inline" , Space , Str "note." , Note [ Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "easier" ] , Space , Str "to" , Space , Str "type." , Space , Str "Inline" , Space , Str "notes" , Space , Str "may" , Space , Str "contain" , SoftBreak , Link ( "" , [] , [] ) [ Str "links" ] ( "http://google.com" , "" ) , Space , Str "and" , Space , Code ( "" , [] , [] ) "]" , SoftBreak , Str "verbatim" , Space , Str "characters," , Space , Str "as" , Space , Str "well" , Space , Str "as" , Space , Str "[bracketed" , Space , Str "text]." ] ] ] , BlockQuote [ Para [ Str "Notes" , Space , Str "can" , Space , Str "go" , Space , Str "in" , Space , Str "quotes." , Note [ Para [ Str "In" , Space , Str "quote." ] ] ] ] , OrderedList ( 1 , Decimal , DefaultDelim ) [ [ Para [ Str "And" , Space , Str "in" , Space , Str "list" , Space , Str "items." , Note [ Para [ Str "In" , Space , Str "list." ] ] ] ] ] , Para [ Str "This" , Space , Str "paragraph" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "part" , Space , Str "of" , Space , Str "the" , Space , Str "note," , Space , Str "as" , Space , Str "it" , Space , Str "is" , Space , Str "not" , Space , Str "indented." ] , Header 1 ( "tables" , [] , [] ) [ Str "Tables" ] , Para [ Str "Simple" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "indented" , Space , Str "two" , Space , Str "spaces:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Here's" , Space , Str "the" , Space , Str "caption." , Space , Str "It" , Space , Str "may" , Space , Str "span" , Space , Str "multiple" , Space , Str "lines." ] ]) [ ( AlignCenter , ColWidth 0.2 ) , ( AlignLeft , ColWidth 0.2 ) , ( AlignRight , ColWidth 0.3 ) , ( AlignLeft , ColWidth 0.3 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , Space , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , Space , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , Space , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , Space , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here's" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , Space , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "with" , Space , Str "attributes" ] , Table ( "mytableid1" , [ "mytableclass1" , "mytableclass2" ] , [ ( "role" , "tablerole1" ) , ( "custom-style" , "mytabstyle1" ) ] ) (Caption Nothing [ Plain [ Str "Attribute" , Space , Str "table" , Space , Str "caption" ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "body" , Space , Str "cell" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "body" , Space , Str "cell" , Space , Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "with" , Space , Str "attributes," , Space , Str "without" , Space , Str "caption" ] , Table ( "mytableid2" , [ "mytableclass3" , "mytableclass4" ] , [ ( "role" , "tablerole2" ) , ( "custom-style" , "mytabstyle2" ) ] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "body" , Space , Str "cell" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "body" , Space , Str "cell" , Space , Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidth 0.1 ) , ( AlignLeft , ColWidth 0.2 ) , ( AlignRight , ColWidth 0.3 ) , ( AlignLeft , ColWidth 0.4 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , Space , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , Space , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , Space , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , Space , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here's" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , Space , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidth 0.25 ) , ( AlignLeft , ColWidth 0.25 ) , ( AlignRight , ColWidth 0.25 ) , ( AlignLeft , ColWidth 0.25 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , Space , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here's" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , Space , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "with" , Space , Str "cells" , Space , Str "spanning" , Space , Str "multiple" , Space , Str "columns" , Space , Str "and" , Space , Str "rows" , Space , Str "without" , Space , Str "caption." ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Columns" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "C" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "A1" , Space , Str "+" , Space , Str "B1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "C1" , Space , Str "+" , Space , Str "C2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 2) [ Plain [ Str "A2" , Space , Str "+" , Space , Str "B2" , Space , Str "+" , Space , Str "A3" , Space , Str "+" , Space , Str "B3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "C3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "A" , Space , Str "Step" ] ] , [ Para [ Str "Another" , Space , Str "Step" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Substeps" , Space , Str "can" , Space , Str "be" , Space , Str "nested" , Space , Str "indefinitely" , Space , Str "deep." ] ] ] ] , [ Para [ Str "A" , Space , Str "Final" , Space , Str "Step" ] ] ] , Header 1 ( "indexterms" , [] , [] ) [ Str "Index" , Space , Str "terms" ] , Para [ Str "In" , Space , Str "the" , Space , Str "simplest" , Space , Str "case," , Space , Str "index" , Space , Str "terms" , Span ( "" , [ "indexterm" ] , [ ( "primary" , "index term" ) ] ) [] , Space , Str "consists" , Space , Str "of" , Space , Str "just" , Space , Str "a" , Space , Code ( "" , [] , [] ) "" , Space , Str "element," , Space , Str "but" , Space , Span ( "" , [ "indexterm" ] , [ ( "primary" , "index term" ) , ( "secondary" , "multi-level" ) ] ) [] , Space , Str "they" , Space , Str "can" , Space , Str "also" , Space , Str "consist" , Space , Str "of" , Space , Str "a" , Space , Code ( "" , [] , [] ) "" , Space , Str "and" , Space , Code ( "" , [] , [] ) "" , Space , Str "element," , Space , Str "and" , Space , Span ( "" , [ "indexterm" ] , [ ( "primary" , "index term" ) , ( "secondary" , "multi-level" ) , ( "tertiary" , "3-level" ) ] ) [] , Space , Str "can" , Space , Str "even" , Space , Str "include" , Space , Str "a" , Space , Code ( "" , [] , [] ) "" , Space , Str "term." ] , Para [ Str "Index" , Space , Str "terms" , Space , Str "can" , Space , Str "also" , Space , Str "refer" , Space , Str "to" , Space , Str "other" , Space , Str "index" , Space , Str "terms:" , Space , Span ( "" , [ "indexterm" ] , [ ( "primary" , "index cross referencing" ) ] ) [] , Span ( "" , [ "indexterm" ] , [ ( "primary" , "index term" ) , ( "secondary" , "cross references" ) , ( "see" , "index cross referencing" ) ] ) [] , Str "exclusively," , Space , Str "using" , Space , Str "the" , Space , Code ( "" , [] , [] ) "" , Space , Str "tag;" , Space , Str "or" , Space , Span ( "" , [ "indexterm" ] , [ ( "primary" , "index cross referencing" ) , ( "seealso" , "cross referencing" ) ] ) [] , Space , Str "as" , Space , Str "a" , Space , Str "reference" , Space , Str "to" , Space , Str "related" , Space , Str "terms," , Space , Str "using" , Space , Str "the" , Space , Code ( "" , [] , [] ) "" , Space , Str "tag." ] , Para [ Span ( "" , [ "indexterm" ] , [ ( "primary" , "food" ) , ( "secondary" , "big baguette supreme" ) ] ) [] , Str "Nested" , Space , Str "content" , Space , Str "in" , Space , Str "index" , Space , Str "term" , Space , Str "elements" , Space , Str "is" , Space , Str "flattened." ] , Header 1 ( "titleabbrev" , [] , [ ( "titleabbrev" , "Abbr. title" ) ] ) [ Str "Abbreviated" , Space , Str "title" ] ] ================================================ FILE: test/docbook-xref.docbook ================================================ An Example Book XRef Samples This paragraph demonstrates several features of XRef. A straight link generates the cross-reference text: . A link to an element with an XRefLabel: . A link with an EndTerm: . A link to an cmdsynopsis element: . A link to an funcsynopsis element: . A link to a figure element: . A link to a table element: . A link to a part element: . A link to a book element: . The Second Chapter Some content here Later Part The Third Chapter Some content here The Fourth Chapter Chapter 4 Some content here chgrp -R -H -L -P -f group file int max int int1 int int2
    The Pythagorean Theorem Illustrated An illustration of the Pythagorean Theorem
    Supported features by version Version Feat Free no
    ================================================ FILE: test/docbook-xref.native ================================================ Pandoc Meta { unMeta = fromList [ ( "title" , MetaInlines [ Str "An" , Space , Str "Example" , Space , Str "Book" ] ) ] } [ Header 2 ( "ch01" , [] , [] ) [ Str "XRef" , Space , Str "Samples" ] , Para [ Str "This" , Space , Str "paragraph" , Space , Str "demonstrates" , Space , Str "several" , Space , Str "features" , Space , Str "of" , SoftBreak , Str "XRef." ] , BulletList [ [ Para [ Str "A" , Space , Str "straight" , Space , Str "link" , Space , Str "generates" , Space , Str "the" , SoftBreak , Str "cross-reference" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "The" , Space , Str "Second" , Space , Str "Chapter" ] ( "#ch02" , "" ) , Str "." ] ] , [ Para [ Str "A" , Space , Str "link" , Space , Str "to" , Space , Str "an" , Space , Str "element" , Space , Str "with" , Space , Str "an" , SoftBreak , Str "XRefLabel:" , SoftBreak , Link ( "" , [] , [] ) [ Str "Chapter" , Space , Str "the" , Space , Str "Third" ] ( "#ch03" , "" ) , Str "." ] ] , [ Para [ Str "A" , Space , Str "link" , Space , Str "with" , Space , Str "an" , SoftBreak , Str "EndTerm:" , SoftBreak , Link ( "" , [] , [] ) [ Str "Chapter" , Space , Str "4" ] ( "#ch04" , "" ) , Str "." ] ] , [ Para [ Str "A" , Space , Str "link" , Space , Str "to" , Space , Str "an" , SoftBreak , Str "cmdsynopsis" , Space , Str "element:" , Space , Link ( "" , [] , [] ) [ Str "chgrp" ] ( "#cmd01" , "" ) , Str "." ] ] , [ Para [ Str "A" , Space , Str "link" , Space , Str "to" , Space , Str "an" , SoftBreak , Str "funcsynopsis" , Space , Str "element:" , Space , Link ( "" , [] , [] ) [ Str "max" ] ( "#func01" , "" ) , Str "." ] ] , [ Para [ Str "A" , Space , Str "link" , Space , Str "to" , Space , Str "a" , SoftBreak , Str "figure" , Space , Str "element:" , Space , Link ( "" , [] , [] ) [ Str "The" , Space , Str "Pythagorean" , Space , Str "Theorem" , Space , Str "Illustrated" ] ( "#fig01" , "" ) , Str "." ] ] , [ Para [ Str "A" , Space , Str "link" , Space , Str "to" , Space , Str "a" , SoftBreak , Str "table" , Space , Str "element:" , Space , Link ( "" , [] , [] ) [ Str "Supported" , Space , Str "features" , Space , Str "by" , Space , Str "version" ] ( "#table01" , "" ) , Str "." ] ] , [ Para [ Str "A" , Space , Str "link" , Space , Str "to" , Space , Str "a" , SoftBreak , Str "part" , Space , Str "element:" , Space , Link ( "" , [] , [] ) [ Str "Later" , Space , Str "Part" ] ( "#part01" , "" ) , Str "." ] ] , [ Para [ Str "A" , Space , Str "link" , Space , Str "to" , Space , Str "a" , SoftBreak , Str "book" , Space , Str "element:" , Space , Link ( "" , [] , [] ) [ Str "An" , Space , Str "Example" , Space , Str "Book" ] ( "#book01" , "" ) , Str "." ] ] ] , Header 2 ( "ch02" , [] , [] ) [ Str "The" , Space , Str "Second" , Space , Str "Chapter" ] , Para [ Str "Some" , Space , Str "content" , Space , Str "here" ] , Header 1 ( "part01" , [] , [] ) [ Str "Later" , Space , Str "Part" ] , Header 2 ( "ch03" , [] , [] ) [ Str "The" , Space , Str "Third" , Space , Str "Chapter" ] , Para [ Str "Some" , Space , Str "content" , Space , Str "here" ] , Header 2 ( "ch04" , [] , [ ( "titleabbrev" , "Chapter 4" ) ] ) [ Str "The" , Space , Str "Fourth" , Space , Str "Chapter" ] , Para [ Str "Some" , Space , Str "content" , Space , Str "here" ] , Plain [ Str "chgrp" ] , Plain [ Str "-R" ] , Plain [ Str "-H" ] , Plain [ Str "-L" ] , Plain [ Str "-P" ] , Plain [ Str "-f" ] , Plain [ Str "group" ] , Plain [ Str "file" ] , Plain [ Str "int" ] , Plain [ Str "max" ] , Plain [ Str "int" ] , Plain [ Str "int1" ] , Plain [ Str "int" ] , Plain [ Str "int2" ] , Figure ( "fig01" , [] , [] ) (Caption Nothing [ Plain [ Str "The" , Space , Str "Pythagorean" , Space , Str "Theorem" , Space , Str "Illustrated" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "An" , Space , Str "illustration" , Space , Str "of" , Space , Str "the" , Space , Str "Pythagorean" , Space , Str "Theorem" ] ( "figures/pythag.png" , "" ) ] ] , Table ( "table01" , [] , [] ) (Caption Nothing [ Plain [ Str "Supported" , Space , Str "features" , Space , Str "by" , Space , Str "version" ] ]) [ ( AlignDefault , ColWidth 0.5 ) , ( AlignDefault , ColWidth 0.5 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Version" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Feat" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Free" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "no" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/0_level_headers.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.8615384615384616 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "User\8217s" , Space , Str "Guide" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "11" , Space , Str "August" , Space , Str "2017" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "CONTENTS" ] , Para [ Strong [ Str "Section" , Space , Str "Page" ] ] , Para [ Str "FIGURES" , Space , Link ( "" , [] , [] ) [ Str "iv" ] ( "#figures" , "" ) ] , Para [ Str "TABLES" , Space , Link ( "" , [] , [] ) [ Str "v" ] ( "#tables" , "" ) ] , Para [ Str "SECTION" , Space , Str "1" , Space , Str "Introduction" , Space , Link ( "" , [] , [] ) [ Str "2" ] ( "#introduction" , "" ) ] , Header 1 ( "figures" , [ "Heading-0" ] , [] ) [ Str "FIGURES" ] , Para [ Strong [ Str "Figure" , Space , Str "Page" ] ] , Para [ Strong [ Str "No" , Space , Str "table" , Space , Str "of" , Space , Str "figures" , Space , Str "entries" , Space , Str "found." ] ] , Header 1 ( "tables" , [ "Heading-0" ] , [] ) [ Str "TABLES" ] , Para [ Strong [ Str "Table" , Space , Str "Page" ] ] , Para [ Strong [ Str "No" , Space , Str "table" , Space , Str "of" , Space , Str "figures" , Space , Str "entries" , Space , Str "found." ] ] , Header 1 ( "introduction" , [] , [] ) [ Str "Introduction" ] , Para [ Str "Nothing" , Space , Str "to" , Space , Str "introduce," , Space , Str "yet." ] ] ================================================ FILE: test/docx/adjacent_codeblocks.native ================================================ [Para [Str "Next,",Space,Str "open",Space,Str "the",Space,Str "terminal",Space,Str "window.",Space,Str "Using",Space,Str "the",Space,Str "terminal",Space,Str "window,",Space,Str "run",Space,Str "the",Space,Str "\"ifconfig",Space,Str "-a\"",Space,Str "command",Space,Str "to",Space,Str "list",Space,Str "all",Space,Str "the",Space,Str "interfaces",Space,Str "on",Space,Str "your",Space,Str "system,",Space,Str "as",Space,Str "shown",Space,Str "here."] ,CodeBlock ("",[],[]) "# ifconfig -a\neth0 Link encap:Ethernet HWaddr 00:0c:29:69:12:7c \n inet addr:172.16.0.108 Bcast:172.16.0.255 Mask:255.255.255.0\n inet6 addr: fc00:660:0:1:20c:29ff:fe69:127c/64 Scope:Global\n inet6 addr: fe80::20c:29ff:fe69:127c/64 Scope:Link\n UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1\n RX packets:9859 errors:0 dropped:0 overruns:0 frame:0\n TX packets:1399 errors:0 dropped:0 overruns:0 carrier:0\n collisions:0 txqueuelen:1000 \n RX bytes:1920894 (1.8 MiB) TX bytes:233088 (227.6 KiB)\n Interrupt:19 Base address:0x2000 \n\nlo Link encap:Local Loopback \n inet addr:127.0.0.1 Mask:255.0.0.0\n inet6 addr: ::1/128 Scope:Host\n UP LOOPBACK RUNNING MTU:65536 Metric:1\n RX packets:372 errors:0 dropped:0 overruns:0 frame:0\n TX packets:372 errors:0 dropped:0 overruns:0 carrier:0\n collisions:0 txqueuelen:0 \n RX bytes:22320 (21.7 KiB) TX bytes:22320 (21.7 KiB)\n\nwlan0 Link encap:Ethernet HWaddr 00:c0:ca:85:00:ba \n UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1\n RX packets:0 errors:0 dropped:0 overruns:0 frame:0\n TX packets:0 errors:0 dropped:0 overruns:0 carrier:0\n collisions:0 txqueuelen:1000 \n RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)" ,Para [Str "The",Space,Str "ALFA",Space,Str "wireless",Space,Str "card",Space,Str "is",Space,Str "represented",Space,Str "by",Space,Str "the",Space,Str "\"wlan0\"",Space,Str "interface."] ,Para [Str "In",Space,Str "addition",Space,Str "to",Space,Str "the",Space,Str "interfaces",Space,Str "shown",Space,Str "in",Space,Str "the",Space,Str "ifconfig",Space,Str "output,",Space,Str "there",Space,Str "is",Space,Str "another",Space,Str "interface",Space,Str "known",Space,Str "as",Space,Str "the",Space,Emph [Str "wireless",Space,Str "physical",Space,Str "interface"],Str ".",Space,Str "We",Space,Str "can",Space,Str "identify",Space,Str "this",Space,Str "interface",Space,Str "by",Space,Str "listing",Space,Str "the",Space,Str "contents",Space,Str "of",Space,Str "the",Space,Str "/sys/class/ieee80211",Space,Str "directory,",Space,Str "as",Space,Str "shown."] ,CodeBlock ("",[],[]) "# ls /sys/class/ieee80211/\nphy0" ,Para [Str "The",Space,Str "\"phy0\"",Space,Str "interface",Space,Str "is",Space,Str "the",Space,Str "parent",Space,Str "interface",Space,Str "used",Space,Str "to",Space,Str "create",Space,Str "child",Space,Str "interfaces.",Space,Str "Note",Space,Str "that",Space,Str "if",Space,Str "you",Space,Str "unplug",Space,Str "and",Space,Str "replug",Space,Str "the",Space,Str "USB",Space,Str "interface,",Space,Str "the",Space,Str "\"phy\"",Space,Str "interface",Space,Str "number",Space,Str "will",Space,Str "increment",Space,Str "by",Space,Str "one",Space,Str "until",Space,Str "you",Space,Str "reboot",Space,Str "your",Space,Str "system."]] ================================================ FILE: test/docx/already_auto_ident.native ================================================ [Header 1 ("anchor-header",[],[]) [Str "Anchor",Space,Str "Header"] ,Para [Str "A",Space,Link ("",[],[]) [Str "link"] ("#anchor-header","")]] ================================================ FILE: test/docx/alternate_document_path.native ================================================ Pandoc (Meta {unMeta = fromList []}) [Header 1 ("test",[],[]) [Str "Test"] ,Para [Str "This",Space,Str "is",Space,Emph [Str "italic"],Str ",",Space,Strong [Str "bold"],Str ",",Space,Underline [Str "underlined"],Str ",",Space,Emph [Underline [Str "italic",Space,Str "underlined"]],Str ",",Space,Strong [Underline [Str "bold",Space,Str "underlined"]],Str ",",Space,Emph [Strong [Underline [Str "bold",Space,Str "italic",Space,Str "underlined"]]],Str "."]] ================================================ FILE: test/docx/anchor_header_after_anchor.native ================================================ [Para [Link ("",[],[]) [Str "A",Space,Str "link",Space,Str "to",Space,Str "the",Space,Str "bookmark"] ("#referenced-title" , "")] ,Para [Strong [Str "Accomplices:"]] ,Para [Str "There",Space,Str "are",Space,Str "bookmarks",Space,Str "around",Space,Str "here."] ,Para [] ,Header 1 ("referenced-title",[],[]) [Str "Referenced" ,Space ,Str "title"] ,Para [Str "Content"]] ================================================ FILE: test/docx/block_quotes.native ================================================ [Header 2 ("some-block-quotes-in-different-ways",[],[]) [Str "Some",Space,Str "block",Space,Str "quotes,",Space,Str "in",Space,Str "different",Space,Str "ways"] ,Para [Str "This",Space,Str "is",Space,Str "the",Space,Str "proper",Space,Str "way,",Space,Str "with",Space,Str "a",Space,Str "style"] ,BlockQuote [Para [Str "I",Space,Str "don\8217t",Space,Str "know",Space,Str "why",Space,Str "this",Space,Str "would",Space,Str "be",Space,Str "in",Space,Str "italics,",Space,Str "but",Space,Str "so",Space,Str "it",Space,Str "appears",Space,Str "to",Space,Str "be",Space,Str "on",Space,Str "my",Space,Str "screen."]] ,Para [Str "And",Space,Str "this",Space,Str "is",Space,Str "also",Space,Str "a",Space,Str "proper",Space,Str "way,",Space,Str "with",Space,Str "a",Space,Str "different", Space, Str "style"] ,BlockQuote [Para [Str "This",Space,Str "is",Space,Str "called",Space,Str "the",Space,Str "Intense",Space,Str "Quote",Space,Str "style."]] ,Para [Str "And",Space,Str "this",Space,Str "is",Space,Str "the",Space,Str "way",Space,Str "that",Space,Str "most",Space,Str "people",Space,Str "do",Space,Str "it:"] ,BlockQuote [Para [Str "I",Space,Str "just",Space,Str "indented",Space,Str "this,",Space,Str "so",Space,Str "it",Space,Str "looks",Space,Str "like",Space,Str "a",Space,Str "block",Space,Str "quote.",Space,Str "I",Space,Str "think",Space,Str "this",Space,Str "is",Space,Str "how",Space,Str "most",Space,Str "people",Space,Str "do",Space,Str "block",Space,Str "quotes",Space,Str "in",Space,Str "their",Space,Str "documents."]] ,Para [Str "And",Space,Str "back",Space,Str "to",Space,Str "the",Space,Str "normal",Space,Str "style."]] ================================================ FILE: test/docx/char_styles.native ================================================ [Para [Emph [Str "This",Space,Str "is",Space,Str "all",Space,Str "in",Space,Str "an"],Space,Emph [Strong [Str "italic",Space,Str "style"],Str "."]] ,Para [Emph [Str "This",Space,Str "is",Space,Str "an",Space,Str "italic"],Space,Str "style",Space,Emph [Str "with",Space,Str "some"],Space,Str "words",Space,Emph [Str "unitalicized."]] ,Para [Strong [Str "This",Space,Str "is",Space,Str "all",Space,Str "in",Space,Str "a",Space,Emph [Str "strong",Space,Str "style"],Str "."]] ,Para [Strong [Str "This",Space,Str "is",Space,Str "a",Space,Str "strong"],Space,Str "style",Space,Strong [Str "with",Space,Str "some"],Space,Str "words",Space,Strong [Str "ubolded."]]] ================================================ FILE: test/docx/codeblock.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "some",Space,Str "code:"] ,CodeBlock ("",[],[]) "readDocx :: ReaderOptions\n -> B.ByteString\n -> Pandoc" ,Para [Str "from",Space,Str "the",Space,Str "beginning",Space,Str "of",Space,Str "the",Space,Str "docx",Space,Str "reader."]] ================================================ FILE: test/docx/comments.native ================================================ [Para [Str "I",Space,Str "want",Space,Span ("",["comment-start"],[("id","0"),("author","Jesse Rosenthal"),("date","2016-05-09T16:13:00Z")]) [Str "I",Space,Str "left",Space,Str "a",Space,Str "comment."],Str "some",Space,Str "text",Space,Str "to",Space,Str "have",Space,Str "a",Space,Str "comment",Space,Span ("",["comment-end"],[("id","0")]) [],Str "on",Space,Str "it."] ,Para [Str "This",Space,Str "is",Space,Span ("",["comment-start"],[("id","1"),("author","Jesse Rosenthal"),("date","2016-05-09T16:13:00Z")]) [Str "A",Space,Str "comment",Space,Str "across",Space,Str "paragraphs."],Str "a",Space,Str "new",Space,Str "paragraph."] ,Para [Str "And",Space,Str "so",Span ("",["comment-end"],[("id","1")]) [],Space,Str "is",Space,Str "this."] ,Para [Str "One",Space,Span ("",["comment-start"],[("id","2"),("author","Jesse Rosenthal"),("date","2016-05-09T16:14:00Z")]) [Str "This",Space,Str "one",Space,Str "has",Space,Str "multiple",Space,Str "paragraphs.",LineBreak, Str "See?"],Str "more",Span ("",["comment-end"],[("id","2")]) [],Str ".",Space,Str "And",Space,Str "this",Space,Str "is",Space,Str "one",Space,Str "with",Space,Str "a",Space,Span ("",["comment-start"],[("id","3"),("author","Jesse Rosenthal"),("date","2016-06-22T14:35:00Z")]) [Str "Do",Space,Str "something."],Span ("",["comment-start"],[("id","4"),("author","Jesse Rosenthal"),("date","2016-06-22T14:36:00Z")]) [Str "Do",Space,Str "something",Space,Str "else."],Str "comment",Space,Str "in",Space,Str "a",Space,Str "comment",Span ("",["comment-end"],[("id","3")]) [Span ("",["comment-end"],[("id","4")]) []],Str "."]] ================================================ FILE: test/docx/comments_no_comments.native ================================================ [Para [Str "I",Space,Str "want",Space,Str "some",Space,Str "text",Space,Str "to",Space,Str "have",Space,Str "a",Space,Str "comment",Space,Str "on",Space,Str "it."] ,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "new",Space,Str "paragraph."] ,Para [Str "And",Space,Str "so",Space,Str "is",Space,Str "this."] ,Para [Str "One",Space,Str "more.",Space,Str "And",Space,Str "this",Space,Str "is",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "comment",Space,Str "in",Space,Str "a",Space,Str "comment."]] ================================================ FILE: test/docx/compact-style-removal.native ================================================ [OrderedList (1,Decimal,Period) [[Plain [Str "One"]] ,[Plain [Str "Two"]] ,[Plain [Str "Three"]] ,[Plain [Str "Four"]]]] ================================================ FILE: test/docx/cross_reference.native ================================================ [ Header 1 ( "title" , [] , [] ) [ Str "TITLE" ] , Para [ Str "Cross-reference:" , Space , Link ( "" , [] , [] ) [ Str "TITLE" ] ( "#title" , "" ) ] ] ================================================ FILE: test/docx/custom-style-no-styles.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "some",Space,Str "text."] ,Para [Str "This",Space,Str "is",Space,Str "text",Space,Str "with",Space,Str "an",Space,Emph [Str "emphasized"],Space,Str "text",Space,Str "style.",Space,Str "And",Space,Str "this",Space,Str "is",Space,Str "text",Space,Str "with",Space,Str "a",Space,Strong [Str "strengthened"],Space,Str "text",Space,Str "style."] ,BlockQuote [Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "styled",Space,Str "paragraph",Space,Str "that",Space,Str "inherits",Space,Str "from",Space,Str "Block",Space,Str "Text."]]] ================================================ FILE: test/docx/custom-style-preserve.native ================================================ [Para [Span ("",[],[("custom-style","MyStyle")]) [Str "This",Space,Str "span",Note [Para [Str "Neither",Space,Str "footnote",Space,Str "nor",Space,Str "footnote",Space,Str "reference",Space,Str "should",Space,Str "get",Space,Str "a",Space,Str "custom",Space,Str "style",Space,Str "from",Space,Str "its",Space,Str "span."]],Space,Str "should",Space,Str "have",Space,Str "a",Space,Str "custom",Space,Str "style",Space,Str "(",Link ("",[],[]) [Str "link"] ("http://example.com/",""),Str "),"],Space,Str "but",Space,Str "the",Space,Str "text",Space,Str "after",Space,Str "the",Space,Str "comma",Space,Str "shouldn\8217t,",Space,Str "nor",Space,Str "should",Space,Str "the",Space,Str "link."] ,Div ("",[],[("custom-style","MyOtherStyle")]) [Para [Str "The",Space,Str "contents",Space,Str "of",Space,Str "this",Space,Str "div",Space,Str "should",Space,Str "have",Space,Str "a",Space,Str "custom",Space,Str "style,",Space,Str "but",Space,Link ("",[],[]) [Str "this",Space,Str "link",Space,Str "should",Space,Str "not"] ("http://example.com/",""),Str "."] ,Header 2 ("this-header-should-not-have-the-divs-custom-style",[],[]) [Str "This",Space,Str "header",Space,Str "should",Space,Str "not",Space,Str "have",Space,Str "the",Space,Str "div\8217s",Space,Str "custom",Space,Str "style"] ,BlockQuote [Para [Str "This",Space,Str "blockquote",Space,Str "should",Space,Str "not."]] ,CodeBlock ("",[],[]) "# This code block should not." ,Para [Str "But",Space,Str "this",Space,Str "paragraph",Space,Str "should.",Note [Para [Str "Neither",Space,Str "footnote",Space,Str "nor",Space,Str "footnote",Space,Str "reference",Space,Str "should",Space,Str "get",Space,Str "a",Space,Str "custom",Space,Str "style",Space,Str "from",Space,Str "its",Space,Str "div."]]]] ,Div ("",[],[("custom-style","MyOuterStyle")]) [Div ("",[],[("custom-style","MyInnerStyle")]) [Para [Str "This",Space,Str "should",Space,Str "have",Space,Str "MyInnerStyle."] ,Header 3 ("this-heading-should-not",[],[]) [Str "This",Space,Str "heading",Space,Str "should",Space,Str "not"]] ,Para [Str "This",Space,Str "should",Space,Str "have",Space,Str "MyOuterStyle,",Space,Str "but",Space,Str "the",Space,Str "following",Space,Str "elision",Space,Str "should",Space,Str "have",Space,Str "its",SoftBreak,Str "own",Space,Str "style.",Space,Span ("",[],[("custom-style","Elision")]) [Str "..."]] ,BlockQuote [Para [Str "This",Space,Str "blockquote",Space,Str "should",Space,Str "include",Space,Strong [Str "bold",Space,Str "text",Space,Str "with",Space,Str "an",Space,Str "elision:",SoftBreak,Span ("",[],[("custom-style","Elision")]) [Str "..."]]]]]] ================================================ FILE: test/docx/custom-style-roundtrip-end.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "custom-styles."] ,Para [Str "Here",Space,Str "is",Space,Str "something",Space,Emph [Str "emphasized"],Str ".",Space,Str "And",Space,Str "here",Space,Str "is",Space,Str "something",Space,Strong [Str "strong"],Str "."] ,BlockQuote [Para [Str "One",Space,Str "paragraph",Space,Str "of",Space,Str "text."] ,Para [Str "And",Space,Str "another",Space,Str "paragraph",Space,Str "of",Space,Emph [Str "really",Space,Str "cool"],Space,Str "text."]]] ================================================ FILE: test/docx/custom-style-with-styles.native ================================================ [Div ("",[],[("custom-style","First Paragraph")]) [Para [Str "This",Space,Str "is",Space,Str "some",Space,Str "text."]] ,Div ("",[],[("custom-style","Body Text")]) [Para [Str "This",Space,Str "is",Space,Str "text",Space,Str "with",Space,Str "an",Space,Span ("",[],[("custom-style","Emphatic")]) [Str "emphasized"],Space,Str "text",Space,Str "style.",Space,Str "And",Space,Str "this",Space,Str "is",Space,Str "text",Space,Str "with",Space,Str "a",Space,Span ("",[],[("custom-style","Strengthened")]) [Str "strengthened"],Space,Str "text",Space,Str "style."]] ,Div ("",[],[("custom-style","My Block Style")]) [BlockQuote [Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "styled",Space,Str "paragraph",Space,Str "that",Space,Str "inherits",Space,Str "from",Space,Str "Block",Space,Str "Text."]]]] ================================================ FILE: test/docx/custom_style.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "custom-styles."] ,Para [Str "Here",Space,Str "is",Space,Str "something",Space,Span ("",[],[("custom-style","Emphatic")]) [Str "emphasized"],Str ".",Space,Str "And",SoftBreak,Str "here",Space,Str "is",Space,Str "something",Space,Span ("",[],[("custom-style","Strengthened")]) [Str "strong"],Str "."] ,Div ("",[],[("custom-style","My Block Style")]) [Para [Str "One",Space,Str "paragraph",Space,Str "of",Space,Str "text."] ,Para [Str "And",Space,Str "another",Space,Str "paragraph",Space,Str "of",Space,Span ("",[],[("custom-style","Emphatic")]) [Str "really",SoftBreak,Str "cool"],Space,Str "text."]]] ================================================ FILE: test/docx/deep_normalize.native ================================================ [OrderedList (1,Decimal,OneParen) [[Para [Str "This",Space,Str "is",Space,Str "at",Space,Str "the",Space,Str "first",Space,Str "level"] ,OrderedList (1,LowerAlpha,OneParen) [[Para [Str "This",Space,Str "is",Space,Str "at",Space,Str "the",Space,Str "second",Space,Str "level"] ,OrderedList (1,LowerRoman,OneParen) [[Para [Str "This",Space,Str "is",Space,Emph [Str "at",Space,Strong [Str "the",Space,Span ("",["mark"],[]) [Str "th"],Str "i",Span ("",["mark"],[]) [Str "rd"],Space,Str "level"],Str ",",Space,Str "and",Space,Str "I",Space,Str "want",Space,Str "to"],Space,Str "test",Space,Str "normalization",Space,Str "here."]]]]]]]] ================================================ FILE: test/docx/definition_list.native ================================================ [ DefinitionList [ ( [ Str "Term" , Space , Str "1" ] , [ [ Para [ Str "Definition" , Space , Str "1" ] ] ] ) , ( [ Str "Term" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "inline" , Space , Str "markup" ] ] , [ [ Para [ Str "Definition" , Space , Str "2" ] , Para [ Code ( "" , [] , [] ) "{ some code, part of Definition 2 }" ] , Para [ Str "Third" , Space , Str "paragraph" , Space , Str "of" , Space , Str "definition" , Space , Str "2." ] ] ] ) ] ] ================================================ FILE: test/docx/diagram.native ================================================ [Header 1 ("diagram-after",[],[]) [Str "Diagram",Space,Str "after:"] ,Para [Span ("",["diagram"],[]) [Str "[DIAGRAM]"]]] ================================================ FILE: test/docx/document-properties-short-desc.native ================================================ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "A.",Space,Str "M."]]),("description",MetaInlines [Str "Short",Space,RawInline (Format "html") "",Str "description",RawInline (Format "html") "",Space,Str "&."]),("keywords",MetaList [MetaInlines [Str "keyword",Space,Str "1"],MetaInlines [Str "keyword",Space,Str "2"]]),("subject",MetaInlines [Str "This",Space,Str "is",Space,Str "the",Space,Str "subject"]),("title",MetaInlines [Str "Testing",Space,Str "custom",Space,Str "properties"])]}) [Para [Str "Testing",Space,Str "document",Space,Str "properties"]] ================================================ FILE: test/docx/document-properties.native ================================================ Pandoc (Meta {unMeta = fromList [("Company",MetaInlines [Str "My",Space,Str "Company"]),("Second Custom Property",MetaInlines [Str "Second",Space,Str "custom",Space,Str "property",Space,Str "value"]),("abstract",MetaBlocks [Plain [Str "Quite",Space,Str "a",Space,Str "long",Space,Str "description",SoftBreak,Str "spanning",Space,Str "several",Space,Str "lines"]]),("author",MetaList [MetaInlines [Str "A.",Space,Str "M."]]),("category",MetaInlines [Str "My",Space,Str "Category"]),("custom1",MetaInlines [Str "First",Space,Str "custom",Space,Str "property",Space,Str "value"]),("custom3",MetaInlines [Str "Escaping",Space,Str "amp",Space,Str "&",Space,Str "."]),("custom4",MetaInlines [Str "Escaping",Space,Str "LT,GT",Space,Str "<",Space,Str "asdf",Space,Str ">",Space,Str "<"]),("custom5",MetaInlines [Str "Escaping",Space,Str "html",Space,RawInline (Format "html") "",Str "asdf",RawInline (Format "html") ""]),("custom6",MetaInlines [Str "Escaping",Space,Emph [Str "MD"],Space,Str "\225",Space,Str "a"]),("custom9",MetaInlines [Str "Extended",Space,Str "chars:",Space,Str "\8364",Space,Str "\225",Space,Str "\233",Space,Str "\237",Space,Str "\243",Space,Str "\250",Space,Str "$"]),("description",MetaBlocks [Para [Str "Long",Space,Str "description",Space,Str "spanning",SoftBreak,Str "several",Space,Str "lines."],Plain [Str "This",Space,Str "is",Space,Str "\225",Space,Str "second",Space,RawInline (Format "html") "",Str "line",RawInline (Format "html") "",Str "."]]),("keywords",MetaList [MetaInlines [Str "keyword",Space,Str "1"],MetaInlines [Str "keyword",Space,Str "2"]]),("lang",MetaInlines [Str "en-US"]),("nested-custom",MetaList [MetaMap (fromList [("custom 7",MetaInlines [Str "Nested",Space,Str "Custom",Space,Str "value",Space,Str "7"])]),MetaMap (fromList [("custom 8",MetaInlines [Str "Nested",Space,Str "Custom",Space,Str "value",Space,Str "8"])])]),("subject",MetaInlines [Str "This",Space,Str "is",Space,Str "the",Space,Str "subject"]),("subtitle",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "subtitle"]),("title",MetaInlines [Str "Testing",Space,Str "custom",Space,Str "properties"])]}) [Para [Str "Testing",Space,Str "document",Space,Str "properties"]] ================================================ FILE: test/docx/drop_cap.native ================================================ [Para [Str "Drop",Space,Str "cap."] ,Para [Str "Next",Space,Str "paragraph."] ,Para [Str "Drop",Space,Str "cap",Space,Str "in",Space,Str "margin."] ,Para [Str "Drop",Space,Str "cap",Space,Str "(not",Space,Str "really)."]] ================================================ FILE: test/docx/dummy_item_after_list_item.native ================================================ [OrderedList (1,Decimal,Period) [[Para [Str "One"] ,Para [Str "Two",LineBreak,LineBreak,Str "Three"]]]] ================================================ FILE: test/docx/dummy_item_after_paragraph.native ================================================ [Para [Str "First",Space,Str "bullet",Space,Str "point",Space,Str "created",Space,Str "and",Space,Str "then",Space,Str "deleted"] ,Para [Str "A",Space,Str "normal",Space,Str "paragraph"] ,Para [Str "First",Space,Str "bullet",Space,Str "point",Space,Str "created",Space,Str "and",Space,Str "then",Space,Str "deleted",Space,Str "after",Space,Str "the",Space,Str "normal",Space,Str "paragraph"]] ================================================ FILE: test/docx/empty_field.native ================================================ [Para [Str "\24076\26395\28145\20837\20102\35299\30340\35835\32773\21487\20197\21435\30475David",Space,Str "French",Span ( "" , [ "indexref" ] , [ ( "entry" , "French" ) ] ) [],Space,Str "Belding\21644Kevin",Space,Str "J.",Space,Str "Mitchell\30340" ,Link ("",[],[]) [Str "Foundations",Space,Str "of",Space,Str "Analysis,",Space,Str "2nd",Space,Str "Edition"] ("https://books.google.com/books?id=sp_Zcb9ot90C&lpg=PR4&hl=zh-CN&pg=PA19#v=onepage&q&f=true",""),Str ",\21487\20174\&19\39029\30475\36215\65292\25110D.C.",Space,Str "Goldrei\30340",Space ,Link ("",[],[]) [Str "Classic",Space,Str "Set",Space,Str "Theory:",Space,Str "For",Space,Str "Guided",Space,Str "Independent",Space,Str "Study"] ("https://books.google.ae/books?id=dlc0DwAAQBAJ&lpg=PT29&hl=zh-CN&pg=PT26#v=onepage&q&f=true","") ,Str "\65292\20174\31532\20108\31456\30475\36215\65292\38405\35835\26102\35201\27880\24847\26412\25991\19982\36825\20123\20070\25152\19981\21516\30340\26159\24182\27809\26377\25226\23454\25968\30475\20316\26159\26377\29702\25968\38598\30340\20998\21106\12290"] ,Para [Str "Index:"] ,Para [Str "French,",Space,Str "1"]] ================================================ FILE: test/docx/enumerated_headings.native ================================================ [Header 1 ("h1",[],[]) [Str "H1"] ,Header 2 ("h2",[],[]) [Str "H2"] ,Header 3 ("h3",[],[]) [Str "H3"] ,Para [Str "And",Space,Str "some",Space,Str "text"]] ================================================ FILE: test/docx/german_styled_lists.native ================================================ [BulletList [[Para [Str "One",Space,Str "level",Space,Str "of",Space,Str "the",Space,Str "list."]] ,[Para [Str "Second",Space,Str "level",Space,Str "of",Space,Str "the",Space,Str "list."] ,BulletList [[Para [Str "Next",Space,Str "level",Space,Str "of",Space,Str "the",Space,Str "list"]]]] ,[Para [Str "Back",Space,Str "to",Space,Str "the",Space,Str "top",Space,Str "level."]]]] ================================================ FILE: test/docx/hanging_indent.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "hanging",Space,Str "indent,",Space,Str "with",Space,Str "the",Space,Str "left",Space,Str "side",Space,Str "set",Space,Str "to",Space,Str "the",Space,Str "left",Space,Str "margin,",Space,Str "and",Space,Str "it",Space,Str "wraps",Space,Str "around",Space,Str "the",Space,Str "line."] ,BlockQuote [Para [Str "Five",Space,Str "years",Space,Str "have",Space,Str "passed,",Space,Str "five",Space,Str "summers",Space,Str "with",Space,Str "the",Space,Str "length"]]] ================================================ FILE: test/docx/headers.native ================================================ [Header 1 ("a-test-of-headers",[],[]) [Str "A",Space,Str "Test",Space,Str "of",Space,Str "Headers"] ,Header 2 ("second-level",[],[]) [Str "Second",Space,Str "Level"] ,Para [Str "Some",Space,Str "plain",Space,Str "text."] ,Header 3 ("third-level",[],[]) [Str "Third",Space,Str "level"] ,Para [Str "Some",Space,Str "more",Space,Str "plain",Space,Str "text."] ,Header 4 ("fourth-level",[],[]) [Str "Fourth",Space,Str "level"] ,Para [Str "Some",Space,Str "more",Space,Str "plain",Space,Str "text."] ,Header 5 ("fifth-level",[],[]) [Str "Fifth",Space,Str "level"] ,Para [Str "Some",Space,Str "more",Space,Str "plain",Space,Str "text."] ,Header 6 ("sixth-level",[],[]) [Str "Sixth",Space,Str "level"] ,Para [Str "Some",Space,Str "more",Space,Str "plain",Space,Str "text."] ,Para [Str "Seventh",Space,Str "level"] ,Para [Str "Since",Space,Str "no",Space,Str "Heading",Space,Str "7",Space,Str "style",Space,Str "exists",Space,Str "in",Space,Str "styles.xml,",Space,Str "this",Space,Str "gets",Space,Str "converted",Space,Str "to",Space,Str "Span."]] ================================================ FILE: test/docx/i18n_blocks.native ================================================ [Header 1 ("this-is-heading-1",[],[]) [Str "This",Space,Str "is",Space,Str "Heading",Space,Str "1"] ,Header 2 ("this-is-heading-2",[],[]) [Str "This",Space,Str "is",Space,Str "Heading",Space,Str "2"] ,BlockQuote [Para [Str "This",Space,Str "is",Space,Str "Quote"] ,Para [Str "This",Space,Str "is",Space,Str "Block",Space,Str "Text"]] ,BulletList [[Para [Str "This",Space,Str "is",Space,Str "list",Space,Str "item",Space,Str "1"]] ,[Para [Str "This",Space,Str "is",Space,Str "list",Space,Str "item",Space,Str "2"]]]] ================================================ FILE: test/docx/image_no_embed.native ================================================ [Para [Str "An",Space,Str "image:"] ,Para [Image ("",[],[("width","6.5in"),("height","5.508333333333334in")]) [Str "He",Space,Str "realizes",Space,Str "he's",Space,Str "making",Space,Str "the",Space,Str "file-size",Space,Str "too",Space,Str "big."] ("media/image1.jpg","An unhappy fish.")]] ================================================ FILE: test/docx/image_no_embed_writer.native ================================================ [Para [Str "An",Space,Str "image:"] ,Para [Image ("",[],[("width","0.4166666666666667in"),("height","0.4166666666666667in")]) [Str "He",Space,Str "realizes",Space,Str "he's",Space,Str "making",Space,Str "the",Space,Str "file-size",Space,Str "too",Space,Str "big."] ("media/rId25.jpg","An unhappy fish.")]] ================================================ FILE: test/docx/image_vml.native ================================================ [Header 1 ("vml-image",[],[]) [Strong [Str "VML",Space,Str "Image"]] ,Para [Str "It",Space,Str "should",Space,Str "follow",Space,Str "below:"] ,Para [Image ("",[],[]) [] ("media/image4.jpeg","")]] ================================================ FILE: test/docx/image_vml_as_object.native ================================================ [Para [Str "Test",Space,Str "with",Space,Str "object",Space,Str "as",Space,Str "image:"] ,Para [Image ("",[],[]) [] ("media/image1.emf","")]] ================================================ FILE: test/docx/image_with_textbox_caption.native ================================================ [ Figure ( "" , [] , [] ) (Caption Nothing [ Para [ Str "1" , Space , Str "Daniel" , Space , Str "Schliebner:" , Space , Str "Cantor'sches" , Space , Str "Diagonalverfahren." , Space , Str "Von" , Space , Str "Mengen," , Space , Str "Unendlichkeiten" , Space , Str "und" , Space , Str "Wahnsinn," , Space , Str "Pdf:" , Space , Str "https://www2.informatik.hu-berlin.de/~kossahl/Uni/Ma1/Cantor.pdf" ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "2.3680555555555554in" ) , ( "height" , "0.9340277777777778in" ) ] ) [] ( "media/image1.emf" , "" ) ] ] ] ================================================ FILE: test/docx/image_writer_test.native ================================================ [Para [Str "No",Space,Str "width",Space,Str "given:"] ,Para [Image ("fig:testimg",[],[]) [Str "testimg"] ("lalune.jpg","fig:")] ,Para [Str "With",Space,Str "height",Space,Str "10cm:"] ,Para [Image ("fig:2testimg",[],[("height","10cm")]) [Str "2testimg"] ("lalune.jpg","fig:")] ,Para [Str "With",Space,Str "width",Space,Str "6cm:"] ,Para [Image ("",[],[("width","6cm")]) [Str "3testimg"] ("lalune.jpg","fig:")] ,Header 1 ("with-height-3in-and-width-6in",[],[]) [Str "With",Space,Str "height",Space,Str "3in",Space,Str "and",Space,Str "width",Space,Str "6in:"] ,Para [Image ("",[],[("width","6in"),("height","3in")]) [Str "4testimg"] ("lalune.jpg","fig:")] ,Para [RawInline (Format "openxml") ""] ,Para [RawInline (Format "openxml") ""]] ================================================ FILE: test/docx/inline_code.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "an",Space,Str "example",Space,Str "of",Space,Code ("",[],[]) "inline code",Space,Str "with",Space,Str "three",Space,Str "spaces."]] ================================================ FILE: test/docx/inline_formatting.native ================================================ Pandoc (Meta {unMeta = fromList []}) [Para [Str "Regular",Space,Str "text",Space,Emph [Str "italics"],Space,Strong [Str "bold",Space,Emph [Str "bold",Space,Str "italics"]],Str "."] ,Para [Str "This",Space,Str "is",Space,SmallCaps [Str "Small",Space,Str "Caps"],Str ",",Space,Str "and",Space,Str "this",Space,Str "is",Space,Strikeout [Str "strikethrough"],Str "."] ,Para [Str "Some",Space,Str "people",Space,Str "use",Space,Underline [Str "single",Space,Str "underlines",Space,Str "for",Space,Emph [Str "emphasis"]],Str "."] ,Para [Str "Above",Space,Str "the",Space,Str "line",Space,Str "is",Space,Superscript [Str "superscript"],Space,Str "and",Space,Str "below",Space,Str "the",Space,Str "line",Space,Str "is",Space,Subscript [Str "subscript"],Str "."] ,Para [Str "A",Space,Str "line",LineBreak,Str "break."]] ================================================ FILE: test/docx/inline_formatting_writer.native ================================================ [Para [Str "Regular",Space,Str "text",Space,Emph [Str "italics"],Space,Strong [Str "bold",Space,Emph [Str "bold",Space,Str "italics"]],Str "."] ,Para [Str "This",Space,Str "is",Space,SmallCaps [Str "Small",Space,Str "Caps"],Str ",",Space,Str "and",Space,Str "this",Space,Str "is",Space,Strikeout [Str "strikethrough"],Str "."] ,Para [Str "Some",Space,Str "people",Space,Str "use",Space,Emph [Str "single",Space,Str "underlines",Space,Str "for",Space,Str "emphasis"],Str "."] ,Para [Str "Above",Space,Str "the",Space,Str "line",Space,Str "is",Space,Superscript [Str "superscript"],Space,Str "and",Space,Str "below",Space,Str "the",Space,Str "line",Space,Str "is",Space,Subscript [Str "subscript"],Str "."] ,Para [Str "A",Space,Str "line",LineBreak,Str "break."]] ================================================ FILE: test/docx/inline_images.native ================================================ [Para [Str "This",Space,Str "picture",Space,Image ("",[],[("width","0.8888888888888888in"),("height","0.8888888888888888in")]) [Str "This",Space,Str "one",Space,Str "is",Space,Str "green",Space,Str "and",Space,Str "looks",Space,Str "like",Space,Str "Sideshow",Space,Str "Bob."] ("media/image1.jpg","First identicon"),Space,Str "is",Space,Str "an",Space,Str "identicon."] ,Para [Str "Here",Space,Str "is",Space,Link ("",[],[]) [Str "one",Space,Image ("",[],[("width","0.8888888888888888in"),("height","0.8888888888888888in")]) [Str "This",Space,Str "one",Space,Str "is",Space,Str "reddish,",Space,Str "and",Space,Str "looks",Space,Str "like",Space,Str "a",Space,Str "heart",Space,Str "that",Space,Str "has",Space,Str "leaked",Space,Str "out."] ("media/image2.jpg","Second identicon"),Space,Str "that"] ("http://www.google.com",""),Space,Str "links."]] ================================================ FILE: test/docx/inline_images_writer.native ================================================ [Para [Str "This",Space,Str "picture",Space,Image ("",[],[("width","0.4166666666666667in"),("height","0.4166666666666667in")]) [] ("media/rId26.jpg",""),Space,Str "is",Space,Str "an",Space,Str "identicon."] ,Para [Str "Here",Space,Str "is",Space,Link ("",[],[]) [Str "one",Space,Image ("",[],[("width","0.4166666666666667in"),("height","0.4166666666666667in")]) [] ("media/rId26.jpg",""),Space,Str "that"] ("http://www.google.com",""),Space,Str "links."]] ================================================ FILE: test/docx/inline_images_writer_test.native ================================================ [Para [Str "This",Space,Str "picture",Space,Image ("",[],[("width","0.8888888888888888in"),("height","0.8888888888888888in")]) [Str "This",Space,Str "one",Space,Str "is",Space,Str "green",Space,Str "and",Space,Str "looks",Space,Str "like",Space,Str "Sideshow",Space,Str "Bob."] ("lalune.jpg","First identicon"),Space,Str "is",Space,Str "an",Space,Str "identicon."] ,Para [Str "Here",Space,Str "is",Space,Link ("",[],[]) [Str "one",Space,Image ("",[],[("width","0.8888888888888888in"),("height","0.8888888888888888in")]) [Str "This",Space,Str "one",Space,Str "is",Space,Str "reddish,",Space,Str "and",Space,Str "looks",Space,Str "like",Space,Str "a",Space,Str "heart",Space,Str "that",Space,Str "has",Space,Str "leaked",Space,Str "out."] ("lalune.jpg","Second identicon"),Space,Str "that"] ("http://www.google.com",""),Space,Str "links."]] ================================================ FILE: test/docx/instrText_hyperlink.native ================================================ [Para [Str "\24076\26395\28145\20837\20102\35299\30340\35835\32773\21487\20197\21435\30475David",Space,Str "French",Space,Str "Belding\21644Kevin",Space,Str "J.",Space,Str "Mitchell\30340",Link ("",[],[]) [Str "Foundations",Space,Str "of",Space,Str "Analysis,",Space,Str "2nd",Space,Str "Edition"] ("https://books.google.com/books?id=sp_Zcb9ot90C&lpg=PR4&hl=zh-CN&pg=PA19#v=onepage&q&f=true",""),Str ",\21487\20174\&19\39029\30475\36215\65292\25110D.C.",Space,Str "Goldrei\30340",Space,Link ("",[],[]) [Str "Classic",Space,Str "Set",Space,Str "Theory:",Space,Str "For",Space,Str "Guided",Space,Str "Independent",Space,Str "Study"] ("https://books.google.ae/books?id=dlc0DwAAQBAJ&lpg=PT29&hl=zh-CN&pg=PT26#v=onepage&q&f=true",""),Str "\65292\20174\31532\20108\31456\30475\36215\65292\38405\35835\26102\35201\27880\24847\26412\25991\19982\36825\20123\20070\25152\19981\21516\30340\26159\24182\27809\26377\25226\23454\25968\30475\20316\26159\26377\29702\25968\38598\30340\20998\21106\12290"]] ================================================ FILE: test/docx/link_in_notes.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Note [Para [Link ("",[],[]) [Str "http://wikipedia.org/"] ("http://wikipedia.org/","")]],Str "."]] ================================================ FILE: test/docx/links.native ================================================ [Header 2 ("an-internal-link-and-an-external-link",[],[]) [Str "An",Space,Str "internal",Space,Str "link",Space,Str "and",Space,Str "an",Space,Str "external",Space,Str "link"] ,Para [Str "An",Space,Link ("",[],[]) [Str "external",Space,Str "link"] ("http://google.com",""),Space,Str "to",Space,Str "a",Space,Str "popular",Space,Str "website."] ,Para [Str "An",Space,Link ("",[],[]) [Str "external",Space,Str "link"] ("http://pandoc.org/README.html#synopsis",""),Space,Str "to",Space,Str "a",Space,Str "website",Space,Str "with",Space,Str "an",Space,Str "anchor."] ,Para [Str "An",Space,Link ("",[],[]) [Str "internal",Space,Str "link"] ("#a-section-for-testing-link-targets",""),Space,Str "to",Space,Str "a",Space,Str "section",Space,Str "header."] ,Para [Str "An",Space,Link ("",[],[]) [Str "internal",Space,Str "link"] ("#my_bookmark",""),Space,Str "to",Space,Str "a",Space,Str "bookmark."] ,Header 2 ("a-section-for-testing-link-targets",[],[]) [Str "A",Space,Str "section",Space,Str "for",Space,Str "testing",Space,Str "link",Space,Str "targets"] ,Para [Str "A",Space,Str "bookmark",Space,Str "right",Space,Span ("my_bookmark",["anchor"],[]) [],Str "here"]] ================================================ FILE: test/docx/links_writer.native ================================================ [Header 2 ("an-internal-link-and-an-external-link",[],[]) [Str "An",Space,Str "internal",Space,Str "link",Space,Str "and",Space,Str "an",Space,Str "external",Space,Str "link"] ,Para [Str "An",Space,Link ("",[],[]) [Str "external",Space,Str "link"] ("http://google.com",""),Space,Str "to",Space,Str "a",Space,Str "popular",Space,Str "website."] ,Para [Str "An",Space,Link ("",[],[]) [Str "external",Space,Str "link"] ("http://pandoc.org/README.html#synopsis",""),Space,Str "to",Space,Str "a",Space,Str "website",Space,Str "with",Space,Str "an",Space,Str "anchor."] ,Para [Str "An",Space,Link ("",[],[]) [Str "internal",Space,Str "link"] ("#a-section-for-testing-link-targets",""),Space,Str "to",Space,Str "a",Space,Str "section",Space,Str "header."] ,Para [Str "An",Space,Link ("",[],[]) [Str "internal",Space,Str "link"] ("#my_bookmark",""),Space,Str "to",Space,Str "a",Space,Str "bookmark."] ,Header 2 ("a-section-for-testing-link-targets",[],[]) [Str "A",Space,Str "section",Space,Str "for",Space,Str "testing",Space,Str "link",Space,Str "targets"]] ================================================ FILE: test/docx/lists-compact.native ================================================ [OrderedList (1,Decimal,Period) [[Plain [Str "One"]] ,[Plain [Str "Two"]] ,[Plain [Str "Three"]] ,[Plain [Str "Four"]]]] ================================================ FILE: test/docx/lists.native ================================================ [Header 2 ("some-nested-lists",[],[]) [Str "Some",Space,Str "nested",Space,Str "lists"] ,OrderedList (1,Decimal,Period) [[Para [Str "one"]] ,[Para [Str "two"] ,OrderedList (1,LowerAlpha,Period) [[Para [Str "a"]] ,[Para [Str "b"]]]]] ,BulletList [[Para [Str "one"]] ,[Para [Str "two"] ,BulletList [[Para [Str "three"] ,BulletList [[Para [Str "four"] ,Para [Str "Sub",Space,Str "paragraph"]]]]]] ,[Para [Str "Same",Space,Str "list"]]] ,BulletList [[Plain [Str "Different",Space,Str "list",Space,Str "adjacent",Space,Str "to",Space,Str "the",Space,Str "one",Space,Str "above."]]]] ================================================ FILE: test/docx/lists_9994.native ================================================ [ OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "one" ] , BulletList [ [ Plain [ Str "bul" ] ] ] ] , [ Plain [ Str "two" ] ] ] ] ================================================ FILE: test/docx/lists_continuing.native ================================================ [OrderedList (1,Decimal,Period) [[Para [Str "Foo"]] ,[Para [Str "Bar"]] ,[Para [Str "Baz"]]] ,Para [Str "Interruption."] ,OrderedList (4,Decimal,Period) [[Para [Str "Bop"]]]] ================================================ FILE: test/docx/lists_div_bullets.native ================================================ [ BulletList [ [ Div ( "", [], []) [ Para [ Str "one" ], Para [ Str "two" ] ] ] , [ Div ( "refs", [], []) [ Header 1 ( "" , [] , [] ) [ Str "three" ], Para [ Str "four" ] ] ] ] ] ================================================ FILE: test/docx/lists_level_override.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Str "For" , Space , Str "each" , Space , Str "initiative" , Space , Str "below" , Space , Str "is" , Space , Str "outlined" , Space , Str "the" , Space , Str "goals," , Space , Str "an" , Space , Str "approximate" , Space , Str "roadmap" , Space , Str "which" , Space , Str "will" , Space , Str "likely" , Space , Str "change" , Space , Str "as" , Space , Str "we" , Space , Str "iterate," , Space , Str "signals/metrics" , Space , Str "to" , Space , Str "measure" , Space , Str "success," , Space , Str "and" , Space , Str "initial" , Space , Str "workitems" , Space , Str "with" , Space , Str "a" , Space , Str "rough" , Space , Strong [ Str "schedule" ] , Space , Str "and" , Space , Str "contacts" , Space , Str "where" , Space , Str "available:" ] , Para [ Str "\160" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "State" , Space , Str "of" , Space , Str "Documentation" ] ] ] , BlockQuote [ Para [ Strong [ Str "Goal:" , Space , Str "Baseline" , Space , Str "and" , Space , Str "ongoing" , Space , Str "metrics" , Space , Str "tracking" , Space , Str "doc" , Space , Str "usefulness" , Space , Str "and" , Space , Str "completeness." ] ] , Para [ Str "\160" ] ] , OrderedList ( 2 , Decimal , Period ) [ [ Para [ Str "Content" , Space , Str "Migration" ] ] ] , BlockQuote [ Para [ Str "Goal:" , Space , Str "Content" , Space , Str "is" , Space , Str "accessible" , Space , Str "to" , Space , Str "new" , Space , Str "employees" , Space , Str "and" , Space , Str "is" , Space , Str "better" , Space , Str "organized/archived." ] , Para [ Str "\160" ] ] , OrderedList ( 3 , Decimal , Period ) [ [ Para [ Str "Wiki" , Space , Str "(xl)" ] ] ] , BlockQuote [ Para [ Strong [ Str "Goal:" , Space , Str "Useful" , Space , Str "documentation" , Space , Str "that" , Space , Str "is" , Space , Str "archived," , Space , Str "searchable" , Space , Str "and" , Space , Str "easy" , Space , Str "to" , Space , Str "create" ] ] , Para [ Str "\160\160" ] ] , OrderedList ( 4 , Decimal , Period ) [ [ Para [ Str "XL" , Space , Str "Code" , Space , Str "Autoreview" , Space , Str "Bot" , Space , Str "(XLCRBot)." ] ] ] , BlockQuote [ Para [ Strong [ Str "Goal:" , Space , Str "Feedback" , Space , Str "on" , Space , Str "basic" , Space , Str "violations" , Space , Str "in" , Space , Str "seconds" , Space , Str "or" , Space , Str "minutes" , Space , Str "at" , Space , Str "most" , Space , Str "in" , Space , Str "either" , Space , Str "VS" , Space , Str "or" , Space , Str "Codeflow." ] ] ] , OrderedList ( 5 , Decimal , Period ) [ [ Para [ Str "Code" , Space , Str "documentation" ] ] ] , BlockQuote [ Para [ Strong [ Str "Goal:" , Space , Str "Useful," , Space , Str "consistent," , Space , Str "tool" , Space , Str "supported" , Space , Str "comments" ] ] ] , Para [ Strong [ Str "\160" ] , Str "\160" ] , OrderedList ( 6 , Decimal , Period ) [ [ Para [ Str "Education" , Space , Str "efforts" ] ] ] , BlockQuote [ Para [ Strong [ Str "Goal:" , Space , Str "Broad," , Space , Str "discoverable" , Space , Str "channels" , Space , Str "for" , Space , Str "updates" , Space , Str "and" , Space , Str "news" ] ] ] , Para [ Strong [ Str "\160" ] ] ] ================================================ FILE: test/docx/lists_multiple_initial.native ================================================ [OrderedList (1,Decimal,Period) [[OrderedList (1,LowerAlpha,TwoParens) [[Para [Str "foo"]] ,[Para [Str "bar"]]]]] ,BulletList [[BulletList [[Para [Str "foo"]] ,[Para [Str "bar"]]]]]] ================================================ FILE: test/docx/lists_restarting.native ================================================ [OrderedList (2,Decimal,Period) [[Para [Str "Foo"]] ,[Para [Str "Bar"]] ,[Para [Str "Baz"]]] ,BlockQuote [Para [Str "Interruption"]] ,OrderedList (1,Decimal,Period) [[Para [Str "Bop."]]]] ================================================ FILE: test/docx/lists_sublist_reset.native ================================================ [OrderedList (1,Decimal,Period) [[Para [Str "Head",Space,Str "1"] ,OrderedList (1,Decimal,DefaultDelim) [[Para [Str "Head",Space,Str "1.1"]] ,[Para [Str "Head",Space,Str "1.2"]]]] ,[Para [Str "Head",Space,Str "2"] ,OrderedList (1,Decimal,DefaultDelim) [[Para [Str "Head",Space,Str "2.1"]]]]]] ================================================ FILE: test/docx/lists_writer.native ================================================ [Header 2 ("some-nested-lists",[],[]) [Str "Some",Space,Str "nested",Space,Str "lists"] ,OrderedList (1,Decimal,Period) [[Para [Str "one"]] ,[Para [Str "two"] ,OrderedList (1,LowerAlpha,DefaultDelim) [[Para [Str "a"]] ,[Para [Str "b"]]]]] ,BulletList [[Para [Str "one"]] ,[Para [Str "two"] ,BulletList [[Para [Str "three"] ,BulletList [[Para [Str "four"]]]]]] ,[Para [Str "Same",Space,Str "list"]]] ,BulletList [[Para [Str "Different",Space,Str "list",Space,Str "adjacent",Space,Str "to",Space,Str "the",Space,Str "one",Space,Str "above."]]]] ================================================ FILE: test/docx/mendeley_citations_minus.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "Mendeley" , Space , Str "test" , Space , Str "(prefix" , Space , Str "Hadwen-Bennett" , Space , Str "et" , Space , Str "al.," , Space , Str "2018," , Space , Str "p." , Space , Str "123" , Space , Str "suffix)." ] , Para [ Str "Another" , Space , Str "test" , Space , Str "(prefix" , Space , Str "Seo," , Space , Str "2019," , Space , Str "pp." , Space , Str "10\8211\&20" , Space , Str "suffix)." ] , Para [ Str "The" , Space , Str "last" , Space , Str "test" , Space , Str "(Koh" , Space , Str "&" , Space , Str "Abbas," , Space , Str "2015;" , Space , Str "Lee" , Space , Str "et" , Space , Str "al.," , Space , Str "2011)." ] , Header 1 ( "references" , [] , [] ) [ Str "References" ] , Para [ Str "Hadwen-Bennett," , Space , Str "A.," , Space , Str "Sentance," , Space , Str "S.," , Space , Str "&" , Space , Str "Morrison," , Space , Str "C." , Space , Str "(2018)." , Space , Str "Making" , Space , Str "Programming" , Space , Str "Accessible" , Space , Str "to" , Space , Str "Learners" , Space , Str "with" , Space , Str "Visual" , Space , Str "Impairments:" , Space , Str "A" , Space , Str "Literature" , Space , Str "Review." , Space , Emph [ Str "International" , Space , Str "Journal" , Space , Str "of" , Space , Str "Computer" , Space , Str "Science" , Space , Str "Education" , Space , Str "in" , Space , Str "Schools" ] , Str "," , Space , Emph [ Str "2" ] , Str "(2)," , Space , Str "3\8211\&13." , Space , Str "https://www.microsoft.com/en-us/research/publication/making-programming-accessible-to-learners-with-visual-impairments-a-literature-review/" ] , Para [ Str "Koh," , Space , Str "K.," , Space , Str "&" , Space , Str "Abbas," , Space , Str "J." , Space , Str "(2015)." , Space , Str "Future" , Space , Str "of" , Space , Str "library" , Space , Str "and" , Space , Str "museum" , Space , Str "services" , Space , Str "supporting" , Space , Str "teen" , Space , Str "learning:" , Space , Str "Perceptions" , Space , Str "of" , Space , Str "professionals" , Space , Str "in" , Space , Str "learning" , Space , Str "labs" , Space , Str "and" , Space , Str "makerspaces." , Space , Emph [ Str "Journal" , Space , Str "of" , Space , Str "Research" , Space , Str "on" , Space , Str "Libraries" , Space , Str "\\&" , Space , Str "Young" , Space , Str "Adults" ] , Str "," , Space , Emph [ Str "6" ] , Str "(4)." ] , Para [ Str "Lee," , Space , Str "I.," , Space , Str "Martin," , Space , Str "F.," , Space , Str "Denner," , Space , Str "J.," , Space , Str "Coulter," , Space , Str "B.," , Space , Str "Allan," , Space , Str "W.," , Space , Str "Erickson," , Space , Str "J.," , Space , Str "Malyn-Smith," , Space , Str "J.," , Space , Str "&" , Space , Str "Werner," , Space , Str "L." , Space , Str "(2011)." , Space , Str "Computational" , Space , Str "Thinking" , Space , Str "for" , Space , Str "Youth" , Space , Str "in" , Space , Str "Practice." , Space , Emph [ Str "ACM" , Space , Str "Inroads" ] , Str "," , Space , Emph [ Str "2" ] , Str "(1)," , Space , Str "32\8211\&37." , Space , Str "https://doi.org/10.1145/1929887.1929902" ] , Para [ Str "Seo," , Space , Str "J." , Space , Str "(2019)." , Space , Str "Is" , Space , Str "the" , Space , Str "Maker" , Space , Str "Movement" , Space , Str "Inclusive" , Space , Str "of" , Space , Str "{ANYONE}?:" , Space , Str "Three" , Space , Str "Accessibility" , Space , Str "Considerations" , Space , Str "to" , Space , Str "Invite" , Space , Str "Blind" , Space , Str "Makers" , Space , Str "to" , Space , Str "the" , Space , Str "Making" , Space , Str "World." , Space , Emph [ Str "TechTrends" ] , Str "," , Space , Emph [ Str "63" ] , Str "(5)," , Space , Str "514\8211\&520." , Space , Str "https://doi.org/10.1007/s11528-019-00377-3" ] ] ================================================ FILE: test/docx/mendeley_citations_plus.native ================================================ Pandoc Meta { unMeta = fromList [ ( "references" , MetaList [ MetaMap (fromList [ ( "DOI" , MetaString "10.1145/1929887.1929902" ) , ( "ISSN" , MetaString "2153-2184" ) , ( "author" , MetaList [ MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Lee" ) , ( "given" , MetaString "Irene" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) , MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Martin" ) , ( "given" , MetaString "Fred" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) , MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Denner" ) , ( "given" , MetaString "Jill" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) , MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Coulter" ) , ( "given" , MetaString "Bob" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) , MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Allan" ) , ( "given" , MetaString "Walter" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) , MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Erickson" ) , ( "given" , MetaString "Jeri" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) , MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Malyn-Smith" ) , ( "given" , MetaString "Joyce" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) , MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Werner" ) , ( "given" , MetaString "Linda" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) ] ) , ( "container-title" , MetaInlines [ Str "ACM" , Space , Str "Inroads" ] ) , ( "id" , MetaString "ITEM-1" ) , ( "issue" , MetaInlines [ Str "1" ] ) , ( "issued" , MetaString "2011-02" ) , ( "page" , MetaInlines [ Str "32-37" ] ) , ( "publisher" , MetaInlines [ Str "ACM" ] ) , ( "publisher-place" , MetaInlines [ Str "New" , Space , Str "York," , Space , Str "NY," , Space , Str "USA" ] ) , ( "title" , MetaInlines [ Str "Computational" , Space , Str "Thinking" , Space , Str "for" , Space , Str "Youth" , Space , Str "in" , Space , Str "Practice" ] ) , ( "type" , MetaString "article-journal" ) , ( "volume" , MetaInlines [ Str "2" ] ) ]) , MetaMap (fromList [ ( "author" , MetaList [ MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Koh" ) , ( "given" , MetaString "Kyungwon" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) , MetaMap (fromList [ ( "dropping-particle" , MetaString "" ) , ( "family" , MetaString "Abbas" ) , ( "given" , MetaString "June" ) , ( "non-dropping-particle" , MetaString "" ) , ( "suffix" , MetaString "" ) ]) ] ) , ( "container-title" , MetaInlines [ Str "Journal" , Space , Str "of" , Space , Str "Research" , Space , Str "on" , Space , Str "Libraries" , Space , Str "\\&" , Space , Str "Young" , Space , Str "Adults" ] ) , ( "id" , MetaString "ITEM-2" ) , ( "issue" , MetaInlines [ Str "4" ] ) , ( "issued" , MetaString "2015" ) , ( "title" , MetaInlines [ Str "Future" , Space , Str "of" , Space , Str "library" , Space , Str "and" , Space , Str "museum" , Space , Str "services" , Space , Str "supporting" , Space , Str "teen" , Space , Str "learning:" , Space , Str "Perceptions" , Space , Str "of" , Space , Str "professionals" , Space , Str "in" , Space , Str "learning" , Space , Str "labs" , Space , Str "and" , Space , Str "makerspaces" ] ) , ( "type" , MetaString "article-journal" ) , ( "volume" , MetaInlines [ Str "6" ] ) ]) ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "Mendeley" , Space , Str "test" , Space , Cite [ Citation { citationId = "ITEM-1" , citationPrefix = [ Str "prefix" ] , citationSuffix = [ Str "," , Space , Str "123" , Space , Str "suffix" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(prefix" , Space , Str "Hadwen-Bennett" , Space , Str "et" , Space , Str "al.," , Space , Str "2018," , Space , Str "p." , Space , Str "123" , Space , Str "suffix)" ] , Str "." ] , Para [ Str "Another" , Space , Str "test" , Space , Cite [ Citation { citationId = "ITEM-1" , citationPrefix = [ Str "prefix" ] , citationSuffix = [ Str "," , Space , Str "10-20" , Space , Str "suffix" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(prefix" , Space , Str "Seo," , Space , Str "2019," , Space , Str "pp." , Space , Str "10\8211\&20" , Space , Str "suffix)" ] , Str "." ] , Para [ Str "The" , Space , Str "last" , Space , Str "test" , Space , Cite [ Citation { citationId = "ITEM-1" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "ITEM-2" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(Koh" , Space , Str "&" , Space , Str "Abbas," , Space , Str "2015;" , Space , Str "Lee" , Space , Str "et" , Space , Str "al.," , Space , Str "2011)" ] , Str "." ] , Header 1 ( "references" , [] , [] ) [ Str "References" ] ] ================================================ FILE: test/docx/metadata.native ================================================ Pandoc (Meta {unMeta = fromList [("abstract",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "how",Space,Str "this",Space,Str "all",Space,Str "works.",Space,Str "I\8217ve",Space,Str "skipped",Space,Str "lines",Space,Str "here,",Space,Str "which",Space,Str "pandoc",Space,Str "doesn\8217t",Space,Str "do,",Space,Str "but",Space,Str "which",Space,Str "shouldn\8217t",Space,Str "make",Space,Str "a",Space,Str "difference."]),("author",MetaList [MetaInlines [Str "Mary",Space,Str "Ann",Space,Str "Evans"],MetaInlines [Str "Aurore",Space,Str "Dupin"]]),("date",MetaInlines [Str "July",Space,Str "28,",Space,Str "2014"]),("title",MetaInlines [Str "This",Space,Str "Is",Space,Str "the",Space,Str "Title"])]}) [Para [Str "And",Space,Str "now",Space,Str "this",Space,Str "is",Space,Str "normal",Space,Str "text."]] ================================================ FILE: test/docx/metadata_after_normal.native ================================================ Pandoc (Meta {unMeta = fromList [("abstract",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "how",Space,Str "this",Space,Str "all",Space,Str "works.",Space,Str "I\8217ve",Space,Str "skipped",Space,Str "lines",Space,Str "here,",Space,Str "which",Space,Str "pandoc",Space,Str "doesn\8217t",Space,Str "do,",Space,Str "but",Space,Str "which",Space,Str "shouldn\8217t",Space,Str "make",Space,Str "a",Space,Str "difference."]),("author",MetaList [MetaInlines [Str "Mary",Space,Str "Ann",Space,Str "Evans"],MetaInlines [Str "Aurore",Space,Str "Dupin"]]),("date",MetaInlines [Str "July",Space,Str "28,",Space,Str "2014"]),("title",MetaInlines [Str "This",Space,Str "Is",Space,Str "the",Space,Str "Title"])]}) [Para [Str "And",Space,Str "now",Space,Str "this",Space,Str "is",Space,Str "normal",Space,Str "text."] ,Para [Str "This",Space,Str "Is",Space,Str "the",Space,Str "Title"] ,Para [Str "Mary",Space,Str "Ann",Space,Str "Evans"] ,Para [Str "Aurore",Space,Str "Dupin"] ,Para [Str "July",Space,Str "28,",Space,Str "2014"] ,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "test",Space,Str "of",Space,Str "how",Space,Str "this",Space,Str "all",Space,Str "works.",Space,Str "I\8217ve",Space,Str "skipped",Space,Str "lines",Space,Str "here,",Space,Str "which",Space,Str "pandoc",Space,Str "doesn\8217t",Space,Str "do,",Space,Str "but",Space,Str "which",Space,Str "shouldn\8217t",Space,Str "make",Space,Str "a",Space,Str "difference."]] ================================================ FILE: test/docx/nested_anchors_in_header.native ================================================ [Header 1 ("\1086\1075\1083\1072\1074\1083\1077\1085\1080\1077",["TOC-Heading"],[]) [Str "\1054\1075\1083\1072\1074\1083\1077\1085\1080\1077"] ,Para [Link ("",[],[]) [Str "Short",Space,Str "instructions",Space,Link ("",[],[]) [Str "1"] ("#short-instructions","")] ("#short-instructions","")] ,Para [Link ("",[],[]) [Str "Some",Space,Str "instructions",Space,Link ("",[],[]) [Str "1"] ("#some-instructions","")] ("#some-instructions","")] ,Para [Link ("",[],[]) [Str "Remote",Space,Str "folder",Space,Str "or",Space,Str "longlonglonglonglong",Space,Str "file",Space,Str "with",Space,Str "manymanymanymany",Space,Str "letters",Space,Str "inside",Space,Str "opening",Space,Link ("",[],[]) [Str "2"] ("#remote-folder-or-longlonglonglonglong-file-with-manymanymanymany-letters-inside-opening","")] ("#remote-folder-or-longlonglonglonglong-file-with-manymanymanymany-letters-inside-opening","")] ,Para [Link ("",[],[]) [Str "Remote",Space,Str "folder",Space,Str "or",Space,Str "longlonglonglonglong",Space,Str "file",Space,Str "with",Space,Str "manymanymanymany",Space,Str "letters",Space,Str "inside",Space,Str "closing",Space,Link ("",[],[]) [Str "2"] ("#remote-folder-or-longlonglonglonglong-file-with-manymanymanymany-letters-inside-closing","")] ("#remote-folder-or-longlonglonglonglong-file-with-manymanymanymany-letters-inside-closing","")] ,Header 1 ("short-instructions",[],[]) [Str "Short",Space,Str "instructions"] ,Para [Link ("",[],[]) [Str "Open",Space,Str "remote",Space,Str "folder"] ("#remote-folder-or-longlonglonglonglong-file-with-manymanymanymany-letters-inside-opening","")] ,Para [Str "Do",Space,Str "staff"] ,Para [Link ("",[],[]) [Str "Close",Space,Str "remote",Space,Str "folder"] ("#remote-folder-or-longlonglonglonglong-file-with-manymanymanymany-letters-inside-closing","")] ,Header 1 ("some-instructions",[],[]) [Str "Some",Space,Str "instructions"] ,Para [Str "Lines"] ,Header 2 ("remote-folder-or-longlonglonglonglong-file-with-manymanymanymany-letters-inside-opening",[],[]) [Str "Remote",Space,Str "folder",Space,Str "or",Space,Str "longlonglonglonglong",Space,Str "file",Space,Str "with",Space,Str "manymanymanymany",Space,Str "letters",Space,Str "inside",Space,Str "opening"] ,Para [Str "Open",Space,Str "folder"] ,Header 2 ("remote-folder-or-longlonglonglonglong-file-with-manymanymanymany-letters-inside-closing",[],[]) [Str "Remote",Space,Str "folder",Space,Str "or",Space,Str "longlonglonglonglong",Space,Str "file",Space,Str "with",Space,Str "manymanymanymany",Space,Str "letters",Space,Str "inside",Space,Str "closing"] ,Para [Str "Close",Space,Str "folder"]] ================================================ FILE: test/docx/nested_instrText.native ================================================ [Para [Str "\24076\26395\28145\20837\20102\35299\30340\35835\32773\21487\20197\21435\30475David",Space,Str "French",Space,Str "Belding\21644Kevin",Space,Str "J.",Space,Str "Mitchell\30340" ,Link ("",[],[]) [Str "Foundations",Space,Str "of",Space,Str "Analysis,",Space,Str "1/16/18",Space,Str "8:40:00",Space,Str "AM,",Space,Str "2nd",Space,Str "Edition"] ("https://books.google.com/books?id=sp_Zcb9ot90C&lpg=PR4&hl=zh-CN&pg=PA19#v=onepage&q&f=true","") ,Str ",\21487\20174\&19\39029\30475\36215\65292\25110D.C.",Space,Str "Goldrei\30340",Space ,Link ("",[],[]) [Str "Classic",Space,Str "Set",Space,Str "Theory:",Space,Str "For",Space,Str "Guided",Space,Str "Independent",Space,Str "Study"] ("https://books.google.ae/books?id=dlc0DwAAQBAJ&lpg=PT29&hl=zh-CN&pg=PT26#v=onepage&q&f=true","") ,Str "\65292\20174\31532\20108\31456\30475\36215\65292\38405\35835\26102\35201\27880\24847\26412\25991\19982\36825\20123\20070\25152\19981\21516\30340\26159\24182\27809\26377\25226\23454\25968\30475\20316\26159\26377\29702\25968\38598\30340\20998\21106\12290"]] ================================================ FILE: test/docx/nested_sdt.native ================================================ [Para [Str "Test",Space,Str "Paragraph1"] ,Para [Str "Test",Space,Str "Paragraph2"] ,Para [Str "Test",Space,Str "Paragraph3"]] ================================================ FILE: test/docx/nested_smart_tags.native ================================================ [Header 2 ("and-it-came-to-pass-in-the-course-of-those-many-days",["Myheading2"],[]) [Str "159.",Space,Str "And",Space,Str "It",Space,Str "Came",Space,Str "to",Space,Str "Pass",Space,Str "in",Space,Str "the",Space,Str "Course",Space,Str "of",Space,Str "Those",Space,Str "Many",Space,Str "Days"] ,Para [Str "I",Space,Str "heard"] ,Para [Str "\8220And",Space,Str "it",Space,Str "came",Space,Str "to",Space,Str "pass",Space,Str "in",Space,Str "the",Space,Str "course",Space,Str "of",Space,Str "those",Space,Str "many",Space,Str "days",Space,Str "that",Space,Str "the",Space,Str "king",Space,Str "of",Space,Str "Egypt",Space,Str "died;",Space,Str "and",Space,Str "the",Space,Str "children",Space,Str "of",Space,Str "Israel",Space,Str "sighed",Space,Str "by",Space,Str "reason",Space,Str "of",Space,Str "the",Space,Str "bondage,",Space,Str "and",Space,Str "they",Space,Str "cried,",Space,Str "and",Space,Str "their",Space,Str "cry",Space,Str "came",Space,Str "up",Space,Str "unto",Space,Str "God",Space,Str "by",Space,Str "reason",Space,Str "of",Space,Str "the",Space,Str "bondage.",Space,Str "And",Space,Str "God",Space,Str "heard",Space,Str "their",Space,Str "groaning\8221",Space,Str "(Exodus",Space,Str "2:23-4).",Space,Str "This",Space,Str "means",Space,Str "that",Space,Str "they",Space,Str "suffered",Space,Str "so",Space,Str "much",Space,Str "that",Space,Str "they",Space,Str "could",Space,Str "not",Space,Str "bear",Space,Str "it",Space,Str "any",Space,Str "longer.",Space,Str "And",Space,Str "they",Space,Str "so",Space,Str "pleaded",Space,Str "with",Space,Str "prayer,",Space,Str "that",Space,Str "\8220their",Space,Str "cry",Space,Str "came",Space,Str "up",Space,Str "unto",Space,Str "God.\8221"] ,Para [Str "But",Space,Str "we",Space,Str "can",Space,Str "see",Space,Str "that",Space,Str "they",Space,Str "were",Space,Str "saying,",Space,Str "\8220Would",Space,Str "that",Space,Str "we",Space,Str "had\8230",Space,Str "when",Space,Str "we",Space,Str "sat",Space,Str "by",Space,Str "the",Space,Str "flesh-pots,",Space,Str "when",Space,Str "we",Space,Str "did",Space,Str "eat",Space,Str "bread",Space,Str "to",Space,Str "the",Space,Str "full.\8221",Space,Str "And",Space,Str "they",Space,Str "also",Space,Str "said,",Space,Str "\8220We",Space,Str "remember",Space,Str "the",Space,Str "fish,",Space,Str "which",Space,Str "we",Space,Str "would",Space,Str "eat",Space,Str "in",Space,Str "Egypt",Space,Str "for",Space,Str "naught;",Space,Str "the",Space,Str "cucumbers,",Space,Str "and",Space,Str "the",Space,Str "melons,",Space,Str "and",Space,Str "the",Space,Str "leeks,",Space,Str "and",Space,Str "the",Space,Str "onions,",Space,Str "and",Space,Str "the",Space,Str "garlic.\8221"] ,Para [Str "The",Space,Str "thing",Space,Str "is",Space,Str "that,",Space,Str "indeed,",Space,Str "they",Space,Str "were",Space,Str "very",Space,Str "fond",Space,Str "of",Space,Str "the",Space,Str "work",Space,Str "in",Space,Str "Egypt.",Space,Str "This",Space,Str "is",Space,Str "the",Space,Str "meaning",Space,Str "of",Space,Str "\8220But",Space,Str "mingled",Space,Str "themselves",Space,Str "with",Space,Str "the",Space,Str "nations,",Space,Str "and",Space,Str "learned",Space,Str "their",Space,Str "works.\8221",Space,Str "It",Space,Str "means",Space,Str "that",Space,Str "if",Space,Str "Israel",Space,Str "are",Space,Str "under",Space,Str "the",Space,Str "dominion",Space,Str "of",Space,Str "a",Space,Str "certain",Space,Str "nation,",Space,Str "that",Space,Str "nation",Space,Str "controls",Space,Str "them",Space,Str "and",Space,Str "they",Space,Str "cannot",Space,Str "retire",Space,Str "from",Space,Str "their",Space,Str "dominion.",Space,Str "Thus,",Space,Str "they",Space,Str "tasted",Space,Str "sufficient",Space,Str "flavor",Space,Str "in",Space,Str "that",Space,Str "work",Space,Str "and",Space,Str "could",Space,Str "not",Space,Str "be",Space,Str "redeemed."] ,Para [Str "So",Space,Str "what",Space,Str "did",Space,Str "the",Space,Str "Creator",Space,Str "do?",Space,Str "\8220The",Space,Str "king",Space,Str "of",Space,Str "Egypt",Space,Str "died,\8221",Space,Str "meaning",Space,Str "they",Space,Str "had",Space,Str "lost",Space,Str "this",Space,Str "servitude.",Space,Str "Thus",Space,Str "they",Space,Str "could",Space,Str "no",Space,Str "longer",Space,Str "work;",Space,Str "they",Space,Str "understood",Space,Str "that",Space,Str "if",Space,Str "there",Space,Str "is",Space,Str "no",Space,Str "perfection",Space,Str "of",Space,Str "the",Space,Emph [Str "Mochin"],Str ",",Space,Str "the",Space,Str "servitude",Space,Str "is",Space,Str "also",Space,Str "incomplete.",Space,Str "Hence,",Space,Str "\8220and",Space,Str "the",Space,Str "children",Space,Str "of",Space,Str "Israel",Space,Str "sighed",Space,Str "by",Space,Str "reason",Space,Str "of",Space,Str "the",Space,Str "bondage.\8221",Space,Str "The",Space,Str "work",Space,Str "means",Space,Str "that",Space,Str "they",Space,Str "did",Space,Str "not",Space,Str "suffice",Space,Str "for",Space,Str "the",Space,Str "work,",Space,Str "that",Space,Str "they",Space,Str "had",Space,Str "no",Space,Str "liveliness",Space,Str "in",Space,Str "the",Space,Str "servitude."] ,Para [Str "This",Space,Str "is",Space,Str "the",Space,Str "meaning",Space,Str "of",Space,Str "\8220the",Space,Str "king",Space,Str "of",Space,Str "Egypt",Space,Str "died,\8221",Space,Str "that",Space,Str "all",Space,Str "the",Space,Str "dominations",Space,Str "of",Space,Str "the",Space,Str "king",Space,Str "of",Space,Str "Egypt,",Space,Str "which",Space,Str "he",Space,Str "was",Space,Str "providing",Space,Str "for",Space,Str "and",Space,Str "nourishing,",Space,Str "had",Space,Str "died.",Space,Str "This",Space,Str "is",Space,Str "why",Space,Str "they",Space,Str "had",Space,Str "room",Space,Str "for",Space,Str "prayer.",Space,Str "And",Space,Str "they",Space,Str "were",Space,Str "immediately",Space,Str "salvaged.",Space,Str "And",Space,Str "afterwards,",Space,Str "when",Space,Str "they",Space,Str "walked",Space,Str "in",Space,Str "the",Space,Str "desert",Space,Str "and",Space,Str "came",Space,Str "to",Space,Str "a",Space,Str "state",Space,Str "of",Space,Emph [Str "Katnut"],Space,Str "(smallness),",Space,Str "they",Space,Str "craved",Space,Str "the",Space,Str "servitude",Space,Str "that",Space,Str "they",Space,Str "had",Space,Str "had",Space,Str "prior",Space,Str "to",Space,Str "the",Space,Str "death",Space,Str "of",Space,Str "the",Space,Str "king",Space,Str "of",Space,Str "Egypt."]] ================================================ FILE: test/docx/normalize.native ================================================ [Para [Str "These",Space,Str "are",Space,Str "different",Space,Str "fonts."] ,Para [Strong [Str "These",Space,Emph [Str "are",Space,Strikeout [Str "different"]],Space,Str "fonts."]]] ================================================ FILE: test/docx/notes.native ================================================ [Header 2 ("a-footnote",[],[]) [Str "A",Space,Str "footnote"] ,Para [Str "Test",Space,Str "footnote.",Note [Para [Str "My",Space,Str "note."]],Space,Str "Test",Space,Str "endnote.",Note [Para [Str "This",Space,Str "is",Space,Str "an",Space,Str "endnote",Space,Str "at",Space,Str "the",Space,Str "end",Space,Str "of",Space,Str "the",Space,Str "document."]]]] ================================================ FILE: test/docx/numbered_header.native ================================================ [Header 1 ("a-numbered-header.",[],[]) [Str "A",Space,Str "Numbered",Space,Str "Header."]] ================================================ FILE: test/docx/overlapping_targets.native ================================================ [Para [Link ("",[],[]) [Str "One",Space,Str "link",Space,Str "to",Space,Str "one",Space,Str "target."] ("#Fizz","")] ,Para [Span ("Fizz",["anchor"],[]) [],Str "This",Space,Str "is",Space,Str "a",Space,Str "target",Space,Str "with",Space,Str "two",Space,Str "names."] ,Para [Link ("",[],[]) [Str "Another",Space,Str "link",Space,Str "to",Space,Str "the",Space,Str "same",Space,Str "target."] ("#Fizz","")]] ================================================ FILE: test/docx/pageref.native ================================================ [Para [Str "Title",Space,Link ("",[],[]) [Str "2"] ("#title","")] ,Para [Str "Title2",Space,Link ("",[],[]) [Str "2"] ("#title2","")] ,Header 1 ("title", [],[]) [Str "Title"] ,Header 1 ("title2",[],[]) [Str "Title2"]] ================================================ FILE: test/docx/paragraph_insertion_deletion_accept.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a"] ,Para [Str "split",Space,Str "Paragraph."]] ================================================ FILE: test/docx/paragraph_insertion_deletion_all.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Span ("",["paragraph-insertion"],[("author","Seeley, Jason"),("date","2017-09-17T16:39:00Z")]) []] ,Para [Str "split",Span ("",["paragraph-deletion"],[("author","Seeley, Jason"),("date","2017-09-17T16:39:00Z")]) []] ,Para [Str "Paragraph."]] ================================================ FILE: test/docx/paragraph_insertion_deletion_reject.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "split"] ,Para [Str "Paragraph."]] ================================================ FILE: test/docx/raw-blocks.native ================================================ [Para [Str "Cell",Space,Str "compartments"] ,RawBlock (Format "openxml") "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" ,Para [Str "Ribosome"] ,RawBlock (Format "openxml") "\n" ,Para [Str "Lysosome"] ,RawBlock (Format "openxml") "\n\n"] ================================================ FILE: test/docx/raw-bookmarks.native ================================================ [Para [Str "Manual",Space,Str "endnotes."] ,Para [Str "Nullam",Space,Str "eu",Space,Str "ante",Space,Str "vel",Space,Str "est",Space,Str "convallis",Space,Str "dignissim.",Space,Str "Nunc",Space,Str "porta",Space,Str "vulputate",Space,Str "tellus.",Space,Str "Nunc",Space,Str "rutrum",Space,Str "turpis",Space,Str "sed",Space,Str "pede.",Space,Str "Sed",Space,Str "bibendum.",RawInline (Format "openxml") "",Str "Aliquam",Space,Str "posuere."] ,Para [Str "Nunc",Space,Str "aliquet,",Space,Str "augue",Space,Str "nec",Space,Str "adipiscing",Space,Str "interdum,",Space,Str "lacus",Space,Str "tellus",Space,Str "malesuada",Space,Str "massa,",Space,Str "quis",Space,Str "varius",Space,Str "mi",Space,Str "purus",Space,Str "non",Space,Str "odio.",RawInline (Format "openxml") "",Str "Pellentesque",Space,Str "condimentum,",Space,Str "magna",Space,Str "ut",Space,Str "suscipit",Space,Str "hendrerit,",Space,Str "ipsum",Space,Str "augue",Space,Str "ornare",Space,Str "nulla,",Space,Str "non",Space,Str "luctus",Space,Str "diam",Space,Str "neque",Space,Str "sit",Space,Str "amet",Space,Str "urna.",Space,Str "Curabitur",Space,Str "vulputate",Space,Str "vestibulum",Space,Str "lorem."]] ================================================ FILE: test/docx/relative_indentation_blockquotes.native ================================================ [Header 1 ("indentation-blockquotes",[],[]) [Str "Indentation",Space,Str "blockquotes"] ,Para [Str "Foobar"] ,Para [Str "First",Space,Str "line",Space,Str "indented."] ,Para [Str "Normal",Space,Str "list",Space,Str "paragraph"] ,Para [Str "List",Space,Str "paragraph",Space,Str "with",Space,Str "less",Space,Str "indent"] ,BlockQuote [Para [Str "List",Space,Str "paragraph",Space,Str "with",Space,Str "more",Space,Str "indent"]]] ================================================ FILE: test/docx/sdt_elements.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.16167023554603854 ) , ( AlignDefault , ColWidth 0.16167023554603854 ) , ( AlignDefault , ColWidth 0.40920770877944324 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "col1Header" ] ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "col2Header" ] ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "col3Header" ] ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col1" , Space , Str "content" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Body" , Space , Str "copy" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col3" , Space , Str "content" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/sdt_in_footnote.native ================================================ [Para [Str "Lorem",Space,Str "ipsum",Space,Str "dolor",Space,Str "sit",Space,Str "amet.",Note [Para [Str "Vgl.",Space,Emph [Str "Bitzios"],Space,Str "u.",Space,Str "a.:",Space,Str "Dissonance",Space,Str "in",Space,Str "the",Space,Str "food",Space,Str "traceability",Space,Str "regulatory",Space,Str "environment",Space,Str "and",Space,Str "food",Space,Str "fraud,",Space,Str "in:",Space,Str "Dries",Space,Str "u.",Space,Str "a.",Space,Str "(Hrsg.):",Space,Str "It\8217s",Space,Str "a",Space,Str "jungle",Space,Str "out",Space,Str "there",Space,Str "-",Space,Str "the",Space,Str "strange",Space,Str "animals",Space,Str "of",Space,Str "economic",Space,Str "organization",Space,Str "in",Space,Str "agri-food",Space,Str "value",Space,Str "chains,",Space,Str "Wageningen",Space,Str "im",Space,Str "Druck,",Space,Str "hier",Space,Str "S.\160\&100-105."]]]] ================================================ FILE: test/docx/special_punctuation.native ================================================ [Para [Str "Soft",Space,Str "hyphen:",Space,Str "[\173]"] ,Para [Str "Non-breaking",Space,Str "hyphen:",Space,Str "[\8209]"]] ================================================ FILE: test/docx/table_captions_no_field.native ================================================ [ Para [ Str "See" , Space , Str "Table" , Space , Str "5.1." ] , Table ( "" , [] , [] ) (Caption Nothing [ Para [ Str "Table" , Space , Str "5.1" ] ]) [ ( AlignDefault , ColWidth 0.7605739372523824 ) , ( AlignDefault , ColWidth 0.11971303137380876 ) , ( AlignDefault , ColWidth 0.11971303137380876 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Count" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "%" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" , Space , Str "option" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "242" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "45" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" , Space , Str "option" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "99" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "18" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "section" , [] , [] ) [] ] ================================================ FILE: test/docx/table_captions_with_field.native ================================================ [ Para [ Str "See" , Space , Link ( "" , [] , [] ) [ Str "Table" , Space , Str "1" ] ( "#_Ref71265628" , "" ) , Str "." ] , Table ( "" , [] , [] ) (Caption Nothing [ Para [ Span ( "_Ref71265628" , [ "anchor" ] , [] ) [] , Str "Table" , Space , Str "1" ] ]) [ ( AlignDefault , ColWidth 0.7605739372523824 ) , ( AlignDefault , ColWidth 0.11971303137380876 ) , ( AlignDefault , ColWidth 0.11971303137380876 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Count" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "%" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" , Space , Str "option" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "242" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "45" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" , Space , Str "option" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "99" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "18" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "section" , [] , [] ) [] , Table ( "" , [] , [] ) (Caption Nothing [ Para [ Span ( "_Ref71265695" , [ "anchor" ] , [] ) [] , Str "Table" , Space , Str "2" ] ]) [ ( AlignDefault , ColWidth 0.33329636202307006 ) , ( AlignDefault , ColWidth 0.33329636202307006 ) , ( AlignDefault , ColWidth 0.33340727595385977 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "One" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Two" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Three" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "See" , Space , Link ( "" , [] , [] ) [ Str "Table" , Space , Str "2" ] ( "#_Ref71265695" , "" ) , Str "." ] ] ================================================ FILE: test/docx/table_gridbefore.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 7.883369330453563e-3 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 3.0669546436285097e-2 ) , ( AlignDefault , ColWidth 0.42861771058315334 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 8) [ Plain [ Str "Bits" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "TEXT" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "BINARY" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "HYPERLINKS" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "FILEURL" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "LOCATION" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "ENHANCED" , Space , Str "STATUS" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Value" , Space , Str "allocated" , Space , Str "for" , Space , Str "use" , Space , Str "in" , Space , Str "interworking" , Space , Str "(NOTE)" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "LOCATION" , Space , Str "ALTITUDE" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "LOCATION" , Space , Str "TIMESTAMP" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "CODED" , Space , Str "TEXT" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 10) [ Plain [ Str "All" , Space , Str "other" , Space , Str "values" , Space , Str "are" , Space , Str "reserved." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 10) [ Plain [ Str "NOTE:" , Space , Str "Usage" , Space , Str "of" , Space , Str "this" , Space , Str "value" , Space , Str "is" , Space , Str "described" , Space , Str "in" , Space , Str "3GPP\160TS\160\&29.582\160[48]." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/table_header_rowspan.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidth 0.30701754385964913 ) , ( AlignDefault , ColWidth 0.13645224171539963 ) , ( AlignDefault , ColWidth 0.10009746588693959 ) , ( AlignDefault , ColWidth 9.707602339181289e-2 ) , ( AlignDefault , ColWidth 7.719298245614035e-2 ) , ( AlignDefault , ColWidth 7.085769980506823e-2 ) , ( AlignDefault , ColWidth 7.09551656920078e-2 ) , ( AlignDefault , ColWidth 0.14035087719298248 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Strong [ Str "B" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Strong [ Str "C" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Strong [ Str "D" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "E" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "F" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "G" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "H" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "I" ] ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/table_one_header_row.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.3330243337195829 ) , ( AlignDefault , ColWidth 0.33325608342989577 ) , ( AlignDefault , ColWidth 0.33371958285052145 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "One" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Table" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/table_one_row.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.33333333333333337 ) , ( AlignDefault , ColWidth 0.33333333333333337 ) , ( AlignDefault , ColWidth 0.33333333333333337 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "One" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Table" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/table_variable_width.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 1.9673503557974047e-2 ) , ( AlignDefault , ColWidth 1.946421096693177e-2 ) , ( AlignDefault , ColWidth 0.21735035579740478 ) , ( AlignDefault , ColWidth 0.4660946002511511 ) , ( AlignDefault , ColWidth 1.0464629552113855e-4 ) , ( AlignDefault , ColWidth 0.25627877773126834 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "h3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "h4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "h5" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "c11" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "c22" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c23" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/table_with_list_cell.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.5 ) , ( AlignDefault , ColWidth 0.5 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "with" , Space , Str "text" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "with" , Space , Str "text" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ BulletList [ [ Para [ Str "Cell" , Space , Str "with" ] ] , [ Para [ Str "A" ] ] , [ Para [ Str "Bullet" , Space , Str "list" ] ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "Cell" , Space , Str "with" ] ] , [ Para [ Str "A" ] ] , [ Para [ Str "Numbered" , Space , Str "list." ] ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/tables-default-widths.native ================================================ [Header 2 ("a-table-with-and-without-a-header-row",[],[]) [Str "A",Space,Str "table,",Space,Str "with",Space,Str "and",Space,Str "without",Space,Str "a",Space,Str "header",Space,Str "row"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Name"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Game"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Fame"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Blame"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Lebron",Space,Str "James"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Basketball"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Very",Space,Str "High"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Leaving",Space,Str "Cleveland"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Ryan",Space,Str "Braun"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Baseball"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Moderate"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Steroids"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Russell",Space,Str "Wilson"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Football"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "High"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Tacky",Space,Str "uniform"]]]])] (TableFoot ("",[],[]) []) ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Sinple"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Table"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Without"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Header"]]]])] (TableFoot ("",[],[]) []) ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "Simple"] ,Para [Str "Multiparagraph"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "Table"] ,Para [Str "Full"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "Of"] ,Para [Str "Paragraphs"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "In",Space,Str "each"] ,Para [Str "Cell."]]]])] (TableFoot ("",[],[]) [])] ================================================ FILE: test/docx/tables.native ================================================ [ Header 2 ( "a-table-with-and-without-a-header-row" , [] , [] ) [ Str "A" , Space , Str "table," , Space , Str "with" , Space , Str "and" , Space , Str "without" , Space , Str "a" , Space , Str "header" , Space , Str "row" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.25 ) , ( AlignDefault , ColWidth 0.25 ) , ( AlignDefault , ColWidth 0.25 ) , ( AlignDefault , ColWidth 0.25 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Name" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Game" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Fame" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Blame" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Lebron" , Space , Str "James" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Basketball" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Very" , Space , Str "High" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Leaving" , Space , Str "Cleveland" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Ryan" , Space , Str "Braun" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Baseball" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Moderate" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Steroids" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Russell" , Space , Str "Wilson" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Football" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "High" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Tacky" , Space , Str "uniform" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.5 ) , ( AlignDefault , ColWidth 0.5 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Sinple" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Table" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Without" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.5 ) , ( AlignDefault , ColWidth 0.5 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Simple" ] , Para [ Str "Multiparagraph" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Table" ] , Para [ Str "Full" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Of" ] , Para [ Str "Paragraphs" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "In" , Space , Str "each" ] , Para [ Str "Cell." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/docx/tables_separated_with_rawblock.native ================================================ [Table ("", [], []) (Caption Nothing []) [(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "a"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "b"]]]]] (TableFoot ("",[],[]) []) ,RawBlock (Format "latex") "" ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "c"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "d"]]]]] (TableFoot ("",[],[]) [])] ================================================ FILE: test/docx/tabs.native ================================================ [Para [Str "Some",Space,Str "text",Space,Str "separated",Space,Str "by",Space,Str "a",Space,Str "tab."] ,Para [Str "Tab-indented",Space,Str "text."]] ================================================ FILE: test/docx/task_list.native ================================================ [ BulletList [ [ Para [ Str "\9744" , Space , Str "Unchecked" ] ] , [ Para [ Str "\9746" , Space , Str "Checked" ] , Para [ Str "with" , Space , Str "continuation" , Space , Str "paragraph" ] ] , [ Para [ Str "\9744" , Space , Str "Unchecked" ] , BulletList [ [ Plain [ Str "\9746" , Space , Str "Checked" , Space , Str "sublist" ] , BulletList [ [ Plain [ Str "\9744" , Space , Str "Unchecked" , Space , Str "subsublist" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "Numbered" , Space , Str "child" ] ] ] ] ] ] ] ] ] ] ================================================ FILE: test/docx/text_in_shape_format.native ================================================ [ Para [ Str "Last" , Space , Str "update:" , Space , Str "May" , Space , Str "1," , Space , Str "2017" ] , Para [ Str "U" , Str "sing" , Space , Str "Microsoft" , Space , Str "Word" , Space , Str "2007/2010" , LineBreak , Str "for" , Space , Str "Writing" , Space , Str "Technical" , Space , Str "Documents" ] , Para [ Str "Valter" , Space , Str "Kiisk" ] , Para [ Str "Institute" , Space , Str "of" , Space , Str "Physics," , Space , Str "University" , Space , Str "of" , Space , Str "Tartu" ] , Para [] ] ================================================ FILE: test/docx/textbox_image.native ================================================ [ Para [ Str "The" , Space , Str "image" , Space , Str "is" , Space , Str "below." ] , Para [ Image ( "" , [] , [ ( "width" , "4.543038057742782in" ) , ( "height" , "2.9166666666666665in" ) ] ) [] ( "media/image1.png" , "" ) ] , Para [ Str "The" , Space , Str "image" , Space , Str "is" , Space , Str "above." ] ] ================================================ FILE: test/docx/textbox_image_duplicate_encoding.native ================================================ [ Para [ Str "The" , Space , Str "image" , Space , Str "is" , Space , Str "below." ] , Para [ Image ( "" , [] , [ ( "width" , "4.543038057742782in" ) , ( "height" , "2.9166666666666665in" ) ] ) [] ( "media/image1.png" , "" ) ] , Para [ Str "The" , Space , Str "image" , Space , Str "is" , Space , Str "above." ] ] ================================================ FILE: test/docx/track_changes_deletion_accept.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "text",Space,Str "with",Space,Str "a",Space,Str "deletion."]] ================================================ FILE: test/docx/track_changes_deletion_all.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "text",Space,Str "with",Space,Str "a",Span ("",["deletion"],[("author","eng-dept"),("date","2014-06-25T10:42:00Z")]) [Str "n",Space,Str "excessively",Space,Str "modified"],Space,Str "deletion."]] ================================================ FILE: test/docx/track_changes_deletion_reject.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "text",Space,Str "with",Space,Str "an",Space,Str "excessively",Space,Str "modified",Space,Str "deletion."]] ================================================ FILE: test/docx/track_changes_insertion_accept.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "text",Space,Str "with",Space,Str "two",Space,Str "exciting",Space,Str "insertions."]] ================================================ FILE: test/docx/track_changes_insertion_all.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "text",Space,Str "with",Space,Span ("",["insertion"],[("author","eng-dept"),("date","2014-06-25T10:40:00Z")]) [Str "two",Space,Str "exciting"],Space,Str "insertions."]] ================================================ FILE: test/docx/track_changes_insertion_reject.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "text",Space,Str "with",Space,Str "insertions."]] ================================================ FILE: test/docx/track_changes_move_accept.native ================================================ [Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "text."] ,Para [Str "Here",Space,Str "is",Space,Str "the",Space,Str "text",Space,Str "to",Space,Str "be",Space,Str "moved."] ,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "more",Space,Str "text."]] ================================================ FILE: test/docx/track_changes_move_all.native ================================================ [Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "text."] ,Para [Span ("",["insertion"],[("author","Jesse Rosenthal"),("date","2016-04-16T08:20:00Z")]) [Str "Here",Space,Str "is",Space,Str "the",Space,Str "text",Space,Str "to",Space,Str "be",Space,Str "moved."]] ,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "more",Space,Str "text."] ,Para [Span ("",["deletion"],[("author","Jesse Rosenthal"),("date","2016-04-16T08:20:00Z")]) [Str "Here",Space,Str "is",Space,Str "the",Space,Str "text",Space,Str "to",Space,Str "be",Space,Str "moved."]]] ================================================ FILE: test/docx/track_changes_move_reject.native ================================================ [Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "text."] ,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "more",Space,Str "text."] ,Para [Str "Here",Space,Str "is",Space,Str "the",Space,Str "text",Space,Str "to",Space,Str "be",Space,Str "moved."]] ================================================ FILE: test/docx/track_changes_scrubbed_metadata.native ================================================ [Para [ Str "Here", Space, Str "is", Space, Str "a", Space , Span ("",["deletion"],[("author","Author")]) [Str "dummy"] , Span ("",["insertion"],[("author","Author")]) [Str "test"] , Space , Span ("",["comment-start"],[("id","3"),("author","Author")]) [Str "With",Space,Str "a",Space,Str "comment!"] , Str "document",Span ("",["comment-end"],[("id","3")]) [],Str "." ] ] ================================================ FILE: test/docx/trailing_spaces_in_formatting.native ================================================ [Para [Str "Turn",Space,Str "my",Space,Emph [Str "formatting"],Space,Str "off",Space,Str "after",Space,Str "the",Space,Str "spaces."]] ================================================ FILE: test/docx/trim_last_inline.native ================================================ [Para [Strong [Str "Foo",Space,Str "bar."]] ,Para [Str "Fizz",Space,Str "pop."]] ================================================ FILE: test/docx/unicode.native ================================================ [Para [Str "Hello,",Space,Str "\19990\30028.",Space,Str "This",Space,Str "costs",Space,Str "\8364\&10.\8744\8744("]] ================================================ FILE: test/docx/unused_anchors.native ================================================ [Header 1 ("my-section",[],[]) [Str "My",Space,Str "Section"] ,Para [Link ("",[],[]) [Str "Here",Space,Str "is",Space,Str "a",Space,Str "link."] ("#Foo","")] ,Para [Span ("Foo",["anchor"],[]) [],Str "Here",Space,Str "is",Space,Str "the",Space,Str "target."]] ================================================ FILE: test/docx/verbatim_subsuper.native ================================================ [Para [Str "m",Superscript [Str "2"]] ,Para [Str "m",Superscript [Code ("",[],[]) "2"]] ,Para [Code ("",[],[]) "m",Superscript [Str "2"]] ,Para [Code ("",[],[]) "m",Superscript [Code ("",[],[]) "2"]] ,Para [Str "m",Subscript [Str "2"]] ,Para [Str "m",Subscript [Code ("",[],[]) "2"]] ,Para [Code ("",[],[]) "m",Subscript [Str "2"]] ,Para [Code ("",[],[]) "m",Subscript [Code ("",[],[]) "2"]]] ================================================ FILE: test/docx/zotero_citations_minus.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "citation" , Space , Str "(Smith" , Space , Str "et" , Space , Str "al.," , Space , Str "2013)" ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "citation" , Space , Str "with" , Space , Str "two" , Space , Str "sources" , Space , Str "(Boer" , Space , Str "et" , Space , Str "al.," , Space , Str "2016;" , Space , Str "Smith" , Space , Str "et" , Space , Str "al.," , Space , Str "2013)." ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "citation" , Space , Str "with" , Space , Str "additional" , Space , Str "prefix," , Space , Str "suffix" , Space , Str "and" , Space , Str "a" , Space , Str "page" , Space , Str "number" , Space , Str "(prefix" , Space , Str "Bellomo" , Space , Str "et" , Space , Str "al.," , Space , Str "2016," , Space , Str "p." , Space , Str "2" , Space , Str "suffix)." ] , Header 1 ( "reference-list" , [] , [] ) [ Str "Reference" , Space , Str "list" ] , Para [ Str "Bellomo," , Space , Str "K.," , Space , Str "Clement," , Space , Str "A." , Space , Str "C.," , Space , Str "Murphy," , Space , Str "L." , Space , Str "N.," , Space , Str "Polvani," , Space , Str "L." , Space , Str "M.," , Space , Str "&" , Space , Str "Cane," , Space , Str "M." , Space , Str "A." , Space , Str "(2016)." , Space , Str "New" , Space , Str "observational" , Space , Str "evidence" , Space , Str "for" , Space , Str "a" , Space , Str "positive" , Space , Str "cloud" , Space , Str "feedback" , Space , Str "that" , Space , Str "amplifies" , Space , Str "the" , Space , Str "Atlantic" , Space , Str "Multidecadal" , Space , Str "Oscillation." , Space , Emph [ Str "Geophys." , Space , Str "Res." , Space , Str "Lett." ] , Str "," , Space , Emph [ Str "43" ] , Str "(18)," , Space , Str "9852\8211\&9859." , Space , Str "https://doi.org/10.1002/2016GL069961" ] , Para [ Str "Boer," , Space , Str "G." , Space , Str "J.," , Space , Str "Smith," , Space , Str "D." , Space , Str "M.," , Space , Str "Cassou," , Space , Str "C.," , Space , Str "Doblas-Reyes," , Space , Str "F.," , Space , Str "Danabasoglu," , Space , Str "G.," , Space , Str "Kirtman," , Space , Str "B.," , Space , Str "Kushnir," , Space , Str "Y.," , Space , Str "Kimoto," , Space , Str "M.," , Space , Str "Meehl," , Space , Str "G." , Space , Str "A.," , Space , Str "Msadek," , Space , Str "R.," , Space , Str "M\252ller," , Space , Str "W." , Space , Str "A.," , Space , Str "Taylor," , Space , Str "K." , Space , Str "E.," , Space , Str "Zwiers," , Space , Str "F.," , Space , Str "Rixen," , Space , Str "M.," , Space , Str "Ruprich-Robert," , Space , Str "Y.," , Space , Str "&" , Space , Str "Eade," , Space , Str "R." , Space , Str "(2016)." , Space , Str "The" , Space , Str "Decadal" , Space , Str "Climate" , Space , Str "Prediction" , Space , Str "Project" , Space , Str "(DCPP)." , Space , Emph [ Str "Geoscientific" , Space , Str "Model" , Space , Str "Development" ] , Str "," , Space , Emph [ Str "9" ] , Str "," , Space , Str "3751\8211\&3777." , Space , Str "https://doi.org/10.5194/gmd-9-3751-2016" ] , Para [ Str "Smith," , Space , Str "D." , Space , Str "M.," , Space , Str "Eade," , Space , Str "R.," , Space , Str "&" , Space , Str "Pohlmann," , Space , Str "H." , Space , Str "(2013)." , Space , Str "A" , Space , Str "comparison" , Space , Str "of" , Space , Str "full-field" , Space , Str "and" , Space , Str "anomaly" , Space , Str "initialization" , Space , Str "for" , Space , Str "seasonal" , Space , Str "to" , Space , Str "decadal" , Space , Str "climate" , Space , Str "prediction." , Space , Emph [ Str "Climate" , Space , Str "Dynamics" ] , Str "," , Space , Emph [ Str "41" ] , Str "(11\8211\&12)," , Space , Str "3325\8211\&3338." , Space , Str "https://doi.org/10.1007/s00382-013-1683-2" ] ] ================================================ FILE: test/docx/zotero_citations_plus.native ================================================ Pandoc Meta { unMeta = fromList [ ( "references" , MetaList [ MetaMap (fromList [ ( "DOI" , MetaString "10.1002/2016GL069961" ) , ( "author" , MetaList [ MetaMap (fromList [ ( "family" , MetaString "Bellomo" ) , ( "given" , MetaString "K." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Clement" ) , ( "given" , MetaString "A.C." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Murphy" ) , ( "given" , MetaString "L.N." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Polvani" ) , ( "given" , MetaString "L.M." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Cane" ) , ( "given" , MetaString "M.A." ) ]) ] ) , ( "container-title" , MetaInlines [ Str "Geophys." , Space , Str "Res." , Space , Str "Lett." ] ) , ( "id" , MetaString "10" ) , ( "issue" , MetaInlines [ Str "18" ] ) , ( "issued" , MetaString "2016" ) , ( "language" , MetaInlines [ Str "en" ] ) , ( "page" , MetaInlines [ Str "9852\8211\&9859" ] ) , ( "title" , MetaInlines [ Str "New" , Space , Str "observational" , Space , Str "evidence" , Space , Str "for" , Space , Str "a" , Space , Str "positive" , Space , Str "cloud" , Space , Str "feedback" , Space , Str "that" , Space , Str "amplifies" , Space , Str "the" , Space , Str "Atlantic" , Space , Str "Multidecadal" , Space , Str "Oscillation" ] ) , ( "type" , MetaString "article-journal" ) , ( "volume" , MetaInlines [ Str "43" ] ) ]) , MetaMap (fromList [ ( "DOI" , MetaString "10.1007/s00382-013-1683-2" ) , ( "ISSN" , MetaString "0930-7575, 1432-0894" ) , ( "author" , MetaList [ MetaMap (fromList [ ( "family" , MetaString "Smith" ) , ( "given" , MetaString "Doug M." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Eade" ) , ( "given" , MetaString "Rosie" ) ]) , MetaMap (fromList [ ( "family" , MetaString "Pohlmann" ) , ( "given" , MetaString "Holger" ) ]) ] ) , ( "container-title" , MetaInlines [ Str "Climate" , Space , Str "Dynamics" ] ) , ( "id" , MetaString "109" ) , ( "issue" , MetaInlines [ Str "11-12" ] ) , ( "issued" , MetaString "2013-12" ) , ( "language" , MetaInlines [ Str "en" ] ) , ( "page" , MetaInlines [ Str "3325\8211\&3338" ] ) , ( "title" , MetaInlines [ Str "A" , Space , Str "comparison" , Space , Str "of" , Space , Str "full-field" , Space , Str "and" , Space , Str "anomaly" , Space , Str "initialization" , Space , Str "for" , Space , Str "seasonal" , Space , Str "to" , Space , Str "decadal" , Space , Str "climate" , Space , Str "prediction" ] ) , ( "type" , MetaString "article-journal" ) , ( "volume" , MetaInlines [ Str "41" ] ) ]) , MetaMap (fromList [ ( "DOI" , MetaString "10.5194/gmd-9-3751-2016" ) , ( "author" , MetaList [ MetaMap (fromList [ ( "family" , MetaString "Boer" ) , ( "given" , MetaString "G.J." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Smith" ) , ( "given" , MetaString "D.M." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Cassou" ) , ( "given" , MetaString "C." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Doblas-Reyes" ) , ( "given" , MetaString "F." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Danabasoglu" ) , ( "given" , MetaString "G." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Kirtman" ) , ( "given" , MetaString "B." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Kushnir" ) , ( "given" , MetaString "Y." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Kimoto" ) , ( "given" , MetaString "M." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Meehl" ) , ( "given" , MetaString "G.A." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Msadek" ) , ( "given" , MetaString "R." ) ]) , MetaMap (fromList [ ( "family" , MetaString "M\252ller" ) , ( "given" , MetaString "W.A." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Taylor" ) , ( "given" , MetaString "K.E." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Zwiers" ) , ( "given" , MetaString "F." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Rixen" ) , ( "given" , MetaString "M." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Ruprich-Robert" ) , ( "given" , MetaString "Y." ) ]) , MetaMap (fromList [ ( "family" , MetaString "Eade" ) , ( "given" , MetaString "R." ) ]) ] ) , ( "container-title" , MetaInlines [ Str "Geoscientific" , Space , Str "Model" , Space , Str "Development" ] ) , ( "id" , MetaString "6" ) , ( "issued" , MetaString "2016" ) , ( "language" , MetaInlines [ Str "en" ] ) , ( "page" , MetaInlines [ Str "3751\8211\&3777" ] ) , ( "title" , MetaInlines [ Str "The" , Space , Str "Decadal" , Space , Str "Climate" , Space , Str "Prediction" , Space , Str "Project" , Space , Str "(DCPP)" ] ) , ( "type" , MetaString "article-journal" ) , ( "volume" , MetaInlines [ Str "9" ] ) ]) ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "citation" , Space , Cite [ Citation { citationId = "109" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(Smith" , Space , Str "et" , Space , Str "al.," , Space , Str "2013)" ] ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "citation" , Space , Str "with" , Space , Str "two" , Space , Str "sources" , Space , Cite [ Citation { citationId = "6" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } , Citation { citationId = "109" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(Boer" , Space , Str "et" , Space , Str "al.," , Space , Str "2016;" , Space , Str "Smith" , Space , Str "et" , Space , Str "al.," , Space , Str "2013)" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "citation" , Space , Str "with" , Space , Str "additional" , Space , Str "prefix," , Space , Str "suffix" , Space , Str "and" , Space , Str "a" , Space , Str "page" , Space , Str "number" , Space , Cite [ Citation { citationId = "10" , citationPrefix = [ Str "prefix" ] , citationSuffix = [ Str "," , Space , Str "2" , Space , Str "suffix" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "(prefix" , Space , Str "Bellomo" , Space , Str "et" , Space , Str "al.," , Space , Str "2016," , Space , Str "p." , Space , Str "2" , Space , Str "suffix)" ] , Str "." ] , Header 1 ( "reference-list" , [] , [] ) [ Str "Reference" , Space , Str "list" ] ] ================================================ FILE: test/dokuwiki_external_images.dokuwiki ================================================ {{https://cooluri.com/image.png|HTTPS image}} {{http://cooluri.com/image.png|HTTP image}} {{ftp://ftp.cooluri.com/image.png|FTP image}} {{file:///tmp/coolimage.png|Filesystem image}} {{/image.jpg|Relative image 1}} {{image.jpg|Relative image 2}} ================================================ FILE: test/dokuwiki_external_images.native ================================================ [Para [Image ("",[],[]) [Str "HTTPS",Space,Str "image"] ("https://cooluri.com/image.png",""),Space,Image ("",[],[]) [Str "HTTP",Space,Str "image"] ("http://cooluri.com/image.png",""),Space,Image ("",[],[]) [Str "FTP",Space,Str "image"] ("ftp://ftp.cooluri.com/image.png",""),Space,Image ("",[],[]) [Str "Filesystem",Space,Str "image"] ("file:///tmp/coolimage.png",""),Space,Image ("",[],[]) [Str "Relative",Space,Str "image",Space,Str "1"] ("/image.jpg",""),Space,Image ("",[],[]) [Str "Relative",Space,Str "image",Space,Str "2"] ("image.jpg","")]] ================================================ FILE: test/dokuwiki_inline_formatting.dokuwiki ================================================ Regular text //italics// **bold //bold italics//**. This is Small Caps, and this is strikethrough. Some people use single underlines for //emphasis//. Above the line is superscript and below the line is subscript. A line\\ break. hello %%//%% world %%**%% from %%__%% me ''%%hello // world ** from __ me%%'' ================================================ FILE: test/dokuwiki_inline_formatting.native ================================================ [Para [Str "Regular",Space,Str "text",Space,Emph [Str "italics"],Space,Strong [Str "bold",Space,Emph [Str "bold",Space,Str "italics"]],Str "."] ,Para [Str "This",Space,Str "is",Space,SmallCaps [Str "Small",Space,Str "Caps"],Str ",",Space,Str "and",Space,Str "this",Space,Str "is",Space,Strikeout [Str "strikethrough"],Str "."] ,Para [Str "Some",Space,Str "people",Space,Str "use",Space,Span ("",[],[("underline","single")]) [Str "single",Space,Str "underlines",Space,Str "for",Space,Emph [Str "emphasis"]],Str "."] ,Para [Str "Above",Space,Str "the",Space,Str "line",Space,Str "is",Space,Superscript [Str "superscript"],Space,Str "and",Space,Str "below",Space,Str "the",Space,Str "line",Space,Str "is",Space,Subscript [Str "subscript"],Str "."] ,Para [Str "A",Space,Str "line",LineBreak,Str "break."] ,Para [Str "hello",Space,Str "//",Space,Str "world",Space,Str "**",Space,Str "from",Space,Str "__",Space,Str "me"] ,Para [Code ("",[],[]) "hello // world ** from __ me"]] ================================================ FILE: test/dokuwiki_multiblock_table.dokuwiki ================================================ Sample grid table. ^Fruit ^Price^Advantages ^ |Bananas|$1.34|built-in wrapper\\ \\ potassium | |Oranges|$2.10|* cures scurvy\\ * tasty | |Apples |$1.10|Some text\\ \\ after two linebreaks| ================================================ FILE: test/dokuwiki_multiblock_table.native ================================================ [Table ("",[],[]) (Caption Nothing [Para [Str "Sample",Space,Str "grid",Space,Str "table."]]) [(AlignDefault,ColWidth 0.2222222222222222) ,(AlignDefault,ColWidth 0.2222222222222222) ,(AlignDefault,ColWidth 0.2916666666666667)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Fruit"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Price"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Advantages"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "Bananas"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "$1.34"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "built-in",Space,Str "wrapper"] ,Para [Str "potassium"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "Oranges"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "$2.10"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [BulletList [[Plain [Str "cures",Space,Str "scurvy"]] ,[Plain [Str "tasty"]]]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "Apples"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "$1.10"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Para [Str "Some",Space,Str "text",LineBreak,LineBreak,Str "after",Space,Str "two",Space,Str "linebreaks"]]]])] (TableFoot ("",[],[]) [])] ================================================ FILE: test/endnotexml-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "nocite" , MetaInlines [ Cite [ Citation { citationId = "*" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ Str "[@*]" ] ] ) , ( "references" , MetaList [ MetaMap (fromList [ ( "author" , MetaList [ MetaMap (fromList [ ( "family" , MetaString "Chapman" ) , ( "given" , MetaString "A. G." ) ]) ] ) , ( "container-title" , MetaInlines [ Str "Ecology" ] ) , ( "id" , MetaString "13264" ) , ( "isbn" , MetaInlines [ Str "0012-9658" ] ) , ( "issued" , MetaString "1937" ) , ( "number" , MetaInlines [ Str "1" ] ) , ( "pages" , MetaInlines [ Str "93-105" ] ) , ( "title" , MetaInlines [ Str "An" , SoftBreak , Str "ecological" , Space , Str "basis" , Space , Str "for" , Space , Str "reforestation" , Space , Str "of" , Space , Str "submariginal" , Space , Str "lands" , SoftBreak , Str "in" , Space , Str "the" , Space , Str "Central" , Space , Str "Hardwood" , Space , Str "Region" ] ) , ( "type" , MetaString "article-journal" ) , ( "volume" , MetaInlines [ Str "18" ] ) ]) , MetaMap (fromList [ ( "author" , MetaList [ MetaMap (fromList [ ( "family" , MetaString "Strohecker" ) , ( "given" , MetaString "H. F." ) ]) ] ) , ( "container-title" , MetaInlines [ Str "Ecology" ] ) , ( "id" , MetaString "13265" ) , ( "isbn" , MetaInlines [ Str "0012-9658" ] ) , ( "issued" , MetaString "1937" ) , ( "number" , MetaInlines [ Str "1" ] ) , ( "pages" , MetaInlines [ Str "162-168" ] ) , ( "title" , MetaInlines [ Str "A" , Space , Str "survey" , SoftBreak , Str "of" , Space , Str "soil" , Space , Str "temperatures" , Space , Str "in" , Space , Str "the" , Space , Str "Chicago" , Space , Str "area" ] ) , ( "type" , MetaString "article-journal" ) , ( "volume" , MetaInlines [ Str "18" ] ) ]) ] ) ] } [] ================================================ FILE: test/endnotexml-reader.xml ================================================ EndNote Ecology.enl EndNote 13264 13264 17 <style face="normal" font="default" size="100%">An ecological basis for reforestation of submariginal lands in the Central Hardwood Region</style> EndNote Ecology.enl EndNote 13265 13265 17 <style face="normal" font="default" size="100%">A survey of soil temperatures in the Chicago area</style> ================================================ FILE: test/epub/features.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "Ori Idan" ] , MetaInlines [ Str "Matt Garrish" ] , MetaInlines [ Str "Marisa DeMeglio" ] , MetaInlines [ Str "Toshiaki Koike" ] , MetaInlines [ Str "Vincent Gros" ] , MetaInlines [ Str "Markus Gylling" ] ] ) , ( "description" , MetaInlines [ Str "Tests for Content Documents in a reflowable context [UNDER CONSTRUCTION]" ] ) , ( "identifier" , MetaInlines [ Str "com.github.epub-testsuite.epub30-test-0100" ] ) , ( "language" , MetaList [ MetaInlines [ Str "jp" ] , MetaInlines [ Str "en" ] ] ) , ( "title" , MetaInlines [ Str "EPUBTEST 0100 - Reflowable Content Tests" ] ) ] } [ Para [ Span ( "front.xhtml" , [] , [] ) [] ] , Div ( "" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "Reflowable" , Space , Str "EPUB" , Space , Str "3" , Space , Str "Conformance" , Space , Str "Test" , Space , Str "Document:" , Space , Str "0100" ] , Div ( "" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Status" , Space , Str "of" , Space , Str "this" , Space , Str "Document" ] , Para [ Str "This" , Space , Str "publication" , Space , Str "is" , Space , Str "currently" , Space , Str "considered" , Space , Span ( "" , [ "status" ] , [] ) [ Str "[UNDER" , Space , Str "DEVELOPMENT]" ] , Space , Str "by" , Space , Str "the" , Space , Str "IDPF." ] , Para [ Str "This" , Space , Str "publication" , Space , Str "is" , Space , Str "part" , Space , Str "of" , Space , Str "version" , Space , Span ( "" , [ "version" ] , [] ) [ Str "X.X" ] , Space , Str "of" , Space , Str "the" , Space , Str "EPUB" , Space , Str "3.0" , Space , Str "Compliance" , Space , Str "Test" , Space , Str "Suite" , Space , Str "released" , SoftBreak , Str "on" , Space , RawInline (Format "html") "" , Str "." ] , Para [ Str "Before" , Space , Str "using" , Space , Str "this" , Space , Str "publication" , Space , Str "to" , Space , Str "evaluate" , Space , Str "reading" , Space , Str "systems," , Space , Str "testers" , Space , Str "are" , Space , Str "strongly" , Space , Str "encouraged" , Space , Str "to" , SoftBreak , Str "verify" , Space , Str "that" , Space , Str "they" , Space , Str "have" , Space , Str "the" , Space , Str "latest" , Space , Str "release" , Space , Str "by" , Space , Str "checking" , Space , Str "the" , Space , Str "current" , Space , Str "release" , Space , Str "version" , Space , Str "and" , Space , Str "date" , Space , Str "of" , SoftBreak , Str "the" , Space , Str "test" , Space , Str "suite" , Space , Str "at" , Space , Link ( "" , [] , [] ) [ Str "TBD" ] ( "http://idpf.org/" , "" ) ] , Para [ Str "This" , Space , Str "publication" , Space , Str "is" , Space , Str "one" , Space , Str "of" , Space , Str "several" , Space , Str "that" , Space , Str "currently" , Space , Str "comprise" , Space , Str "the" , Space , Str "EPUB" , Space , Str "3" , Space , Str "conformance" , Space , Str "test" , Space , Str "suite" , SoftBreak , Str "for" , Space , Str "reflowable" , Space , Str "content." , Space , Str "The" , Space , Str "complete" , Space , Str "test" , Space , Str "suite" , Space , Str "includes" , Space , Str "all" , Space , Str "of" , Space , Str "the" , Space , Str "following" , Space , Str "publications:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "." ] ] ] ] , Div ( "" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "About" , Space , Str "this" , Space , Str "Document" ] , Para [ Str "This" , Space , Str "document" , Space , Str "focuses" , Space , Str "on" , Space , Str "human-evaluated" , Space , Str "binary" , Space , Str "(pass/fail)" , Space , Str "tests" , Space , Str "in" , Space , Str "a" , SoftBreak , Str "reflowable" , Space , Str "context." , Space , Str "Tests" , Space , Str "for" , Space , Str "fixed-layout" , Space , Str "content" , Space , Str "and" , Space , Str "other" , Space , Str "individual" , Space , Str "tests" , Space , Str "that" , SoftBreak , Str "require" , Space , Str "a" , Space , Str "dedicated" , Space , Str "epub" , Space , Str "file" , Space , Str "are" , Space , Str "available" , Space , Str "in" , Space , Str "additional" , Space , Str "sibling" , Space , Str "documents;" , Space , Str "refer" , Space , Str "to" , SoftBreak , Str "the" , Space , Link ( "" , [] , [] ) [ Str "test" , Space , Str "suite" , SoftBreak , Str "wiki" ] ( "https://github.com/mgylling/epub-testsuite/wiki/Overview" , "" ) , Space , Str "(" , Code ( "" , [] , [] ) "https://github.com/mgylling/epub-testsuite/wiki/Overview" , Str ")" , Space , Str "for" , Space , Str "additional" , SoftBreak , Str "information." ] ] , Div ( "" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Conventions" ] , Para [ Str "The" , Space , Str "following" , Space , Str "conventions" , Space , Str "are" , Space , Str "used" , Space , Str "throughout" , Space , Str "the" , Space , Str "document:" ] , DefinitionList [ ( [ Str "1." , Space , Str "Locating" , Space , Str "a" , Space , Str "test" ] , [ [ Div ( "" , [ "ctest" ] , [] ) [ Para [ Str "Tests" , Space , Str "for" , Space , Emph [ Str "required" ] , Space , Str "Reading" , Space , Str "System" , Space , Str "functionality" , Space , Str "are" , SoftBreak , Str "preceded" , Space , Str "by" , Space , Str "the" , Space , Str "label:" , Space , Span ( "" , [ "nature" ] , [ ( "style" , "display: inline; font-size: 100%" ) ] ) [ Str "[REQUIRED]" ] ] ] , Div ( "" , [ "otest" ] , [] ) [ Para [ Str "Tests" , Space , Str "for" , Space , Emph [ Str "optional" ] , Space , Str "Reading" , Space , Str "System" , Space , Str "functionality" , Space , Str "are" , SoftBreak , Str "preceded" , Space , Str "by" , Space , Str "the" , Space , Str "label:" , Space , Span ( "" , [ "nature" ] , [ ( "style" , "display: inline; font-size: 100%" ) ] ) [ Str "[OPTIONAL]" ] ] ] ] ] ) , ( [ Str "2." , Space , Str "Performing" , Space , Str "the" , Space , Str "test" ] , [ [ Plain [ Str "Each" , Space , Str "test" , Space , Str "includes" , Space , Str "a" , Space , Str "description" , Space , Str "of" , Space , Str "its" , Space , Str "purpose" , Space , Str "followed" , Space , Str "by" , Space , Str "the" , Space , Str "actual" , Space , Strong [ Str "test" , Space , Str "statement," , SoftBreak , Str "which" , Space , Str "can" , Space , Str "always" , Space , Str "be" , Space , Str "evaluated" , Space , Str "to" , Space , Str "true" , Space , Str "or" , Space , Str "false" ] , Str "." , Space , Str "These" , Space , Str "statements" , Space , Str "typically" , Space , Str "have" , Space , Str "the" , Space , Str "form:" , SoftBreak , Str "\"If" , Space , Str "[some" , Space , Str "condition]," , Space , Str "the" , Space , Str "test" , Space , Str "passes\"." ] ] ] ) , ( [ Str "3." , Space , Str "Scoring" , Space , Str "in" , Space , Str "the" , Space , Str "results" , Space , Str "form" ] , [ [ Plain [ Str "@@@TODO" , Space , Str "provide" , Space , Str "info" , Space , Str "on" , Space , Str "where" , Space , Str "to" , Space , Str "get" , Space , Str "the" , Space , Str "results" , Space , Str "form" ] ] ] ) ] ] ] , Para [ Span ( "content-mathml-001.xhtml" , [] , [] ) [] ] , Div ( "" , [ "section" ] , [] ) [ Header 2 ( "content-mathml-001.xhtml_mathml" , [] , [] ) [ Str "MathML" ] , Div ( "content-mathml-001.xhtml_mathml-010" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-010" ] , Space , Str "Rendering" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "MathML" , Space , Str "equation" , Space , Str "rendering" , Space , Str "is" , Space , Str "supported." ] , Plain [ Math DisplayMath "\\int_{- \\infty}^{\\infty}e^{- x^{2}}\\, dx = \\sqrt{\\pi}" , SoftBreak , Math DisplayMath "\\sum\\limits_{n = 1}^{\\infty}\\frac{1}{n^{2}} = \\frac{\\pi^{2}}{6}" , SoftBreak , Math DisplayMath "x = \\frac{- b \\pm \\sqrt{b^{2} - 4ac}}{2a}" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "equations" , Space , Str "are" , Space , Str "not" , Space , Str "presented" , Space , Str "as" , Space , Str "linear" , Space , Str "text" , Space , Str "(e.g.," , Space , Str "x=-b\177b2-4ac2a)," , SoftBreak , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "content-mathml-001.xhtml_mathml-020" , [ "section" , "otest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[OPTIONAL]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-020" ] , Space , Str "CSS" , Space , Str "Styling" , Space , Str "of" , Space , Str "the" , Space , Code ( "" , [] , [] ) "math" , Space , Str "element" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "basic" , Space , Str "CSS" , Space , Str "styling" , Space , Str "of" , Space , Str "MathML" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "the" , Space , Code ( "" , [] , [] ) "math" , Space , Str "element." ] , Plain [ Math InlineMath "{2x}{+ y - z}" ] , Para [ Str "The" , Space , Str "test" , Space , Str "passes" , Space , Str "if" , Space , Str "the" , Space , Str "equation" , Space , Str "has" , Space , Str "a" , Space , Str "yellow" , Space , Str "background" , Space , Str "and" , Space , Str "a" , Space , Str "dashed" , Space , Str "border." ] , Para [ Str "If" , Space , Str "the" , Space , Str "reading" , Space , Str "system" , Space , Str "does" , Space , Str "not" , Space , Str "have" , Space , Str "a" , Space , Str "viewport," , Space , Str "or" , Space , Str "does" , Space , Str "not" , Space , Str "support" , SoftBreak , Str "CSS" , Space , Str "styles," , Space , Str "this" , Space , Str "test" , Space , Str "should" , Space , Str "be" , Space , Str "marked" , Space , Code ( "" , [] , [] ) "Not Supported" , Str "." ] ] , Div ( "content-mathml-001.xhtml_mathml-021" , [ "section" , "otest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[OPTIONAL]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-021" ] , Space , Str "CSS" , Space , Str "Styling" , Space , Str "of" , Space , Str "the" , Space , Code ( "" , [] , [] ) "mo" , Space , Str "element" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "basic" , Space , Str "CSS" , Space , Str "styling" , Space , Str "of" , Space , Str "MathML" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "the" , Space , Code ( "" , [] , [] ) "mo" , Space , Str "element." ] , Plain [ Math InlineMath "{2x}{+ y - z}" ] , Para [ Str "The" , Space , Str "test" , Space , Str "passes" , Space , Str "if" , Space , Str "the" , Space , Str "operators" , Space , Str "are" , Space , Str "enlarged" , Space , Str "relative" , Space , Str "to" , Space , Str "the" , Space , Str "other" , Space , Str "symbols" , Space , Str "and" , Space , Str "numbers." ] , Para [ Str "If" , Space , Str "the" , Space , Str "reading" , Space , Str "system" , Space , Str "does" , Space , Str "not" , Space , Str "have" , Space , Str "a" , Space , Str "viewport," , Space , Str "or" , Space , Str "does" , Space , Str "not" , Space , Str "support" , SoftBreak , Str "CSS" , Space , Str "styles," , Space , Str "this" , Space , Str "test" , Space , Str "should" , Space , Str "be" , Space , Str "marked" , Space , Code ( "" , [] , [] ) "Not Supported" , Str "." ] ] , Div ( "content-mathml-001.xhtml_mathml-022" , [ "section" , "otest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[OPTIONAL]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-022" ] , Space , Str "CSS" , Space , Str "Styling" , Space , Str "of" , Space , Str "the" , Space , Code ( "" , [] , [] ) "mi" , Space , Str "element" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "basic" , Space , Str "CSS" , Space , Str "styling" , Space , Str "of" , Space , Str "MathML" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "the" , Space , Code ( "" , [] , [] ) "mi" , Space , Str "element." ] , Plain [ Math InlineMath "{2x}{+ y - z}" ] , Para [ Str "The" , Space , Str "test" , Space , Str "passes" , Space , Str "if" , Space , Str "the" , Space , Str "identifiers" , Space , Str "are" , Space , Str "bolded" , Space , Str "and" , Space , Str "blue." ] , Para [ Str "If" , Space , Str "the" , Space , Str "reading" , Space , Str "system" , Space , Str "does" , Space , Str "not" , Space , Str "have" , Space , Str "a" , Space , Str "viewport," , Space , Str "or" , Space , Str "does" , Space , Str "not" , Space , Str "support" , SoftBreak , Str "CSS" , Space , Str "styles," , Space , Str "this" , Space , Str "test" , Space , Str "should" , Space , Str "be" , Space , Str "marked" , Space , Code ( "" , [] , [] ) "Not Supported" , Str "." ] ] , Div ( "content-mathml-001.xhtml_mathml-023" , [ "section" , "otest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[OPTIONAL]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-023" ] , Space , Str "CSS" , Space , Str "Styling" , Space , Str "of" , Space , Str "the" , Space , Code ( "" , [] , [] ) "mn" , Space , Str "element" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "basic" , Space , Str "CSS" , Space , Str "styling" , Space , Str "of" , Space , Str "MathML" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "the" , Space , Code ( "" , [] , [] ) "mn" , Space , Str "element." ] , Plain [ Math InlineMath "{2x}{+ y - z}" ] , Para [ Str "The" , Space , Str "test" , Space , Str "passes" , Space , Str "if" , Space , Str "the" , Space , Str "number" , Space , Str "2" , Space , Str "is" , Space , Str "italicized" , Space , Str "and" , Space , Str "blue." ] , Para [ Str "If" , Space , Str "the" , Space , Str "reading" , Space , Str "system" , Space , Str "does" , Space , Str "not" , Space , Str "have" , Space , Str "a" , Space , Str "viewport," , Space , Str "or" , Space , Str "does" , Space , Str "not" , Space , Str "support" , SoftBreak , Str "CSS" , Space , Str "styles," , Space , Str "this" , Space , Str "test" , Space , Str "should" , Space , Str "be" , Space , Str "marked" , Space , Code ( "" , [] , [] ) "Not Supported" , Str "." ] ] , Div ( "content-mathml-001.xhtml_mathml-024" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-024" ] , Str "Horizontal" , Space , Str "stretch," , Space , Code ( "" , [] , [] ) "mover" , Str "," , Space , Code ( "" , [] , [] ) "munder" , Str "," , Space , Str "and" , Space , Code ( "" , [] , [] ) "mspace" , Space , Str "elements" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "horizontal" , Space , Str "stretch," , Space , Code ( "" , [] , [] ) "mover" , Str "," , Space , Code ( "" , [] , [] ) "munder" , Str "," , Space , Code ( "" , [] , [] ) "mspace" , Space , Str "elements" , Space , Str "are" , Space , Str "supported." ] , Plain [ Math DisplayMath "c = \\overset{\\text{complex number}}{\\overbrace{\\underset{\\text{real}}{\\underbrace{\\mspace{20mu} a\\mspace{20mu}}} + \\underset{\\text{imaginary}}{\\underbrace{\\quad b{\\mathbb{i}}\\quad}}}}" ] , Para [ Str "The" , Space , Str "test" , Space , Str "passes" , Space , Str "if" , Space , Str "the" , Space , Str "rendering" , Space , Str "looks" , Space , Str "like" , Space , Str "." ] ] , Div ( "content-mathml-001.xhtml_mathml-025" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-025" ] , Str "Testing" , Space , Code ( "" , [] , [] ) "mtable" , Space , Str "with" , Space , Code ( "" , [] , [] ) "colspan" , Space , Str "and" , Space , Code ( "" , [] , [] ) "rowspan" , Space , Str "attributes," , Space , Str "Hebrew" , Space , Str "and" , Space , Str "Script" , Space , Str "fonts" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Code ( "" , [] , [] ) "mtable" , Space , Str "with" , Space , Code ( "" , [] , [] ) "colspan" , Space , Str "and" , Space , Code ( "" , [] , [] ) "mspace" , Space , Str "attributes" , Space , Str "(column" , Space , Str "and" , Space , Str "row" , Space , Str "spanning)" , Space , Str "are" , Space , Str "supported;" , Space , Str "uses" , Space , Str "Hebrew" , Space , Str "and" , Space , Str "Script" , Space , Str "alphabets." ] , Plain [ Math DisplayMath "\\begin{matrix}\n & {\\operatorname{cov}(\\mathcal{L})} & \\longrightarrow & {\\operatorname{non}(\\mathcal{K})} & \\longrightarrow & {\\operatorname{cof}(\\mathcal{K})} & \\longrightarrow & {\\operatorname{cof}(\\mathcal{L})} & \\longrightarrow & 2^{\\aleph_{0}} \\\\\n & \\uparrow & & \\uparrow & & \\uparrow & & \\uparrow & & \\\\\n & {\\mathfrak{b}} & \\longrightarrow & {\\mathfrak{d}} & & & & & & \\\\\n & \\uparrow & & \\uparrow & & & & & & \\\\\n\\aleph_{1} & \\longrightarrow & {\\operatorname{add}(\\mathcal{L})} & \\longrightarrow & {\\operatorname{add}(\\mathcal{K})} & \\longrightarrow & {\\operatorname{cov}(\\mathcal{K})} & \\longrightarrow & {\\operatorname{non}(\\mathcal{L})} & \n\\end{matrix}" ] , Para [ Str "The" , Space , Str "test" , Space , Str "passes" , Space , Str "if" , Space , Str "the" , Space , Str "rendering" , Space , Str "looks" , Space , Str "like" , Space , Link ( "" , [] , [] ) [ Str "Cicho\324's" , Space , Str "Diagram" ] ( "http://en.wikipedia.org/wiki/Cicho%C5%84's_diagram" , "" ) , Str ":" , Space , Str "." ] ] , Div ( "content-mathml-001.xhtml_mathml-026" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-026" ] , Str "BiDi," , Space , Str "RTL" , Space , Str "and" , Space , Str "Arabic" , Space , Str "alphabets" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "right-to-left" , Space , Str "and" , Space , Str "Arabic" , Space , Str "alphabets" , Space , Str "are" , Space , Str "supported." ] , Plain [ Math DisplayMath "{\1583(\1587)} = \\left\\{ \\begin{matrix}\n{\\sum\\limits_{\1646 = 1}^{\1589}\1587^{\1646}} & {\\text{\1573\1584\1575\1603\1575\1606}\1587 > 0} \\\\\n{\\int_{1}^{\1589}{\1587^{\1646}\1569\1587}} & {\\text{\1573\1584\1575\1603\1575\1606}\1587 \\in \1605} \\\\\n{{\1591\1575}\\pi} & {\\text{\1594\1610\1585\1584\1604\1603}\\left( \\text{\1605\1593}\\pi \\simeq 3,141 \\right)}\n\\end{matrix} \\right." ] , Para [ Str "The" , Space , Str "test" , Space , Str "passes" , Space , Str "if" , Space , Str "the" , Space , Str "rendering" , Space , Str "looks" , Space , Str "like" , Space , Str "the" , Space , Str "following" , Space , Str "image:" ] ] , Div ( "content-mathml-001.xhtml_mathml-027" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "mathml-027" ] , Str "Elementary" , Space , Str "math:" , Space , Str "long" , Space , Str "division" , Space , Str "notation" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Code ( "" , [] , [] ) "mlongdiv" , Space , Str "elements" , Space , Str "(from" , Space , Str "elementary" , Space , Str "math)" , Space , Str "are" , Space , Str "supported." ] , Plain [ Span ( "" , [ "math" ] , [ ( "xmlns" , "http://www.w3.org/1998/Math/MathML" ) ] ) [ SoftBreak , Str "3" , SoftBreak , Str "435.3" , SoftBreak , Str "1306" , SoftBreak , Str "12" , SoftBreak , Str "10" , SoftBreak , Str "9" , SoftBreak , Str "16" , SoftBreak , Str "15" , SoftBreak , Str "1.0" , SoftBreak , Str "9" , SoftBreak , Str "1" , SoftBreak ] ] , Para [ Str "The" , Space , Str "test" , Space , Str "passes" , Space , Str "if" , Space , Str "the" , Space , Str "rendering" , Space , Str "looks" , Space , Str "like" , Space , Str "the" , Space , Str "following" , Space , Str "image:" , Space , Str "." ] ] ] , Para [ Span ( "content-switch-001.xhtml" , [] , [] ) [] ] , Div ( "content-switch-001.xhtml_epub-switch" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Code ( "" , [] , [] ) "epub:switch" ] , Div ( "content-switch-001.xhtml_switch-010" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "switch-010" ] , Space , Str "Support" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Link ( "" , [] , [] ) [ Code ( "" , [] , [] ) "epub:switch" ] ( "http://idpf.org/epub/30/spec/epub30-contentdocs.html#sec-xhtml-content-switch" , "" ) , Space , Str "element" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "PASS" ] , Para [ Str "If" , Space , Str "only" , Space , Str "the" , Space , Str "word" , Space , Str "\"PASS\"" , Space , Str "is" , Space , Str "rendered" , Space , Str "before" , Space , Str "this" , Space , Str "paragraph," , Space , Str "the" , Space , Str "test" , Space , Str "passes." , Space , Str "If" , Space , Str "both" , Space , Str "\"PASS\"" , Space , Str "and" , Space , Str "\"FAIL\"" , Space , Str "are" , Space , Str "rendered," , Space , Str "or" , Space , Str "neither" , SoftBreak , Str "\"PASS\"" , Space , Str "nor" , Space , Str "\"FAIL\"" , Space , Str "is" , Space , Str "rendered," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "content-switch-001.xhtml_switch-020" , [ "section" , "otest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[OPTIONAL]" ] , SoftBreak , Span ( "" , [ "test-id" ] , [] ) [ Str "switch-020" ] , SoftBreak , Str "MathML" , Space , Str "Embedding" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Str "MathML" , Space , Str "namespace" , Space , Str "is" , Space , Str "recognized" , Space , Str "when" , Space , Str "used" , Space , Str "in" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Code ( "" , [] , [] ) "epub:case" ] ( "http://idpf.org/epub/30/spec/epub30-contentdocs.html#sec-xhtml-epub-case" , "" ) , Space , Str "element." ] , Para [ Math InlineMath "{2x}{+ y - z}" ] , Para [ Str "If" , Space , Str "a" , Space , Str "MathML" , Space , Str "equation" , Space , Str "is" , Space , Str "rendered" , Space , Str "before" , Space , Str "this" , Space , Str "paragraph," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] , Para [ Str "If" , Space , Str "test" , Space , Code ( "" , [] , [] ) "switch-010" , Space , Str "did" , Space , Str "not" , Space , Str "pass," , Space , Str "this" , Space , Str "test" , Space , Str "should" , Space , Str "be" , Space , Str "marked" , Space , Code ( "" , [] , [] ) "Not Supported" , Str "." ] ] ] ] ================================================ FILE: test/epub/formatting.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "Ori Idan" ] , MetaInlines [ Str "Matt Garrish" ] , MetaInlines [ Str "Marisa DeMeglio" ] , MetaInlines [ Str "Toshiaki Koike" ] , MetaInlines [ Str "Vincent Gros" ] , MetaInlines [ Str "Markus Gylling" ] ] ) , ( "description" , MetaInlines [ Str "Tests for Styling [UNDER CONSTRUCTION]" ] ) , ( "identifier" , MetaInlines [ Str "com.github.epub-testsuite.epub30-test-0101" ] ) , ( "language" , MetaInlines [ Str "en" ] ) , ( "title" , MetaInlines [ Str "EPUBTEST 0101 - Styling Tests" ] ) ] } [ Para [ Span ( "front.xhtml" , [] , [] ) [] ] , Div ( "" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "EPUB" , Space , Str "3" , Space , Str "Styling" , Space , Str "Test" , Space , Str "Document:" , Space , Str "0101" ] , Div ( "" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Status" , Space , Str "of" , Space , Str "this" , Space , Str "Document" ] , Para [ Str "This" , Space , Str "publication" , Space , Str "is" , Space , Str "currently" , Space , Str "considered" , Space , Span ( "" , [ "status" ] , [] ) [ Str "[UNDER" , Space , Str "DEVELOPMENT]" ] , Space , Str "by" , Space , Str "the" , Space , Str "IDPF." ] , Para [ Str "This" , Space , Str "publication" , Space , Str "is" , Space , Str "part" , Space , Str "of" , Space , Str "version" , Space , Span ( "" , [ "version" ] , [] ) [ Str "X.X" ] , Space , Str "of" , Space , Str "the" , Space , Str "EPUB" , Space , Str "3.0" , Space , Str "Compliance" , Space , Str "Test" , Space , Str "Suite" , Space , Str "released" , SoftBreak , Str "on" , Space , RawInline (Format "html") "" , Str "." ] , Para [ Str "Before" , Space , Str "using" , Space , Str "this" , Space , Str "publication" , Space , Str "to" , Space , Str "evaluate" , Space , Str "reading" , Space , Str "systems," , Space , Str "testers" , Space , Str "are" , Space , Str "strongly" , Space , Str "encouraged" , Space , Str "to" , SoftBreak , Str "verify" , Space , Str "that" , Space , Str "they" , Space , Str "have" , Space , Str "the" , Space , Str "latest" , Space , Str "release" , Space , Str "by" , Space , Str "checking" , Space , Str "the" , Space , Str "current" , Space , Str "release" , Space , Str "version" , Space , Str "and" , Space , Str "date" , Space , Str "of" , SoftBreak , Str "the" , Space , Str "test" , Space , Str "suite" , Space , Str "at" , Space , Link ( "" , [] , [] ) [ Str "TBD" ] ( "http://idpf.org/" , "" ) ] , Para [ Str "This" , Space , Str "publication" , Space , Str "is" , Space , Str "one" , Space , Str "of" , Space , Str "several" , Space , Str "that" , Space , Str "currently" , Space , Str "comprise" , Space , Str "the" , Space , Str "EPUB" , Space , Str "3" , Space , Str "conformance" , Space , Str "test" , Space , Str "suite" , SoftBreak , Str "for" , Space , Str "reflowable" , Space , Str "content." , Space , Str "The" , Space , Str "complete" , Space , Str "test" , Space , Str "suite" , Space , Str "includes" , Space , Str "all" , Space , Str "of" , Space , Str "the" , Space , Str "following" , Space , Str "publications:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "." ] ] ] ] , Div ( "" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "About" , Space , Str "this" , Space , Str "Document" ] , Para [ Str "This" , Space , Str "document" , Space , Str "focuses" , Space , Str "on" , Space , Str "human-evaluated" , Space , Str "binary" , Space , Str "(pass/fail)" , Space , Str "tests" , Space , Str "in" , Space , Str "a" , SoftBreak , Str "reflowable" , Space , Str "context." , Space , Str "Tests" , Space , Str "for" , Space , Str "fixed-layout" , Space , Str "content" , Space , Str "and" , Space , Str "other" , Space , Str "individual" , Space , Str "tests" , Space , Str "that" , SoftBreak , Str "require" , Space , Str "a" , Space , Str "dedicated" , Space , Str "epub" , Space , Str "file" , Space , Str "are" , Space , Str "available" , Space , Str "in" , Space , Str "additional" , Space , Str "sibling" , Space , Str "documents;" , Space , Str "refer" , Space , Str "to" , SoftBreak , Str "the" , Space , Link ( "" , [] , [] ) [ Str "test" , Space , Str "suite" , SoftBreak , Str "wiki" ] ( "https://github.com/mgylling/epub-testsuite/wiki/Overview" , "" ) , Space , Str "(" , Code ( "" , [] , [] ) "https://github.com/mgylling/epub-testsuite/wiki/Overview" , Str ")" , Space , Str "for" , Space , Str "additional" , SoftBreak , Str "information." ] ] , Div ( "" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Conventions" ] , Para [ Str "The" , Space , Str "following" , Space , Str "conventions" , Space , Str "are" , Space , Str "used" , Space , Str "throughout" , Space , Str "the" , Space , Str "document:" ] , DefinitionList [ ( [ Str "1." , Space , Str "Locating" , Space , Str "a" , Space , Str "test" ] , [ [ Div ( "" , [ "ctest" ] , [] ) [ Para [ Str "Tests" , Space , Str "for" , Space , Emph [ Str "required" ] , Space , Str "Reading" , Space , Str "System" , Space , Str "functionality" , Space , Str "are" , SoftBreak , Str "preceded" , Space , Str "by" , Space , Str "the" , Space , Str "label:" , Space , Span ( "" , [ "nature" ] , [ ( "style" , "display: inline; font-size: 100%" ) ] ) [ Str "[REQUIRED]" ] ] ] , Div ( "" , [ "otest" ] , [] ) [ Para [ Str "Tests" , Space , Str "for" , Space , Emph [ Str "optional" ] , Space , Str "Reading" , Space , Str "System" , Space , Str "functionality" , Space , Str "are" , SoftBreak , Str "preceded" , Space , Str "by" , Space , Str "the" , Space , Str "label:" , Space , Span ( "" , [ "nature" ] , [ ( "style" , "display: inline; font-size: 100%" ) ] ) [ Str "[OPTIONAL]" ] ] ] ] ] ) , ( [ Str "2." , Space , Str "Performing" , Space , Str "the" , Space , Str "test" ] , [ [ Plain [ Str "Each" , Space , Str "test" , Space , Str "includes" , Space , Str "a" , Space , Str "description" , Space , Str "of" , Space , Str "its" , Space , Str "purpose" , Space , Str "followed" , Space , Str "by" , Space , Str "the" , Space , Str "actual" , Space , Strong [ Str "test" , Space , Str "statement," , SoftBreak , Str "which" , Space , Str "can" , Space , Str "always" , Space , Str "be" , Space , Str "evaluated" , Space , Str "to" , Space , Str "true" , Space , Str "or" , Space , Str "false" ] , Str "." , Space , Str "These" , Space , Str "statements" , Space , Str "typically" , Space , Str "have" , Space , Str "the" , Space , Str "form:" , SoftBreak , Str "\"If" , Space , Str "[some" , Space , Str "condition]," , Space , Str "the" , Space , Str "test" , Space , Str "passes\"." ] ] ] ) , ( [ Str "3." , Space , Str "Scoring" , Space , Str "in" , Space , Str "the" , Space , Str "results" , Space , Str "form" ] , [ [ Plain [ Str "@@@TODO" , Space , Str "provide" , Space , Str "info" , Space , Str "on" , Space , Str "where" , Space , Str "to" , Space , Str "get" , Space , Str "the" , Space , Str "results" , Space , Str "form" ] ] ] ) ] ] ] , Para [ Span ( "styling-xhtml-001.xhtml" , [] , [] ) [] ] , Div ( "styling-xhtml-001.xhtml_epub-css" , [ "section" ] , [] ) [ Header 1 ( "" , [] , [] ) [ Str "EPUB" , Space , Str "Style" , Space , Str "Sheets" ] , Para [ Str "This" , Space , Str "section" , Space , Str "contains" , Space , Str "tests" , Space , Str "for" , Space , Str "styling" , Space , Str "and" , Space , Str "layout." ] ] , Para [ Span ( "styling-xhtml-003.xhtml" , [] , [] ) [] ] , Div ( "styling-xhtml-003.xhtml_style-110" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-110" ] , Space , Str "Multi-Column" , Space , Str "Layouts" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Link ( "" , [] , [] ) [ Code ( "" , [] , [] ) "CSS Multi-Column Layout" ] ( "http://idpf.org/epub/30/spec/epub30-contentdocs.html#sec-css-multi-column" , "" ) , Space , Str "properties" , Space , Str "are" , Space , Str "supported." ] , Div ( "" , [ "multicol" ] , [] ) [ Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "text" , Space , Str "is" , Space , Str "rendered" , Space , Str "in" , Space , Str "three" , Space , Str "columns," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Para [ Span ( "styling-xhtml-002.xhtml" , [] , [] ) [] ] , Div ( "styling-xhtml-002.xhtml_style-lists" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Lists" ] , Div ( "styling-xhtml-002.xhtml_style-list-style-type" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "The" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" ] , Div ( "styling-xhtml-002.xhtml_style-009" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-009" ] , Space , Code ( "" , [] , [] ) "decimal" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "decimal" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "decimal" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-010" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-010" ] , Space , Code ( "" , [] , [] ) "circle" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "circle" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ul" , Space , Str "element." ] , BulletList [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "circle" , Space , Str "markers," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-011" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-011" ] , Space , Code ( "" , [] , [] ) "square" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "square" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ul" , Space , Str "element." ] , BulletList [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "square" , Space , Str "markers," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-012" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-012" ] , Space , Code ( "" , [] , [] ) "disc" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "disc" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ul" , Space , Str "element." ] , BulletList [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "disc" , Space , Str "markers," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-013" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-013" ] , Space , Code ( "" , [] , [] ) "lower-latin" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "lower-latin" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "lower-latin" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-014" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-014" ] , Space , Code ( "" , [] , [] ) "lower-roman" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "lower-roman" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "lower-roman" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-015" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-015" ] , Space , Code ( "" , [] , [] ) "upper-alpha" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "upper-alpha" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "upper-alpha" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-016" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-016" ] , Space , Code ( "" , [] , [] ) "hiragana" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "hiragana" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "hiragana" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-017" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-017" ] , Space , Code ( "" , [] , [] ) "hiragana-iroha" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "hiragana-iroha" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "hiragana-iroha" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-018" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-018" ] , Space , Code ( "" , [] , [] ) "katakana" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "katakana" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "katakana" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-019" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-019" ] , Space , Code ( "" , [] , [] ) "katakana-iroha" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "katakana-iroha" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "katakana-iroha" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-020" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-020" ] , Space , Code ( "" , [] , [] ) "upper-roman" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "upper-roman" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "upper-roman" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-021" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-021" ] , Space , Code ( "" , [] , [] ) "upper-latin" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "upper-latin" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "upper-latin" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-022" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-022" ] , Space , Code ( "" , [] , [] ) "lower-alpha" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "lower-alpha" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "lower-alpha" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-023" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-023" ] , Space , Code ( "" , [] , [] ) "lower-greek" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "lower-greek" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "lower-greek" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-024" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-024" ] , Space , Code ( "" , [] , [] ) "armenian" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "armenian" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "armenian" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-025" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-025" ] , Space , Code ( "" , [] , [] ) "cjk-ideographic" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "cjk-ideographic" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "cjk-ideographic" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-026" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-026" ] , Space , Code ( "" , [] , [] ) "decimal-leading-zero" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "decimal-leading-zero" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "decimal-leading-zero" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-027" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-027" ] , Space , Code ( "" , [] , [] ) "georgian" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "georgian" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "georgian" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-028" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-028" ] , Space , Code ( "" , [] , [] ) "hebrew" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "hebrew" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "hebrew" , Space , Str "markers" , Space , Str "in" , Space , Str "ascending" , Space , Str "order," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-029" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-029" ] , Space , Code ( "" , [] , [] ) "none" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "none" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "no" , Space , Str "markers," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] ] , Div ( "styling-xhtml-002.xhtml_style-list-style" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "The" , Space , Code ( "" , [] , [] ) "list-style" , Space , Str "property" ] , Div ( "styling-xhtml-002.xhtml_style-030" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-030" ] , Space , Str "images" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style" , Space , Str "shorthand" , Space , Str "property" , Space , Str "is" , Space , Str "supported" , Space , Str "using" , Space , Str "a" , Space , Str "gif" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ul" , Space , Str "element." ] , BulletList [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "the" , Space , Str "purple" , Space , Str "and" , Space , Str "aqua" , Space , Str "square" , Space , Str "bullet" , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] ] , Div ( "styling-xhtml-002.xhtml_style-list-style-position" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "The" , Space , Code ( "" , [] , [] ) "list-style-position" , Space , Str "property" ] , Div ( "styling-xhtml-002.xhtml_style-040" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-040" ] , Space , Str "The" , Space , Code ( "" , [] , [] ) "list-style-position" , Space , Str "property:" , Space , Code ( "" , [] , [] ) "inside" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-position" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "inside" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ul" , Space , Str "element." ] , BulletList [ [ Plain [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." ] ] , [ Plain [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." ] ] , [ Plain [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "markers" , Space , Str "inside" , Space , Str "the" , Space , Str "indentation," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-041" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-041" ] , Space , Str "The" , Space , Code ( "" , [] , [] ) "list-style-position" , Space , Str "property:" , Space , Code ( "" , [] , [] ) "outside" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "list-style-position" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "outside" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ul" , Space , Str "element." ] , BulletList [ [ Plain [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." ] ] , [ Plain [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." ] ] , [ Plain [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "has" , Space , Str "the" , Space , Str "default" , Space , Str "setting" , Space , Str "(marker" , Space , Str "outside" , Space , Str "the" , Space , Str "indentation)," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] ] , Div ( "styling-xhtml-002.xhtml_style-list-start" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "The" , Space , Str "HTML" , Space , Code ( "" , [] , [] ) "start" , Space , Str "attribute" ] , Div ( "styling-xhtml-002.xhtml_style-050" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-050" ] , Space , Str "Without" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "set" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "start" , Space , Str "attribute" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element" , Space , Str "with" , Space , Str "no" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property." ] , OrderedList ( 25 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "starts" , Space , Str "at" , Space , Str "25," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-002.xhtml_style-051" , [ "section" , "ctest" ] , [] ) [ Header 4 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-051" ] , Space , Str "With" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "set" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "start" , Space , Str "attribute" , Space , Str "is" , Space , Str "supported" , Space , Str "on" , Space , Str "a" , Space , Code ( "" , [] , [] ) "ol" , Space , Str "element" , Space , Str "with" , Space , Str "a" , Space , Code ( "" , [] , [] ) "list-style-type" , Space , Str "property." ] , OrderedList ( 50 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Lorem" ] ] , [ Plain [ Str "Ipsum" ] ] , [ Plain [ Str "Dolor" ] ] , [ Plain [ Str "Sit" ] ] , [ Plain [ Str "Amet" ] ] ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "list" , Space , Str "starts" , Space , Str "at" , Space , Str "'L'" , Space , Str "(50)," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] ] ] , Para [ Span ( "styling-xhtml-004.xhtml" , [] , [] ) [] ] , Div ( "styling-xhtml-004.xhtml_style-media-rules" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Code ( "" , [] , [] ) "@media" , Space , Str "Rules" ] , Div ( "styling-xhtml-004.xhtml_style-210" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-210" ] , Space , Code ( "" , [] , [] ) "all" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "all" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-211" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-211" ] , Space , Code ( "" , [] , [] ) "screen" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "screen" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-212" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-212" ] , Space , Code ( "" , [] , [] ) "handheld" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "handheld" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-213" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-213" ] , Space , Code ( "" , [] , [] ) "tv" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "tv" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-220" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-220" ] , Space , Code ( "" , [] , [] ) "orientation:landscape" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "orientation:landscape" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"" , Space , Str "when" , Space , Str "the" , Space , Str "device" , Space , Str "is" , Space , Str "held" , Space , Str "in" , Space , Str "landscape" , Space , Str "mode," , Space , Str "and" , Space , Str "the" , Space , Str "device" , Space , Str "supports" , Space , Str "multiple" , Space , Str "orientations," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-221" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-221" ] , Space , Code ( "" , [] , [] ) "orientation:portrait" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "orientation:portrait" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"" , Space , Str "when" , Space , Str "the" , Space , Str "device" , Space , Str "is" , Space , Str "held" , Space , Str "in" , Space , Str "portrait" , Space , Str "mode," , Space , Str "and" , Space , Str "the" , Space , Str "device" , Space , Str "supports" , Space , Str "multiple" , Space , Str "orientations," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-230" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-230" ] , Space , Code ( "" , [] , [] ) "min-width" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "min-width:200px" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-231" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-231" ] , Space , Code ( "" , [] , [] ) "max-width" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "max-width:2000px" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-240" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-240" ] , Space , Code ( "" , [] , [] ) "min-device-width" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "min-device-width:200px" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] , Div ( "styling-xhtml-004.xhtml_style-241" , [ "section" , "ctest" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-241" ] , Space , Code ( "" , [] , [] ) "max-device-width" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "@media" , Space , Str "rule" , Space , Str "set" , Space , Str "to" , Space , Code ( "" , [] , [] ) "max-device-width:2000px" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "FAIL" ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "reads" , Space , Str "\"FAIL\"," , Space , Str "the" , Space , Str "test" , Space , Str "fails." ] ] ] , Para [ Span ( "styling-xhtml-005.xhtml" , [] , [] ) [] ] , Div ( "styling-xhtml-005.xhtml_style-text-xform" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "The" , Space , Code ( "" , [] , [] ) "text-transform" , Space , Str "property" ] , Div ( "styling-xhtml-005.xhtml_style-310" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-310" ] , Space , Code ( "" , [] , [] ) "uppercase" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "text-transform" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Str "uppercase" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "is" , Space , Str "in" , Space , Str "upper" , Space , Str "case," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-005.xhtml_style-311" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-311" ] , Space , Code ( "" , [] , [] ) "capitalize" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "text-transform" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Str "capitalize" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "If" , Space , Str "each" , Space , Str "first" , Space , Str "letter" , Space , Str "of" , Space , Str "each" , Space , Str "word" , Space , Str "in" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "is" , Space , Str "in" , Space , Str "upper" , Space , Str "case," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-005.xhtml_style-312" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-312" ] , Space , Code ( "" , [] , [] ) "lowercase" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "text-transform" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Str "lowercase" , Space , Str "is" , Space , Str "supported." ] , Para [ Str "Lorem" , Space , Str "ipsum" , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipisicing" , Space , Str "elit," , Space , Str "sed" , Space , Str "do" , Space , Str "eiusmod" , Space , Str "tempor" , Space , Str "incididunt" , Space , Str "ut" , Space , Str "labore" , Space , Str "et" , Space , Str "dolore" , Space , Str "magna" , Space , Str "aliqua." , Space , Str "Ut" , Space , Str "enim" , Space , Str "ad" , Space , Str "minim" , Space , Str "veniam," , Space , Str "quis" , Space , Str "nostrud" , Space , Str "exercitation" , Space , Str "ullamco" , Space , Str "laboris" , Space , Str "nisi" , Space , Str "ut" , Space , Str "aliquip" , Space , Str "ex" , Space , Str "ea" , Space , Str "commodo" , Space , Str "consequat." , Space , Str "Duis" , Space , Str "aute" , Space , Str "irure" , Space , Str "dolor" , Space , Str "in" , Space , Str "reprehenderit" , Space , Str "in" , Space , Str "voluptate" , Space , Str "velit" , Space , Str "esse" , Space , Str "cillum" , Space , Str "dolore" , Space , Str "eu" , Space , Str "fugiat" , Space , Str "nulla" , Space , Str "pariatur." , Space , Str "Excepteur" , Space , Str "sint" , Space , Str "occaecat" , Space , Str "cupidatat" , Space , Str "non" , Space , Str "proident," , Space , Str "sunt" , Space , Str "in" , Space , Str "culpa" , Space , Str "qui" , Space , Str "officia" , Space , Str "deserunt" , Space , Str "mollit" , Space , Str "anim" , Space , Str "id" , Space , Str "est" , Space , Str "laborum." ] , Para [ Str "If" , Space , Str "the" , Space , Str "preceding" , Space , Str "paragraph" , Space , Str "is" , Space , Str "in" , Space , Str "lower" , Space , Str "case," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] ] , Para [ Span ( "styling-xhtml-006.xhtml" , [] , [] ) [] ] , Div ( "styling-xhtml-006.xhtml_style-ruby" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "The" , Space , Code ( "" , [] , [] ) "epub-ruby-position" , Space , Str "property" ] , Div ( "styling-xhtml-006.xhtml_style-410" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-410" ] , Space , Code ( "" , [] , [] ) "over" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "-epub-ruby-position" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Str "over" , Space , Str "is" , Space , Str "supported." ] , Plain [ RawInline (Format "html") "" , Strong [ Str "Lorem" , Space , Str "Ipsum" ] , Space , RawInline (Format "html") "" , Str "(" , RawInline (Format "html") "" , RawInline (Format "html") "" , Str "Lorem" , Space , Str "Ipsum" , RawInline (Format "html") "" , RawInline (Format "html") "" , Str ")" , RawInline (Format "html") "" , RawInline (Format "html") "" ] , Para [ Str "If" , Space , Str "the" , Space , Str "Ruby" , Space , Str "text" , Space , Str "is" , Space , Str "positioned" , Space , Str "on" , Space , Str "the" , Space , Link ( "" , [] , [] ) [ Str "over" ] ( "http://www.w3.org/TR/css3-writing-modes/#over" , "" ) , Space , Str "side" , Space , Str "of" , Space , Str "the" , Space , Str "ruby" , Space , Str "base," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-006.xhtml_style-411" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-411" ] , Space , Code ( "" , [] , [] ) "under" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "-epub-ruby-position" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Str "under" , Space , Str "is" , Space , Str "supported." ] , Plain [ RawInline (Format "html") "" , Strong [ Str "Lorem" , Space , Str "Ipsum" ] , Space , RawInline (Format "html") "" , Str "(" , RawInline (Format "html") "" , RawInline (Format "html") "" , Str "Lorem" , Space , Str "Ipsum" , RawInline (Format "html") "" , RawInline (Format "html") "" , Str ")" , RawInline (Format "html") "" , RawInline (Format "html") "" ] , Para [ Str "If" , Space , Str "the" , Space , Str "Ruby" , Space , Str "text" , Space , Str "is" , Space , Str "positioned" , Space , Str "on" , Space , Str "the" , Space , Link ( "" , [] , [] ) [ Str "under" ] ( "http://www.w3.org/TR/css3-writing-modes/#under" , "" ) , Space , Str "side" , Space , Str "of" , Space , Str "the" , Space , Str "ruby" , Space , Str "base," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] , Div ( "styling-xhtml-006.xhtml_style-412" , [ "section" , "ctest" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Span ( "" , [ "nature" ] , [] ) [ Str "[REQUIRED]" ] , Space , Span ( "" , [ "test-id" ] , [] ) [ Str "style-412" ] , Space , Code ( "" , [] , [] ) "inter-character" ] , Para [ Str "Tests" , Space , Str "whether" , Space , Str "the" , Space , Code ( "" , [] , [] ) "-epub-ruby-position" , Space , Str "property" , Space , Str "set" , Space , Str "to" , Space , Str "inter-caracter" , Space , Str "is" , Space , Str "supported." ] , Plain [ RawInline (Format "html") "" , Strong [ Str "Lorem" , Space , Str "Ipsum" ] , Space , RawInline (Format "html") "" , Str "(" , RawInline (Format "html") "" , RawInline (Format "html") "" , Str "Lorem" , Space , Str "Ipsum" , RawInline (Format "html") "" , RawInline (Format "html") "" , Str ")" , RawInline (Format "html") "" , RawInline (Format "html") "" ] , Para [ Str "If" , Space , Str "the" , Space , Str "Ruby" , Space , Str "text" , Space , Str "is" , Space , Str "positioned" , Space , Str "on" , Space , Str "the" , Space , Str "right" , Space , Str "side" , Space , Str "of" , Space , Str "the" , Space , Str "base" , Space , Str "text," , Space , Str "the" , Space , Str "test" , Space , Str "passes." ] ] ] ] ================================================ FILE: test/epub/wasteland.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaInlines [ Str "T.S. Eliot" ] ) , ( "date" , MetaInlines [ Str "2011-09-01" ] ) , ( "identifier" , MetaInlines [ Str "code.google.com.epub-samples.wasteland-basic" ] ) , ( "language" , MetaInlines [ Str "en-US" ] ) , ( "rights" , MetaInlines [ Str "This work is shared with the public using the Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) license." ] ) , ( "title" , MetaInlines [ Str "The Waste Land" ] ) ] } [ Para [ Image ( "" , [] , [] ) [] ( "wasteland-cover.jpg" , "" ) ] , Para [ Span ( "wasteland-content.xhtml" , [] , [] ) [] ] , Div ( "wasteland-content.xhtml_frontmatter" , [ "section" , "frontmatter" ] , [] ) [] , Div ( "wasteland-content.xhtml_bodymatter" , [ "section" , "bodymatter" ] , [] ) [ Div ( "wasteland-content.xhtml_ch1" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "I." , Space , Str "THE" , Space , Str "BURIAL" , Space , Str "OF" , Space , Str "THE" , Space , Str "DEAD" ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "April" , Space , Str "is" , Space , Str "the" , Space , Str "cruellest" , Space , Str "month," , Space , Str "breeding" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Lilacs" , Space , Str "out" , Space , Str "of" , Space , Str "the" , Space , Str "dead" , Space , Str "land," , Space , Str "mixing" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Memory" , Space , Str "and" , Space , Str "desire," , Space , Str "stirring" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Dull" , Space , Str "roots" , Space , Str "with" , Space , Str "spring" , Space , Str "rain." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Winter" , Space , Str "kept" , Space , Str "us" , Space , Str "warm," , Space , Str "covering" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Earth" , Space , Str "in" , Space , Str "forgetful" , Space , Str "snow," , Space , Str "feeding" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "A" , Space , Str "little" , Space , Str "life" , Space , Str "with" , Space , Str "dried" , Space , Str "tubers." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Summer" , Space , Str "surprised" , Space , Str "us," , Space , Str "coming" , Space , Str "over" , Space , Str "the" , Space , Str "Starnbergersee" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "With" , Space , Str "a" , Space , Str "shower" , Space , Str "of" , Space , Str "rain;" , Space , Str "we" , Space , Str "stopped" , Space , Str "in" , Space , Str "the" , Space , Str "colonnade," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "went" , Space , Str "on" , Space , Str "in" , Space , Str "sunlight," , Space , Str "into" , Space , Str "the" , Space , Str "Hofgarten," , Span ( "" , [ "lnum" ] , [] ) [ Str "10" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "drank" , Space , Str "coffee," , Space , Str "and" , Space , Str "talked" , Space , Str "for" , Space , Str "an" , Space , Str "hour." ] ] , Div ( "" , [] , [ ( "lang" , "de" ) ] ) [ Plain [ Str "Bin" , Space , Str "gar" , Space , Str "keine" , Space , Str "Russin," , Space , Str "stamm'" , Space , Str "aus" , Space , Str "Litauen," , Space , Str "echt" , SoftBreak , Str "deutsch." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "when" , Space , Str "we" , Space , Str "were" , Space , Str "children," , Space , Str "staying" , Space , Str "at" , Space , Str "the" , Space , Str "archduke's," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "My" , Space , Str "cousin's," , Space , Str "he" , Space , Str "took" , Space , Str "me" , Space , Str "out" , Space , Str "on" , Space , Str "a" , Space , Str "sled," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "I" , Space , Str "was" , Space , Str "frightened." , Space , Str "He" , Space , Str "said," , Space , Str "Marie," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Marie," , Space , Str "hold" , Space , Str "on" , Space , Str "tight." , Space , Str "And" , Space , Str "down" , Space , Str "we" , Space , Str "went." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "In" , Space , Str "the" , Space , Str "mountains," , Space , Str "there" , Space , Str "you" , Space , Str "feel" , Space , Str "free." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "I" , Space , Str "read," , Space , Str "much" , Space , Str "of" , Space , Str "the" , Space , Str "night," , Space , Str "and" , Space , Str "go" , Space , Str "south" , Space , Str "in" , Space , Str "the" , Space , Str "winter." ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "What" , Space , Str "are" , Space , Str "the" , Space , Str "roots" , Space , Str "that" , Space , Str "clutch," , Space , Str "what" , Space , Str "branches" , Space , Str "grow" ] ] , Div ( "wasteland-content.xhtml_ln20" , [] , [] ) [ Plain [ Str "Out" , Space , Str "of" , Space , Str "this" , Space , Str "stony" , Space , Str "rubbish?" , Space , Str "Son" , Space , Str "of" , Space , Str "man," , Note [ Para [ Link ( "" , [] , [] ) [ Str "Line" , Space , Str "20." ] ( "#wasteland-content.xhtml_ln20" , "" ) , Space , Str "Cf." , Space , Str "Ezekiel" , Space , Str "2:1." ] ] , Span ( "" , [ "lnum" ] , [] ) [ Str "20" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "You" , Space , Str "cannot" , Space , Str "say," , Space , Str "or" , Space , Str "guess," , Space , Str "for" , Space , Str "you" , Space , Str "know" , Space , Str "only" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "A" , Space , Str "heap" , Space , Str "of" , Space , Str "broken" , Space , Str "images," , Space , Str "where" , Space , Str "the" , Space , Str "sun" , Space , Str "beats," ] ] , Div ( "wasteland-content.xhtml_ln23" , [] , [] ) [ Plain [ Str "And" , Space , Str "the" , Space , Str "dead" , Space , Str "tree" , Space , Str "gives" , Space , Str "no" , Space , Str "shelter," , Space , Str "the" , Space , Str "cricket" , Space , Str "no" , Space , Str "relief," , Note [ Para [ Link ( "" , [] , [] ) [ Str "23." ] ( "#wasteland-content.xhtml_ln23" , "" ) , Space , Str "Cf." , Space , Str "Ecclesiastes" , Space , Str "12:5." ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "the" , Space , Str "dry" , Space , Str "stone" , Space , Str "no" , Space , Str "sound" , Space , Str "of" , Space , Str "water." , Space , Str "Only" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "There" , Space , Str "is" , Space , Str "shadow" , Space , Str "under" , Space , Str "this" , Space , Str "red" , Space , Str "rock," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "(Come" , Space , Str "in" , Space , Str "under" , Space , Str "the" , Space , Str "shadow" , Space , Str "of" , Space , Str "this" , Space , Str "red" , Space , Str "rock)," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "I" , Space , Str "will" , Space , Str "show" , Space , Str "you" , Space , Str "something" , Space , Str "different" , Space , Str "from" , Space , Str "either" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Your" , Space , Str "shadow" , Space , Str "at" , Space , Str "morning" , Space , Str "striding" , Space , Str "behind" , Space , Str "you" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Or" , Space , Str "your" , Space , Str "shadow" , Space , Str "at" , Space , Str "evening" , Space , Str "rising" , Space , Str "to" , Space , Str "meet" , Space , Str "you;" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "I" , Space , Str "will" , Space , Str "show" , Space , Str "you" , Space , Str "fear" , Space , Str "in" , Space , Str "a" , Space , Str "handful" , Space , Str "of" , Space , Str "dust." , Span ( "" , [ "lnum" ] , [] ) [ Str "30" ] ] ] , BlockQuote [ Div ( "" , [] , [] ) [ Div ( "wasteland-content.xhtml_ln31" , [] , [] ) [ Plain [ Str "Frisch" , Space , Str "weht" , Space , Str "der" , Space , Str "Wind" , Note [ Para [ Link ( "" , [] , [] ) [ Str "31." ] ( "#wasteland-content.xhtml_ln31" , "" ) , Space , Str "V." , Space , Str "Tristan" , Space , Str "und" , Space , Str "Isolde," , Space , Str "i," , Space , Str "verses" , Space , Str "5-8." ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Der" , Space , Str "Heimat" , Space , Str "zu" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Mein" , Space , Str "Irisch" , Space , Str "Kind," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Wo" , Space , Str "weilest" , Space , Str "du?" ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"You" , Space , Str "gave" , Space , Str "me" , Space , Str "hyacinths" , Space , Str "first" , Space , Str "a" , Space , Str "year" , Space , Str "ago;" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"They" , Space , Str "called" , Space , Str "me" , Space , Str "the" , Space , Str "hyacinth" , Space , Str "girl.\"" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\8213Yet" , Space , Str "when" , Space , Str "we" , Space , Str "came" , Space , Str "back," , Space , Str "late," , Space , Str "from" , Space , Str "the" , Space , Str "Hyacinth" , SoftBreak , Str "garden," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Your" , Space , Str "arms" , Space , Str "full," , Space , Str "and" , Space , Str "your" , Space , Str "hair" , Space , Str "wet," , Space , Str "I" , Space , Str "could" , Space , Str "not" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Speak," , Space , Str "and" , Space , Str "my" , Space , Str "eyes" , Space , Str "failed," , Space , Str "I" , Space , Str "was" , Space , Str "neither" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Living" , Space , Str "nor" , Space , Str "dead," , Space , Str "and" , Space , Str "I" , Space , Str "knew" , Space , Str "nothing," , Span ( "" , [ "lnum" ] , [] ) [ Str "40" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Looking" , Space , Str "into" , Space , Str "the" , Space , Str "heart" , Space , Str "of" , Space , Str "light," , Space , Str "the" , Space , Str "silence." ] ] , Div ( "wasteland-content.xhtml_ln42" , [] , [ ( "lang" , "de" ) ] ) [ Plain [ Emph [ Str "Od'" , Space , Str "und" , Space , Str "leer" , Space , Str "das" , Space , Str "Meer" ] , Str "." , Note [ Para [ Link ( "" , [] , [] ) [ Str "42." ] ( "#wasteland-content.xhtml_ln42" , "" ) , Space , Str "Id." , Space , Str "iii," , Space , Str "verse" , Space , Str "24." ] ] ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "Madame" , Space , Str "Sosostris," , Space , Str "famous" , Space , Str "clairvoyante," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Had" , Space , Str "a" , Space , Str "bad" , Space , Str "cold," , Space , Str "nevertheless" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Is" , Space , Str "known" , Space , Str "to" , Space , Str "be" , Space , Str "the" , Space , Str "wisest" , Space , Str "woman" , Space , Str "in" , Space , Str "Europe," ] ] , Div ( "wasteland-content.xhtml_ln46" , [] , [] ) [ Plain [ Str "With" , Space , Str "a" , Space , Str "wicked" , Space , Str "pack" , Space , Str "of" , Space , Str "cards." , Space , Str "Here," , Space , Str "said" , Space , Str "she," , Note [ Para [ Link ( "" , [] , [] ) [ Str "46." ] ( "#wasteland-content.xhtml_ln46" , "" ) , Space , Str "I" , Space , Str "am" , Space , Str "not" , Space , Str "familiar" , Space , Str "with" , Space , Str "the" , Space , Str "exact" , Space , Str "constitution" , Space , Str "of" , Space , Str "the" , Space , Str "Tarot" , Space , Str "pack" , Space , Str "of" , SoftBreak , Str "cards," , Space , Str "from" , Space , Str "which" , Space , Str "I" , Space , Str "have" , Space , Str "obviously" , Space , Str "departed" , Space , Str "to" , Space , Str "suit" , Space , Str "my" , Space , Str "own" , Space , Str "convenience." , SoftBreak , Str "The" , Space , Str "Hanged" , Space , Str "Man," , Space , Str "a" , Space , Str "member" , Space , Str "of" , Space , Str "the" , Space , Str "traditional" , Space , Str "pack," , Space , Str "fits" , Space , Str "my" , Space , Str "purpose" , Space , Str "in" , Space , Str "two" , SoftBreak , Str "ways:" , Space , Str "because" , Space , Str "he" , Space , Str "is" , Space , Str "associated" , Space , Str "in" , Space , Str "my" , Space , Str "mind" , Space , Str "with" , Space , Str "the" , Space , Str "Hanged" , Space , Str "God" , Space , Str "of" , Space , Str "Frazer," , SoftBreak , Str "and" , Space , Str "because" , Space , Str "I" , Space , Str "associate" , Space , Str "him" , Space , Str "with" , Space , Str "the" , Space , Str "hooded" , Space , Str "figure" , Space , Str "in" , Space , Str "the" , Space , Str "passage" , Space , Str "of" , Space , Str "the" , SoftBreak , Str "disciples" , Space , Str "to" , Space , Str "Emmaus" , Space , Str "in" , Space , Str "Part" , Space , Str "V." , Space , Str "The" , Space , Str "Phoenician" , Space , Str "Sailor" , Space , Str "and" , Space , Str "the" , Space , Str "Merchant" , SoftBreak , Str "appear" , Space , Str "later;" , Space , Str "also" , Space , Str "the" , Space , Str "\"crowds" , Space , Str "of" , Space , Str "people,\"" , Space , Str "and" , Space , Str "Death" , Space , Str "by" , Space , Str "Water" , Space , Str "is" , SoftBreak , Str "executed" , Space , Str "in" , Space , Str "Part" , Space , Str "IV." , Space , Str "The" , Space , Str "Man" , Space , Str "with" , Space , Str "Three" , Space , Str "Staves" , Space , Str "(an" , Space , Str "authentic" , Space , Str "member" , Space , Str "of" , SoftBreak , Str "the" , Space , Str "Tarot" , Space , Str "pack)" , Space , Str "I" , Space , Str "associate," , Space , Str "quite" , Space , Str "arbitrarily," , Space , Str "with" , Space , Str "the" , Space , Str "Fisher" , Space , Str "King" , SoftBreak , Str "himself." ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Is" , Space , Str "your" , Space , Str "card," , Space , Str "the" , Space , Str "drowned" , Space , Str "Phoenician" , Space , Str "Sailor," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "(Those" , Space , Str "are" , Space , Str "pearls" , Space , Str "that" , Space , Str "were" , Space , Str "his" , Space , Str "eyes." , Space , Str "Look!)" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Here" , Space , Str "is" , Space , Str "Belladonna," , Space , Str "the" , Space , Str "Lady" , Space , Str "of" , Space , Str "the" , Space , Str "Rocks," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "The" , Space , Str "lady" , Space , Str "of" , Space , Str "situations." , Span ( "" , [ "lnum" ] , [] ) [ Str "50" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "man" , Space , Str "with" , Space , Str "three" , Space , Str "staves," , Space , Str "and" , Space , Str "here" , Space , Str "the" , Space , Str "Wheel," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "here" , Space , Str "is" , Space , Str "the" , Space , Str "one-eyed" , Space , Str "merchant," , Space , Str "and" , Space , Str "this" , Space , Str "card," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Which" , Space , Str "is" , Space , Str "blank," , Space , Str "is" , Space , Str "something" , Space , Str "he" , Space , Str "carries" , Space , Str "on" , Space , Str "his" , Space , Str "back," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Which" , Space , Str "I" , Space , Str "am" , Space , Str "forbidden" , Space , Str "to" , Space , Str "see." , Space , Str "I" , Space , Str "do" , Space , Str "not" , Space , Str "find" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "The" , Space , Str "Hanged" , Space , Str "Man." , Space , Str "Fear" , Space , Str "death" , Space , Str "by" , Space , Str "water." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "I" , Space , Str "see" , Space , Str "crowds" , Space , Str "of" , Space , Str "people," , Space , Str "walking" , Space , Str "round" , Space , Str "in" , Space , Str "a" , Space , Str "ring." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Thank" , Space , Str "you." , Space , Str "If" , Space , Str "you" , Space , Str "see" , Space , Str "dear" , Space , Str "Mrs." , Space , Str "Equitone," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Tell" , Space , Str "her" , Space , Str "I" , Space , Str "bring" , Space , Str "the" , Space , Str "horoscope" , Space , Str "myself:" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "One" , Space , Str "must" , Space , Str "be" , Space , Str "so" , Space , Str "careful" , Space , Str "these" , Space , Str "days." ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "wasteland-content.xhtml_ln60" , [] , [] ) [ Plain [ Str "Unreal" , Space , Str "City," , Note [ Para [ Link ( "" , [] , [] ) [ Str "60." ] ( "#wasteland-content.xhtml_ln60" , "" ) , Space , Str "Cf." , Space , Str "Baudelaire:" ] , BlockQuote [ Para [ Str "\"Fourmillante" , Space , Str "cite;," , Space , Str "cite;" , Space , Str "pleine" , Space , Str "de" , Space , Str "reves," , LineBreak , Str "Ou" , Space , Str "le" , Space , Str "spectre" , Space , Str "en" , SoftBreak , Str "plein" , Space , Str "jour" , Space , Str "raccroche" , Space , Str "le" , Space , Str "passant.\"" ] ] ] , Span ( "" , [ "lnum" ] , [] ) [ Str "60" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Under" , Space , Str "the" , Space , Str "brown" , Space , Str "fog" , Space , Str "of" , Space , Str "a" , Space , Str "winter" , Space , Str "dawn," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "A" , Space , Str "crowd" , Space , Str "flowed" , Space , Str "over" , Space , Str "London" , Space , Str "Bridge," , Space , Str "so" , Space , Str "many," ] ] , Div ( "wasteland-content.xhtml_ln63" , [] , [] ) [ Plain [ Str "I" , Space , Str "had" , Space , Str "not" , Space , Str "thought" , Space , Str "death" , Space , Str "had" , Space , Str "undone" , Space , Str "so" , Space , Str "many." , Note [ Para [ Link ( "" , [] , [] ) [ Str "63." ] ( "#wasteland-content.xhtml_ln63" , "" ) , Space , Str "Cf." , Space , Str "Inferno," , Space , Str "iii." , Space , Str "55-7." ] , BlockQuote [ Para [ Str "\"si" , Space , Str "lunga" , Space , Str "tratta" , LineBreak , Str "di" , Space , Str "gente," , Space , Str "ch'io" , Space , Str "non" , Space , Str "avrei" , Space , Str "mai" , Space , Str "creduto" , LineBreak , Str "che" , SoftBreak , Str "morte" , Space , Str "tanta" , Space , Str "n'avesse" , Space , Str "disfatta.\"" ] ] ] ] ] , Div ( "wasteland-content.xhtml_ln64" , [] , [] ) [ Plain [ Str "Sighs," , Space , Str "short" , Space , Str "and" , Space , Str "infrequent," , Space , Str "were" , Space , Str "exhaled," , Note [ Para [ Link ( "" , [] , [] ) [ Str "64." ] ( "#wasteland-content.xhtml_ln64" , "" ) , Space , Str "Cf." , Space , Str "Inferno," , Space , Str "iv." , Space , Str "25-7:" ] , BlockQuote [ Para [ Str "\"Quivi," , Space , Str "secondo" , Space , Str "che" , Space , Str "per" , Space , Str "ascoltahre," , LineBreak , Str "\"non" , Space , Str "avea" , Space , Str "pianto," , Space , Str "ma'" , Space , Str "che" , Space , Str "di" , SoftBreak , Str "sospiri," , LineBreak , Str "\"che" , Space , Str "l'aura" , Space , Str "eterna" , Space , Str "facevan" , Space , Str "tremare.\"" ] ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "each" , Space , Str "man" , Space , Str "fixed" , Space , Str "his" , Space , Str "eyes" , Space , Str "before" , Space , Str "his" , Space , Str "feet." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Flowed" , Space , Str "up" , Space , Str "the" , Space , Str "hill" , Space , Str "and" , Space , Str "down" , Space , Str "King" , Space , Str "William" , Space , Str "Street," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "To" , Space , Str "where" , Space , Str "Saint" , Space , Str "Mary" , Space , Str "Woolnoth" , Space , Str "kept" , Space , Str "the" , Space , Str "hours" ] ] , Div ( "wasteland-content.xhtml_ln68" , [] , [] ) [ Plain [ Str "With" , Space , Str "a" , Space , Str "dead" , Space , Str "sound" , Space , Str "on" , Space , Str "the" , Space , Str "final" , Space , Str "stroke" , Space , Str "of" , Space , Str "nine." , Note [ Para [ Link ( "" , [] , [] ) [ Str "68." ] ( "#wasteland-content.xhtml_ln68" , "" ) , Space , Str "A" , Space , Str "phenomenon" , Space , Str "which" , Space , Str "I" , Space , Str "have" , Space , Str "often" , Space , Str "noticed." ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "There" , Space , Str "I" , Space , Str "saw" , Space , Str "one" , Space , Str "I" , Space , Str "knew," , Space , Str "and" , Space , Str "stopped" , Space , Str "him," , Space , Str "crying" , SoftBreak , Str "\"Stetson!" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"You" , Space , Str "who" , Space , Str "were" , Space , Str "with" , Space , Str "me" , Space , Str "in" , Space , Str "the" , Space , Str "ships" , Space , Str "at" , Space , Str "Mylae!" , Span ( "" , [ "lnum" ] , [] ) [ Str "70" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"That" , Space , Str "corpse" , Space , Str "you" , Space , Str "planted" , Space , Str "last" , Space , Str "year" , Space , Str "in" , Space , Str "your" , Space , Str "garden," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"Has" , Space , Str "it" , Space , Str "begun" , Space , Str "to" , Space , Str "sprout?" , Space , Str "Will" , Space , Str "it" , Space , Str "bloom" , Space , Str "this" , Space , Str "year?" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"Or" , Space , Str "has" , Space , Str "the" , Space , Str "sudden" , Space , Str "frost" , Space , Str "disturbed" , Space , Str "its" , Space , Str "bed?" ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "wasteland-content.xhtml_ln74" , [] , [] ) [ Plain [ Str "\"Oh" , Space , Str "keep" , Space , Str "the" , Space , Str "Dog" , Space , Str "far" , Space , Str "hence," , Space , Str "that's" , Space , Str "friend" , Space , Str "to" , Space , Str "men," , Note [ Para [ Link ( "" , [] , [] ) [ Str "74." ] ( "#wasteland-content.xhtml_ln74" , "" ) , Space , Str "Cf." , Space , Str "the" , Space , Str "Dirge" , Space , Str "in" , Space , Str "Webster's" , Space , Str "White" , Space , Str "Devil" , Space , Str "." ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"Or" , Space , Str "with" , Space , Str "his" , Space , Str "nails" , Space , Str "he'll" , Space , Str "dig" , Space , Str "it" , Space , Str "up" , Space , Str "again!" ] ] , Div ( "wasteland-content.xhtml_ln76" , [] , [] ) [ Plain [ Str "\"You!" , Space , Span ( "" , [] , [ ( "lang" , "fr" ) ] ) [ Str "hypocrite" , Space , Str "lecteur!" , Space , Str "-" , Space , Str "mon" , Space , Str "semblable," , Space , Str "-" , SoftBreak , Str "mon" , Space , Str "frere" ] , Space , Str "!\"" , Note [ Para [ Link ( "" , [] , [] ) [ Str "76." ] ( "#wasteland-content.xhtml_ln76" , "" ) , Space , Str "V." , Space , Str "Baudelaire," , Space , Str "Preface" , Space , Str "to" , Space , Str "Fleurs" , Space , Str "du" , Space , Str "Mal." ] ] ] ] ] ] , Div ( "wasteland-content.xhtml_ch2" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "II." , Space , Str "A" , Space , Str "GAME" , Space , Str "OF" , Space , Str "CHESS" ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "wasteland-content.xhtml_ln77" , [] , [] ) [ Plain [ Str "The" , Space , Str "Chair" , Space , Str "she" , Space , Str "sat" , Space , Str "in," , Space , Str "like" , Space , Str "a" , Space , Str "burnished" , Space , Str "throne," , Note [ Para [ Link ( "" , [] , [] ) [ Str "77." ] ( "#wasteland-content.xhtml_ln77" , "" ) , Space , Str "Cf." , Space , Str "Antony" , Space , Str "and" , Space , Str "Cleopatra," , Space , Str "II." , Space , Str "ii.," , Space , Str "l." , Space , Str "190." ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Glowed" , Space , Str "on" , Space , Str "the" , Space , Str "marble," , Space , Str "where" , Space , Str "the" , Space , Str "glass" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Held" , Space , Str "up" , Space , Str "by" , Space , Str "standards" , Space , Str "wrought" , Space , Str "with" , Space , Str "fruited" , Space , Str "vines" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "From" , Space , Str "which" , Space , Str "a" , Space , Str "golden" , Space , Str "Cupidon" , Space , Str "peeped" , Space , Str "out" , Span ( "" , [ "lnum" ] , [] ) [ Str "80" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "(Another" , Space , Str "hid" , Space , Str "his" , Space , Str "eyes" , Space , Str "behind" , Space , Str "his" , Space , Str "wing)" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Doubled" , Space , Str "the" , Space , Str "flames" , Space , Str "of" , Space , Str "sevenbranched" , Space , Str "candelabra" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Reflecting" , Space , Str "light" , Space , Str "upon" , Space , Str "the" , Space , Str "table" , Space , Str "as" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "The" , Space , Str "glitter" , Space , Str "of" , Space , Str "her" , Space , Str "jewels" , Space , Str "rose" , Space , Str "to" , Space , Str "meet" , Space , Str "it," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "From" , Space , Str "satin" , Space , Str "cases" , Space , Str "poured" , Space , Str "in" , Space , Str "rich" , Space , Str "profusion;" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "In" , Space , Str "vials" , Space , Str "of" , Space , Str "ivory" , Space , Str "and" , Space , Str "coloured" , Space , Str "glass" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Unstoppered," , Space , Str "lurked" , Space , Str "her" , Space , Str "strange" , Space , Str "synthetic" , Space , Str "perfumes," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Unguent," , Space , Str "powdered," , Space , Str "or" , Space , Str "liquid" , Space , Str "-" , Space , Str "troubled," , Space , Str "confused" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "drowned" , Space , Str "the" , Space , Str "sense" , Space , Str "in" , Space , Str "odours;" , Space , Str "stirred" , Space , Str "by" , Space , Str "the" , Space , Str "air" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "That" , Space , Str "freshened" , Space , Str "from" , Space , Str "the" , Space , Str "window," , Space , Str "these" , Space , Str "ascended" , Span ( "" , [ "lnum" ] , [] ) [ Str "90" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "In" , Space , Str "fattening" , Space , Str "the" , Space , Str "prolonged" , Space , Str "candle-flames," ] ] , Div ( "wasteland-content.xhtml_ln92" , [] , [] ) [ Plain [ Str "Flung" , Space , Str "their" , Space , Str "smoke" , Space , Str "into" , Space , Str "the" , Space , Str "laquearia," , Note [ Para [ Link ( "" , [] , [] ) [ Str "92." ] ( "#wasteland-content.xhtml_ln92" , "" ) , Space , Str "Laquearia." , Space , Str "V." , Space , Str "Aeneid," , Space , Str "I." , Space , Str "726:" ] , BlockQuote [ Para [ Str "dependent" , Space , Str "lychni" , Space , Str "laquearibus" , Space , Str "aureis" , Space , Str "incensi," , Space , Str "et" , Space , Str "noctem" , SoftBreak , Str "flammis" , LineBreak , Str "funalia" , Space , Str "vincunt." ] ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Stirring" , Space , Str "the" , Space , Str "pattern" , Space , Str "on" , Space , Str "the" , Space , Str "coffered" , Space , Str "ceiling." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Huge" , Space , Str "sea-wood" , Space , Str "fed" , Space , Str "with" , Space , Str "copper" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Burned" , Space , Str "green" , Space , Str "and" , Space , Str "orange," , Space , Str "framed" , Space , Str "by" , Space , Str "the" , Space , Str "coloured" , Space , Str "stone," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "In" , Space , Str "which" , Space , Str "sad" , Space , Str "light" , Space , Str "a" , Space , Str "carved" , Space , Str "dolphin" , Space , Str "swam." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Above" , Space , Str "the" , Space , Str "antique" , Space , Str "mantel" , Space , Str "was" , Space , Str "displayed" ] ] , Div ( "wasteland-content.xhtml_ln98" , [] , [] ) [ Plain [ Str "As" , Space , Str "though" , Space , Str "a" , Space , Str "window" , Space , Str "gave" , Space , Str "upon" , Space , Str "the" , Space , Str "sylvan" , Space , Str "scene" , Note [ Para [ Link ( "" , [] , [] ) [ Str "98." ] ( "#wasteland-content.xhtml_ln98" , "" ) , Space , Str "Sylvan" , Space , Str "scene." , Space , Str "V." , Space , Str "Milton," , Space , Str "Paradise" , Space , Str "Lost," , Space , Str "iv." , Space , Str "140." ] ] ] ] , Div ( "wasteland-content.xhtml_ln99" , [] , [] ) [ Plain [ Str "The" , Space , Str "change" , Space , Str "of" , Space , Str "Philomel," , Space , Str "by" , Space , Str "the" , Space , Str "barbarous" , Space , Str "king" , Note [ Para [ Link ( "" , [] , [] ) [ Str "99." ] ( "#wasteland-content.xhtml_ln99" , "" ) , Space , Str "V." , Space , Str "Ovid," , Space , Str "Metamorphoses," , Space , Str "vi," , Space , Str "Philomela." ] ] ] ] , Div ( "wasteland-content.xhtml_ln100" , [] , [] ) [ Plain [ Str "So" , Space , Str "rudely" , Space , Str "forced;" , Space , Str "yet" , Space , Str "there" , Space , Str "the" , Space , Str "nightingale" , Note [ Para [ Link ( "" , [] , [] ) [ Str "100." ] ( "#wasteland-content.xhtml_ln100" , "" ) , Space , Str "Cf." , Space , Str "Part" , Space , Str "III," , Space , Str "l." , Space , Str "204." ] ] , SoftBreak , Span ( "" , [ "lnum" ] , [] ) [ Str "100" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Filled" , Space , Str "all" , Space , Str "the" , Space , Str "desert" , Space , Str "with" , Space , Str "inviolable" , Space , Str "voice" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "still" , Space , Str "she" , Space , Str "cried," , Space , Str "and" , Space , Str "still" , Space , Str "the" , Space , Str "world" , Space , Str "pursues," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"Jug" , Space , Str "Jug\"" , Space , Str "to" , Space , Str "dirty" , Space , Str "ears." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "other" , Space , Str "withered" , Space , Str "stumps" , Space , Str "of" , Space , Str "time" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Were" , Space , Str "told" , Space , Str "upon" , Space , Str "the" , Space , Str "walls;" , Space , Str "staring" , Space , Str "forms" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Leaned" , Space , Str "out," , Space , Str "leaning," , Space , Str "hushing" , Space , Str "the" , Space , Str "room" , Space , Str "enclosed." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Footsteps" , Space , Str "shuffled" , Space , Str "on" , Space , Str "the" , Space , Str "stair." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Under" , Space , Str "the" , Space , Str "firelight," , Space , Str "under" , Space , Str "the" , Space , Str "brush," , Space , Str "her" , Space , Str "hair" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Spread" , Space , Str "out" , Space , Str "in" , Space , Str "fiery" , Space , Str "points" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Glowed" , Space , Str "into" , Space , Str "words," , Space , Str "then" , Space , Str "would" , Space , Str "be" , Space , Str "savagely" , Space , Str "still." , Span ( "" , [ "lnum" ] , [] ) [ Str "110" ] ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "\"My" , Space , Str "nerves" , Space , Str "are" , Space , Str "bad" , Space , Str "to-night." , Space , Str "Yes," , Space , Str "bad." , Space , Str "Stay" , Space , Str "with" , Space , Str "me." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"Speak" , Space , Str "to" , Space , Str "me." , Space , Str "Why" , Space , Str "do" , Space , Str "you" , Space , Str "never" , Space , Str "speak." , Space , Str "Speak." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"What" , Space , Str "are" , Space , Str "you" , Space , Str "thinking" , Space , Str "of?" , Space , Str "What" , Space , Str "thinking?" , Space , Str "What?" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"I" , Space , Str "never" , Space , Str "know" , Space , Str "what" , Space , Str "you" , Space , Str "are" , Space , Str "thinking." , Space , Str "Think.\"" ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "wasteland-content.xhtml_ln115" , [] , [] ) [ Plain [ Str "I" , Space , Str "think" , Space , Str "we" , Space , Str "are" , Space , Str "in" , Space , Str "rats'" , Space , Str "alley" , Note [ Para [ Link ( "" , [] , [] ) [ Str "115." ] ( "#wasteland-content.xhtml_ln115" , "" ) , Space , Str "Cf." , Space , Str "Part" , Space , Str "III," , Space , Str "l." , Space , Str "195." ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Where" , Space , Str "the" , Space , Str "dead" , Space , Str "men" , Space , Str "lost" , Space , Str "their" , Space , Str "bones." ] ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "\"What" , Space , Str "is" , Space , Str "that" , Space , Str "noise?\"" ] ] , Div ( "wasteland-content.xhtml_ln118" , [ "indent" ] , [] ) [ Plain [ Str "The" , Space , Str "wind" , Space , Str "under" , Space , Str "the" , Space , Str "door." , Note [ Para [ Link ( "" , [] , [] ) [ Str "118." ] ( "#wasteland-content.xhtml_ln118" , "" ) , Space , Str "Cf." , Space , Str "Webster:" ] , BlockQuote [ Para [ Str "\"Is" , Space , Str "the" , Space , Str "wind" , Space , Str "in" , Space , Str "that" , Space , Str "door" , Space , Str "still?\"" ] ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"What" , Space , Str "is" , Space , Str "that" , Space , Str "noise" , Space , Str "now?" , Space , Str "What" , Space , Str "is" , Space , Str "the" , Space , Str "wind" , Space , Str "doing?\"" ] ] , Div ( "" , [ "indent" ] , [] ) [ Plain [ Str "Nothing" , Space , Str "again" , Space , Str "nothing." , Span ( "" , [ "lnum" ] , [] ) [ Str "120" ] ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "\"Do" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"You" , Space , Str "know" , Space , Str "nothing?" , Space , Str "Do" , Space , Str "you" , Space , Str "see" , Space , Str "nothing?" , Space , Str "Do" , Space , Str "you" , Space , Str "remember" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"Nothing?\"" ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "I" , Space , Str "remember" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Those" , Space , Str "are" , Space , Str "pearls" , Space , Str "that" , Space , Str "were" , Space , Str "his" , Space , Str "eyes." ] ] , Div ( "wasteland-content.xhtml_ln126" , [] , [] ) [ Plain [ Str "\"Are" , Space , Str "you" , Space , Str "alive," , Space , Str "or" , Space , Str "not?" , Space , Str "Is" , Space , Str "there" , Space , Str "nothing" , Space , Str "in" , Space , Str "your" , Space , Str "head?\"" , Note [ Para [ Link ( "" , [] , [] ) [ Str "126." ] ( "#wasteland-content.xhtml_ln126" , "" ) , Space , Str "Cf." , Space , Str "Part" , Space , Str "I," , Space , Str "l." , Space , Str "37," , Space , Str "48." ] ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "But" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "O" , Space , Str "O" , Space , Str "O" , Space , Str "O" , Space , Str "that" , Space , Str "Shakespeherian" , Space , Str "Rag\8213" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "It's" , Space , Str "so" , Space , Str "elegant" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "So" , Space , Str "intelligent" , Span ( "" , [ "lnum" ] , [] ) [ Str "130" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"What" , Space , Str "shall" , Space , Str "I" , Space , Str "do" , Space , Str "now?" , Space , Str "What" , Space , Str "shall" , Space , Str "I" , Space , Str "do?\"" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "I" , Space , Str "shall" , Space , Str "rush" , Space , Str "out" , Space , Str "as" , Space , Str "I" , Space , Str "am," , Space , Str "and" , Space , Str "walk" , Space , Str "the" , Space , Str "street" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"With" , Space , Str "my" , Space , Str "hair" , Space , Str "down," , Space , Str "so." , Space , Str "What" , Space , Str "shall" , Space , Str "we" , Space , Str "do" , Space , Str "to-morrow?" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "\"What" , Space , Str "shall" , Space , Str "we" , Space , Str "ever" , Space , Str "do?\"" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "The" , Space , Str "hot" , Space , Str "water" , Space , Str "at" , Space , Str "ten." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "if" , Space , Str "it" , Space , Str "rains," , Space , Str "a" , Space , Str "closed" , Space , Str "car" , Space , Str "at" , Space , Str "four." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "we" , Space , Str "shall" , Space , Str "play" , Space , Str "a" , Space , Str "game" , Space , Str "of" , Space , Str "chess," ] ] , Div ( "wasteland-content.xhtml_ln138" , [] , [] ) [ Plain [ Str "Pressing" , Space , Str "lidless" , Space , Str "eyes" , Space , Str "and" , Space , Str "waiting" , Space , Str "for" , Space , Str "a" , Space , Str "knock" , Space , Str "upon" , Space , Str "the" , Space , Str "door." , Note [ Para [ Link ( "" , [] , [] ) [ Str "138." ] ( "#wasteland-content.xhtml_ln138" , "" ) , Space , Str "Cf." , Space , Str "the" , Space , Str "game" , Space , Str "of" , Space , Str "chess" , Space , Str "in" , Space , Str "Middleton's" , Space , Str "Women" , Space , Str "beware" , Space , Str "Women." ] ] ] ] ] , Div ( "" , [ "linegroup" ] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "When" , Space , Str "Lil's" , Space , Str "husband" , Space , Str "got" , Space , Str "demobbed," , Space , Str "I" , Space , Str "said" , Space , Str "-" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "I" , Space , Str "didn't" , Space , Str "mince" , Space , Str "my" , Space , Str "words," , Space , Str "I" , Space , Str "said" , Space , Str "to" , Space , Str "her" , Space , Str "myself," , Span ( "" , [ "lnum" ] , [] ) [ Str "140" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "HURRY" , Space , Str "UP" , Space , Str "PLEASE" , Space , Str "ITS" , Space , Str "TIME" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Now" , Space , Str "Albert's" , Space , Str "coming" , Space , Str "back," , Space , Str "make" , Space , Str "yourself" , Space , Str "a" , Space , Str "bit" , Space , Str "smart." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "He'll" , Space , Str "want" , Space , Str "to" , Space , Str "know" , Space , Str "what" , Space , Str "you" , Space , Str "done" , Space , Str "with" , Space , Str "that" , Space , Str "money" , Space , Str "he" , Space , Str "gave" , SoftBreak , Str "you" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "To" , Space , Str "get" , Space , Str "yourself" , Space , Str "some" , Space , Str "teeth." , Space , Str "He" , Space , Str "did," , Space , Str "I" , Space , Str "was" , Space , Str "there." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "You" , Space , Str "have" , Space , Str "them" , Space , Str "all" , Space , Str "out," , Space , Str "Lil," , Space , Str "and" , Space , Str "get" , Space , Str "a" , Space , Str "nice" , Space , Str "set," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "He" , Space , Str "said," , Space , Str "I" , Space , Str "swear," , Space , Str "I" , Space , Str "can't" , Space , Str "bear" , Space , Str "to" , Space , Str "look" , Space , Str "at" , Space , Str "you." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "no" , Space , Str "more" , Space , Str "can't" , Space , Str "I," , Space , Str "I" , Space , Str "said," , Space , Str "and" , Space , Str "think" , Space , Str "of" , Space , Str "poor" , Space , Str "Albert," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "He's" , Space , Str "been" , Space , Str "in" , Space , Str "the" , Space , Str "army" , Space , Str "four" , Space , Str "years," , Space , Str "he" , Space , Str "wants" , Space , Str "a" , Space , Str "good" , Space , Str "time," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "if" , Space , Str "you" , Space , Str "don't" , Space , Str "give" , Space , Str "it" , Space , Str "him," , Space , Str "there's" , Space , Str "others" , Space , Str "will," , Space , Str "I" , SoftBreak , Str "said." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Oh" , Space , Str "is" , Space , Str "there," , Space , Str "she" , Space , Str "said." , Space , Str "Something" , Space , Str "o'" , Space , Str "that," , Space , Str "I" , Space , Str "said." , Span ( "" , [ "lnum" ] , [] ) [ Str "150" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Then" , Space , Str "I'll" , Space , Str "know" , Space , Str "who" , Space , Str "to" , Space , Str "thank," , Space , Str "she" , Space , Str "said," , Space , Str "and" , Space , Str "give" , Space , Str "me" , Space , Str "a" , Space , Str "straight" , SoftBreak , Str "look." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "HURRY" , Space , Str "UP" , Space , Str "PLEASE" , Space , Str "ITS" , Space , Str "TIME" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "If" , Space , Str "you" , Space , Str "don't" , Space , Str "like" , Space , Str "it" , Space , Str "you" , Space , Str "can" , Space , Str "get" , Space , Str "on" , Space , Str "with" , Space , Str "it," , Space , Str "I" , Space , Str "said." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Others" , Space , Str "can" , Space , Str "pick" , Space , Str "and" , Space , Str "choose" , Space , Str "if" , Space , Str "you" , Space , Str "can't." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "But" , Space , Str "if" , Space , Str "Albert" , Space , Str "makes" , Space , Str "off," , Space , Str "it" , Space , Str "won't" , Space , Str "be" , Space , Str "for" , Space , Str "lack" , Space , Str "of" , SoftBreak , Str "telling." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "You" , Space , Str "ought" , Space , Str "to" , Space , Str "be" , Space , Str "ashamed," , Space , Str "I" , Space , Str "said," , Space , Str "to" , Space , Str "look" , Space , Str "so" , Space , Str "antique." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "(And" , Space , Str "her" , Space , Str "only" , Space , Str "thirty-one.)" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "I" , Space , Str "can't" , Space , Str "help" , Space , Str "it," , Space , Str "she" , Space , Str "said," , Space , Str "pulling" , Space , Str "a" , Space , Str "long" , Space , Str "face," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "It's" , Space , Str "them" , Space , Str "pills" , Space , Str "I" , Space , Str "took," , Space , Str "to" , Space , Str "bring" , Space , Str "it" , Space , Str "off," , Space , Str "she" , Space , Str "said." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "(She's" , Space , Str "had" , Space , Str "five" , Space , Str "already," , Space , Str "and" , Space , Str "nearly" , Space , Str "died" , Space , Str "of" , Space , Str "young" , Space , Str "George.)" , Span ( "" , [ "lnum" ] , [] ) [ Str "160" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "The" , Space , Str "chemist" , Space , Str "said" , Space , Str "it" , Space , Str "would" , Space , Str "be" , Space , Str "all" , Space , Str "right," , Space , Str "but" , Space , Str "I've" , Space , Str "never" , Space , Str "been" , Space , Str "the" , SoftBreak , Str "same." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "You" , Space , Emph [ Str "are" ] , Space , Str "a" , Space , Str "proper" , Space , Str "fool," , Space , Str "I" , Space , Str "said." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Well," , Space , Str "if" , Space , Str "Albert" , Space , Str "won't" , Space , Str "leave" , Space , Str "you" , Space , Str "alone," , Space , Str "there" , Space , Str "it" , Space , Str "is," , Space , Str "I" , SoftBreak , Str "said," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "What" , Space , Str "you" , Space , Str "get" , Space , Str "married" , Space , Str "for" , Space , Str "if" , Space , Str "you" , Space , Str "don't" , Space , Str "want" , Space , Str "children?" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "HURRY" , Space , Str "UP" , Space , Str "PLEASE" , Space , Str "ITS" , Space , Str "TIME" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Well," , Space , Str "that" , Space , Str "Sunday" , Space , Str "Albert" , Space , Str "was" , Space , Str "home," , Space , Str "they" , Space , Str "had" , Space , Str "a" , Space , Str "hot" , SoftBreak , Str "gammon," ] ] , Div ( "" , [] , [] ) [ Plain [ Str "And" , Space , Str "they" , Space , Str "asked" , Space , Str "me" , Space , Str "in" , Space , Str "to" , Space , Str "dinner," , Space , Str "to" , Space , Str "get" , Space , Str "the" , Space , Str "beauty" , Space , Str "of" , Space , Str "it" , SoftBreak , Str "hot\8213" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "HURRY" , Space , Str "UP" , Space , Str "PLEASE" , Space , Str "ITS" , Space , Str "TIME" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "HURRY" , Space , Str "UP" , Space , Str "PLEASE" , Space , Str "ITS" , Space , Str "TIME" ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Goonight" , Space , Str "Bill." , Space , Str "Goonight" , Space , Str "Lou." , Space , Str "Goonight" , Space , Str "May." , Space , Str "Goonight." , Span ( "" , [ "lnum" ] , [] ) [ Str "170" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Ta" , Space , Str "ta." , Space , Str "Goonight." , Space , Str "Goonight." ] ] , Div ( "" , [] , [] ) [ Plain [ Str "Good" , Space , Str "night," , Space , Str "ladies," , Space , Str "good" , Space , Str "night," , Space , Str "sweet" , Space , Str "ladies," , Space , Str "good" , Space , Str "night," , Space , Str "good" , SoftBreak , Str "night." ] ] ] ] ] , Div ( "wasteland-content.xhtml_backmatter" , [ "section" , "backmatter" ] , [] ) [ Div ( "wasteland-content.xhtml_rearnotes" , [ "rearnotes" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "NOTES" , Space , Str "ON" , Space , Str "\"THE" , Space , Str "WASTE" , Space , Str "LAND\"" ] , Para [ Str "Not" , Space , Str "only" , Space , Str "the" , Space , Str "title," , Space , Str "but" , Space , Str "the" , Space , Str "plan" , Space , Str "and" , Space , Str "a" , Space , Str "good" , Space , Str "deal" , Space , Str "of" , Space , Str "the" , Space , Str "incidental" , Space , Str "symbolism" , Space , Str "of" , SoftBreak , Str "the" , Space , Str "poem" , Space , Str "were" , Space , Str "suggested" , Space , Str "by" , Space , Str "Miss" , Space , Str "Jessie" , Space , Str "L." , Space , Str "Weston's" , Space , Str "book" , Space , Str "on" , Space , Str "the" , Space , Str "Grail" , Space , Str "legend:" , SoftBreak , Str "From" , Space , Str "Ritual" , Space , Str "to" , Space , Str "Romance" ] , Para [ Str "Indeed," , Space , Str "so" , Space , Str "deeply" , Space , Str "am" , Space , Str "I" , Space , Str "indebted," , Space , Str "Miss" , Space , Str "Weston's" , Space , Str "book" , Space , Str "will" , Space , Str "elucidate" , Space , Str "the" , SoftBreak , Str "difficulties" , Space , Str "of" , Space , Str "the" , Space , Str "poem" , Space , Str "much" , Space , Str "better" , Space , Str "than" , Space , Str "my" , Space , Str "notes" , Space , Str "can" , Space , Str "do;" , Space , Str "and" , Space , Str "I" , Space , Str "recommend" , Space , Str "it" , SoftBreak , Str "(apart" , Space , Str "from" , Space , Str "the" , Space , Str "great" , Space , Str "interest" , Space , Str "of" , Space , Str "the" , Space , Str "book" , Space , Str "itself)" , Space , Str "to" , Space , Str "any" , Space , Str "who" , Space , Str "think" , Space , Str "such" , SoftBreak , Str "elucidation" , Space , Str "of" , Space , Str "the" , Space , Str "poem" , Space , Str "worth" , Space , Str "the" , Space , Str "trouble." , Space , Str "To" , Space , Str "another" , Space , Str "work" , Space , Str "of" , Space , Str "anthropology" , Space , Str "I" , Space , Str "am" , SoftBreak , Str "indebted" , Space , Str "in" , Space , Str "general," , Space , Str "one" , Space , Str "which" , Space , Str "has" , Space , Str "influenced" , Space , Str "our" , Space , Str "generation" , Space , Str "profoundly;" , Space , Str "I" , Space , Str "mean" , SoftBreak , Str "The" , Space , Str "Golden" , Space , Str "Bough;" , Space , Str "I" , Space , Str "have" , Space , Str "used" , Space , Str "especially" , Space , Str "the" , Space , Str "two" , Space , Str "volumes" , Space , Str "Adonis," , Space , Str "Attis," , Space , Str "Osiris." , SoftBreak , Str "Anyone" , Space , Str "who" , Space , Str "is" , Space , Str "acquainted" , Space , Str "with" , Space , Str "these" , Space , Str "works" , Space , Str "will" , Space , Str "immediately" , Space , Str "recognise" , Space , Str "in" , Space , Str "the" , Space , Str "poem" , SoftBreak , Str "certain" , Space , Str "references" , Space , Str "to" , Space , Str "vegetation" , Space , Str "ceremonies." ] , Div ( "" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "I." , Space , Str "THE" , Space , Str "BURIAL" , Space , Str "OF" , Space , Str "THE" , Space , Str "DEAD" ] ] , Div ( "" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "II." , Space , Str "A" , Space , Str "GAME" , Space , Str "OF" , Space , Str "CHESS" ] ] ] ] ] ================================================ FILE: test/fb2/basic.fb2 ================================================ unrecognised pandoc <p />
    <p>Top-level title</p>
    <p>Section</p>
    <p>Subsection</p>

    This emphasized strong verbatim markdown. See this link.

    Ordered list:

    1. one

    2. two

    3. three

    Blockquote is for citatons.

    Code

    block

    is

    for

    code.

    Strikeout is Pandoc’s extension. Superscript and subscripts too: H2O is a liquid[1]. 210 is 1024.

    Math is another Pandoc extension: E = m c^2.

    <p>1</p>

    Sometimes.

    ================================================ FILE: test/fb2/basic.markdown ================================================ # Top-level title ## Section ### Subsection This *emphasized* **strong** `verbatim` markdown. See this [link](http://example.com/). Ordered list: 1. one 1. two 1. three > Blockquote > is > for > citatons. Code block is for code. ~~Strikeout~~ is Pandoc's extension. Superscript and subscripts too: H~2~O is a liquid[^1]. 2^10^ is 1024. Math is another Pandoc extension: $E = m c^2$. [^1]: Sometimes. ================================================ FILE: test/fb2/images-embedded.fb2 ================================================ unrecognisedpandoc<p />
    This image was embedded using data URI scheme

    This image was embedded using data URI scheme

    iVBORw0KGgoAAAANSUhEUgAAADAAAAAgCAIAAADbtmxLAAABmGlDQ1BpY2MAAHjapdG/axMBGMbxTy4tldJSwSAiHW4ootKCqIOrVShIkRIrJNUluUvaQi4Nd1dEXAQHF4cOXVRcLOLirJv4BygIggqCi7sUBRcpcbiDgtBBfOGF5/315eV9qR7qRUk2EpL087S+MB82mivh2BeBcUcddrwVZYOLS0uLDrRfH1Xgw1wvSjL/ZpNxJ4uohFiKBmlOZYC7t/JBTmUXtWitFROMYDZtNFcIzqDWLvRl1FYL3UAtXa5fIughXC30A4TtQr9AGK2lCcFbzMRJPybYxWScxDFVGE16m1G5ZwUTnf71aziPaXUsYB4h2tjEOnrIMVfG/QJyAC/GtKvYKFlRqQe4jbTkrGKtZM+WvZvI0CnjbtnfKb1XMtBoroR//yzrnjtbbDRxhdFvw+HP04w9Zu/+cPj76XC4t0P1M2+29+c3trnwnerWfm7mCVP3ePl6P9d+xqstjn0dtNIWitMG3S4/njPZ5Mh7xm/8b734Z1m384nlOyy+4+EjTnSZunkyzsP1ft5J+63eKWT1hXn4AzDofghlJQBJAAAACXBIWXMAAAsSAAALEgHS3X78AAACInpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjahVRJtiQhCNx7ijpCMIhyHNPU9/oGffxeaNY3p/5VC5IAQkAhtL9/evh8Pp+PiwaNKZubAYDuugNcMH4ZIAN6A6ATo68kdAA8VQ1DkoEIq2EILiBiALIQDTISWvz3SSQNJHwnilWTGgC/ZMSa1Fc8TDznZH4rgWOtRrwQKGh8VyNZ8bAY9Ccj1EGXUI0JwNE3n3itxrzis7Sq1TgBiNMwesKo1TjfcdZqXBaiWRpLrcbbEjBLU63G9QGv1bit+CSKWi2W8+3QLDluF/wIBgBEaNQWNSjFzHj7/zgOP92EBap3v2BqlNi2pEbGVi0yBNwkcRPiTVxIJDVLRgJxEXGLRgAgLBASkiIIwlIEYgyBJCnC4lKExN6yGSW6SD961nvvQaxhBZq4rbptbX1HlJPSokN37t9m9957a5utDux7Xwk06WnWWOJ2yqgkPqW4e2urnmNPK0HMtq0Hkkc7ZbSXUleHiNMIoGy7r/ppEwAIV+Amv1rS/3ghgCz23ns+m/HrASdJMWT2chsBiS2z73fcLGd+3E8hZ05nQ81zzOW2n8Saj1VzwTMHZ+g6xcPg5ozLASM7Z/hl9kaPnPFQmrcyvm8lFKbrAQwAoegtYFy34rEXRSFP/qEo4tmQ0wywlwPyG5G/BJQXvF5wOR4k7m9HjlupR/y6Mp42RjhWxm+Oh99BvMrwD3UCiGvkpxuRAAAACXZwQWcAAAAwAAAAIACELJ4GAAALGklEQVRYw11YW48dV1b+1tq7qs6t+/TldPsSx3bbcRwncWY0A4LMCOYFXpgHJCR4QvwAnpAQj/wB/gR/ACR4QUKDECMUEjLOZew4zsRxuu122+52n9Pnfuqy9/p4qNNtD1tLpVKpap/vrMu31rdl/nAgIjSpF0kAJAEHGJwAAIQheqeARVqomCQZGUkjSYLmTAyAkCJiESRVNYQAaL3h8k3SzPj6MjEzsr7SqyoAcRABQItRVZ14kqifmNEkTZNiUZiZ934xm4WE3mva8GYGAA5UJ8YYIyCipiKk1ZuTBAhQBGZQFRKvTAgBaQBIeOekBqpa+0gBxFgAlmhqhixJzUKMRVEUx/3RWmel3z9xzrVX2+tuRb3WnrDKVFUEgKlCVWI055WMZ76AieipX0AYzYwQUZqZqJD0tKCqFJAxhKiqSZKooirj06fPMt/odlezhngFYar+0e7jxWLR6/Vc7ubzpNnM4BlDSDLvvQ8hnAYFIlCFRUDEjASgIGFGEcBIoSrMCFAVhgiKTxJPkgJVSeBBhKqaTCb5Ip4cD/efPP/g9q2soSvdTp7nAuv2Vk/2BoHFyWgRYxVjbDezRiMDMicNxqCqqhIZVZxZVK3zaRkgXYaMdeDMoMoapSpo8GSE1t6ihaCqPs3WV/VwPpxOp8fHRw++cUmq7XYzSbLV1W5vu3fw7Emn0xwcD+ZzF8vKQtlI09R7J6SXOldgEDEwAmdguMyYV/lEVWEkARExEkoVBxHWxeUSFefK6fTJ48dlme/sXN7a3vjVnf8NIQ6Hk1ajOZ+Nf/3ZZ9Uij0V+fedqM0v2nz5ut7LtCz1GC2WVJqmry1KgZKLqlbo0qLPanJrAVKgSRX/LPElxQF0smiDy6Ojo7pd3qXLjxo2r1y8fnwxORpN8Nt/u9fLF5OTFcRVKDWE0HJZF2O71IsOzZ0+e7D3tdDo7168miU9SJ6IhBIGD2KuKMpHaPwoFXkXq9Aak+/u//TsFRCEgEAljjFtbW+UiHPb765sbJ8M+Y9HtdAbDgWs0pSge7O1u9Tar+Xwwmy0m03I0XpgbjydZlk2n43armaVOJIqQDEIIIKBABK/iBYuAnT4HwJrx1CkhRosAIRBBo93ImunO25dv/eBmb231+rUrRTH/we1ba+udhw/uSoZGOzt38Vx/1D85folYLhazqpg3mj5ruNWVTqPhxcEsAOa8ihJiAhMEQVChCgXmhKcWT41O6IGq5kCjqPOTk9F4PF7rrlvKRtQHjx/d2Lk+mgy+evrwhzs3Xuzv0tmbb5z79LNPzm9utdorEoNkzPPx+x+812o10tR7DyCqUwCMQUUIUqm2JOolbb/GjmbwoDGS9GZBnYM4IS0E5+TChS1Nk7Z28uPhW29euvPRf2fN1pdffNE1vXnzxsGgf/B036duvbce5tXG2uZkfnL79q3t8+sA4Rws0CJIAOI8LADUusLqeC0rTl4RJgyg1Fe+fB7yUrxTQoRWVoExbWTjl6P5fD7dO6yIF4P+bDK/92T3hzffLWGffPq5c7K1uf7hj3+PrLqbqxffuDQc9VdWVtY31846l1CFqJlalk8gr/UxAIzLLrZkbVK5WHgHhFIYmS+K6ZhFkY/Hi/5gPOhLUzvnu73L59KVxjyffXH314vFIsbYaTXeuXFtcHx4sPd9S+3l84PMwTsyBrMgQhUIgrEUREWUU4O8uhcLdW45BsWpiVRgoSyYTxYn/ee7e9/f/2Z8eOwYm8pko4EktDvpzOZXL5z3Dd3b2/vi/r1G0795cat//GxrvWPTaW+lvdVdXUkTCaVagAUrF6wKVSAGxIAQJEZYiVjSSrBSq4SVWCFWKINYpQzK4FHmhqjOgbF/+Hx0dJyXtv/46ZuXtrrd1V6ruRhNJqPZlfbq5999/Dt/+LNf/tf/nEyH585t73338OrVS3E8Pnl+MJnOi7i4fG1no7cJ79Sh7vPISwCwZYCERN3TjQCFZFxGE3FJV97iXCGIKBZlcyV799zbIdhsNnvyaP/x3r6Z+Tx02i6Kv/7WO8bFX/3ZT37xyZ2yf9Ta2n73vZ1vf/Xlv338+R/97MOVjc63X99fbXduvfuONBJTAlDxiBGAWKQZSVn2BZ4lDYwAlDQzkF7LEmYQyUSTdkvTzNuiZLz25vlOt3N0cPTwxcH7W2/9+NbN0Uef3Pvs6w/+9KcraePb3+z+6NKF+cHLvvN/89d/2SQsTSdr3clwMh0M1nvr0ki4yI0iQgBSu8dYOwxm4BIEyTrTEY1mHrNpZD3KiMIhlLEokljKWueyytsX3ji/2rqzuzt4Obh95eLd3+ztf7Xv1e2sp8OXw73h8Od//FNNtIh5Rqx322tZWoWimo1c7hQi4mgBxtPpA3VBkXQ0M8BOh5VoS3D8j3+CACqsR4EYzUyIalqaCqCxyIsB/uFf//lPfv93R/2jf/z4m0Zmf/6j977af/wXH/7B6ubK+dVV2+g6D0Yzg4OoA0kRrYoiEXcarFfzq7IueJKsYZktAXl7MRURiARakiQ08xAACHCGybj/i3v3ZoNyDa3/vPPg59cuf/fi4GKjPRmH99cuHT5+Ouv3ipV5b2ve2lxRdfPJLMa4ttKxEEl68ZH52bh4xkDhbLI2OYNiBpL+cHfmnKvfq2nf+eWXi2L+6PgQh42nk+GVc1uf7h9+f1T8ZOftXz76flu7L07Gs7FttCYvknH74PiDt24kjWa/PyvLElupJ2OMZFXBROS3hnoz8sxBS9+YYemhrw9L55xCFCYSalgR7Ehy/+DFnChC0kq3m37j9nr2L7uPnrw8UBb//t2emGsnybW8OW9M3uhc+fbprNWW0Qwh6MwKAQGEEIhlpccYSVLl/wkPM4unqEj6+7MyUZeIpj6pRxOJjDGO5qNpbNLsWTiSyt/dex5mR1Ui+9Phxe72w+moI1mmViSbG7xQNNpHlrhKBnkkcbIwi5WZGegpgMYYq6oCoKoiamZAfB1TWCoq+o/2DxzEiU/TNHXeBCGEqqqKGKGIDPOFTedDY8x8M5HEIRlXi5D7TtrsVpv5cDbZyB69eL7a6IjIfD6NNBHJy1A3tcRJCOV4Nmq1OiqZUgFznonzZVWIECoqXjWpijJGevVipFkZqipYKt6JE1XXjj5tJHk+n41jptrtrifqptNpq9WaTMcqyXon9VlqxHgyi5GjWa6qeZ5DJcsyM6OomVm04WQwnY3XIGkCQGEhzktjORoN8yp3zqVpI0lSUNXB59OhiChE1RdaSK0XSTNqoSSbmQe8F8Bi4jRN00WRr3TY7jSzJAEAQZIkCBUQo1aqmtSTHmJkrGApQtu7lodKKXDQGCyaaJo4mgeAEEPM667sd59/dyoQpUYDUQBCpZiqqmrNIgCEIKlgWS1OTl4OYl9gRoo40VgrRhGpP0G9qMZgFieTKgaKOFXQgolaCE7MBOrgnEO0aKWfLI7rWRaAnC4ATv3ZpiLiVQFYoCmzLAuhHI5eMsInIBkNUXTZUAERAVlvFSrziZoZFxOaaK3HAJOw1K9ArQXKMl/MJ7Ld6Z0l/1m86qMCGEXEe7+EW0+g4iFljCHxLQGcWq0NUp/Ur52K+Vc3ohrrFrs8PzhlJgBiKt7MYoxVKIpiIRudtdc9QSxheTPnHIAYCaNzTlXNzKg+YWRQpgKQRjGIB8Pyb0BJnv1ikFoYOwBEBKAEqFQlaRalFtqkc440n3IZrNp7Z4LExMpgy4SAVBaFVp9zhBCIaBQnDqhPSQgKIAJPksal+AG8ogpRnQCIUVRFoSEE9QIaLNCpqiPFQUK0/wPxadi/ncvxsAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMS0wMi0yOFQwMjo1NTowMiswMTowMGbLlncAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTEtMDItMjhUMDI6NTU6MDIrMDE6MDAXli7LAAAAEXRFWHRqcGVnOmNvbG9yc3BhY2UAMix1VZ8AAAAgdEVYdGpwZWc6c2FtcGxpbmctZmFjdG9yADF4MSwxeDEsMXgx6ZX8cAAAAABJRU5ErkJggg==
    ================================================ FILE: test/fb2/images-embedded.html ================================================
    This image was embedded using data URI scheme

    This image was embedded using data URI scheme

    ================================================ FILE: test/fb2/images.fb2 ================================================ unrecognisedpandoc<p />

    This example test if Pandoc correctly embeds images into FictionBook.

    Small inline image: alt text a small PNG image.

    Paragraph image:

    alt text of a big JPEG image

    alt text of a big missing image

    A missing image inline: alt text of missing image.

    /9j/4AAQSkZJRgABAQEASABIAAD/4QOoRXhpZgAATU0AKgAAAAgAFgD+AAQAAAABAAAAAQEPAAIAAAAUAAABFgEQAAIAAAAUAAABKgESAAMAAAABAAEAAAExAAIAAAAdAAABPgEyAAIAAAAUAAABXEdGAAkAAAABAAAAAkdJAAkAAAABAAAAKIdpAAQAAAABAAACXMYSAAEAAAAEAQEAAMYTAAEAAAAEAQEAAMYUAAIAAAAMAAABcMYhAAoAAAAJAAABfMYiAAoAAAAJAAABxMYnAAUAAAADAAACDMYoAAUAAAADAAACJMYqAAoAAAABAAACPMYrAAUAAAABAAACRMYsAAUAAAABAAACTMYuAAUAAAABAAACVMZaAAMAAAABABEAAMZbAAMAAAABABUAAAAAAABQRU5UQVggICAgICAgICAgICAgAFBFTlRBWCBLMjBEICAgICAgICAAZGFya3RhYmxlIDAuNy4xKzkxM35nYTA5MzllYQAAMjAxMTowMjowNiAwNzoyOToxNgBQRU5UQVggSzIwRAAAAZM/AAEAAP//NuAAAQAA///jlgABAAD//2viAAEAAAABh0EAAQAAAABNLwABAAD//+62AAEAAAAAKd8AAQAAAAFHQAABAAAAASNbAAEAAP//py8AAQAA///Z7gABAAD//4X3AAEAAAABWGsAAQAAAAAZVgABAAD//9qsAAEAAAAAUBMAAQAAAACr2QABAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAEAAAABdAAAAQAAAAEAAAABAAAAAWX//4AAAAEAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAVgpoABQAAAAEAAANegp0ABQAAAAEAAANmiCIAAwAAAAEAAQAAiCcAAwAAAAEAyAAAkAMAAgAAABQAAANukAQAAgAAABQAAAOCkgQACgAAAAEAAAOWkgcAAwAAAAEABQAAkgkAAwAAAAEAEAAAkgoABQAAAAEAAAOeoAEAAwAAAAEAAQAAohcAAwAAAAEAAgAApAEAAwAAAAEAAAAApAIAAwAAAAEAAQAApAMAAwAAAAEAAAAApAUAAwAAAAEAhwAApAYAAwAAAAEAAAAApAgAAwAAAAEAAAAApAkAAwAAAAEAAAAApAoAAwAAAAEAAAAApAwAAwAAAAEAAwAAAAAAAAAAAAEAAAAyAAAAHAAAAAoyMDExOjAyOjA2IDA3OjI5OjE2ADIwMTE6MDI6MDYgMDc6Mjk6MTYAAAAACgAAAAoAAP/iAxhJQ0NfUFJPRklMRQABAQAAAwhsY21zBCAAAG1udHJSR0IgWFlaIAfbAAIACgAWABAAGmFjc3BBUFBMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMtbGNtcwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWRlc2MAAAEgAAAAUGNwcnQAAAFwAAAAgHd0cHQAAAHwAAAAFGNoYWQAAAIEAAAALHJYWVoAAAIwAAAAFGJYWVoAAAJEAAAAFGdYWVoAAAJYAAAAFHJUUkMAAAJsAAAAIGdUUkMAAAKMAAAAIGJUUkMAAAKsAAAAIGNocm0AAALMAAAAJGRtbmQAAALwAAAADWRtZGQAAAMAAAAABW1sdWMAAAAAAAAAAQAAAAxlblVTAAAANAAAABwAUgAAAEcAAABCAAAAIAAAAGIAAAB1AAAAaQAAAGwAAAB0AAAALQAAAGkAAABuAAAAAAAAbWx1YwAAAAAAAAABAAAADGVuVVMAAABkAAAAHABOAAAAbwAAACAAAABjAAAAbwAAAHAAAAB5AAAAcgAAAGkAAABnAAAAaAAAAHQAAAAsAAAAIAAAAHUAAABzAAAAZQAAACAAAABmAAAAcgAAAGUAAABlAAAAbAAAAHkAAAAAAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMSgAABeP///MqAAAHmwAA/Yf///ui///9owAAA9gAAMCUWFlaIAAAAAAAAG+UAAA47gAAA5BYWVogAAAAAAAAJJ0AAA+DAAC2vlhZWiAAAAAAAABipQAAt5AAABjecGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltwYXJhAAAAAAADAAAAAmZmAADypwAADVkAABPQAAAKW3BhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbY2hybQAAAAAAAwAAAACj1wAAVHsAAEzNAACZmgAAJmYAAA9cKGR0IGludGVybmFsKQAAAHNSR0IAAAAA/9sAQwAbEhQXFBEbFxYXHhwbIChCKyglJShROj0wQmBVZWRfVV1baniZgWpxkHNbXYW1hpCeo6utq2eAvMm6pseZqKuk/9sAQwEcHh4oIyhOKytOpG5dbqSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSk/8AAEQgAgADAAwERAAIRAQMRAf/EABkAAAMBAQEAAAAAAAAAAAAAAAEAAwQCBf/EADIQAAEDAwMDAQYGAgMAAAAAAAEAAhEDEiExE1EEQWEiQpFxI4GxFFLwMqEF0eEkYsH/xAAXAQEBAQEAAAAAAAAAAAAAAAAAAQID/8QAGxEBAQEBAAMBAAAAAAAAAAAAABEBEgIxIUH/2gAMAwEAAhEDEQA/APehabMIGEDCBhAwgYQMoATGdQgQ4GI7oCgEIFAoGEAVAhAoAgYQBAECigqNCwhRSiFBw54aEUWvDhIKDpERqGDMqqg6rIsbr2Co7Y4lrc50Ed8qC4cDjugMogoFAECgUUEAhAoAgEKhQBBoWAoFAHCfiqMtZxaCKhjz2VGShXLPS6QQ3ug1N6gbgaQZicaICKm48lkY0M6oOOoDGupkyWE5xp+iiAWupsYHD1EAADlFdF74ggNdOkIKNJLrSSecILtUBhAECgCBQBAqgIAgCBQaFlCgCDJ1FdzMNJB4wqrhlZrzZVBDoiI1QZOrpOkOa8ENabCRM+CqE1A1jS4EEDMHA+iDU53oaYDT2jUf5UGZ1V0xJudaSD7JDhn9cKjS2s0VxcC5wAjnKg5fdV6p0uAa0DPYKjRRe1lIxmO/KhCOpaHkGdYQXDge6AoGEAQKAIAgUAQBAEGhZQoFBCo2nock9lVQNA33tdLRm3Uqo46mi1/TuioSw5k+yeUEqDTUZ8zBJ26k89j+uQipvApN/Duabg2WPHcDsgl1Nz6FOrTa6bXH08RP+EGiqW0unNYGXO9cHXJwPsglcRSD3ujxMl0qi7XAUjL4GjQ6JKgpSBj0tdkyC46oNNJrvaIlBaFAoFAECiggCBKAIAqLrLJQcvzgIoNZGpKI5exuvqEdwUGd729O4vvDmu/cMSPKqslaqyk8vJ/49QWuH5D2093uQ9KPO70oeS4GnrGsDBQZqrrehaWEQabmEf8Aa0/eFUFkGmKlYkuHpYzsTpJ5RVWU3Bw26bQ4CCT7I+PPj7IK0KT3m4YaDgnNyg2tptmXep3JQdhoGigKAIFFBAoAgUAQBAFRZZQzCDlzgAgQ7X4IIPebvVFvCqpWhziXMf4kf4QQqspNllgdRcYgey49vH68Koy9NVrs6mpRAc5zRJDtXdp90IJdU67pOlptxc4N8yJE/wAomrUKdT8W8UTdTZ6GnWwd/wCZRWi5oI6ZrvDiOOPiUVrY4N0LwYjtCgowOH7iZQWBwiFQKoVFBAoAilAEAQBUVlZZAnCKy1XuktbBPHCKl+IcwAvY4azkH7FUVaWuALXYJmRlB2RTAFznE83FSpGbqdupSfFQ3EYBP/qtIxMu6nrBd8vqqTDkcgj+DJV/Eef1dRzXFjwWPbUJI4mMq4mvV6YGh07KDQQ97cN5Pcnx/pRrMEDp6QABugF1xzn7ZRcaadakykJdaJ0/0oq9PqGVDgzGAiRYOlEgygZQCVFMoFAJQMooSgZQCVR1KyhJkIOQwNGNUHDgACYM+EVnqUHXBzKQBPMEfUK1GZ+410VDafyDv8Ciqsp06jMummYwMZ8oRne/Z/selc4Sc0y8e0IxPmVfxncms398xv4qi4NBLhHxz3V8U8saemfuPcX66uz7m/AKa3mNvUPLg2lSaHVHic+yOSpienNDotl0l7nuOpHpn3JuqtUokmWvLTyM/dKho3NJbUJ8HlFaAUDKhAlCGUIEoplCBKBlCBKLAlCDcoQbkIbkI5JzPCEcl0unhCOKoY8RUAI4KpGDqKh6T1seXUnDLXajyr7SRz1RNb+vp1Wj1sAqN76Jns3LlYeurs6ur04ozI9+VrMjHltkaw5tB220es4lx08lZ9unr439I9tMRfcXZLjqSppy1NqBxQg3IQCQTlCCHQhDcoQLkWG5CBchDchAuRYFyENyEC5UiW8FeWoO8FJpB3gk0h3gkIBqgDukIx9RVvBjLQJE5WsxNZWl7hEkjTOR9VYyFB1nyKl8tGCDqO2EiePz5rA+KXW/KcCA4QSr+OW/PL49I7TAHON9QnLu/wCvCjtMxQVWgztyfphSKvT6m15Gg7JFahVBCkWODW+aMwISI73UindCQDdHKQO4kA3PKQDcSB3UgG6kA3UgG6rBlFUcrpHPsdzykOhvKQ6O4kOk6tUuFo96Q6RqOhoaDMJGenDazGttkDukOk6lQ1HE0yL2SQQYlIm+VY5NSvI18qOdutTasumJI/hWN55K0nuyBE95KRrPJelJ/f7kjXTQHwNUi9OTU+Z9EidO9xIvQbiQ6O55SHTk1Y7pDo7iQ6O4kOg3PKQ6DcKQ6O4eUh0BqFInTFkFbcBDnA4JQd3P/MULoEuOpJQc2lBzUkDRBwAXGFEFzYHhFZSbapjGVn9RSnnAKuC4vGkiOFpape8QbihdHdf+YoXXO48u/cULpudyUKEnlCg57gNShXLajicklCu7jOqFC9w7lCg6s8DBKhdD8Q8nVC6O+7lC6d1x9pUup7zuVmhFZ3KtB33cpSnfdylKd535ilKTWJ1KUEVY0SgGpISjNVy6eVnUVoODW6K4K7ytUd5KBveEoG4JnKUdbyUDdSgGpIhKgMdalB3AlU3jlKOS4FKjlQHEaqgY5QQuKwG8oDeVQ3lShvPCoN6BvShvQckyoC10CFR1egb0DegbwgbwgbggbvKBvHKBuQN/lKG/ygF3lAl3lKOHunRQCFAIKA5QDKBQGUCqFAoplEMoplEFAoFAJQKBQP0QKBQBAoFB/9k=iVBORw0KGgoAAAANSUhEUgAAADAAAAAgCAIAAADbtmxLAAABmGlDQ1BpY2MAAHjapdG/axMBGMbxTy4tldJSwSAiHW4ootKCqIOrVShIkRIrJNUluUvaQi4Nd1dEXAQHF4cOXVRcLOLirJv4BygIggqCi7sUBRcpcbiDgtBBfOGF5/315eV9qR7qRUk2EpL087S+MB82mivh2BeBcUcddrwVZYOLS0uLDrRfH1Xgw1wvSjL/ZpNxJ4uohFiKBmlOZYC7t/JBTmUXtWitFROMYDZtNFcIzqDWLvRl1FYL3UAtXa5fIughXC30A4TtQr9AGK2lCcFbzMRJPybYxWScxDFVGE16m1G5ZwUTnf71aziPaXUsYB4h2tjEOnrIMVfG/QJyAC/GtKvYKFlRqQe4jbTkrGKtZM+WvZvI0CnjbtnfKb1XMtBoroR//yzrnjtbbDRxhdFvw+HP04w9Zu/+cPj76XC4t0P1M2+29+c3trnwnerWfm7mCVP3ePl6P9d+xqstjn0dtNIWitMG3S4/njPZ5Mh7xm/8b734Z1m384nlOyy+4+EjTnSZunkyzsP1ft5J+63eKWT1hXn4AzDofghlJQBJAAAACXBIWXMAAAsSAAALEgHS3X78AAACInpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjahVRJtiQhCNx7ijpCMIhyHNPU9/oGffxeaNY3p/5VC5IAQkAhtL9/evh8Pp+PiwaNKZubAYDuugNcMH4ZIAN6A6ATo68kdAA8VQ1DkoEIq2EILiBiALIQDTISWvz3SSQNJHwnilWTGgC/ZMSa1Fc8TDznZH4rgWOtRrwQKGh8VyNZ8bAY9Ccj1EGXUI0JwNE3n3itxrzis7Sq1TgBiNMwesKo1TjfcdZqXBaiWRpLrcbbEjBLU63G9QGv1bit+CSKWi2W8+3QLDluF/wIBgBEaNQWNSjFzHj7/zgOP92EBap3v2BqlNi2pEbGVi0yBNwkcRPiTVxIJDVLRgJxEXGLRgAgLBASkiIIwlIEYgyBJCnC4lKExN6yGSW6SD961nvvQaxhBZq4rbptbX1HlJPSokN37t9m9957a5utDux7Xwk06WnWWOJ2yqgkPqW4e2urnmNPK0HMtq0Hkkc7ZbSXUleHiNMIoGy7r/ppEwAIV+Amv1rS/3ghgCz23ns+m/HrASdJMWT2chsBiS2z73fcLGd+3E8hZ05nQ81zzOW2n8Saj1VzwTMHZ+g6xcPg5ozLASM7Z/hl9kaPnPFQmrcyvm8lFKbrAQwAoegtYFy34rEXRSFP/qEo4tmQ0wywlwPyG5G/BJQXvF5wOR4k7m9HjlupR/y6Mp42RjhWxm+Oh99BvMrwD3UCiGvkpxuRAAAACXZwQWcAAAAwAAAAIACELJ4GAAALGklEQVRYw11YW48dV1b+1tq7qs6t+/TldPsSx3bbcRwncWY0A4LMCOYFXpgHJCR4QvwAnpAQj/wB/gR/ACR4QUKDECMUEjLOZew4zsRxuu122+52n9Pnfuqy9/p4qNNtD1tLpVKpap/vrMu31rdl/nAgIjSpF0kAJAEHGJwAAIQheqeARVqomCQZGUkjSYLmTAyAkCJiESRVNYQAaL3h8k3SzPj6MjEzsr7SqyoAcRABQItRVZ14kqifmNEkTZNiUZiZ934xm4WE3mva8GYGAA5UJ8YYIyCipiKk1ZuTBAhQBGZQFRKvTAgBaQBIeOekBqpa+0gBxFgAlmhqhixJzUKMRVEUx/3RWmel3z9xzrVX2+tuRb3WnrDKVFUEgKlCVWI055WMZ76AieipX0AYzYwQUZqZqJD0tKCqFJAxhKiqSZKooirj06fPMt/odlezhngFYar+0e7jxWLR6/Vc7ubzpNnM4BlDSDLvvQ8hnAYFIlCFRUDEjASgIGFGEcBIoSrMCFAVhgiKTxJPkgJVSeBBhKqaTCb5Ip4cD/efPP/g9q2soSvdTp7nAuv2Vk/2BoHFyWgRYxVjbDezRiMDMicNxqCqqhIZVZxZVK3zaRkgXYaMdeDMoMoapSpo8GSE1t6ihaCqPs3WV/VwPpxOp8fHRw++cUmq7XYzSbLV1W5vu3fw7Emn0xwcD+ZzF8vKQtlI09R7J6SXOldgEDEwAmdguMyYV/lEVWEkARExEkoVBxHWxeUSFefK6fTJ48dlme/sXN7a3vjVnf8NIQ6Hk1ajOZ+Nf/3ZZ9Uij0V+fedqM0v2nz5ut7LtCz1GC2WVJqmry1KgZKLqlbo0qLPanJrAVKgSRX/LPElxQF0smiDy6Ojo7pd3qXLjxo2r1y8fnwxORpN8Nt/u9fLF5OTFcRVKDWE0HJZF2O71IsOzZ0+e7D3tdDo7168miU9SJ6IhBIGD2KuKMpHaPwoFXkXq9Aak+/u//TsFRCEgEAljjFtbW+UiHPb765sbJ8M+Y9HtdAbDgWs0pSge7O1u9Tar+Xwwmy0m03I0XpgbjydZlk2n43armaVOJIqQDEIIIKBABK/iBYuAnT4HwJrx1CkhRosAIRBBo93ImunO25dv/eBmb231+rUrRTH/we1ba+udhw/uSoZGOzt38Vx/1D85folYLhazqpg3mj5ruNWVTqPhxcEsAOa8ihJiAhMEQVChCgXmhKcWT41O6IGq5kCjqPOTk9F4PF7rrlvKRtQHjx/d2Lk+mgy+evrwhzs3Xuzv0tmbb5z79LNPzm9utdorEoNkzPPx+x+812o10tR7DyCqUwCMQUUIUqm2JOolbb/GjmbwoDGS9GZBnYM4IS0E5+TChS1Nk7Z28uPhW29euvPRf2fN1pdffNE1vXnzxsGgf/B036duvbce5tXG2uZkfnL79q3t8+sA4Rws0CJIAOI8LADUusLqeC0rTl4RJgyg1Fe+fB7yUrxTQoRWVoExbWTjl6P5fD7dO6yIF4P+bDK/92T3hzffLWGffPq5c7K1uf7hj3+PrLqbqxffuDQc9VdWVtY31846l1CFqJlalk8gr/UxAIzLLrZkbVK5WHgHhFIYmS+K6ZhFkY/Hi/5gPOhLUzvnu73L59KVxjyffXH314vFIsbYaTXeuXFtcHx4sPd9S+3l84PMwTsyBrMgQhUIgrEUREWUU4O8uhcLdW45BsWpiVRgoSyYTxYn/ee7e9/f/2Z8eOwYm8pko4EktDvpzOZXL5z3Dd3b2/vi/r1G0795cat//GxrvWPTaW+lvdVdXUkTCaVagAUrF6wKVSAGxIAQJEZYiVjSSrBSq4SVWCFWKINYpQzK4FHmhqjOgbF/+Hx0dJyXtv/46ZuXtrrd1V6ruRhNJqPZlfbq5999/Dt/+LNf/tf/nEyH585t73338OrVS3E8Pnl+MJnOi7i4fG1no7cJ79Sh7vPISwCwZYCERN3TjQCFZFxGE3FJV97iXCGIKBZlcyV799zbIdhsNnvyaP/x3r6Z+Tx02i6Kv/7WO8bFX/3ZT37xyZ2yf9Ta2n73vZ1vf/Xlv338+R/97MOVjc63X99fbXduvfuONBJTAlDxiBGAWKQZSVn2BZ4lDYwAlDQzkF7LEmYQyUSTdkvTzNuiZLz25vlOt3N0cPTwxcH7W2/9+NbN0Uef3Pvs6w/+9KcraePb3+z+6NKF+cHLvvN/89d/2SQsTSdr3clwMh0M1nvr0ki4yI0iQgBSu8dYOwxm4BIEyTrTEY1mHrNpZD3KiMIhlLEokljKWueyytsX3ji/2rqzuzt4Obh95eLd3+ztf7Xv1e2sp8OXw73h8Od//FNNtIh5Rqx322tZWoWimo1c7hQi4mgBxtPpA3VBkXQ0M8BOh5VoS3D8j3+CACqsR4EYzUyIalqaCqCxyIsB/uFf//lPfv93R/2jf/z4m0Zmf/6j977af/wXH/7B6ubK+dVV2+g6D0Yzg4OoA0kRrYoiEXcarFfzq7IueJKsYZktAXl7MRURiARakiQ08xAACHCGybj/i3v3ZoNyDa3/vPPg59cuf/fi4GKjPRmH99cuHT5+Ouv3ipV5b2ve2lxRdfPJLMa4ttKxEEl68ZH52bh4xkDhbLI2OYNiBpL+cHfmnKvfq2nf+eWXi2L+6PgQh42nk+GVc1uf7h9+f1T8ZOftXz76flu7L07Gs7FttCYvknH74PiDt24kjWa/PyvLElupJ2OMZFXBROS3hnoz8sxBS9+YYemhrw9L55xCFCYSalgR7Ehy/+DFnChC0kq3m37j9nr2L7uPnrw8UBb//t2emGsnybW8OW9M3uhc+fbprNWW0Qwh6MwKAQGEEIhlpccYSVLl/wkPM4unqEj6+7MyUZeIpj6pRxOJjDGO5qNpbNLsWTiSyt/dex5mR1Ui+9Phxe72w+moI1mmViSbG7xQNNpHlrhKBnkkcbIwi5WZGegpgMYYq6oCoKoiamZAfB1TWCoq+o/2DxzEiU/TNHXeBCGEqqqKGKGIDPOFTedDY8x8M5HEIRlXi5D7TtrsVpv5cDbZyB69eL7a6IjIfD6NNBHJy1A3tcRJCOV4Nmq1OiqZUgFznonzZVWIECoqXjWpijJGevVipFkZqipYKt6JE1XXjj5tJHk+n41jptrtrifqptNpq9WaTMcqyXon9VlqxHgyi5GjWa6qeZ5DJcsyM6OomVm04WQwnY3XIGkCQGEhzktjORoN8yp3zqVpI0lSUNXB59OhiChE1RdaSK0XSTNqoSSbmQe8F8Bi4jRN00WRr3TY7jSzJAEAQZIkCBUQo1aqmtSTHmJkrGApQtu7lodKKXDQGCyaaJo4mgeAEEPM667sd59/dyoQpUYDUQBCpZiqqmrNIgCEIKlgWS1OTl4OYl9gRoo40VgrRhGpP0G9qMZgFieTKgaKOFXQgolaCE7MBOrgnEO0aKWfLI7rWRaAnC4ATv3ZpiLiVQFYoCmzLAuhHI5eMsInIBkNUXTZUAERAVlvFSrziZoZFxOaaK3HAJOw1K9ArQXKMl/MJ7Ld6Z0l/1m86qMCGEXEe7+EW0+g4iFljCHxLQGcWq0NUp/Ur52K+Vc3ohrrFrs8PzhlJgBiKt7MYoxVKIpiIRudtdc9QSxheTPnHIAYCaNzTlXNzKg+YWRQpgKQRjGIB8Pyb0BJnv1ikFoYOwBEBKAEqFQlaRalFtqkc440n3IZrNp7Z4LExMpgy4SAVBaFVp9zhBCIaBQnDqhPSQgKIAJPksal+AG8ogpRnQCIUVRFoSEE9QIaLNCpqiPFQUK0/wPxadi/ncvxsAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMS0wMi0yOFQwMjo1NTowMiswMTowMGbLlncAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTEtMDItMjhUMDI6NTU6MDIrMDE6MDAXli7LAAAAEXRFWHRqcGVnOmNvbG9yc3BhY2UAMix1VZ8AAAAgdEVYdGpwZWc6c2FtcGxpbmctZmFjdG9yADF4MSwxeDEsMXgx6ZX8cAAAAABJRU5ErkJggg==
    ================================================ FILE: test/fb2/images.markdown ================================================ This example test if Pandoc correctly embeds images into FictionBook. Small inline image: ![alt text a small PNG image][inline-image]. Paragraph image: ![alt text of a big JPEG image](fb2/test.jpg "image title text") ![alt text of a big missing image](missing.jpg) A missing image inline: ![alt text of missing image](missing.jpg). [inline-image]: fb2/test-small.png ================================================ FILE: test/fb2/math.fb2 ================================================ unrecognisedpandoc<p />

    List math:

    • E = m c^2

    • A = \pi r^2

    Inline math: x=\frac{-b \pm \sqrt {b^2-4ac}}{2a}.

    Display math:

    \int_a^b \! f(x)\,dx = F(b) - F(a).
    ================================================ FILE: test/fb2/math.markdown ================================================ List math: - $E = m c^2$ - $A = \pi r^2$ Inline math: $x=\frac{-b \pm \sqrt {b^2-4ac}}{2a}$. Display math: $$\int_a^b \! f(x)\,dx = F(b) - F(a).$$ ================================================ FILE: test/fb2/meta.fb2 ================================================ unrecognisedBook title

    This is the abstract.

    It consists of two paragraphs.

    pandoc
    <p>Book title</p>
    ================================================ FILE: test/fb2/meta.markdown ================================================ --- title: Book title abstract: | This is the abstract. It consists of two paragraphs. --- ================================================ FILE: test/fb2/reader/emphasis.fb2 ================================================

    Plain, strong, emphasis, strong emphasis, emphasized strong.

    Strikethrough: deleted

    Subscript and superscript

    Some code

    ================================================ FILE: test/fb2/reader/emphasis.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Div ( "" , [ "section" ] , [] ) [ Para [ Str "Plain," , Space , Strong [ Str "strong" ] , Str "," , Space , Emph [ Str "emphasis" ] , Str "," , Space , Strong [ Emph [ Str "strong" , Space , Str "emphasis" ] ] , Str "," , Space , Emph [ Strong [ Str "emphasized" , Space , Str "strong" ] ] , Str "." ] , Para [ Str "Strikethrough:" , Space , Strikeout [ Str "deleted" ] ] , Para [ Subscript [ Str "Subscript" ] , Space , Str "and" , Space , Superscript [ Str "superscript" ] ] , Para [ Str "Some" , Space , Code ( "" , [] , [] ) "code" ] ] ] ================================================ FILE: test/fb2/reader/epigraph.fb2 ================================================

    Body epigraph

    Section epigraph

    Subsection epigraph

    ================================================ FILE: test/fb2/reader/epigraph.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Div ( "" , [ "epigraph" ] , [] ) [ Para [ Str "Body" , Space , Str "epigraph" ] ] , Div ( "" , [ "section" ] , [] ) [ Div ( "" , [ "epigraph" ] , [] ) [ Para [ Str "Section" , Space , Str "epigraph" ] ] , Div ( "" , [ "section" ] , [] ) [ Div ( "" , [ "epigraph" ] , [] ) [ Para [ Str "Subsection" , Space , Str "epigraph" ] ] ] ] ] ================================================ FILE: test/fb2/reader/meta.fb2 ================================================ First Middle Last Another Author Book title

    Book annotation

    Second paragraph of book annotation

    foo, bar, baz 2018
    <p>Body title</p>
    ================================================ FILE: test/fb2/reader/meta.native ================================================ Pandoc Meta { unMeta = fromList [ ( "abstract" , MetaBlocks [ Para [ Str "Book" , Space , Str "annotation" ] , Para [ Str "Second" , Space , Str "paragraph" , Space , Str "of" , Space , Str "book" , Space , Str "annotation" ] ] ) , ( "author" , MetaList [ MetaInlines [ Str "First" , Space , Str "Middle" , Space , Str "Last" ] , MetaInlines [ Str "Another" , Space , Str "Author" ] ] ) , ( "date" , MetaInlines [ Str "2018" ] ) , ( "keywords" , MetaList [ MetaString "foo" , MetaString "bar" , MetaString "baz" ] ) , ( "title" , MetaInlines [ Str "Book" , Space , Str "title" ] ) ] } [ Header 1 ( "" , [] , [] ) [ Str "Body" , Space , Str "title" ] ] ================================================ FILE: test/fb2/reader/notes.fb2 ================================================

    Note 1.

    Second note 2.

    <p>1</p>

    Note contents

    <p>2</p>

    Second note contents.

    ================================================ FILE: test/fb2/reader/notes.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Div ( "" , [ "section" ] , [] ) [ Para [ Str "Note" , Space , Note [ Para [ Str "Note" , Space , Str "contents" ] ] , Str "." ] , Para [ Str "Second" , Space , Str "note" , Space , Note [ Para [ Str "Second" , Space , Str "note" , Space , Str "contents." ] ] , Str "." ] ] ] ================================================ FILE: test/fb2/reader/poem.fb2 ================================================
    <p>Poem title</p>

    Poem epigraph

    Subtitle <p>First stanza title</p> Verse More verse One more stanza Author April 2018
    ================================================ FILE: test/fb2/reader/poem.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Div ( "" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Poem" , Space , Str "title" ] , Div ( "" , [ "epigraph" ] , [] ) [ Para [ Str "Poem" , Space , Str "epigraph" ] ] , Header 2 ( "" , [ "unnumbered" ] , [] ) [ Str "Subtitle" ] , Header 2 ( "" , [] , [] ) [ Str "First" , Space , Str "stanza" , Space , Str "title" ] , LineBlock [ [ Str "Verse" ] , [ Emph [ Str "More" ] , Space , Str "verse" ] ] , LineBlock [ [ Str "One" , Space , Str "more" , Space , Str "stanza" ] ] , Para [ Str "Author" ] , Para [ Str "April" , Space , Str "2018" ] ] ] ================================================ FILE: test/fb2/reader/titles.fb2 ================================================ <p>Body title</p>
    <p>Section title</p>
    <p>Subsection title</p> <p>with multiple paragraphs</p>
    <p>Another subsection title</p>
    ================================================ FILE: test/fb2/reader/titles.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "" , [] , [] ) [ Str "Body" , Space , Str "title" ] , Div ( "" , [ "section" ] , [] ) [ Header 2 ( "" , [] , [] ) [ Str "Section" , Space , Str "title" ] , Div ( "" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "Subsection" , Space , Str "title" , LineBreak , Str "with" , Space , Str "multiple" , Space , Str "paragraphs" ] ] , Div ( "" , [ "section" ] , [] ) [ Header 3 ( "" , [] , [] ) [ Str "Another" , Space , Str "subsection" , Space , Str "title" ] ] ] ] ================================================ FILE: test/fb2/titles.fb2 ================================================ unrecognisedpandoc<p />
    <p>Simple title</p>

    This example tests FictionBook titles.

    <p><emphasis>Emphasized</emphasis> <strong>Strong</strong> Title</p>
    ================================================ FILE: test/fb2/titles.markdown ================================================ # Simple title This example tests FictionBook titles. # *Emphasized* **Strong** Title ================================================ FILE: test/haddock-reader.haddock ================================================ This file tests the Pandoc reader for Haddock. We've borrowed examples from Haddock's documentation: . The following characters have special meanings in Haddock, \/, \', \`, \", \@, \<, so they must be escaped. \* This is a paragraph, not a list item. \> This sentence is not code. \>\>\> This is not an example. The references λ, λ and λ all represent the lower-case letter lambda. This is a code block: > map :: (a -> b) -> [a] -> [b] > map _ [] = [] > map f (x:xs) = f x : map f xs This is another code block: @ f x = x + x. The \@...\@ code block /interprets markup normally/. "Module.Foo" \"Hello World\" @ Haddock supports REPL examples: >>> fib 10 55 >>> putStrLn "foo\nbar" foo bar That was /really cool/! I had no idea @fib 10 = 55@. This module defines the type 'T'. The identifier 'M.T' is not in scope I don't have to escape my apostrophes; great, isn't it? This is a reference to the "Foo" module. This is a bulleted list: * first item * second item This is an enumerated list: (1) first item 2. second item This is a definition list: [@foo@] The description of @foo@. [@bar@] The description of @bar@. Here is a link: is a fun language! ================================================ FILE: test/haddock-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Str "This" , Space , Str "file" , Space , Str "tests" , Space , Str "the" , Space , Str "Pandoc" , Space , Str "reader" , Space , Str "for" , Space , Str "Haddock." , SoftBreak , Str "We've" , Space , Str "borrowed" , Space , Str "examples" , Space , Str "from" , Space , Str "Haddock's" , Space , Str "documentation:" , Space , Link ( "" , [] , [] ) [ Str "http://www.haskell.org/haddock/doc/html/ch03s08.html" ] ( "http://www.haskell.org/haddock/doc/html/ch03s08.html" , "http://www.haskell.org/haddock/doc/html/ch03s08.html" ) , Str "." ] , Para [ Str "The" , Space , Str "following" , Space , Str "characters" , Space , Str "have" , Space , Str "special" , Space , Str "meanings" , Space , Str "in" , Space , Str "Haddock," , Space , Str "/," , Space , Str "'," , Space , Str "`," , Space , Str "\"," , Space , Str "@," , Space , Str "<," , Space , Str "so" , Space , Str "they" , Space , Str "must" , Space , Str "be" , Space , Str "escaped." ] , Para [ Str "*" , Space , Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "paragraph," , Space , Str "not" , Space , Str "a" , Space , Str "list" , Space , Str "item." , SoftBreak , Str ">" , Space , Str "This" , Space , Str "sentence" , Space , Str "is" , Space , Str "not" , Space , Str "code." , SoftBreak , Str ">>>" , Space , Str "This" , Space , Str "is" , Space , Str "not" , Space , Str "an" , Space , Str "example." ] , Para [ Str "The" , Space , Str "references" , Space , Str "\955," , Space , Str "\955" , Space , Str "and" , Space , Str "\955" , Space , Str "all" , Space , Str "represent" , Space , Str "the" , Space , Str "lower-case" , Space , Str "letter" , Space , Str "lambda." ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "code" , Space , Str "block:" ] , CodeBlock ( "" , [] , [] ) "map :: (a -> b) -> [a] -> [b]\nmap _ [] = []\nmap f (x:xs) = f x : map f xs" , Para [ Str "This" , Space , Str "is" , Space , Str "another" , Space , Str "code" , Space , Str "block:" ] , Para [ Code ( "" , [] , [] ) "f x = x + x." , LineBreak , Code ( "" , [] , [] ) "The @...@ code block " , Emph [ Code ( "" , [] , [] ) "interprets markup normally" ] , Code ( "" , [] , [] ) "." , Code ( "" , [ "haskell" , "module" ] , [] ) "Module.Foo" , Code ( "" , [] , [] ) "" , LineBreak , Code ( "" , [] , [] ) "\"Hello World\"" ] , Para [ Str "Haddock" , Space , Str "supports" , Space , Str "REPL" , Space , Str "examples:" ] , Para [ Code ( "" , [ "prompt" ] , [] ) ">>>" , Space , Code ( "" , [ "haskell" , "expr" ] , [] ) "fib 10" , LineBreak , Code ( "" , [ "result" ] , [] ) "55" ] , Para [ Code ( "" , [ "prompt" ] , [] ) ">>>" , Space , Code ( "" , [ "haskell" , "expr" ] , [] ) "putStrLn \"foo\\nbar\"" , LineBreak , Code ( "" , [ "result" ] , [] ) "foo" , LineBreak , Code ( "" , [ "result" ] , [] ) "bar" ] , Para [ Str "That" , Space , Str "was" , Space , Emph [ Str "really" , Space , Str "cool" ] , Str "!" , SoftBreak , Str "I" , Space , Str "had" , Space , Str "no" , Space , Str "idea" , Space , Code ( "" , [] , [] ) "fib 10 = 55" , Str "." ] , Para [ Str "This" , Space , Str "module" , Space , Str "defines" , Space , Str "the" , Space , Str "type" , Space , Code ( "" , [ "haskell" , "identifier" ] , [] ) "T" , Str "." , SoftBreak , Str "The" , Space , Str "identifier" , Space , Code ( "" , [ "haskell" , "identifier" ] , [] ) "M.T" , Space , Str "is" , Space , Str "not" , Space , Str "in" , Space , Str "scope" , SoftBreak , Str "I" , Space , Str "don't" , Space , Str "have" , Space , Str "to" , Space , Str "escape" , Space , Str "my" , Space , Str "apostrophes;" , Space , Str "great," , Space , Str "isn't" , Space , Str "it?" , SoftBreak , Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "reference" , Space , Str "to" , Space , Str "the" , Space , Code ( "" , [ "haskell" , "module" ] , [] ) "Foo" , Space , Str "module." ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "bulleted" , Space , Str "list:" ] , BulletList [ [ Para [ Str "first" , Space , Str "item" ] ] , [ Para [ Str "second" , Space , Str "item" ] ] ] , Para [ Str "This" , Space , Str "is" , Space , Str "an" , Space , Str "enumerated" , Space , Str "list:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "first" , Space , Str "item" ] ] , [ Para [ Str "second" , Space , Str "item" ] ] ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "definition" , Space , Str "list:" ] , DefinitionList [ ( [ Code ( "" , [] , [] ) "foo" ] , [ [ Para [ Str "The" , Space , Str "description" , Space , Str "of" , Space , Code ( "" , [] , [] ) "foo" , Str "." ] ] ] ) , ( [ Code ( "" , [] , [] ) "bar" ] , [ [ Para [ Str "The" , Space , Str "description" , Space , Str "of" , Space , Code ( "" , [] , [] ) "bar" , Str "." ] ] ] ) ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "link:" , Space , Link ( "" , [] , [] ) [ Str "http://haskell.org" ] ( "http://haskell.org" , "http://haskell.org" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Haskell" ] ( "http://haskell.org" , "http://haskell.org" ) , Space , Str "is" , Space , Str "a" , Space , Str "fun" , Space , Str "language!" ] , Para [ Link ( "" , [] , [] ) [ Str "Click" , Space , Str "Here!" ] ( "http://example.com" , "http://example.com" ) ] ] ================================================ FILE: test/html-reader.html ================================================ Pandoc Test Suite

    Pandoc Test Suite

    This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite.


    Headers

    Level 2 with an embedded link

    Level 3 with emphasis

    Level 4

    Level 5

    Level 1

    Level 2 with emphasis

    Level 3

    with no blank line

    Level 2

    with no blank line


    Paragraphs

    Here's a regular paragraph.

    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 a list item.

    Here's one with a bullet. * criminey.

    There should be a hard line break
    here.


    Block Quotes

    E-mail style:

    This is a block quote. It is pretty short.

    Code in a block quote:

    sub status {
        print "working";
    }
    

    A list:

    1. item one
    2. item two

    Nested block quotes:

    nested

    nested

    This should not be a block quote: 2 > 1.

    Box-style:

    Example:

    sub status {
        print "working";
    }
    
    1. do laundry
    2. take out the trash

    Here's a nested one:

    Joe said:

    Don't quote me.

    And a following paragraph.


    Inline quotes

    Normal text but then a inline quote.

    Missing a cite attribute means its just normal text


    Code Blocks

    Code:

    ---- (should be four hyphens)
    
    sub status {
        print "working";
    }
    
    this code block is indented by one tab
    

    And:

        this code block is indented by two tabs
    
    These should not be escaped:  \$ \\ \> \[ \{
    

    Lists

    Unordered

    Asterisks tight:

    • asterisk 1
    • asterisk 2
    • asterisk 3

    Asterisks loose:

    • asterisk 1

    • asterisk 2

    • asterisk 3

    Pluses tight:

    • Plus 1
    • Plus 2
    • Plus 3

    Pluses loose:

    • Plus 1

    • Plus 2

    • Plus 3

    Minuses tight:

    • Minus 1
    • Minus 2
    • Minus 3

    Minuses loose:

    • Minus 1

    • Minus 2

    • Minus 3

    Ordered

    Tight:

    1. First
    2. Second
    3. Third

    and:

    1. One
    2. Two
    3. Three

    Loose using tabs:

    1. First

    2. Second

    3. Third

    and using spaces:

    1. One

    2. Two

    3. Three

    Multiple paragraphs:

    1. Item 1, graf one.

      Item 1. graf two. The quick brown fox jumped over the lazy dog's back.

    2. Item 2.

    3. Item 3.

    List styles:

                Nested

                • Tab
                  • Tab
                    • Tab

                Here's another:

                1. First
                2. Second:
                  • Fee
                  • Fie
                  • Foe
                3. Third

                Same thing but with paragraphs:

                1. First

                2. Second:

                  • Fee
                  • Fie
                  • Foe
                3. Third

                Tabs and spaces

                • this is a list item indented with tabs

                • this is a list item indented with spaces

                  • this is an example list item indented with tabs

                  • this is an example list item indented with spaces

                Fancy list markers

                1. begins with 2
                2. and now 3

                  with a continuation

                  1. sublist with roman numerals, starting with 4
                  2. more items
                    1. a subsublist
                    2. a subsublist

                Nesting:

                1. Upper Alpha
                  1. Upper Roman.
                    1. Decimal start with 6
                      1. Lower alpha with paren

                Autonumbering:

                1. Autonumber.
                2. More.
                  1. Nested.

                Definition

                Violin
                Stringed musical instrument.
                Torture device.
                Cello
                Violoncello
                Low-voiced stringed instrument.

                Inline Markup

                This is emphasized, and so is this.

                This is strong, and so is this.

                Empty and .

                An emphasized link.

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is small caps.

                These are all underlined: foo and bar.

                These are all strikethrough: foo, bar, and baz.


                Smart quotes, ellipses, dashes

                "Hello," said the spider. "'Shelob' is my name."

                'A', 'B', and 'C' are letters.

                'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'

                'He said, "I want to go."' Were you alive in the 70's?

                Here is some quoted 'code' and a "quoted link".

                Some dashes: one---two --- three--four -- five.

                Dashes between numbers: 5-7, 255-66, 1987-1999.

                Ellipses...and. . .and . . . .


                LaTeX

                • \cite[22-23]{smith.1899}
                • \doublespacing
                • $2+2=4$
                • $x \in y$
                • $\alpha \wedge \omega$
                • $223$
                • $p$-Tree
                • $\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$
                • Here's one that has a line break in it: $\alpha + \omega \times x^2$.

                These shouldn't be math:

                • To get the famous equation, write $e = mc^2$.
                • $22,000 is a lot of money. So is $34,000. (It worked if "lot" is emphasized.)
                • Escaped $: $73 this should be emphasized 23$.

                Here's a LaTeX table:

                \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular}


                Special Characters

                Here is some unicode:

                • I hat: Î
                • o umlaut: ö
                • section: §
                • set membership: ∈
                • copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -


                Links

                Explicit

                Just a URL.

                URL and title.

                URL and title.

                URL and title.

                URL and title

                URL and title

                Email link (nobody [at] nowhere.net)

                Empty.

                Reference

                Foo bar.

                Foo bar.

                Foo bar.

                With embedded [brackets].

                b by itself should be a link.

                Indented once.

                Indented twice.

                Indented thrice.

                This should [not] be a link.

                [not]: /url
                

                Foo bar.

                Foo biz.

                With ampersands

                Here's a link with an ampersand in the URL.

                Here's a link with an amersand in the link text: AT&T.

                Here's an inline link.

                Here's an inline link in pointy braces.

                Autolinks

                With an ampersand: http://example.com/?foo=1&bar=2

                An e-mail address: nobody [at] nowhere.net

                Blockquoted: http://example.com/

                Auto-links should not occur here: <http://example.com/>

                or here: <http://example.com/>
                

                Images

                From "Voyage dans la Lune" by Georges Melies (1902):

                lalune

                Here is a movie movie icon.


                Footnotes

                Here is a footnote reference(1), and another(longnote). This should not be a footnote reference, because it contains a space^(my note).

                (1) Here is the footnote. It can go anywhere in the document, not just at the end.

                (longnote) Here's the other note. This one contains multiple blocks.

                Caret characters are used to indicate that the blocks all belong to a single footnote (as with block quotes).

                  { <code> }
                

                If you want, you can use a caret at the beginning of every line, as with blockquotes, but all that you need is a caret at the beginning of the first line of the block and any preceding blank lines.

                text Leading space

                Trailing space text

                text Leading spaces

                Trailing spaces text

                Tables

                Tables with Headers

                X Y Z
                1 2 3
                4 5 6

                X Y Z
                1 2 3
                4 5 6

                Row headers

                X Y Z
                1 2 3
                4 5 6

                X Y Z
                1 2 3
                Details
                4 5 6

                X Y Z
                1 2 3
                Details
                4 5 6

                X Y Z
                1 2 3
                4 5 6

                X Y Z
                1 2 3
                4 5 6

                X Y Z
                1 2 3
                4 5 6

                X Y Z
                1 2 3
                4 5 6

                X Y Z
                1

                2

                3
                4 5 6

                Tables without Headers

                1 2 3
                4 5 6

                tbody tags omitted

                1 2 3
                4 5 6

                empty head

                1 2 3
                4 5 6

                explicit body and foot

                1 2 3
                4 5 6

                Colspans and Rowspans

                1 and 2 3
                4, 5, and 6

                Numbers
                1 and 4 2 3
                5 6

                Attributes

                Cat X
                1 2 3
                4 5 6

                Tag omission

                thead, tbody, and tfoot

                X Y Z
                1 2 3
                4 5 6

                Empty Tables

                This section should be empty.

                ================================================ FILE: test/html-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "generator" , MetaInlines [ Str "pandoc" ] ) , ( "title" , MetaInlines [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] ) ] } [ Header 1 ( "pandoc-test-suite" , [ "title" ] , [] ) [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , Space , Str "John" , Space , Str "Gruber's" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , HorizontalRule , Header 1 ( "headers" , [] , [] ) [ Str "Headers" ] , Header 2 ( "level-2-with-an-embedded-link" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "/url" , "" ) ] , Header 3 ( "level-3-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 4 ( "level-4" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Header 5 ( "level-5" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 1 ( "level-1" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Header 2 ( "level-2-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 3 ( "level-3" , [] , [] ) [ Str "Level" , Space , Str "3" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 2 ( "level-2" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , HorizontalRule , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , Space , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , Space , Str "item." , Space , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , Space , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , Space , Str "a" , Space , Str "list" , Space , Str "item." ] , Para [ Str "Here's" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , Space , Str "*" , Space , Str "criminey." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "hard" , Space , Str "line" , Space , Str "break" , LineBreak , Str "here." ] , HorizontalRule , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , Space , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , BlockQuote [ Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "item" , Space , Str "one" ] ] , [ Plain [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] ] , BlockQuote [ Para [ Str "nested" ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" , Space , Str "2" , Space , Str ">" , Space , Str "1." ] , Para [ Str "Box-style:" ] , BlockQuote [ Para [ Str "Example:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" ] , BlockQuote [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "do" , Space , Str "laundry" ] ] , [ Plain [ Str "take" , Space , Str "out" , Space , Str "the" , Space , Str "trash" ] ] ] ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "nested" , Space , Str "one:" ] , BlockQuote [ Para [ Str "Joe" , Space , Str "said:" ] , BlockQuote [ Para [ Str "Don't" , Space , Str "quote" , Space , Str "me." ] ] ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , HorizontalRule , Header 1 ( "inline-quotes" , [] , [] ) [ Str "Inline" , Space , Str "quotes" ] , Para [ Str "Normal" , Space , Str "text" , Space , Str "but" , Space , Str "then" , Space , Str "a" , Space , Quoted DoubleQuote [ Span ( "" , [] , [ ( "cite" , "https://www.imdb.com/title/tt0062622/quotes/qt0396921" ) ] ) [ Str "inline" , Space , Str "quote" ] ] , Str "." ] , Para [ Quoted DoubleQuote [ Str "Missing" , Space , Str "a" , Space , Str "cite" , Space , Str "attribute" , Space , Str "means" , Space , Str "its" , Space , Str "just" , Space , Str "normal" , Space , Str "text" ] ] , HorizontalRule , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n\nthis code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" , HorizontalRule , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] , [ Plain [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Plus" , Space , Str "1" ] ] , [ Plain [ Str "Plus" , Space , Str "2" ] ] , [ Plain [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Minus" , Space , Str "1" ] ] , [ Plain [ Str "Minus" , Space , Str "2" ] ] , [ Plain [ Str "Minus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second" ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "and:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "One" ] ] , [ Plain [ Str "Two" ] ] , [ Plain [ Str "Three" ] ] ] , Para [ Str "Loose" , Space , Str "using" , Space , Str "tabs:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and" , Space , Str "using" , Space , Str "spaces:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog's" , Space , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Para [ Str "List" , Space , Str "styles:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [] , OrderedList ( 1 , LowerRoman , DefaultDelim ) [] , OrderedList ( 1 , LowerRoman , DefaultDelim ) [] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [] , OrderedList ( 1 , LowerRoman , DefaultDelim ) [] , OrderedList ( 1 , LowerRoman , DefaultDelim ) [] , Header 2 ( "nested" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here's" , Space , Str "another:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing" , Space , Str "but" , Space , Str "with" , Space , Str "paragraphs:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "tabs-and-spaces" , [] , [] ) [ Str "Tabs" , Space , Str "and" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] ] ] ] ] , Header 2 ( "fancy-list-markers" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , OrderedList ( 2 , Decimal , DefaultDelim ) [ [ Plain [ Str "begins" , Space , Str "with" , Space , Str "2" ] ] , [ Para [ Str "and" , Space , Str "now" , Space , Str "3" ] , Para [ Str "with" , Space , Str "a" , Space , Str "continuation" ] , OrderedList ( 4 , LowerRoman , DefaultDelim ) [ [ Plain [ Str "sublist" , Space , Str "with" , Space , Str "roman" , Space , Str "numerals," , Space , Str "starting" , Space , Str "with" , Space , Str "4" ] ] , [ Plain [ Str "more" , Space , Str "items" ] , OrderedList ( 1 , UpperAlpha , DefaultDelim ) [ [ Plain [ Str "a" , Space , Str "subsublist" ] ] , [ Plain [ Str "a" , Space , Str "subsublist" ] ] ] ] ] ] ] , Para [ Str "Nesting:" ] , OrderedList ( 1 , UpperAlpha , DefaultDelim ) [ [ Plain [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , DefaultDelim ) [ [ Plain [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , DefaultDelim ) [ [ Plain [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , DefaultDelim ) [ [ Plain [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Autonumber." ] ] , [ Plain [ Str "More." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Nested." ] ] ] ] ] , HorizontalRule , Header 2 ( "definition" , [] , [] ) [ Str "Definition" ] , DefinitionList [ ( [ Str "Violin" ] , [ [ Plain [ Str "Stringed" , Space , Str "musical" , Space , Str "instrument." ] ] , [ Plain [ Str "Torture" , Space , Str "device." ] ] ] ) , ( [ Str "Cello" , LineBreak , Str "Violoncello" ] , [ [ Plain [ Str "Low-voiced" , Space , Str "stringed" , Space , Str "instrument." ] ] ] ) ] , HorizontalRule , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "Empty" , Space , Strong [] , Space , Str "and" , Space , Emph [] , Str "." ] , Para [ Str "An" , Space , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , Space , Str "link" ] ( "/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , Space , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , Space , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , SmallCaps [ Str "small" , Space , Str "caps" ] , Str "." ] , Para [ Str "These" , Space , Str "are" , Space , Str "all" , Space , Str "underlined:" , Space , Underline [ Str "foo" ] , Space , Str "and" , Space , Underline [ Str "bar" ] , Str "." ] , Para [ Str "These" , Space , Str "are" , Space , Str "all" , Space , Str "strikethrough:" , Space , Strikeout [ Str "foo" ] , Str "," , Space , Strikeout [ Str "bar" ] , Str "," , Space , Str "and" , Space , Strikeout [ Str "baz" ] , Str "." ] , HorizontalRule , Header 1 ( "smart-quotes-ellipses-dashes" , [] , [] ) [ Str "Smart" , Space , Str "quotes," , Space , Str "ellipses," , Space , Str "dashes" ] , Para [ Str "\"Hello,\"" , Space , Str "said" , Space , Str "the" , Space , Str "spider." , Space , Str "\"'Shelob'" , Space , Str "is" , Space , Str "my" , Space , Str "name.\"" ] , Para [ Str "'A'," , Space , Str "'B'," , Space , Str "and" , Space , Str "'C'" , Space , Str "are" , Space , Str "letters." ] , Para [ Str "'Oak,'" , Space , Str "'elm,'" , Space , Str "and" , Space , Str "'beech'" , Space , Str "are" , Space , Str "names" , Space , Str "of" , Space , Str "trees." , Space , Str "So" , Space , Str "is" , Space , Str "'pine.'" ] , Para [ Str "'He" , Space , Str "said," , Space , Str "\"I" , Space , Str "want" , Space , Str "to" , Space , Str "go.\"'" , Space , Str "Were" , Space , Str "you" , Space , Str "alive" , Space , Str "in" , Space , Str "the" , Space , Str "70's?" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "quoted" , Space , Str "'" , Code ( "" , [] , [] ) "code" , Str "'" , Space , Str "and" , Space , Str "a" , Space , Str "\"" , Link ( "" , [] , [] ) [ Str "quoted" , Space , Str "link" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "\"." ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one---two" , Space , Str "---" , Space , Str "three--four" , Space , Str "--" , Space , Str "five." ] , Para [ Str "Dashes" , Space , Str "between" , Space , Str "numbers:" , Space , Str "5-7," , Space , Str "255-66," , Space , Str "1987-1999." ] , Para [ Str "Ellipses...and." , Space , Str "." , Space , Str ".and" , Space , Str "." , Space , Str "." , Space , Str "." , Space , Str "." ] , HorizontalRule , Header 1 ( "latex" , [] , [] ) [ Str "LaTeX" ] , BulletList [ [ Plain [ Str "\\cite[22-23]{smith.1899}" ] ] , [ Plain [ Str "\\doublespacing" ] ] , [ Plain [ Str "$2+2=4$" ] ] , [ Plain [ Str "$x" , Space , Str "\\in" , Space , Str "y$" ] ] , [ Plain [ Str "$\\alpha" , Space , Str "\\wedge" , Space , Str "\\omega$" ] ] , [ Plain [ Str "$223$" ] ] , [ Plain [ Str "$p$-Tree" ] ] , [ Plain [ Str "$\\frac{d}{dx}f(x)=\\lim_{h\\to" , Space , Str "0}\\frac{f(x+h)-f(x)}{h}$" ] ] , [ Plain [ Str "Here's" , Space , Str "one" , Space , Str "that" , Space , Str "has" , Space , Str "a" , Space , Str "line" , Space , Str "break" , Space , Str "in" , Space , Str "it:" , Space , Str "$\\alpha" , Space , Str "+" , Space , Str "\\omega" , Space , Str "\\times" , Space , Str "x^2$." ] ] ] , Para [ Str "These" , Space , Str "shouldn't" , Space , Str "be" , Space , Str "math:" ] , BulletList [ [ Plain [ Str "To" , Space , Str "get" , Space , Str "the" , Space , Str "famous" , Space , Str "equation," , Space , Str "write" , Space , Code ( "" , [] , [] ) "$e = mc^2$" , Str "." ] ] , [ Plain [ Str "$22,000" , Space , Str "is" , Space , Str "a" , Space , Emph [ Str "lot" ] , Space , Str "of" , Space , Str "money." , Space , Str "So" , Space , Str "is" , Space , Str "$34,000." , Space , Str "(It" , Space , Str "worked" , Space , Str "if" , Space , Str "\"lot\"" , Space , Str "is" , Space , Str "emphasized.)" ] ] , [ Plain [ Str "Escaped" , Space , Code ( "" , [] , [] ) "$" , Str ":" , Space , Str "$73" , Space , Emph [ Str "this" , Space , Str "should" , Space , Str "be" , Space , Str "emphasized" ] , Space , Str "23$." ] ] ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "LaTeX" , Space , Str "table:" ] , Para [ Str "\\begin{tabular}{|l|l|}\\hline" , Space , Str "Animal" , Space , Str "&" , Space , Str "Number" , Space , Str "\\\\" , Space , Str "\\hline" , Space , Str "Dog" , Space , Str "&" , Space , Str "2" , Space , Str "\\\\" , Space , Str "Cat" , Space , Str "&" , Space , Str "1" , Space , Str "\\\\" , Space , Str "\\hline" , Space , Str "\\end{tabular}" ] , HorizontalRule , Header 1 ( "special-characters" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Plain [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Plain [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Plain [ Str "section:" , Space , Str "\167" ] ] , [ Plain [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Plain [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , HorizontalRule , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Header 2 ( "explicit" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by two spaces" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by a tab" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title with \"quotes\" in it" ) ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title with single quotes" ) ] , Para [ Str "Email" , Space , Str "link" , Space , Str "(nobody" , Space , Str "[at]" , Space , Str "nowhere.net)" ] , Para [ Link ( "" , [] , [] ) [ Str "Empty" ] ( "" , "" ) , Str "." ] , Header 2 ( "reference" , [] , [] ) [ Str "Reference" ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "[brackets]" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "b" ] ( "/url/" , "" ) , Space , Str "by" , Space , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "once" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "twice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "thrice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "This" , Space , Str "should" , Space , Str "[not]" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , CodeBlock ( "" , [] , [] ) "[not]: /url" , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "Title with \"quotes\" inside" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "biz" ] ( "/url/" , "Title with \"quote\" inside" ) , Str "." ] , Header 2 ( "with-ampersands" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here's" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "http://att.com/" , "AT&T" ) , Str "." ] , Para [ Str "Here's" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here's" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" , Space , Str "in" , Space , Str "pointy" , Space , Str "braces" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Header 2 ( "autolinks" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , Space , Link ( "" , [] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Plain [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Plain [ Link ( "" , [] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Plain [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , Space , Str "nobody" , Space , Str "[at]" , Space , Str "nowhere.net" ] , BlockQuote [ Para [ Str "Blockquoted:" , Space , Link ( "" , [] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , Para [ Str "Auto-links" , Space , Str "should" , Space , Str "not" , Space , Str "occur" , Space , Str "here:" , Space , Code ( "" , [] , [] ) "" ] , CodeBlock ( "" , [] , [] ) "or here: " , HorizontalRule , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Str "\"Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune\"" , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Para [ Image ( "" , [] , [] ) [ Str "lalune" ] ( "lalune.jpg" , "Voyage dans la Lune" ) ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "movie" ] ( "movie.jpg" , "" ) , Space , Str "icon." ] , HorizontalRule , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference" , Link ( "" , [] , [] ) [ Str "(1)" ] ( "#note_1" , "" ) , Str "," , Space , Str "and" , Space , Str "another" , Link ( "" , [] , [] ) [ Str "(longnote)" ] ( "#note_longnote" , "" ) , Str "." , Space , Str "This" , Space , Str "should" , Space , Emph [ Str "not" ] , Space , Str "be" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Space , Str "because" , Space , Str "it" , Space , Str "contains" , Space , Str "a" , Space , Str "space^(my" , Space , Str "note)." ] , Para [ Link ( "" , [] , [] ) [ Str "(1)" ] ( "#ref_1" , "" ) , Space , Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "footnote." , Space , Str "It" , Space , Str "can" , Space , Str "go" , Space , Str "anywhere" , Space , Str "in" , Space , Str "the" , Space , Str "document," , Space , Str "not" , Space , Str "just" , Space , Str "at" , Space , Str "the" , Space , Str "end." ] , Para [ Link ( "" , [] , [] ) [ Str "(longnote)" ] ( "#ref_longnote" , "" ) , Space , Str "Here's" , Space , Str "the" , Space , Str "other" , Space , Str "note." , Space , Str "This" , Space , Str "one" , Space , Str "contains" , Space , Str "multiple" , Space , Str "blocks." ] , Para [ Str "Caret" , Space , Str "characters" , Space , Str "are" , Space , Str "used" , Space , Str "to" , Space , Str "indicate" , Space , Str "that" , Space , Str "the" , Space , Str "blocks" , Space , Str "all" , Space , Str "belong" , Space , Str "to" , Space , Str "a" , Space , Str "single" , Space , Str "footnote" , Space , Str "(as" , Space , Str "with" , Space , Str "block" , Space , Str "quotes)." ] , CodeBlock ( "" , [] , [] ) " { }" , Para [ Str "If" , Space , Str "you" , Space , Str "want," , Space , Str "you" , Space , Str "can" , Space , Str "use" , Space , Str "a" , Space , Str "caret" , Space , Str "at" , Space , Str "the" , Space , Str "beginning" , Space , Str "of" , Space , Str "every" , Space , Str "line," , Space , Str "as" , Space , Str "with" , Space , Str "blockquotes," , Space , Str "but" , Space , Str "all" , Space , Str "that" , Space , Str "you" , Space , Str "need" , Space , Str "is" , Space , Str "a" , Space , Str "caret" , Space , Str "at" , Space , Str "the" , Space , Str "beginning" , Space , Str "of" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "of" , Space , Str "the" , Space , Str "block" , Space , Str "and" , Space , Str "any" , Space , Str "preceding" , Space , Str "blank" , Space , Str "lines." ] , Para [ Str "text" , Space , Emph [ Str "Leading" , Space , Str "space" ] ] , Para [ Emph [ Str "Trailing" , Space , Str "space" ] , Space , Str "text" ] , Para [ Str "text" , Space , Emph [ Str "Leading" , Space , Str "spaces" ] ] , Para [ Emph [ Str "Trailing" , Space , Str "spaces" ] , Space , Str "text" ] , Header 1 ( "tables" , [] , [] ) [ Str "Tables" ] , Header 2 ( "tables-with-headers" , [] , [] ) [ Str "Tables" , Space , Str "with" , Space , Str "Headers" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Para [ Str "Row" , Space , Str "headers" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 1) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Details" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ]) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Details" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ]) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] , TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] , TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "tables-without-headers" , [] , [] ) [ Str "Tables" , Space , Str "without" , Space , Str "Headers" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Para [ Str "tbody" , Space , Str "tags" , Space , Str "omitted" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Para [ Str "empty" , Space , Str "head" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Para [ Str "explicit" , Space , Str "body" , Space , Str "and" , Space , Str "foot" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ]) , Header 2 ( "colspans-and-rowspans" , [] , [] ) [ Str "Colspans" , Space , Str "and" , Space , Str "Rowspans" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "1" , Space , Str "and" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "4," , Space , Str "5," , Space , Str "and" , Space , Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , HorizontalRule , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Numbers" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "1" , Space , Str "and" , Space , Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "attributes" , [] , [] ) [ Str "Attributes" ] , Table ( "attrib-test-table" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [ "table-head" ] , [] ) [ Row ( "" , [ "table-head-row" ] , [] ) [ Cell ( "" , [] , [ ( "abbr" , "x" ) ] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Cat" , Space , Str "X" ] ] ] ]) [ TableBody ( "" , [ "main" ] , [ ( "part" , "body" ) ] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [ ( "part" , "row" ) ] ) [ Cell ( "" , [] , [ ( "part" , "cell" ) ] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [ ( "valign" , "bottom" ) ] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [ ( "style" , "color: #151950" ) ] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] ] (TableFoot ( "" , [ "summary" ] , [] ) [ Row ( "" , [] , [ ( "bgcolor" , "#ccc" ) ] ) [ Cell ( "" , [] , [ ( "square" , "true" ) ] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ]) , Header 2 ( "tag-omission" , [] , [] ) [ Str "Tag" , Space , Str "omission" ] , Para [ Str "thead," , Space , Str "tbody," , Space , Str "and" , Space , Str "tfoot" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] ] ]) , Header 2 ( "empty-tables" , [] , [] ) [ Str "Empty" , Space , Str "Tables" ] , Para [ Str "This" , Space , Str "section" , Space , Str "should" , Space , Str "be" , Space , Str "empty." ] ] ================================================ FILE: test/insert ================================================ STUFF INSERTED ================================================ FILE: test/ipynb/mime.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": 1, "id": "0ad1fbe7-107b-4668-ae4d-8ce4ae9a4400", "metadata": {}, "outputs": [], "source": [ "from __future__ import annotations\n", "\n", "from dataclasses import dataclass" ] }, { "cell_type": "code", "execution_count": 2, "id": "c2d3a9f4-dfdb-4ced-bbcd-3dfd1780af80", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7.29.0\n" ] } ], "source": [ "import IPython\n", "\n", "print(IPython.__version__)" ] }, { "cell_type": "markdown", "id": "21e7a4a1-0cf8-48cc-823c-dca698ae6853", "metadata": {}, "source": [ "Supported IPython display formatters:" ] }, { "cell_type": "code", "execution_count": 3, "id": "053cdbc4-b157-4e3e-9c86-8f374770d006", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "text/plain\n", "text/html\n", "text/markdown\n", "image/svg+xml\n", "image/png\n", "application/pdf\n", "image/jpeg\n", "text/latex\n", "application/json\n", "application/javascript\n" ] } ], "source": [ "ip = get_ipython()\n", "for mime in ip.display_formatter.formatters:\n", " print(mime)" ] }, { "cell_type": "markdown", "id": "d79b063d-ce81-497b-a0ea-5b2e2972e845", "metadata": {}, "source": [ "Let's write a simple class that will output different mime:" ] }, { "cell_type": "code", "execution_count": 4, "id": "c847636c-1c45-432e-9d8d-7310dd7f5637", "metadata": {}, "outputs": [], "source": [ "@dataclass\n", "class Mime:\n", " math: str\n", "\n", " def _repr_mimebundle_(\n", " self,\n", " include: Container[str] | None = None,\n", " exclude: Container[str] | None = None,\n", " **kwargs,\n", " ) -> dict[str, str]:\n", " string = self.math\n", " data = {\n", " \"text/plain\": string,\n", " \"text/html\": (latex := f\"\\\\[{string}\\\\]\"),\n", " \"text/markdown\": f\"$${string}$$\",\n", " # \"image/svg+xml\":,\n", " # \"image/png\":,\n", " # \"application/pdf\":,\n", " # \"image/jpeg\":,\n", " \"text/latex\": latex,\n", " # \"application/json\":,\n", " # \"application/javascript\":,\n", " }\n", " if include:\n", " data = {k: v for k, v in data.items() if k in include}\n", " if exclude:\n", " data = {k: v for k, v in data.items() if k not in exclude}\n", " return data" ] }, { "cell_type": "code", "execution_count": 5, "id": "4fa54f22-0c3a-4809-91f7-ea7101ff1907", "metadata": {}, "outputs": [], "source": [ "mime = Mime(\"E = mc^2\")" ] }, { "cell_type": "code", "execution_count": 6, "id": "c419e6a6-240c-4af0-a244-5f1526705c30", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\\[E = mc^2\\]" ], "text/latex": [ "\\[E = mc^2\\]" ], "text/markdown": [ "$$E = mc^2$$" ], "text/plain": [ "E = mc^2" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mime" ] }, { "cell_type": "markdown", "id": "bf140b8e-16ac-4670-9778-f1c1d9486f9d", "metadata": {}, "source": [ "Note that #7561 made ipynb reader aware of this, and #7563 made ipynb writer aware of this." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" } }, "nbformat": 4, "nbformat_minor": 5 } ================================================ FILE: test/ipynb/mime.native ================================================ [ Div ( "0ad1fbe7-107b-4668-ae4d-8ce4ae9a4400" , [ "cell" , "code" ] , [ ( "execution_count" , "1" ) ] ) [ CodeBlock ( "" , [ "python" ] , [] ) "from __future__ import annotations\n\nfrom dataclasses import dataclass" ] , Div ( "c2d3a9f4-dfdb-4ced-bbcd-3dfd1780af80" , [ "cell" , "code" ] , [ ( "execution_count" , "2" ) ] ) [ CodeBlock ( "" , [ "python" ] , [] ) "import IPython\n\nprint(IPython.__version__)" , Div ( "" , [ "output" , "stream" , "stdout" ] , [] ) [ CodeBlock ( "" , [] , [] ) "7.29.0\n" ] ] , Div ( "21e7a4a1-0cf8-48cc-823c-dca698ae6853" , [ "cell" , "markdown" ] , [] ) [ Para [ Str "Supported" , Space , Str "IPython" , Space , Str "display" , Space , Str "formatters:" ] ] , Div ( "053cdbc4-b157-4e3e-9c86-8f374770d006" , [ "cell" , "code" ] , [ ( "execution_count" , "3" ) ] ) [ CodeBlock ( "" , [ "python" ] , [] ) "ip = get_ipython()\nfor mime in ip.display_formatter.formatters:\n print(mime)" , Div ( "" , [ "output" , "stream" , "stdout" ] , [] ) [ CodeBlock ( "" , [] , [] ) "text/plain\ntext/html\ntext/markdown\nimage/svg+xml\nimage/png\napplication/pdf\nimage/jpeg\ntext/latex\napplication/json\napplication/javascript\n" ] ] , Div ( "d79b063d-ce81-497b-a0ea-5b2e2972e845" , [ "cell" , "markdown" ] , [] ) [ Para [ Str "Let's" , Space , Str "write" , Space , Str "a" , Space , Str "simple" , Space , Str "class" , Space , Str "that" , Space , Str "will" , Space , Str "output" , Space , Str "different" , Space , Str "mime:" ] ] , Div ( "c847636c-1c45-432e-9d8d-7310dd7f5637" , [ "cell" , "code" ] , [ ( "execution_count" , "4" ) ] ) [ CodeBlock ( "" , [ "python" ] , [] ) "@dataclass\nclass Mime:\n math: str\n\n def _repr_mimebundle_(\n self,\n include: Container[str] | None = None,\n exclude: Container[str] | None = None,\n **kwargs,\n ) -> dict[str, str]:\n string = self.math\n data = {\n \"text/plain\": string,\n \"text/html\": (latex := f\"\\\\[{string}\\\\]\"),\n \"text/markdown\": f\"$${string}$$\",\n # \"image/svg+xml\":,\n # \"image/png\":,\n # \"application/pdf\":,\n # \"image/jpeg\":,\n \"text/latex\": latex,\n # \"application/json\":,\n # \"application/javascript\":,\n }\n if include:\n data = {k: v for k, v in data.items() if k in include}\n if exclude:\n data = {k: v for k, v in data.items() if k not in exclude}\n return data" ] , Div ( "4fa54f22-0c3a-4809-91f7-ea7101ff1907" , [ "cell" , "code" ] , [ ( "execution_count" , "5" ) ] ) [ CodeBlock ( "" , [ "python" ] , [] ) "mime = Mime(\"E = mc^2\")" ] , Div ( "c419e6a6-240c-4af0-a244-5f1526705c30" , [ "cell" , "code" ] , [ ( "execution_count" , "6" ) ] ) [ CodeBlock ( "" , [ "python" ] , [] ) "mime" , Div ( "" , [ "output" , "execute_result" ] , [ ( "execution_count" , "6" ) ] ) [ RawBlock (Format "html") "\\[E = mc^2\\]" , RawBlock (Format "latex") "\\[E = mc^2\\]" , RawBlock (Format "markdown") "$$E = mc^2$$" , CodeBlock ( "" , [] , [] ) "E = mc^2" ] ] , Div ( "bf140b8e-16ac-4670-9778-f1c1d9486f9d" , [ "cell" , "markdown" ] , [] ) [ Para [ Str "Note" , Space , Str "that" , Space , Str "#7561" , Space , Str "made" , Space , Str "ipynb" , Space , Str "reader" , Space , Str "aware" , Space , Str "of" , Space , Str "this," , Space , Str "and" , Space , Str "#7563" , Space , Str "made" , Space , Str "ipynb" , Space , Str "writer" , Space , Str "aware" , Space , Str "of" , Space , Str "this." ] ] ] ================================================ FILE: test/ipynb/mime.out.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from __future__ import annotations\n", "\n", "from dataclasses import dataclass" ], "id": "0ad1fbe7-107b-4668-ae4d-8ce4ae9a4400" }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "7.29.0\n" ] } ], "source": [ "import IPython\n", "\n", "print(IPython.__version__)" ], "id": "c2d3a9f4-dfdb-4ced-bbcd-3dfd1780af80" }, { "cell_type": "markdown", "metadata": {}, "source": [ "Supported IPython display formatters:" ], "id": "21e7a4a1-0cf8-48cc-823c-dca698ae6853" }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "text/plain\n", "text/html\n", "text/markdown\n", "image/svg+xml\n", "image/png\n", "application/pdf\n", "image/jpeg\n", "text/latex\n", "application/json\n", "application/javascript\n" ] } ], "source": [ "ip = get_ipython()\n", "for mime in ip.display_formatter.formatters:\n", " print(mime)" ], "id": "053cdbc4-b157-4e3e-9c86-8f374770d006" }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's write a simple class that will output different mime:" ], "id": "d79b063d-ce81-497b-a0ea-5b2e2972e845" }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "@dataclass\n", "class Mime:\n", " math: str\n", "\n", " def _repr_mimebundle_(\n", " self,\n", " include: Container[str] | None = None,\n", " exclude: Container[str] | None = None,\n", " **kwargs,\n", " ) -> dict[str, str]:\n", " string = self.math\n", " data = {\n", " \"text/plain\": string,\n", " \"text/html\": (latex := f\"\\\\[{string}\\\\]\"),\n", " \"text/markdown\": f\"$${string}$$\",\n", " # \"image/svg+xml\":,\n", " # \"image/png\":,\n", " # \"application/pdf\":,\n", " # \"image/jpeg\":,\n", " \"text/latex\": latex,\n", " # \"application/json\":,\n", " # \"application/javascript\":,\n", " }\n", " if include:\n", " data = {k: v for k, v in data.items() if k in include}\n", " if exclude:\n", " data = {k: v for k, v in data.items() if k not in exclude}\n", " return data" ], "id": "c847636c-1c45-432e-9d8d-7310dd7f5637" }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "mime = Mime(\"E = mc^2\")" ], "id": "4fa54f22-0c3a-4809-91f7-ea7101ff1907" }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "output_type": "execute_result", "execution_count": 6, "metadata": {}, "data": { "text/html": [ "\\[E = mc^2\\]" ], "text/latex": [ "\\[E = mc^2\\]" ], "text/markdown": [ "$$E = mc^2$$" ], "text/plain": [ "E = mc^2" ] } } ], "source": [ "mime" ], "id": "c419e6a6-240c-4af0-a244-5f1526705c30" }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that #7561 made ipynb reader aware of this, and #7563 made ipynb writer aware of this." ], "id": "bf140b8e-16ac-4670-9778-f1c1d9486f9d" } ], "nbformat": 4, "nbformat_minor": 5, "metadata": {} } ================================================ FILE: test/ipynb/rank.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": 1, "id": "5cf8f54d-bf3c-4db2-996d-22662a86ad43", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 2, "id": "a0228622-9ff8-4392-9ddd-f70a90f0e106", "metadata": {}, "outputs": [ { "data": { "text/html": "

                you should see this when converting from ipynb to html instead of the image below.

                ", "image/png": "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8/fFQqAAAACXBIWXMAAACdAAAAnQGPcuduAAAASUlEQVR4nGNkYGBgc1HM+/lfkI/hqQ0XAwsDAwPDzSphBi6h/wwlahsgAiJCHxkkBL4zWLA8YGBkYGBgZGBg4GRgYPjDwMDABADgfgxL+wQIRAAAAABJRU5ErkJggg==\n", "text/plain": [ "
                " ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(figsize=(1, 1), dpi=4)\n", "ax.imshow([[0, 1], [2, 3]]);" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 5 } ================================================ FILE: test/ipynb/rank.out.html ================================================
                import matplotlib.pyplot as plt
                fig, ax = plt.subplots(figsize=(1, 1), dpi=4)
                ax.imshow([[0, 1], [2, 3]]);

                you should see this when converting from ipynb to html instead of the image below.

                ================================================ FILE: test/ipynb/simple.in.native ================================================ Pandoc (Meta {unMeta = fromList [("jupyter",MetaMap (fromList [("nbformat",MetaInlines [Str "4"]),("nbformat_minor",MetaInlines [Str "5"])]))]}) [Div ("uid1",["cell","markdown"],[]) [Header 1 ("lorem-ipsum",[],[]) [Str "Lorem",Space,Str "ipsum"] ,Para [Strong [Str "Lorem",Space,Str "ipsum"],Space,Str "dolor",Space,Str "sit",Space,Str "amet,",Space,Str "consectetur",Space,Str "adipiscing",Space,Str "elit.",Space,Str "Nunc",Space,Str "luctus",SoftBreak,Str "bibendum",Space,Str "felis",Space,Str "dictum",Space,Str "sodales."]] ,Div ("uid2",["cell","code"],[]) [CodeBlock ("",["python"],[]) "print(\"hello\")"] ,Div ("uid3",["cell","markdown"],[]) [Header 2 ("pyout",[],[]) [Str "Pyout"]] ,Div ("uid4",["cell","code"],[("execution_count","2")]) [CodeBlock ("",["python"],[]) "from IPython.display import HTML\nHTML(\"\"\"\n\nHTML\n\"\"\")" ,Div ("uid5",["output","execute_result"],[("execution_count","2")]) [RawBlock (Format "html") "\nHTML\nhello"]] ,Div ("uid6",["cell","markdown"],[("tags","[\"foo\",\"bar\"]")]) [Header 2 ("image",[],[]) [Str "Image"] ,Para [Str "This",Space,Str "image",Space,Image ("",[],[]) [Str "the",Space,Str "moon"] ("lalune.jpg",""),Space,Str "will",Space,Str "be",Space,Str "included",Space,Str "as",Space,Str "a",Space,Str "cell",SoftBreak,Str "attachment."]]] ================================================ FILE: test/ipynb/simple.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Lorem ipsum\n", "===========\n", "\n", "**Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus\n", "bibendum felis dictum sodales." ], "id": "uid1" }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"hello\")" ], "id": "uid2" }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pyout\n", "-----" ], "id": "uid3" }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "output_type": "execute_result", "execution_count": 2, "metadata": {}, "data": { "text/html": [ "\n", "HTML\n", "hello" ] } } ], "source": [ "from IPython.display import HTML\n", "HTML(\"\"\"\n", "\n", "HTML\n", "\"\"\")" ], "id": "uid4" }, { "cell_type": "markdown", "metadata": { "tags": [ "foo", "bar" ] }, "source": [ "Image\n", "-----\n", "\n", "This image ![the moon](attachment:lalune.jpg) will be included as a cell\n", "attachment." ], "attachments": { "lalune.jpg": { "image/jpeg": "/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcU\nFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgo\nKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAD6APoDAREA\nAhEBAxEB/8QAHAAAAAcBAQAAAAAAAAAAAAAAAQIDBAUGBwAI/8QAPhAAAgEDAwIEBAQFAgUFAAMA\nAQIDAAQRBRIhBjETIkFRB2FxgRQykaEjQlKxwRXwFjNictEIJEPh8SZTgv/EABcBAQEBAQAAAAAA\nAAAAAAAAAAABAgT/xAAbEQEBAQEAAwEAAAAAAAAAAAAAARECEiExQf/aAAwDAQACEQMRAD8A2t0Y\noQpwT2qVzMV+N3UHgrDY2eoM0y58VEbgfp9K1yMRmnuJ5h40jyYHGSeKrWE8u2QAApOMdqGCsmT8\nh70TAJwMAZx249aKBy4c9vTNUC0zDCgmmmG7Ockjkj1PrUTAjcy5XP0ouCgHae4IomOJHhgIc55P\nHY0Uk5IXLMcUBQ27n96JYO2MYLebHtRBA7BcMx29sdxQJqwZRtIP+BQKpjHHc+xzigNGoAO/k+nP\nAoAYlee5oBiGeWySO9AJCgY5PHagFCADzj2GaA2N2TkjA/U0HMwbPPeiyBLDfkkj04FCl1cBMgn6\nURwYFGySR6D2oAeQDAxnHGKAhU4IbGc+tFwnwDj9aK7f8v2oNu+IHxNvJdXmt9EmKWSqArA/mPvx\nUxMZNe3Ml1dvNcMzSSEsxPOferJhht/OWyAPc0UfdgDcuM8n50AMCykZFARsngcY/egTcbjnJz9O\n9AB2kZGSQOcUCX8x83bntQCMruJ4B7D1oCyOGzxtJ9M80CAdg5UjFE0aFJrghLeNpHY4IRdx/QUN\nWCw6D6q1EZttEvirHAZ4ig/U4qw1b9H+CHVN3Mq6hJaWMJ5ZjJ4hA/7R3P3q3ET+pf8Ap/lWNm03\nXkkkA8qTW+3PHupP9qxopV78G+s7VSV0+OcAn/kzqSfscVvIKzqPTWu6XKE1LSL+Bhz5oDg/cd6l\nEZzGwLrtPqrA8frUCJfcw9gfegUjZsEAffNADyHt78UAjCjzDJxRcO5Pw3gwCGOVJQp8ZncMGOeN\noxwMY96GCbQffFFcUXKjDDt2NEo+N3yyM5z3okKuqJgIzONoJyuMGi4QfGcqSfXBoYHJx659qKIR\nnnsfUGgJn/poJYoTIGLY+eDzQFlQK2G/KCTmgbspfO0qce/agPGcR7nHf9vnQFfBPlOc88Gg7uuc\nc/M0Bd208YJJweKAYrea4kKQICRGW5IUYUZJ570DYqcknt3FE0VuVyDzj1oamOlulda6puvC0a0e\nZVIWSbtGn1Y1NNbX0x8ENH0qL8X1NdtqDoNxiQbIh8u+WpqL70Tc6fcxypouiRadbW8hhLFFXcB7\nEdz+tNFvEZxkmmgShbA9PlUA+Hgg/wBqDgmBkd6ArJuJBGR7VdEdqWgaVqMfh6hp9pcLj/5Ig2Ka\nKJrvwW6S1EFoLaWwmPIe2fAz81ORTRm3UfwI1mzBbRL+K/ReyS/w3x/b+1Wexmev9O6xoE2zWdOu\nbUDszr5T9G7H9auCJj2n3PPrUXTlGBB2kYx96GlQMjJJHuRRXBgDgk8DtRKH8w4OfYA0SUlIMsFX\nJ4oujHH8ufnRRGOSNoJNAeFC77F2jPucfvQFEqgY3nj/AKaCUY58wwq54AoCzOmVMke9QeRnGR7Z\noEIF7pnaTk49KDpSSwQntQJsGKjgggZ9uDQc4OOe1Am2UCkHOR7dqA8t/cSW8MEkrGGEsUTPCk4z\nj9KJT3pzQtS6m1aPT9Jh8SVxlmJwqL/UfYURuuhfArR7f8NLrF1cXciKDJCrbI2b7c4+9NGtaRpt\nrpdqltYW0VtAn5Y41wBUodvGjqUdQyn0YZqAIreOBFSFFRF7BQAKA1xcRwKplcJuOBn1NAR7y2ik\nWMzoZnGVQHJNAuQcD3oBKkD2FBy8jnvQFxnjjmg4rxwKBMqCBtPNA3vbCC+tngvYo54HGGSRQQR9\nDV0Y91n8DNOvFkuOmZmsrk5PgSNuiY98D1X+1XRhWu6DqWgX72er2j2069t/ZvmD2IoGG7jbnj1F\nFlB224PB+VClN4DYJHyAojmPGCck8cetCAxgjPp6UaAGKtx6+9ATAXO7nFBw8HHLN+goJhBuj2Fe\nAcnmgNazW8U0vjweODGyqpYrsYjytx3x3oGa5LEEjH9XvQGlgmjjMmQq4HBPfPYgevagG5nhe3tk\nFuInQHxJQTmQntn0wKBKTlAeDx60DSY+U9zn+mgsnQvROr9Y3W2xi8KxV8SXUnCrjvj1Y/IUR6c6\nA6H03o6wMVgrSXMoBmuX/NIf8Djt/eiLfjJwO9ZBiOfmKDhktzQAzYBLZ8oyaDF+rOptVv8AUjNZ\nL4tjA/lT+kr3wvqTQX/pi3Y+DqFxKXurmFWAaPaVzg4I/b0oHlxqV7penRTXFu93dPLsESYB2k8n\n7CgnradLq1WaIOFI/K42sPkRQCg3Kcd6Dgp3d6AdrGg5VxnjmgKWB8uQGxnFAUgKuSefSghuqNC0\njXbAWGtxQyJKdsYc4YMf6T6GtDzR8S/hnqfSUz3NvuvNILYSZR5o+ezj/Pb6UGfLzyD/AJoFFySQ\nVBHpQDJ5kGByPahAbWxn5+po0OF3D+XPtQJsNwOe+aAuygmMkebgHnHFALHYpJwSeGz2oGpOJWAI\n49BQEZlYAHkg4oARVOMvtBIJJ7AUAX6xxSOsUgmjViFcKRuHviiVfvhT8NZuqpk1LVFeHRkPlHZp\nznsP+n50qPS+mWVppdnFa2cEcFtGu1I41ChR8qyHVxK8cLPDD4kgGVQHBNAa0maaBJGTYzDJXOcU\nCy5JOaA2OMfoaArkheM7vlQNYNOtoWLJCgLHJwo5NApPKLaNpGRQB6j2oGmnRvcyNd3O/DkeErLh\nkWgklIdCyZOCRzxzQEeRxhdpUnncBkD5UCxXjJ7+tAlctMsIMLohz5mcZAH09aBQYdQwyAeaAuA7\nMAQxHH0oG1481nbGVInuWU5Kr+bHrgepoKB1u+o6jqlvBH05NevEBPBK0pQR4I4BHZj+1Bb9IS7l\nsFtNWtYwDGFYB/EXHqpJ7/WtQYx8VfhGbdZtV6Uh8gy81mpyR6koPb5UGKY4YkeYd88fbFAI5AC9\n8c5oQBb+U9+9GnN5RgDgjOPWgAN3yMfWgAqc91/UUD2RSSRg9+49KCR6e0WfX9WS0icRwgb55WOF\nijH5nP0FBYNRi6dSR7HRNPmu0hOW1GaXaZMdwBwAP3oynE0XRYrFtV02wS4ECj8dp1wcsE7eJEf3\n9qlFZ616ZttPu7Kbp9Zbi0vYzNCcgjHqoHuKsEp8LPh7P1PqjXerxywaXaviRSu1pWH8g+XuflQe\nmIIY7S3SK3hVIo12pGoAAA7AClEL1N1RH0/oTalcwx+IACLaSQKx59Ppmshv0D1jH1ZbTubU27xk\nkAnKsuSMg/UUFluLlLaJXETyecKAg554zigXiubeRnSKeJ5FOGVXBIPsaBLULoWkIfw3kYsAqIOS\nTQJMbpm3oqlmwACeF9yfn+1A+Bx34oE5IY5P+YFbnPIzQKAckHuRQCAQOO1AL8r9KDhkZOT9M8UC\ncrxgAyYJzwD70CT3Itxm8kgi3fly+P7/AOKA9pskhEkZysnOfeg6RH8w3tgjAHtQRZ1uystSg0m5\neRJ2UbHceV8fP3oJkBSAVII9xQFdSRwKDDvjN8L/AMSJte6chxcgFrm1QcSf9aj39x61YMH8Q+Ck\nfhqpQncxBDH5H6VRwXJ/Ke1Am2QchuMYOaNFSAVznB9qAm8f10D2RmX8jDHP3oLbebtA6ej0m2Lr\nfX6LcX7IMskf8kf6HcffIoG8yTadZxSTxCK3kRZUwSFfkruIJ78GhiS6Y1OS3160uZJFWO5bwZtx\nzuQ8bcfPNMZXvo2wsLnQ9R0q/maJNNv5Yo3bjCuMAHPzqA2jdUan0lF0/ZXcElxp9zE+5WVd/DE7\n1IPPB7H2po1bSNXsdYthLp1ykyEcj+ZT817ioITrnoux6vs1gv5JYnjz4ckZ/Ln5etA+6N0BemdB\nttMina4WEFfFdQpIJJ7D60E5I4Vo9qnnsQO1A3k0yzeTxhCizZJ3qNpz9RQO449igMSxHGW5NAIw\nBtUAUAMORkfegMhG3jtQD8+fvQGXJz7UAHuRQA5YDI5FB0qCQA5yaCs2/SFit/Jd3AmvJ2bO64ct\nt5zwD2oLMilVAUDgcAelAJLbhgZz3oGN9HPIYmhtrWRw2czjt7Y+dA+h3mJS67W9gc0AvuLYANAD\npkZABHY85oPOnxy+Hx06Z+odGjC2jt/7qBRwjH+cY9D6/OrKMebcceHwfaqCYIyDgZ96GhHOFJI4\n/WjQpXnsaCz9J6fDqGvRC8OLO3Vri5PB/hqMkfc4H3oDT3UupapcXrKS9zISgDdhnAGPbsKC5aLL\nBHq9p01c6bbagPE23kpJYhmz5IySAAMj6nNGdRnT2lu3V9vaQQrJDHfCMFj5kAfufsMUFogu5H0j\nrLUYXK+Lq0aRse/lf/8AOKlFfudagvbnQpNQRmtILydCwPdCQcgMOMZFQanPoeiawBd9M6s9jeKP\nK1vKQp+RFA4septa6fuFtuqbRrmzx5b+BAdo927A+vsflQXfTr2z1O3W5025juIW/mjOR9KAZI91\n4khaRNo4XdwT9KAl3b2+oWpjMoZWbOVfnI9sUCrXUNssUU8w3sQoJH5jQLvwQQC3NAKvuUPtK54w\naDg23v6UA7weBnNAIOBigMr+hoOjdZQdhBx3waAVG0Z7UBWfAOQSflQChyNxBAxQRutarb6bHALi\n9trSW4kEcJnGd7ewFA/j8QEK/IA/MBjmgWDDBB7igj9dupLTTbiaHZ4oQ7A7bQW9ATVgwXSNV6on\nl8azW6t45pWdxHIxWA/zNtz7A8Glg2S1u7fX+nt0J/H2c4MMhmQoW9GBUjj60g8sfEHpebpDqi4s\nHLG2Y77eQ486E8fcdvtVFekGW4UfegKVAAKgnFGhuDzxQXbpDTZF6a13UnUqrCOzQ5wGZmXIJ+lE\n0ppkEK6nJcRWcTW9hA08iKcjcowpye/mxQ0+6VRbC/jvLm48L8LG9y8pIOXxkDnuSTipqHXQMng3\nes9S3fhn8DbvcZI5Mr/lH700dc3Dad8NtPs4nU6jeXD6nMCwBRF5XOfU8YHrTNJFF1X8RawW1jc4\nGxTKNrZB385yPkBTFw1stSu7Ni9tPLGSQfK5Aphi8J8UNUm6fn0u72yvJ5fGbuF/39aYYtGgadp9\n/axXnRetzaXqnhqZI3bEcj4547Ak/X6UxFisPiXe6NMdO65057eQAr+LhUlHHbOPX07UwWXpQ6Bq\nMo1LpgW0sioVI8Qgxk+684qC028M5890Y3kHKbUwF+lA4LDOzu2M4FAOG3DaoI9cntQdJxzQEyR2\n59f/AKoGl5fSQRFo7ZpB/MhYIR9zxQdayyXKb7gqox5Yo2yB9WHc0DPUIWnhWKxkuYFRs5gcKWbP\nY59KBkx6isVeSGW31JNwHhyOUkA+o8v9qCfjkMo/LJFKqBmRvSgc2swnRyFcYODuXGfpQMtRsLK8\nvYJL+wjuGiUtHK6hghz6Z7H6UEmCsig84I9RigiruC9t0DaaVmIIHhTOQMeuGwT9qCJ1ywv9T0U2\n9xFFiaVBJGHz5M5ODgYPY/arKJPTtLW1t44i7SKq48w8x+ZPrTRJoipGFQAAdgKgzX47dMJrXSrX\nkUe6807MykDkp/MP8/aro80FQyZ+tUJ7hvH0x270XQ7KGtXvIk0T4c9P2bIhkvpnvJVfjIxhf7qf\ntRDXpu0/1DpzXltUlkvmWMBI+2zdnn64oYa6yX0XTm0i4jQ3t6wmuV53xov5UPpyeeKyLbpFtZ6X\npmn6TqNq7/ic6pqQRR/DVf8Alq2fTOP0FXBnXU+ox32o3lzeW+JrxlMXHKR9wfbJ/tVWK5f3AnaA\njafCTwwcY4BOM/qKKSjA4Dg8j37UHZKkE5P0olSFlcLDdJPbTNBOigjxOVZu3+80Rbbnrq9l0t9I\n6isRd2rgKpPlZMdyre9An07oupoh1zo2+lea2fMlr+WZFx7ZwwqWDVug/ihDq7R6b1EPwmpMNokP\nlVj8xng1BqEUe1EAJOMDOc5oDSxq6YYeuaAJF4oCBUQ7mJ45zQHYB14wR86AVjBXyjge1AEcRTHl\nA9hQE8kbgEohJ5yQM0ETHNqMOr3IZQ9tIMQyEjKt7D3FBLqywRPJKTuxlj3zQI3Ut14e+yhWRj28\nQ7RjH60EfpF3rU/jLqFrHbS4/hqpJXH19aCRa8jgiVr1xGwXzYyf99qA9tc29/aRXFnKs1vINyOO\n2KByoxwe9AYocHGKBvdwLcWzxSLuR1KuD6gjBoPHXWujt071Nf6YSSkUnkJ4yp5H7f2rQgWAA3Y+\n1An4j/1t+tBrHxKuYS+gx24LRx6ZFtI/lz60FY0+/v8ASphNpd68EpXY5AGNvzFF1YOirZbzVrvX\n9dkNxZWH8eeaY5Lyj8qj7kcVlETqOqXd/HrPUNzcNE16Tbwxf1JkEgD2AA/etBte9R2Oq2cv+p6X\nHJfBFjgmjkMaRgAAEqO5o1FWfbgjsR8+9AlI5CgEggeoNAq0iug8uD7g80KKmCcZ7fPmjJzJfT/h\nWtjJvhOPK/OOe49u9A96X1W90/VrRtNkkSfxQF8I5yScdvX6UGidSLpfVFzcvbRiy6kgZBGysFW7\nB9T7HHNSjU+o9S1iz0e2uNLmX8RYxJ+KgYeVwVGTn5d6gjug/iU3UOt/6TewQpP59skL5B29x/8A\nYoNHPB78Ggb2l3bXO78PKsoyVyvIBHBFAoSkbfyhn4GfWgTnmWFN7ybAvc4Jz9hQRdx1dp0S3Dw+\nJJHbDdPIUZUjX3yRz9Bmrgzbqb4x9Oxho4bB751O5HPkXPsc80wQHT/xrJ1IHUbGKO0kdRiBiAgz\n+YjnsPpTKN/tLy3vLOK5t5klt5F3LKhyCPemAYLuK5XMDEj1OCP71ArGWLMPT0oIbU7h11u2t49O\nllWWNm/FIRsjI4AI/egfQ2ktpbww2XgxoDl9wJ49cUCHUGv2GixM13Mkcm0squwUH5/SrgxDW/jF\ncXOteHb3otrKEEiRISRM3zGc49v1qDT+gfiBpvV7y2unxTxywRhz4ozuHbOR2+9Bmf8A6kNIEWpa\nZqiooEqtBIR6kHI/atfRjDEt3AKjgVQjug9j+lQWh72e/htTOzyeCnhHPomeMYoJvQum7vVD47K9\njpsQBkvZ5NoAHcgUAa7rKamE0Lp9Xh0G1OZZTwZSO8jn9cCsivdS38F9cJDZIY7G2URxKe5x/MT7\nnNaEKrENwAFPPlosFwS2cd/cc0UlIm3JOeKDo2LH+UA0SjgDk98URzPiJ2449e/NAbS7v8PdpKkp\nikQ5WQLkqccGgmYNQmXWLeQLG9wVRQVPlcj+Yn3xQa98OviAjz3WjdXSpFdliEuJCNjDtsJ7enep\nRdel+kdL0rqOTVdIsoYklV1dixO3nunpg9jUCnU3WMeka5b2EUcl3JInmigQs6ZPlJAHY8+vpQP9\nO1m3nthNo0cTwM2JDwoVj6H5gd6CbhtUiVn8TcXO4ktkZ+We1BAf8Z6fZ2uqXWpyxQrbStGseQzM\nB2IA961B59+IHXmodXal+HsPFh04HbHCo25+bY/zQWv4f/CCxvII73qC8iuXYb1tYZeF9txHf6U3\nBatX+DvSl86x6cr2dwjbnEUmcj6Enj6U8hLdJdEX/SmowJp2tTT6Oc+La3HO0442+3NBf1LmRUjj\nQAfmc+nyHvWQockYyQcY3CgaabaPZxGNnaUFi3mPb6f+KA2q3RstNurnBxDE0mPfCk1YPMemaP1L\n8RtYN9fJPc2aMUaVmCKg54H0z6VRYendf6Z6T1W56a6j6fgfwJyguhGJmPzbIzjHtSjTn0zSunbi\n01fSkt9Os5GAmWNCDOGxtXb6HnNZEZ8etOF90DPKFy1rKk3zAzg/3rXI8u7zvOTg4zVoTLDJ81QW\nDTb2SwuvFgcrkbXwM5H0PFGqsjpd6+kcT61Nc2ieb8OikFc/9PA+WfSjKA1nWBzpFlZ/hLWM4KH8\nzsPVj6mghN4IyQRk5NGo5BkFmyAfSgVjChdpGO/FAXYpOHLBe/FAQqoBJbA9sUBGxgtgEj/eaCf6\nDGjt1TZf8RNGumKS7mQZQkDIB+WaMrf8Ub/ovV7V20JIYL62K4khhCLOCcEcAdu9BmCuEQvxvyFU\ng42+v+/rQaj0zax/EXRY9Nns0t9TtM+BqCKAjEclXA98jn+1Si7Cz6u6O0tLjTrxLu2tQJJrDwcK\nE/m2M2SfeoLrpupDV9Mh1OytUS2vIN8m4BZQf6T7+vPpj50GfdK9L6rJqk1y1y0elRDKRqdjHHoy\nDhjx39e9BZr7fagW0j3kul3iETRqHkeF8ZBUjkZIxjtk5rQ86dW6r+O1OcW0UtvaRsY4oWfLKBxz\n7k/5NA46P6X1rqS6WPS7V9v88rAqi/f3oN46X6C1DSotkus+BIwKl8hn2+3PapROXPT2t20bPY6k\nJ5UGYmbIfIHGW5z68VBI6DrzzWSrrAjtrwFUbDja5OBlfuaCbluJLeNwIpLiVF3bVXAP0Pv8qBxL\nO8cYcW7vnuqkAigNFKs8CyxlwG/lcYI+1A31ayF/pt1auSFmiaM/LIxVgwfoO413o3qqfSLyUSwo\ndogAyZVGcbPTPr71aNDvendJ6wtbu7Fi1lezK0bS4VZMjtnFZE0bC5u9Jh0qRAr2yw4uWx59vBI4\n4PegN1tpbap0lqOk2sipLPB4aFsnHbBNOR5A1exFhqFxbeKkngyMhdOxIPcVuhiZFz/zBUEwcKvY\nnP6fWi0+6chjn6h062uATFLcRrIMnzAsO9EehNR+GvTV3GUh0+O2YsGaWHIf9amjIfib0no3S0Vt\nFY3M000zMzLJtLKvvkenyx96oz0rwNjA8cj2osFLbVAbOc9jRQiXOAwxnj3oBlAxwDj37UDY+vHO\nQeTQBIdqjcPMfnQwJclWyBgCjJBFeefw4VaVycBUGST2wAKD0L8H9C1rSIILjWLSCytY1lZASVnl\nL4PI/wD8+vvUo1uwbxI5GkjdVc7isvOBjtj2qBWKFZiQ8CJCB5FHYj5jHFArDbQ20ey3RY1HOAOP\nsKCH1u61CPSLt9MtlXUHUrbCbJBbPdtvYetXR5T1y2udD6lni1ErJdJLvlK4wWOCePvVgsV/8Sr6\n7UW1vA0NiowIonMe4+7FeT9ARQRmodWa9EYpPBhs1Tygw26rk9xknkn70EjonxZ17TXjAeKTkZ3L\n+YZ7N8vpSjX+lOpNM6umgkMG3EgBV1DYbG4kewz2NZGkC43CP8MPFBONysMAD50DaHVH8S6N1a+B\naxMUjd280pA5wPb296DrXWLK9WNoJdtwybxDKPDcAnHIoJBifTBzzmgaz2UFzPFNNbwvLCcxuygl\nT7igdRRKg8qAZ5JAAzQEnuYoHiSWQIZW2ID/ADH2H6UERr12BY6hueIQJaO7SK/nHfnHtx3pyPGW\noN4jynuCfU963RF+DL/UtQWTkjaWY/8ATnHFGql+j1VerdJY8r+KiJz/ANwoy9C/EjqSbpbRY723\nRJC8ojIcZ4IJ/wAVkecer9en1+9FzeLCCq4URjgDP7mtLhteadBY2kMczyHUpcO0YxtiUjgH/q9c\nelAiLy1kjCX1ruyMLNGdrj0+h+lE0+t+kNQltJ7yKS3jgiTxUFw/hySp7qp70NV6YEBgWUNjBoaK\neAODnHrRoVgDnBP0ozpxZ2f4y5trVeGuJFiBPpk4zQep9C0LTembS30fQbWP8ZsDyTugZgf6ix9f\nYZpbgmbXSmXULaa6kMzpltzcjJ//AGpaLCY1CDsF74PrUCgHY0HbSx7Z96BGUfxB2xjtQZ11t0Tb\n6jNfyw2wM18gV5AcBdpzyPnV0Yp1F0o/TEczXjXaTOQYpIk3QlT3B9Rj0zVl0VKbVppImheUSwbs\ngFfXHc0De0tri/ujFYQSSyfmKopPHqaDV/g9p+padr/gkSRTzKu0kZRlPLYPbOPf2pg9GWzRCMJA\nFxH5do4wayKX1z/G0CdzqLWRkiaTxQBLudclQvovbv3oMU/4Z67uwnUAt3u1Zw42yhmx3/Ln8v0o\nN86L1d00i3i1UiGQIocNnEbnkqT2xgiguEbI4DIysp7EHNAZnxQQ/Usksej3EsCl5EUthR5sY52/\nMjigr6Qrp3R15LqEcIlmgdpFGAsY2navPJApyPJtwd8rnGBuJz6Gt0MzGSTyf0qCwSKA5ZsAjnn2\notTXQYj/AOMNIDqCrXUZwf8AuGDRG6fF6Gyfo6+ub0CR4EPgIScLIeAcfc1keatN0661a+S3sYTP\nKzAbV9B7/StLrQ/iXp9pYLp8elWsUM11AzXMqt53I7g7j244oiB6W6Tn6j2TeAy2FspTeB+Z+/8A\nmgtnWlvpdl1Dp1pq07Ja20GFQpuDHHAwPf39KDHriVTKSPOCeBnHHtQGsrG5v5pfwcTOIlMjgEeV\nfck0XRIreS7uUigRpJXOEVe5PtRE/wBJ9HaxqvUcdhNFJp0lviaSWVcMgzxgdySeBipo9T6O8Npp\nUJ1K4iW5KgSvIyqxb5jPH0paJm1NvKivE6Mp7MpyP1FQLRTwy58F0cjuAckfagOTtO3+Y8igMWCI\nWbOPlzQNhNBOWEbq5Q+YKc4+tAD7JEZgQfXj0oI/VtIttXsZLW5hRopByCP/ADVlGRa78Erae63a\nXK1tG3LAncM/Kmh10l8IZdBv4rxtTE0yggJsyoz6/P0po0zSNKEMdo9xGnjxuzkqMAEgjj7Gmial\njWQMgyCRyQcGoITqHT7q/a30+G2jFmwLSzl8GPBGFA9c5NBPRwJDbpHCipEi4CjtigqfWltqCaG8\nWhNbxyzOBIs8W8FcY4+dWQQ/wtuZdIGqadrknhy2u2QyOSEZOeRngY+XvTBZZevOmhC8janbqiZ5\n3Zzj2FMFcs+sh1ZqsFrp8UkGkrlpbh+DNzhVX5Z5NQTfXyWUXSV2t+wW3EZ5I4HHt61eYPI0mA5C\n9snFaoLsPv8AvUEk5JcA8cZG480WnOlXX4PVLO4yQ0cyP244Yf8AiiPUfUump1B0/c2O8xfi4v8A\nmL3UcGpgw/SujNX0Trj8PpckimOMvHO/kEg9Rjs3PpV0aFq/Qqa1ZJ/qcrverEqNOwGM+uMfemiV\ntrKbQdMNjp9rvtkhPht6mU5yT8u1BkvXg1qXUtOvddgRY1R3j2YHiMvZSD27CgzSCyuNQ1KK0giL\n3Mz4VAOc/wDignoNNOnaHeiW8hgkku/Al2+Ziqgn09M0ETp0qpqSmGKOdFcEeLwMfPHag3JLuCRt\nPmQWsDhNphtVAcn1w3BPFSwDdWGpX1/OYdOtbbSrlQ80szHdn0GR24/c1AbWemdatLbTJdGvJIJy\npDQwMV3exwOPatSz9Ei/R+txy2mr3evyHV4miWIDhGwwyGA7nGRS2YNZAUBWYDdjGayEvxMYB3nY\nu4KCfU+woG93c2enWs1xcPFDCp8zEgDPzq4GGgz22saS1zZSZhkdsFePXt86YHWmySeLNDMYikZ4\nYNlvvUD+VARxQJqgwRQHUAAe2O1AWOFRM8mDlgB37fagb6reXFt4ItLZJnZsuWfaI0Hdjwcn2FAz\n0nWX1i4u4xY3VpFbv4eZ1x4vGdy/KgkriN2aMRlQoOW3DOR/5qwYr1P1tp2pdS3WnanKkGh24kRl\n2eaYgcb/AFxnnAqiv9Jno0dRLJPbtdQtkNPIALaMnODsPPpSjbdK0DTbWQXui+Gsco3BU5hPP5gP\nT7VkU74t6PZHpq/1N5ZZbwrtRnmOwDPOFJwPsK1xR5ybudw788VaC5X2WoJRULSBpAe5PA/aiinA\nZnHck4A70THq/Qr23/4Y0u4lmCpLBGA7epxjH60Du+WGOBvFlFuWOFcYyCfbPrUojri6k06xX8PF\nNfBUJ3ltzM3scVBjfVvVXVNit5dapNDZGQGK3shjeAe7YHIwAOT3zVggNTiu+orrR4p7m+upJFR7\nh3TPhggDaoBwRjnPH5hV0af0xotnoD3l5dWdrY2YjGLhwPEHoef996CC6m0HpuPpk3Wny2s9sJPH\nJ3AeI2D39T37UGU9QTDULuGPSLPwIyoVIYk2lj6/X70G2/DPp0hVudRuBLcwxhRGkeEjB9M+p96D\nSLprVHiieaAE+YxHkke4H1qUOIBawL4uAuc8nvj71AwjRtQ1eO78QNp9odyLju/qT8uf70Gb6r8S\npLzryy0vp+4NxYSSCEswI2u2Rn3OOD9qC4dVamen9NlaC7tUaIFvCmnHiy4HJXJxnOeDVwed+rOv\nLnqSyWO4jZSru/kc7ck+30GKosXw2+KmqaDJDY3jR3OmqNoRhtZAP6SP7Ggtmt63qbTJ1XLazJps\nlwBFblypEOAA7L2OWANS+xrnR2vW3UmjJeWp8wJSRf6WHeoJdSPMCRmgMq8DmgBpNsgUIxBGSccD\n70DczmS8MDWoe28MN4+f588rj980CktuJZYpFdlKZ4B4OfegQ1hpIrVjbsRMBhBj8x9qsHnX/hm3\n6y1O/u9V1ddPmS6aD8OkQdyxOSe4OMmqLUvwQsYY4Xjv7m4YEEhwFyMY7fXB5pRbvhp0jqfSMV7b\n6jqZvLGQAwxAEBDk54PuD6VkVb49a5DBpiaNaeF/FIaQDumOwpzMGDEZQZ5+VboR8In1I+9QWDY6\ngFn7jjHpQhtJEFbAGBnijT0P8H7qPVOh47a42yNaymPBOcDupoykep+m73V7g/8Av2itQowvJIx6\nj5/OpRjfUWrax051RPY6LqFy8YACkebO4Z7HjNWCY0ToW2utJbqPreW5na4O4R78cehY9+fQVKLX\n05p1ro97awC4kX8VFmJLeEKdoyfOxJPbHbHYVAz0rqKPWNauri9t1ktJgILYgEiNFJ/Op9STmrBB\ndeaFCo0y3jt444DI3jLE204Y5DD+9UPPhv0NaRtPq99mSLOy22nOfdh75oNC0vT7m1uJGvGiii3A\nW8UDbQAeDu9zUohLlhouqap1VciF4I1FtbxSthtobBIPuTn7VBJ2vUth1TYk2ULi9iALwyKQYz6Z\n9CM0Ft060/DWEcDHe2Mucdye9BFW3SekWt3LPb2cUTsd2UGCG9x7VYMzufhzdX/WmoXj+BPpx3I7\nXZMmXYckc8EVRKaP8I+nXikLQuxOQSTnBzj6UEjonw90XSrq3S3s7dplJcl1EhGDx396lFx1TQ4N\nVjaC7UNCU2lAO/8AvNWDCLp9X+E/WgWImXSp2LRq7eSRT3B9iP8AFSjd9P1+21TRodVsMS2rLmTb\nyUGOePXFQSltcLcW0c1vh4mXcjDswoDLdRm4FvISsgXeTghDzjAPbPyzmgVFxCzuiOC0WN3sM+5o\nDqySJmNg3rx7UFb60tNUubGJdFdEvhIdryflUFSM49TVgyTQenJemOorf/U4H1Fpp1edAh/hOQTu\nQ9375JA4q0bnbPBcxxT20wkjKkqYzlT9ayGWu38um2MbLEJ7iRtoUds+/wAgBzQeW/iHqi6j1Pdz\neL44HkL9txHtWhVUOVyvHNB2F9zQT8hUAhAdp5FCG0mSAzE4HGDRppvwL1bwOpJbEsFiuYyVX3de\nR98Zoy2ZtRgmjkSRZocEp51K7se3vUow/rfpFE124mVpfD4mk2MWdCc4A+VWCH1281/UmFnpklzP\nYRFBEG8uGC4yQfcn9alGgaJo95rRsbi53WaxwrHOm7BjYcHnvz/moJaw07pXSI5IW1K0CQnDhpAW\nB9R796CudY62msTRW+gadI8KnDXMkLLv9MA8HGOKC3dAXF1dRfh75f41moi4G0AdwcfTj7UFhv7R\njqMBV5AJFZHkR8FRjIx6CgpXVNjJ1JqNn07p26CztSJLlpIydyj2J+fGaC+afplrazqLa3SKNIgg\nx3IHYUEsBk4wQc4oK11L1z070/M9rql6wuVA3QopLcjNBDwfFboqeSO2W7kQNxuaEhQfnQLt8Tui\nreVoV1UeXnckTFT9DigHRuv+mbu9ZV1W3Nyc4IRlVl9O47/KgtU+s6baw+JcX1umRkAuM/p3oK/1\nt0rYdX6cqXKESqN8Ug/Mp9P1qwZ702mo9GdUTWJsmOn3EY8CAORGXJAwScjJ5q0bJDNLb6YklxbK\nsgA3wwndg9sDtWQN3aw3ZKTwLLEQOGORn6ehoG1vYAw3FikRt7JSuH3Hc/GSc5P0oHn4aO2uGuYy\niose044wBQIm6F1dwfh5ARs8R8L2BHGfnQHv9PS4PjxrGLtFKxysm4qD3oCxboIIo7e1jhQHzAYU\nJ8wP8VYM56j1ktaal1BMrS2sAaK1OQDD6eUepY9yfQVR5zv7hrmaSaRtzyHJY0DcE4BPIPb5UBwO\nO4oJYzFvzEYHAH9XvQhNZN7AEgDOSDRo/wBA1SXRdVtNQgb/AJUgfBHcZ/8AGaGPVlhPbarZ2t5C\nEeORBKje2RUrI1zYxTBhMinIwcjvUEcugWkO4AMisMEA8N69u3yoERrOhWNxNYy6hapcxAeJHM+D\n8u9ASLStLlm/EWdpZyxy+Z3RQ3I5B44oJKTT4blFWSNBEOeBg5+goFYbOK1TKhIxnIbGMH50Cpcy\no6AMrIcM2OD68Ggb6Lbbllu5Cd88hYBu6rztWglSNkfm4P70Gaat8Rba96w0vp3R2mhufxyi4kO3\nYyDOV9+f8VYMw+P0cP8Ax4JVuEKzW8bEr5tuMj0+lUZ7Y2X4288GK+towRlZXYqv9uKCQi6YmbT2\nu11GzaJWKnYxbBB49KCFnhubdiwL+U8OhP60ElpXUFxY6nDdXQF0qYbZKxwT9vWg3npb42aHcmC1\n1C3uLSQjEkpIdQfr3xUondP6x6e6tv7e101hczRzrNtaFiFC/wAxPYHtUF+lj8bYCTgMG59cUC4O\nBQQOo2eoXepFTeL/AKYQN1sEwWx6Fu+DQLX2kw3Ok3Vjas9qJ48Exd1PHb9KCE6R0G86Ut7mK71K\nK4gklM7TyKRIBjtjtjj96AOreudJsrMJbXksk8jBCbVdzRjONxBHP/3QQ9x1jcWGkERWWqXdpMPC\nt7x4wfFOOWPbA+fAqwZr8TJ9Qbp2ymvEjsrSTEVvawyHz45Lv6E4wPqaoyl8g91OKDlYEc8mgKW5\nPH7UE80f8PPHl7gDFAZkUjawUIQG8w5ouknAUbl9Dg59KK1X4Z9XXFvo8mlRXax3KHfBG8Rk3qe6\njHOc54+dMZO7jr/qK8vWtba4tYu38TwvDOMc8N60wPLbrW10PS7pnvrnUtbAKobgBUUk9jg8f/lM\nC2rydMdRSaRqWoLEbx4UefwxkL2BVvvn9Klgv3Tp0lPxFno6wJ4IVmEOMEHsf2xUD2e4ks7n+NGD\naCMu8q8lCMcEfPNA6tW/EwrMybEYZUHnI9Cf/FAzu7G4LXTWs38SRNqhs4Bz3z9KCO6x07UNT6al\ntNMvEs7xkC7nPlI9R2/egwbrDT+r+kupLCeK9nu3KBYGRy+QvdWFWCU6avtA6h1iKDqLRhpmpvkx\nz2p8JGb3z7k557VRX77TdHteuPBut401CAouyXGcdvmASaC069030brYaay/CwPFwWspRErfUN7U\nGZX2hWSiY6ZrMc0CvhUlBUk/UcGgiLq2mtG8F54yDwfDfIoGkrRsSZXwOB2zmgsvw06XHVfUcFvI\n22xQ753Ze4H8v3OBUo9a6XodjpltHbabDHZIhVsQqBuA9DxznFQTQUe5oDYGMnn7UEbpV3JqDyz+\nBJFbBtsXiDBf/qx6CgDXL42cSRwGM3UzBQrHGFzy32oG2i2kKTSI80lzMow0rqQoyew+3tQO59Kt\nJJRI9rEzgg7igz3z/egZ6paJdGGwW4eBXy7pGeXUdwT6A5qwebPjJrcOr9TvbWZQ2Onr+Gi2nIJH\ncj7+vyqigOuRk+vtxQAqEk4BU0ABj7j96CzzKxYD0GeM0CQG3OMAjkfOgbSZwzE4PJyfWi6caTdz\naffW95akrPFIrLg/tRHpGzs9C6t0W31FrO3Y43MrcbH9c/eloZ6v0JpWoKlrHHBbScSFEHYc5Pz7\n1NDXUoJrK1g0dvw9qsspW3nXaN6KMrHnH5ieSfan0U3Rr1uidaRbiwk8BUSS6naQkjc3ZcHaRnn7\n0wbja6lZX+nw3NvMksM+FXnPJ9DUCeoXj2hSG2t2km7op4U/f/FAvHaNePb3N0jRzRA7VD8Akc9u\n/FAvcxnawZQ3HYtjJoKfDFAdeub6Vo1dSULIBtTbgYOfU8jNWUVvrm5ih0m1urixhlsI5HJliOwR\n5/LkkHHJPamjHdQ0HWdemlutN0+YWBYtC0rHDhjwVz3zWgx1n4e9U6QE8XT5Zd//APR5se9XBXbj\nS9S0zAvbO5tyWKAOhG4/KpQ3ZHXO5JFK98qRUGhfCbph77Uvx91pv463wVjR0LR7s483796WjW7r\nTrXpHWrSW2YK7lmXTbaIFpCRjjHOOSeeBipaNLtXuIre3R43lnkGXYADZnnmoH6Dkbzn6UDGWe9a\n/hKG3jsW8riQMJS3svp86B6zgMQmDtGT8qDNb6XUpOoPx72tzOkjFYowOduDwDjj70Gg6Wsq2KNe\nAJKRuKk52fIn5DvVkENrvW+iaSAsl0txI2Asdud5Yk4A4pgzv4l9ST6JZSXbyyprWpw+FFa5G21g\nz5icfzH3pgwCSQlh688+tUEwjjngZ7UHAHuWAHpn+1AXj5frQWXOGBZcKSe3c/rQIyAtnI27eBj/\nADQIMAuH8vHBB9DQwVpPLjOckEZ/ahi9/Czqj/S9VhtLm6aG1uZFUsT5VOfX5Gpg9GiNJArxsrxs\nv1BHypYGF7pljeG3kvLZSlqzNGGxhSRjP6GoG1yLUWiWc2nSPA4KJGItyYHYHHarop3wu0jWYNUv\nzriNBp8ErraRMANxJ/N7nA7ZqDUHgSQLvAbacjI7H3oOuIFuYzGS68jJRyp4+lA0m0yDwGjiTw3b\nzBwTuDe+TQVbV+mLmW1NtbSok9weZiC2zPLEZ+/FBM6Xo40/TYdL8Jr21G4vJcuCck55HbFWUKQ/\nh4tR/DTz2o8TK21qmMgKOf8AfpmrokljG1i5BHI57D5VNorut9Lab1LA638W6H8sboNrqQckq3pn\ntV0RWsfD6K7SGC3vTFahQJY2iVmkI9d+Mimie0Hp2DQ7AQacio3JZgqjcT3zxk1KHGldPWtnqMup\nS5uNTmGGnk5KjGNqf0r8qgmkhVGcquGblm96BDUZZYLGVrdN8+MIvux7UGKTdXdbaRrFvbaxbWN4\nd58BmwCjHPORycLx2q4LNe9S9TdN6I13qkWmzSXdwBCGlO4hiMKAB2A9ag0WySQwpLLtMjDcQBhV\nz6CgoHxF17XbnUYunulgsUsu4TTvjIUAEhR+x4qwZwtkOi7651PXJobm4tohHbQhdgecjnaPZeOf\neqMy1vWLvWNQlvb+ZpJpWyT2A9gB6Cgjy5AO4A5oAGRgBR39aA7Z8MkFtxPY0AbV9UGfpQWhj5Tk\nBQGxuBoEGG1iQCyHvj1oELgSkK6oRnIBIxzRdJ28Q3+fHiAds96LoH3AF1wCfyijNbF8JviI1rbQ\n6Pq/iSopxFOx5Uf0n3qUbWQk8II2SRyDPuCDUHMpSIiJQSBwDwKAgTxApnEbyIQ+APyn0+9A5B3A\ngfmoEWgcb3VlMpGFYjt+negSs7zxH/C3RWO9UElM8SAHG5fl/agNdXcEbJAZ1WadvDQDJO7Gf7c0\nED1dqWv2enzw6Rb24nZfJd3EwREHqxyMZHzOKBbT7H8PZWTK5uZ9o3Xm1SzEry5PsT7UFF+JnUez\nSZ9LttRs2km2m5KSFWXDZI491AyBzVwK6J8T7CRtPjee0tbaGAtdNISdoXgLEo5JJxyfSmC6aF1f\no2vELZXDJOxwkMybHYe4HtUFiJWJd8rKqjuTQcZV8SNI0dy43BlGVA+ZoBniE0RU7tp77Tg5zQUv\n4hNrU2tdNWOhylPEnaS5UHGYlAzn5cn74oHGt6l0z07k6nJC123HhKPFlbPptGTj9q0Kx0XMvU+t\ny6vqQtpWlZo4LOdGDWsak8Aflycgk+v2qC8a1q8OnaXLPOz2kCIWkZ+CqjgYx6n0xTBkmp9Sabp0\nKdRyI5vJkaGw08MVKIDw8jA557896QY1q2o3eqXr3N7O8skjnlnzgn5e1UMCGV/MOM0BJFOVwfX0\noFtgZTjkg8UBtpOeO1B3hg85FBZXVMM+VJAA2+h96BO4IMJ/p7qvuKBKacmOKB5CYYx5UzgDPfHz\noGwD5OApJHAHrQEAk8NWdNpx+XdkA0AIGhkGWOQcgg8UGw/DP4kmwhi07WCTZqAiSbstGc4+pFKN\nvs5o7q2Sa3kWaJxkOp4NZDOHUh/qL2k8LW78eG7kbZv+0+/yoEZp7fUpX/BXpgvYZTCSRtO7vtwe\n/vQdY6jeyatPp91FEPw8aSNMoYbi2cADt6Z70DS60KW7luJdV1JniJzBtURG2b0KN7+/vQKSWUWn\nLLqN3cSLP4ex5Y8jxiPykr23+nzzigpXUPVOu6Vqmmf8UWttb9OXDqkjRnfI3H849uRkDOKC0axL\nFr+nLB0rrUMM0bqCIGGGX1AH09qsGc9UfBiTV9Vhu9Pu5oPGLNeG6bczN7jHvVEr058Gre3sLeDV\nrmOdo3LmSFNjEH+XdntQXO51XQOl5YrCKGWa8SMYS3tzMyLjjJHb9alDqz1S5ktJ7nVdLmSVDiOO\nNfEMiE4Xy+h9xUDm11CaTxEOn3VmpHFxKFCr9s5GPmKAus6jb9M6RJf3c88yxpjcxL7uM5OO3HrQ\nVTSupoOuYdZMTSpptriNFtXKXDA/Pjhs9h2xQOJ7Xpnpa2S91WK2swqjbGw3ysfcnuxrQsGmapYv\npwvra1FtDL52Mi+Gx49sZoMb+LXV0t+jWl3OYLGTO2zjx4h2nyszHsG4/Sgxt5ZJmEsjl3PB3GgS\nlyXBxkDnNADseB2PsaA8SoXQyFgmQCV70B5R/EIjYmPJxnvigEKcYDfrQF2/X9aCwqC7l2zwfT/F\nAJJ5747E5oGTqZArKOfccftQHZWwmOD23Z4NAEkTEBmwR7g5AoG7KSSE5HyFAMTyQsMHa/cEcEc5\noL58P+v7rppdryPNAXx+GfsQe7Z9D2/Wg3zSdX0fqzT08F433eYwscOpHt9PcVkQPW632mX9vfWd\nrbXiRgrIdu2eHIwGD57+nIoKdc/ELVdC0u5afp27SUtta5vJMMzk+UDjzYHtQWDpj4gxXmif/wAi\ntXZkGZzFGW8MehZO+PmM0Fibr/poWQmF6xXA2xmFg59sKRk0C2nQP1KFvdb0vwIUJNtDKcsVP8zD\n0Jx2oJDTNA0vR5p7qzs44pJOXkUc/SgNfa/pNvbF5L2JgTsCo2WJzjGKA1jcNcxOF/m/I0zbt4+g\nxgenNA5s7CCxWWYQxpNLgyMiY3nt2oHajcuexAoEL1C1uyFkVHO1ixHb17/KgwT4rdXWep6oul2O\npywaTYqVLxDyySDjaM9x6Z+tWDPdB1TW7Wa9sumpGlursqMQRlpXwd3BA4571RcdN0i41G7h/wBS\nmNxqdkwn1O6u5/4cAXlYgcnngE/pQNvih8S211obHRyYbWInfJG/Ex9MD2+tBmNzcTXTtJcSSSSH\nH5jngDAH0oEdxbg5A9wKAzPwO+fegSLfLJoFoR5fUfegXOcHGAT8qAM+XaBzQCCwGDuyPlQWJpV3\n7Q5TIweM7u/f9qA9vNYpFML2KaR8YiaJgAh55PvQRvnYoqA+MThcDnPai4PKWQlH3K6tggjnPaiE\nmz4ZznBPfFAVWG0AZDH1z2HzoE7lhv8AJIHI/mGRQI8AEeuOMGgndN6pu7V4RJLKY4WDLhypXj0o\nNL6d+MMS4ttetDdQgDNwAPEwPRh2NZGkabrvTXVZiexvba5aI+ILWZQG3Y4IDdvtQScvTdjK8cht\nFjkQ+VlYgqPXGKA17daV07apJrV9CsZbELTgbu3YY78UEHf9evJ4K9P6JqGoiR1XxjCUjAJ5OT3N\nBM9YdSWPTenwy3t7bWbSuBunUthfUhRyT+1BA2vXnS/jGdeo9LnIHljeLwSCe5zgmgejrOz1S3kG\nhazoaXYGAJ5Sw3e38uaCsJfX02rPD1XfXtvcCXdBJGjLbOO+EK88Y7nIoLB1H1Bb6WkN1ddSQ29i\no/5MZEjzt7DGTjj2FXNGUfEH4wRaxCtnp2kwGGM7llvBvIOO4XOAe/fNMwZbqusalfLBHfzSvFH+\nSIgKo+igYqiwWfWV30rpp03p6exJnUPJexQnxuR+Ulu2PkKCrT6jcSiTxZnbxCWcFidxPJJ96BBD\nlQ35fbFAbahBHJIGO+KAIwg5YnB455oDkKcbW7UAOowSMjOORQcCVXPp+9AqDkHcDmgMNpHPcUBT\nuzQWCJXcFvKR2Y0CMiqjnz4UcH/6oG8gKluwxyAfSjQ6ylEJBJfv37/OiYQMjbjkZXOSP/FEELAE\nkNn1waBCXdjORnPoc80CZeTb5wQe2BQAm4y7jlhjvQcWO3cW4GB7ZpgGC5a3uUeNyGQ5GCR+45pg\nt+l/EzqLTgxj1O5Zc4CSOXCj70wOE+Jd/Pq0V7qVvb3bISAWUBhnuc+/2pgvkHx0soLaNIdKkEiq\nR5yDg47cYpgresfELSNc1n8VrFtAw2ZBiiywwcgeb14x2xTBYNA13ozUo/GOqWVizDc1nf6crIh/\n7wOf1rOURvVupdE6ncpFeakiSWsZdbjSLfw1Zs+VVyMHA75xWsFDHU0idTJdf65rT28YKpPvHjKp\n9Bk49qYK/rGpXF/qU9zPdyzyyMSJJAAx9ifnVlwNZ7vxYEh8GAEHO8DDH5H5U0IPK8jHxSzFQAMn\nOKgJkFwPT6UBlAII5z3zQCjnA5OKBXeuAT9KABMA208DtxQHRhzgUC3KjJx9z2oAP6/L3NAHC8Hg\n/XNAcNtGe4oEy5yeaCdLk7yx2qQCAO1AmXZSSexHbPNAhJuLJkgjvzRonuAJy2cd8UCbyOi453Dj\n7GiYTZyVPPl74oYLJIyq68EH3Gf3oYTDEjLbiP1FEDHOUOdgOfXIBFAm77j5Mnng/WgLI5UE8Eg9\niO9AnuJbuQx5wOBQFRxuIbOc54PrQK28ws76F722EyI4d4HJUOPb35yKBm8oeQsi4BPbPb71RyYI\nHmPGRg00GRsbsHIPY0Bg52AEEseBUCQJyOPXtQDtcdvvjtQCQ/GBwKAuXU4PrQBvZE5IGeO1Affj\nBJ57UBvEO045HagFWAAzktQKIzBeMg0C5kz5mOG/WgMrDJJ7jmgEnIyOccYoA3cEMRj05oC7/wDe\nBQTduzEoNxwcZGaAJOWfPNAlISVOT60aIQfkj+amgJ3bnnigKeFGPQUCf/x0Smw4V8exogX/AOY3\n+/SgKxKxeU459KAgJOckntQJkkcgkGgAAFFz/XQEmJaY7jnk96BM9yPQelAf/wCX7UBv/jagGP8A\nKB6ZoDf00HMfO/0oAH5TQA/5moECSMDPFAvGASMjPP8AigVX+b60BW7/AHoHEJJD55oDd4snv70C\nsSjCcDmgVAAbgYoGYJ3nk9qBUAYHAoP/2Q==\n" } }, "id": "uid6" } ], "nbformat": 4, "nbformat_minor": 5, "metadata": {} } ================================================ FILE: test/ipynb/simple.out.native ================================================ Pandoc Meta { unMeta = fromList [ ( "jupyter" , MetaMap (fromList [ ( "nbformat" , MetaString "4" ) , ( "nbformat_minor" , MetaString "5" ) ]) ) ] } [ Div ( "uid1" , [ "cell" , "markdown" ] , [] ) [ Header 1 ( "lorem-ipsum" , [] , [] ) [ Str "Lorem" , Space , Str "ipsum" ] , Para [ Strong [ Str "Lorem" , Space , Str "ipsum" ] , Space , Str "dolor" , Space , Str "sit" , Space , Str "amet," , Space , Str "consectetur" , Space , Str "adipiscing" , Space , Str "elit." , Space , Str "Nunc" , Space , Str "luctus" , SoftBreak , Str "bibendum" , Space , Str "felis" , Space , Str "dictum" , Space , Str "sodales." ] ] , Div ( "uid2" , [ "cell" , "code" ] , [] ) [ CodeBlock ( "" , [ "python" ] , [] ) "print(\"hello\")" ] , Div ( "uid3" , [ "cell" , "markdown" ] , [] ) [ Header 2 ( "pyout" , [] , [] ) [ Str "Pyout" ] ] , Div ( "uid4" , [ "cell" , "code" ] , [ ( "execution_count" , "2" ) ] ) [ CodeBlock ( "" , [ "python" ] , [] ) "from IPython.display import HTML\nHTML(\"\"\"\n\nHTML\n\"\"\")" , Div ( "" , [ "output" , "execute_result" ] , [ ( "execution_count" , "2" ) ] ) [ RawBlock (Format "html") "\nHTML\nhello" ] ] , Div ( "uid6" , [ "cell" , "markdown" ] , [ ( "tags" , "[\"foo\",\"bar\"]" ) ] ) [ Header 2 ( "image" , [] , [] ) [ Str "Image" ] , Para [ Str "This" , Space , Str "image" , Space , Image ( "" , [] , [] ) [ Str "the" , Space , Str "moon" ] ( "uid6-lalune.jpg" , "" ) , Space , Str "will" , Space , Str "be" , Space , Str "included" , Space , Str "as" , Space , Str "a" , Space , Str "cell" , SoftBreak , Str "attachment." ] ] ] ================================================ FILE: test/jats-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "John" , Space , Str "MacFarlane" ] , MetaInlines [ Str "Anonymous" ] ] ) , ( "copyright" , MetaMap (fromList [ ( "holder" , MetaString "MacFarlane et al" ) , ( "statement" , MetaString "\169 2023, MacFarlane et al" ) , ( "year" , MetaString "2023" ) ]) ) , ( "license" , MetaMap (fromList [ ( "link" , MetaString "https://creativecommons.org/licenses/by/4.0/" ) , ( "text" , MetaString "This document is distributed under a Creative Commons Attribution 4.0 International license." ) , ( "type" , MetaString "open-access" ) ]) ) , ( "references" , MetaList [ MetaMap (fromList [ ( "id" , MetaString "note_1" ) ]) , MetaMap (fromList [ ( "id" , MetaString "note_longnote" ) ]) ] ) , ( "title" , MetaInlines [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , Space , Str "John" , Space , Str "Gruber's" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , Header 1 ( "headers" , [] , [] ) [ Str "Headers" ] , Header 2 ( "level-2-with-an-embedded-link" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , SoftBreak , Link ( "" , [] , [] ) [ Str "embedded" , SoftBreak , Str "link" ] ( "/url" , "" ) ] , Header 3 ( "level-3-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 4 ( "level-4" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Header 5 ( "level-5" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 1 ( "level-1" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Header 2 ( "level-2-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 3 ( "level-3" , [] , [] ) [ Str "Level" , Space , Str "3" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 2 ( "level-2" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , Space , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , Space , Str "item." , Space , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , Space , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , Space , Str "a" , Space , Str "list" , Space , Str "item." ] , Para [ Str "Here's" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , Space , Str "*" , Space , Str "criminey." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Strong [ Str "hard" , Space , Str "line" , Space , Str "break" , LineBreak ] , Str "here." ] , Header 1 ( "statements" , [] , [] ) [ Str "Statements" ] , Header 2 ( "" , [] , [] ) [ Str "CAP" , Space , Str "TITLE" ] , Para [ Str "Some" , Space , Str "text" , Space , Str "to" , Space , Str "make" , Space , Str "this" , Space , Str "regular" ] , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , Space , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , BlockQuote [ Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "item" , Space , Str "one" ] ] , [ Para [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] ] , BlockQuote [ Para [ Str "nested" ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" , Space , Str "2" , Space , Str ">" , Space , Str "1." ] , Para [ Str "Box-style:" ] , BlockQuote [ Para [ Str "Example:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" ] , BlockQuote [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "do" , Space , Str "laundry" ] ] , [ Para [ Str "take" , Space , Str "out" , Space , Str "the" , Space , Str "trash" ] ] ] ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "nested" , Space , Str "one:" ] , BlockQuote [ Para [ Str "Joe" , Space , Str "said:" ] , BlockQuote [ Para [ Str "Don't" , Space , Str "quote" , Space , Str "me." ] ] ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote" , Space , Str "inside" , Space , Str "a" , Space , Str "paragraph." , Space , Str "Start" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "is" , Space , Str "here," , Space , Str "and" , Space , Str "the" , Space , Str "quote" , Space , Str "is" , Space , Str "below:" ] , BlockQuote [ Para [ Str "My" , Space , Str "block" , Space , Str "quote" ] ] , Para [ Str "End" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" ] , Header 1 ( "formulae" , [] , [] ) [ Str "Formulae" ] , Header 2 ( "inline-formulae" , [] , [] ) [ Str "Inline" , Space , Str "formulae" , Space , Str "with" , Space , Emph [ Str "inline-formula" ] ] , Para [ Strong [ Str "Inside" , Space , Str "a" , Space , Str "paragraph" ] , Str "," , Space , Str "should" , Space , Str "be" , Space , Str "natively" , Space , Str "coded" , Space , Str "inside" , Space , Str "the" , Space , Str "paragraph," , Space , Str "and" , Space , Str "show" , Space , Str "inline:" ] , Para [ Str "Einstein" , Space , Str "showed" , Space , Str "that" , Space , Math InlineMath "e=mc^2" , Str "." , SoftBreak , Str "This" , Space , Str "formula" , Space , Str "is" , Space , Str "important" , Space , Str "because\8230" ] , Header 2 ( "disp-formulae" , [] , [] ) [ Str "Block" , Space , Str "formulae" , Space , Str "with" , Space , Emph [ Str "disp-formula" ] ] , Para [ Strong [ Str "Inside" , Space , Str "a" , Space , Str "paragraph" ] , Str "," , Space , Str "should" , Space , Str "be" , Space , Str "natively" , Space , Str "coded" , Space , Str "inside" , Space , Str "the" , Space , Str "paragraph," , Space , Str "but" , Space , Str "show" , Space , Str "as" , Space , Str "block:" ] , Para [ Str "Einstein" , Space , Str "showed" , Space , Str "that" , Space , Math DisplayMath "e=mc^2" , Str "." , SoftBreak , Str "This" , Space , Str "formula" , Space , Str "is" , Space , Str "important" , Space , Str "because\8230" ] , Para [ Strong [ Str "Outside" , Space , Str "a" , Space , Str "paragraph" ] , Str "," , Space , Str "should" , Space , Str "show" , Space , Str "as" , Space , Str "block:" ] , Para [ Str "Einstein" , Space , Str "showed" , Space , Str "that" ] , Div ( "" , [ "disp-formula" ] , [] ) [ Para [ Math DisplayMath "e=mc^2" ] ] , Para [ Str "This" , Space , Str "formula" , Space , Str "is" , Space , Str "important" , Space , Str "because\8230" ] , Header 2 ( "no-formula-wrapper" , [] , [] ) [ Str "Without" , Space , Str "a" , Space , Str "formula" , Space , Str "wrapper" ] , Para [ Strong [ Str "Inside" , Space , Str "a" , Space , Str "paragraph" ] , Str "," , Space , Str "should" , Space , Str "show" , Space , Str "inline:" ] , Para [ Str "Einstein" , Space , Str "showed" , Space , Str "that" , Space , Math InlineMath "e=mc^2" , Str "." , SoftBreak , Str "This" , Space , Str "formula" , Space , Str "is" , Space , Str "important" , Space , Str "because\8230" ] , Para [ Strong [ Str "Outside" , Space , Str "a" , Space , Str "paragraph" ] , Str "," , Space , Str "should" , Space , Str "show" , Space , Str "as" , Space , Str "block:" ] , Para [ Str "Einstein" , Space , Str "showed" , Space , Str "that" ] , Para [ Math DisplayMath "e=mc^2" ] , Para [ Str "This" , Space , Str "formula" , Space , Str "is" , Space , Str "important" , Space , Str "because\8230" ] , Header 2 ( "misc-formulae" , [] , [] ) [ Str "Formulae" , Space , Str "with" , Space , Str "miscelaneus" , Space , Str "elements" ] , Para [ Str "Regardless" , Space , Str "of" , Space , Str "whether" , Space , Str "or" , Space , Str "not" , Space , Str "they" , Space , Str "are" , Space , Str "inside" , Space , Str "a" , Space , Str "paragraph," , Space , Str "should" , Space , Str "be" , Space , Str "wrapped" , Space , Str "in" , Space , Str "a" , Space , Str "div," , Space , Str "and" , Space , Str "displayed" , Space , Str "in" , Space , Str "a" , Space , Str "block:" ] , Para [ Strong [ Str "Inside" , Space , Str "a" , Space , Str "paragraph:" ] ] , Para [ Str "Einstein" , Space , Str "definitely" , Space , Str "showed" , Space , Str "that" ] , Div ( "" , [ "disp-formula" ] , [] ) [ Para [ Str "Abstract" , Space , Str "text" ] , Para [ Image ( "graphic001" , [ "This" , "is" , "the" , "role" , "of" , "the" , "graphic" ] , [] ) [ Str "Alternative" , Space , Str "text" , Space , Str "of" , Space , Str "the" , Space , Str "graphic" ] ( "https://lh3.googleusercontent.com/dB7iirJ3ncQaVMBGE2YX-WCeoAVIChb6NAzoFcKCFChMsrixJvD7ZRbvcaC-ceXEzXYaoH4K5vaoRDsUyBHFkpIDPnsn3bnzovbvi0a2Gg=s660" , "This is the title of the graphic" ) ] , Para [ Math DisplayMath "e=mc^2" ] ] , Para [ Str "." , SoftBreak , Str "This" , Space , Str "formula" , Space , Str "is" , Space , Str "important" , Space , Str "because\8230" ] , Para [ Strong [ Str "Outside" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph:" ] ] , Para [ Str "Paragraph" , Space , Str "before:" , Space , Str "Einstein" , Space , Str "showed" , Space , Str "that" ] , Div ( "" , [ "disp-formula" ] , [] ) [ Para [ Str "Abstract" , Space , Str "text" ] , Para [ Image ( "graphic003" , [] , [] ) [ Str "Alternative" , Space , Str "text" , Space , Str "1" ] ( "Title 3" , "" ) ] , Para [ Math DisplayMath "e=mc^2" ] ] , Para [ Str "This" , Space , Str "formula" , Space , Str "is" , Space , Str "important" , Space , Str "because\8230" ] , Figure ( "fig-1" , [] , [] ) (Caption Nothing [ Plain [ Str "bar" ] ]) [ Plain [ Str "alternative-decription" ] , Para [ Image ( "" , [] , [] ) [] ( "foo.png" , "" ) ] ] , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n\nthis code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "tight:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "tight:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Loose" , Space , Str "using" , Space , Str "tabs:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and" , Space , Str "using" , Space , Str "spaces:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , SoftBreak , Str "dog's" , Space , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Para [ Str "List" , Space , Str "styles:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] , [ Para [ Str "three" ] ] ] , OrderedList ( 1 , LowerRoman , DefaultDelim ) [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] , [ Para [ Str "three" ] ] ] , Header 2 ( "nested" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here's" , Space , Str "another:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing" , Space , Str "but" , Space , Str "with" , Space , Str "paragraphs:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "tabs-and-spaces" , [] , [] ) [ Str "Tabs" , Space , Str "and" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] ] ] ] ] , Header 2 ( "fancy-list-markers" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Autonumber." ] ] , [ Para [ Str "More." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Nested." ] ] ] ] ] , Header 2 ( "definition" , [] , [] ) [ Str "Definition" ] , DefinitionList [ ( [ Str "Violin" ] , [ [ Para [ Str "Stringed" , Space , Str "musical" , Space , Str "instrument." ] , Para [ Str "Torture" , Space , Str "device." ] ] ] ) , ( [ Str "Cello" , Strong [ LineBreak ] , Str "Violoncello" ] , [ [ Para [ Str "Low-voiced" , Space , Str "stringed" , Space , Str "instrument." ] ] ] ) ] , Header 2 ( "list-inside-paragraph" , [] , [] ) [ Str "List" , Space , Str "inside" , Space , Str "a" , Space , Str "paragraph" ] , Para [ Str "Start" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph." ] , BulletList [ [ Para [ Str "Red" ] ] , [ Para [ Str "Blue" ] ] ] , Para [ Str "End" , Space , Str "of" , Space , Str "paragraph." ] , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , SoftBreak , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , SoftBreak , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "Empty" , Space , Strong [] , Space , Str "and" , Space , Emph [] , Str "." ] , Para [ Str "An" , SoftBreak , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , SoftBreak , Str "link" ] ( "/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , SoftBreak , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , SoftBreak , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , SmallCaps [ Str "small" , Space , Str "caps" ] , Str "." ] , Para [ Str "These" , Space , Str "are" , Space , Str "all" , Space , Str "underlined:" , Space , Str "foo" , Space , Str "and" , Space , Str "bar." ] , Para [ Str "These" , Space , Str "are" , Space , Str "all" , Space , Str "strikethrough:" , Space , Strikeout [ Str "foo" ] , Str "," , SoftBreak , Strikeout [ Str "bar" ] , Str "," , Space , Str "and" , Space , Strikeout [ Str "baz" ] , Str "." ] , Header 1 ( "smart-quotes-ellipses-dashes" , [] , [] ) [ Str "Smart" , Space , Str "quotes," , Space , Str "ellipses," , Space , Str "dashes" ] , Para [ Str "\"Hello,\"" , Space , Str "said" , Space , Str "the" , Space , Str "spider." , Space , Str "\"'Shelob'" , Space , Str "is" , Space , Str "my" , Space , Str "name.\"" ] , Para [ Str "'A'," , Space , Str "'B'," , Space , Str "and" , Space , Str "'C'" , Space , Str "are" , Space , Str "letters." ] , Para [ Str "'Oak,'" , Space , Str "'elm,'" , Space , Str "and" , Space , Str "'beech'" , Space , Str "are" , Space , Str "names" , Space , Str "of" , Space , Str "trees." , Space , Str "So" , Space , Str "is" , Space , Str "'pine.'" ] , Para [ Str "'He" , Space , Str "said," , Space , Str "\"I" , Space , Str "want" , Space , Str "to" , Space , Str "go.\"'" , Space , Str "Were" , Space , Str "you" , Space , Str "alive" , Space , Str "in" , Space , Str "the" , Space , Str "70's?" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "quoted" , Space , Str "'" , Code ( "" , [] , [] ) "code" , Str "'" , Space , Str "and" , Space , Str "a" , SoftBreak , Str "\"" , Link ( "" , [] , [] ) [ Str "quoted" , SoftBreak , Str "link" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "\"." ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one---two" , Space , Str "---" , Space , Str "three--four" , Space , Str "--" , Space , Str "five." ] , Para [ Str "Dashes" , Space , Str "between" , Space , Str "numbers:" , Space , Str "5-7," , Space , Str "255-66," , Space , Str "1987-1999." ] , Para [ Str "Ellipses...and." , Space , Str "." , Space , Str ".and" , Space , Str "." , Space , Str "." , Space , Str "." , Space , Str "." ] , Header 1 ( "latex" , [] , [] ) [ Str "LaTeX" ] , BulletList [ [ Para [ Str "\\cite[22-23]{smith.1899}" ] ] , [ Para [ Str "\\doublespacing" ] ] , [ Para [ Str "$2+2=4$" ] ] , [ Para [ Str "$x" , Space , Str "\\in" , Space , Str "y$" ] ] , [ Para [ Str "$\\alpha" , Space , Str "\\wedge" , Space , Str "\\omega$" ] ] , [ Para [ Str "$223$" ] ] , [ Para [ Str "$p$-Tree" ] ] , [ Para [ Str "$\\frac{d}{dx}f(x)=\\lim_{h\\to" , Space , Str "0}\\frac{f(x+h)-f(x)}{h}$" ] ] , [ Para [ Str "Here's" , Space , Str "one" , Space , Str "that" , Space , Str "has" , Space , Str "a" , Space , Str "line" , Space , Str "break" , Space , Str "in" , Space , Str "it:" , Space , Str "$\\alpha" , Space , Str "+" , Space , Str "\\omega" , Space , Str "\\times" , SoftBreak , Str "x^2$." ] ] ] , Para [ Str "These" , Space , Str "shouldn't" , Space , Str "be" , Space , Str "math:" ] , BulletList [ [ Para [ Str "To" , Space , Str "get" , Space , Str "the" , Space , Str "famous" , Space , Str "equation," , Space , Str "write" , SoftBreak , Code ( "" , [] , [] ) "$e = mc^2$" , Str "." ] ] , [ Para [ Str "$22,000" , Space , Str "is" , Space , Str "a" , Space , Emph [ Str "lot" ] , Space , Str "of" , Space , Str "money." , Space , Str "So" , Space , Str "is" , Space , Str "$34,000." , Space , Str "(It" , SoftBreak , Str "worked" , Space , Str "if" , Space , Str "\"lot\"" , Space , Str "is" , Space , Str "emphasized.)" ] ] , [ Para [ Str "Escaped" , Space , Code ( "" , [] , [] ) "$" , Str ":" , Space , Str "$73" , Space , Emph [ Str "this" , Space , Str "should" , Space , Str "be" , SoftBreak , Str "emphasized" ] , Space , Str "23$." ] ] ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "LaTeX" , Space , Str "table:" ] , Para [ Str "\\begin{tabular}{|l|l|}\\hline" , Space , Str "Animal" , Space , Str "&" , Space , Str "Number" , Space , Str "\\\\" , Space , Str "\\hline" , Space , Str "Dog" , Space , Str "&" , SoftBreak , Str "2" , Space , Str "\\\\" , Space , Str "Cat" , Space , Str "&" , Space , Str "1" , Space , Str "\\\\" , Space , Str "\\hline" , Space , Str "\\end{tabular}" ] , Header 1 ( "special-characters" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Para [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Para [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Para [ Str "section:" , Space , Str "\167" ] ] , [ Para [ Str "set" , Space , Str "membership:" , Space , Str "elem" ] ] , [ Para [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Header 2 ( "explicit" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , SoftBreak , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , SoftBreak , Str "and" , Space , Str "title" ] ( "/url/" , "title" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , SoftBreak , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by two spaces" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , SoftBreak , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by a tab" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , SoftBreak , Str "and" , Space , Str "title" ] ( "/url/" , "title with \"quotes\" in it" ) ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , SoftBreak , Str "and" , Space , Str "title" ] ( "/url/" , "title with single quotes" ) ] , Para [ Str "Email" , Space , Str "link" , Space , Str "(nobody" , Space , Str "[at]" , Space , Str "nowhere.net)" ] , Para [ Link ( "" , [] , [] ) [ Str "Empty" ] ( "" , "" ) , Str "." ] , Header 2 ( "reference" , [] , [] ) [ Str "Reference" ] , Para [ Str "Foo" , SoftBreak , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , SoftBreak , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , SoftBreak , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , SoftBreak , Str "[brackets]" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "b" ] ( "/url/" , "" ) , Space , Str "by" , SoftBreak , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Para [ Str "Indented" , SoftBreak , Link ( "" , [] , [] ) [ Str "once" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , SoftBreak , Link ( "" , [] , [] ) [ Str "twice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , SoftBreak , Link ( "" , [] , [] ) [ Str "thrice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "This" , Space , Str "should" , Space , Str "[not]" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , CodeBlock ( "" , [] , [] ) "[not]: /url" , Para [ Str "Foo" , SoftBreak , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "Title with \"quotes\" inside" ) , Str "." ] , Para [ Str "Foo" , SoftBreak , Link ( "" , [] , [] ) [ Str "biz" ] ( "/url/" , "Title with \"quote\" inside" ) , Str "." ] , Header 2 ( "with-ampersands" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here's" , Space , Str "a" , SoftBreak , Link ( "" , [] , [] ) [ Str "link" , SoftBreak , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , SoftBreak , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "http://att.com/" , "AT&T" ) , Str "." ] , Para [ Str "Here's" , Space , Str "an" , SoftBreak , Link ( "" , [] , [] ) [ Str "inline" , SoftBreak , Str "link" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here's" , Space , Str "an" , SoftBreak , Link ( "" , [] , [] ) [ Str "inline" , SoftBreak , Str "link" , Space , Str "in" , Space , Str "pointy" , Space , Str "braces" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Header 2 ( "autolinks" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , SoftBreak , Link ( "" , [] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Para [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Para [ Link ( "" , [] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Para [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , Space , Str "nobody" , Space , Str "[at]" , Space , Str "nowhere.net" ] , BlockQuote [ Para [ Str "Blockquoted:" , SoftBreak , Link ( "" , [] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , Para [ Str "Auto-links" , Space , Str "should" , Space , Str "not" , Space , Str "occur" , Space , Str "here:" , SoftBreak , Code ( "" , [] , [] ) "" ] , CodeBlock ( "" , [] , [] ) "or here: " , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Str "\"Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune\"" , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Para [ Image ( "" , [] , [] ) [] ( "lalune.jpg" , "Voyage dans la Lune" ) ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , SoftBreak , Image ( "" , [] , [] ) [] ( "movie.jpg" , "" ) , SoftBreak , Str "icon." ] , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference" , Link ( "" , [] , [] ) [ Str "(1)" ] ( "#note_1" , "" ) , Str "," , SoftBreak , Str "and" , SoftBreak , Str "another" , Link ( "" , [] , [] ) [ Str "(longnote)" ] ( "#note_longnote" , "" ) , Str "." , SoftBreak , Str "This" , Space , Str "should" , Space , Emph [ Str "not" ] , Space , Str "be" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Space , Str "because" , Space , Str "it" , SoftBreak , Str "contains" , Space , Str "a" , Space , Str "space^(my" , Space , Str "note)." ] , Para [ Link ( "" , [] , [] ) [ Str "(1)" ] ( "#ref_1" , "" ) , Space , Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "footnote." , Space , Str "It" , Space , Str "can" , SoftBreak , Str "go" , Space , Str "anywhere" , Space , Str "in" , Space , Str "the" , Space , Str "document," , Space , Str "not" , Space , Str "just" , Space , Str "at" , Space , Str "the" , Space , Str "end." ] , Para [ Link ( "" , [] , [] ) [ Str "(longnote)" ] ( "#ref_longnote" , "" ) , Space , Str "Here's" , SoftBreak , Str "the" , Space , Str "other" , Space , Str "note." , Space , Str "This" , Space , Str "one" , Space , Str "contains" , Space , Str "multiple" , Space , Str "blocks." ] , Para [ Str "Caret" , Space , Str "characters" , Space , Str "are" , Space , Str "used" , Space , Str "to" , Space , Str "indicate" , Space , Str "that" , Space , Str "the" , Space , Str "blocks" , Space , Str "all" , Space , Str "belong" , Space , Str "to" , SoftBreak , Str "a" , Space , Str "single" , Space , Str "footnote" , Space , Str "(as" , Space , Str "with" , Space , Str "block" , Space , Str "quotes)." ] , CodeBlock ( "" , [] , [] ) " { }" , Para [ Str "If" , Space , Str "you" , Space , Str "want," , Space , Str "you" , Space , Str "can" , Space , Str "use" , Space , Str "a" , Space , Str "caret" , Space , Str "at" , Space , Str "the" , Space , Str "beginning" , Space , Str "of" , Space , Str "every" , Space , Str "line," , Space , Str "as" , SoftBreak , Str "with" , Space , Str "blockquotes," , Space , Str "but" , Space , Str "all" , Space , Str "that" , Space , Str "you" , Space , Str "need" , Space , Str "is" , Space , Str "a" , Space , Str "caret" , Space , Str "at" , Space , Str "the" , Space , Str "beginning" , SoftBreak , Str "of" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "of" , Space , Str "the" , Space , Str "block" , Space , Str "and" , Space , Str "any" , Space , Str "preceding" , Space , Str "blank" , Space , Str "lines." ] , Para [ Str "text" , Space , Emph [ Str "Leading" , Space , Str "space" ] ] , Para [ Emph [ Str "Trailing" , Space , Str "space" ] , Space , Str "text" ] , Para [ Str "text" , Space , Emph [ Str "Leading" , Space , Str "spaces" ] ] , Para [ Emph [ Str "Trailing" , Space , Str "spaces" ] , Space , Str "text" ] , Header 1 ( "tables" , [] , [] ) [ Str "Tables" ] , Header 2 ( "tables-with-headers" , [] , [] ) [ Str "Tables" , Space , Str "with" , Space , Str "Headers" ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "r1a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "r1b" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "r1c" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "X" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Y" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Z" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Header 2 ( "tables-without-headers" , [] , [] ) [ Str "Tables" , Space , Str "without" , Space , Str "Headers" ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Header 2 ( "table-with-spans-and-alignments" , [] , [] ) [ Str "Tables" , Space , Str "with" , Space , Str "spans" , Space , Str "and" , Space , Str "alignments" ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Para [ Str "7" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Header 2 ( "table-with-footer" , [] , [] ) [ Str "Table" , Space , Str "with" , Space , Str "footer" ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Str "f1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "f2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "f3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Str "f4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "f5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "f6" ] ] ] ]) ] , Header 2 ( "table-with-multiple-bodies" , [] , [] ) [ Str "Table" , Space , Str "With" , Space , Str "Multiple" , Space , Str "Bodies" ] , Div ( "" , [ "table-wrap" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Str "a1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "a2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "a3" ] ] ] ] , TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "b1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "b2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "b3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Header 2 ( "empty-tables" , [] , [] ) [ Str "Empty" , Space , Str "Tables" ] , Para [ Str "This" , Space , Str "section" , Space , Str "should" , Space , Str "be" , Space , Str "empty." ] , Header 1 ( "" , [] , [] ) [ Str "References" ] , Div ( "refs" , [] , [] ) [] , Header 1 ( "" , [] , [] ) [ Str "Notes" ] , Div ( "refs" , [] , [] ) [] ] ================================================ FILE: test/jats-reader.xml ================================================
                pmc Pandoc Test Suite MacFarlane John Anonymous © 2023, MacFarlane et al 2023 MacFarlane et al https://creativecommons.org/licenses/by/4.0/ This document is distributed under a Creative Commons Attribution 4.0 International license.

                This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite.

                Headers Level 2 with an <ext-link ext-link-type="uri" xlink:href="/url">embedded link</ext-link> Level 3 with <italic>emphasis</italic> Level 4 Level 5 Level 1 Level 2 with <italic>emphasis</italic> Level 3

                with no blank line

                Level 2

                with no blank line

                Paragraphs

                Here's a regular paragraph.

                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 a list item.

                Here's one with a bullet. * criminey.

                There should be a hard line breakhere.

                Statements CAP TITLE

                Some text to make this regular

                Block Quotes

                E-mail style:

                This is a block quote. It is pretty short.

                Code in a block quote:

                sub status { print "working"; }

                A list:

                item one

                item two

                Nested block quotes:

                nested

                nested

                This should not be a block quote: 2 > 1.

                Box-style:

                Example:

                sub status { print "working"; }

                do laundry

                take out the trash

                Here's a nested one:

                Joe said:

                Don't quote me.

                And a following paragraph.

                Here is a block quote inside a paragraph. Start of a paragraph is here, and the quote is below:

                My block quote

                End of a paragraph

                Formulae Inline formulae with <italic>inline-formula</italic>

                Inside a paragraph, should be natively coded inside the paragraph, and show inline:

                Einstein showed that e=mc2. This formula is important because…

                Block formulae with <italic>disp-formula</italic>

                Inside a paragraph, should be natively coded inside the paragraph, but show as block:

                Einstein showed that e=mc2. This formula is important because…

                Outside a paragraph, should show as block:

                Einstein showed that

                e=mc2

                This formula is important because…

                Without a formula wrapper

                Inside a paragraph, should show inline:

                Einstein showed that e=mc2. This formula is important because…

                Outside a paragraph, should show as block:

                Einstein showed that

                e=mc2

                This formula is important because…

                Formulae with miscelaneus elements

                Regardless of whether or not they are inside a paragraph, should be wrapped in a div, and displayed in a block:

                Inside a paragraph:

                Einstein definitely showed that

                Abstract text

                Alternative text of the graphic This is the title of the caption

                Google doodle from 14 March 2003

                e=mc2 Alternative text 2

                Google doodle from 14 March 2003

                . This formula is important because…

                Outside of a paragraph:

                Paragraph before: Einstein showed that

                Abstract text

                Alternative text 1

                Google doodle from 14 March 2003

                e=mc2 Alternative text 2

                Google doodle from 14 March 2003

                This formula is important because…

                bar

                alternative-decription
                Code Blocks

                Code:

                ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab

                And:

                this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{
                Lists Unordered

                Asterisks tight:

                asterisk 1

                asterisk 2

                asterisk 3

                Asterisks loose:

                asterisk 1

                asterisk 2

                asterisk 3

                Pluses tight:

                Plus 1

                Plus 2

                Plus 3

                Pluses loose:

                Plus 1

                Plus 2

                Plus 3

                Minuses tight:

                Minus 1

                Minus 2

                Minus 3

                Minuses loose:

                Minus 1

                Minus 2

                Minus 3

                Ordered

                Tight:

                First

                Second

                Third

                and:

                One

                Two

                Three

                Loose using tabs:

                First

                Second

                Third

                and using spaces:

                One

                Two

                Three

                Multiple paragraphs:

                Item 1, graf one.

                Item 1. graf two. The quick brown fox jumped over the lazy dog's back.

                Item 2.

                Item 3.

                List styles:

                one

                ,

                two

                ,

                three

                one

                ,

                two

                ,

                three

                Nested

                Tab

                Tab

                Tab

                Here's another:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Same thing but with paragraphs:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Tabs and spaces

                this is a list item indented with tabs

                this is a list item indented with spaces

                this is an example list item indented with tabs

                this is an example list item indented with spaces

                Fancy list markers

                Autonumbering:

                Autonumber.

                More.

                Nested.

                Definition Violin

                Stringed musical instrument.

                Torture device.

                CelloVioloncello

                Low-voiced stringed instrument.

                List inside a paragraph

                Start of a paragraph.

                Red

                Blue

                End of paragraph.

                Inline Markup

                This is emphasized, and so is this.

                This is strong, and so is this.

                Empty and .

                An emphasized link.

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is small caps.

                These are all underlined: foo and bar.

                These are all strikethrough: foo, bar, and baz.

                Smart quotes, ellipses, dashes

                "Hello," said the spider. "'Shelob' is my name."

                'A', 'B', and 'C' are letters.

                'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'

                'He said, "I want to go."' Were you alive in the 70's?

                Here is some quoted 'code' and a "quoted link".

                Some dashes: one---two --- three--four -- five.

                Dashes between numbers: 5-7, 255-66, 1987-1999.

                Ellipses...and. . .and . . . .

                LaTeX

                \cite[22-23]{smith.1899}

                \doublespacing

                $2+2=4$

                $x \in y$

                $\alpha \wedge \omega$

                $223$

                $p$-Tree

                $\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$

                Here's one that has a line break in it: $\alpha + \omega \times x^2$.

                These shouldn't be math:

                To get the famous equation, write $e = mc^2$.

                $22,000 is a lot of money. So is $34,000. (It worked if "lot" is emphasized.)

                Escaped $: $73 this should be emphasized 23$.

                Here's a LaTeX table:

                \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular}

                Special Characters

                Here is some unicode:

                I hat: Î

                o umlaut: ö

                section: §

                set membership: elem

                copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -

                Links Explicit

                Just a URL.

                URL and title.

                URL and title.

                URL and title.

                URL and title

                URL and title

                Email link (nobody [at] nowhere.net)

                Empty.

                Reference

                Foo bar.

                Foo bar.

                Foo bar.

                With embedded [brackets].

                b by itself should be a link.

                Indented once.

                Indented twice.

                Indented thrice.

                This should [not] be a link.

                [not]: /url

                Foo bar.

                Foo biz.

                With ampersands

                Here's a link with an ampersand in the URL.

                Here's a link with an amersand in the link text: AT&T.

                Here's an inline link.

                Here's an inline link in pointy braces.

                Autolinks

                With an ampersand: http://example.com/?foo=1&bar=2

                In a list?

                http://example.com/

                It should.

                An e-mail address: nobody [at] nowhere.net

                Blockquoted: http://example.com/

                Auto-links should not occur here: <http://example.com/>

                or here: <http://example.com/>
                Images

                From "Voyage dans la Lune" by Georges Melies (1902):

                Here is a movie icon.

                Footnotes

                Here is a footnote reference(1), and another(longnote). This should not be a footnote reference, because it contains a space^(my note).

                (1) Here is the footnote. It can go anywhere in the document, not just at the end.

                (longnote) Here's the other note. This one contains multiple blocks.

                Caret characters are used to indicate that the blocks all belong to a single footnote (as with block quotes).

                { <code> }

                If you want, you can use a caret at the beginning of every line, as with blockquotes, but all that you need is a caret at the beginning of the first line of the block and any preceding blank lines.

                text Leading space

                Trailing space text

                text Leading spaces

                Trailing spaces text

                Tables Tables with Headers

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                r1a

                r1b

                r1c

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                X

                Y

                Z

                1

                2

                3

                4

                5

                6

                Tables without Headers

                1

                2

                3

                4

                5

                6

                1

                2

                3

                4

                5

                6

                1

                2

                3

                4

                5

                6

                1

                2

                3

                4

                5

                6

                Tables with spans and alignments

                1

                2

                1

                2

                4

                5

                6

                7

                Table with footer

                f1

                f2

                f3

                f4

                f5

                f6

                1

                2

                3

                Table With Multiple Bodies

                a1

                a2

                a3

                b1

                b2

                b3

                Empty Tables

                This section should be empty.

                References

                This is reference 1

                This is a long note

                Notes

                This is note 1

                This is another long note

                ================================================ FILE: test/jira-reader.jira ================================================ h1. {anchor:headers}Headers h2. {anchor:level-2-with-an-embedded-link}Level 2 with an [embedded link|https://test.example/url] h3. {anchor:level-3-with-emphasis}Level 3 with _emphasis_ h4. Level 4 h5. Level 5 h6. Level 6 h0. this is not a header. ---- h1. Paragraphs Here’s a regular paragraph. Here’s one with a bullet. * criminey. There should be a hard line break here. ---- h1. Block Quotes E-mail style: bq. This is a block quote. It is pretty short. {quote} Code in a block quote: {code:java} sub status { print "working"; } {code} An enumeration: # item one # item two {quote} A following paragraph. ---- h1. Code Blocks Code: {code:java} ---- (should be four hyphens) sub status { print "working"; } {code} And: {code:java} this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ {code} ---- h1. {anchor:lists}Lists h2. {anchor:unordered}Unordered Asterisks: * asterisk 1 * asterisk 2 * asterisk 3 Minuses: - Minus 1 - Minus 2 - Minus 3 h2. Ordered # First # Second # Third Linebreak in paragraph: # Item 1, line one. Item 1. line two. The quick brown fox jumped over the lazy dog’s back. # Item 2. # Item 3. h2. Nested * Tab ** Tab *** Tab Here’s another: # First # Second: #* Fee #* Fie #* Foe # Third Nested enumerations: # Essential ## Important ### Relevant #### Insignificant ---- h1. Linebreaks and Markup in Lists * *apple* red fruit * *orange* orange fruit * *banana* yellow fruit Multiple blocks with italics: * *_apple_* red fruit contains seeds, crisp, pleasant to taste * *_orange_* orange fruit {code:java} { orange code block } {code} bq. orange block quote ---- h1. Colored Text Blocks {color:red} This is red. {color} h2. Eiffel 65 {color:blue} da ba dee {color} ---- h1. Inline Markup This is _emphasized_, and so _is this_. This is *strong*, and so *is this*. An _[emphasized link|https://my.example/url]_. *_This is strong and em._* So is *_this_* word. This is code: {{>}}, {{$}}, {{\}}, {{\$}}, {{}}. -This is _strikeout_.- Superscripts: a{^}bc{^}d a{^}_hello_{^} a{^}hello there{^}. Subscripts: H{~}2{~}O, C{~}6{~}H{~}12{~}O{~}6{~}, C{~} n {~}H{~}_2n_{~}O{~}n{~}. These should not be superscripts or subscripts, because of markers used within words: a^b c^d, a~b c~d. ---- h1. Dashes, and emoticons Some dashes: one -- two --- three. Sure (/) Nope (x) Nice :D Capital d\:D ---- h1. Math * 2 + 2 = 4 * _x_ ∈ {_}y{_} * _α_ ∧ {_}ω{_} * _p_-Tree * Here’s one more: _α_ + {_}ω{_} × {_}x{_}^2^. ---- h1. Special Characters Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - ---- h1. Links h2. Explicit Just a [URL|https://example.org/url]. [File URL|file://some/file/name/]. [IRC link|irc://example.org/pandoc]. [Email link|mailto:nobody@nowhere.invalid] [Not a link|not a URL]. h2. Reference With [embedded \[brackets\]|https://example.net/url/]. https://pandoc.org by itself should be a link. h2. With ampersands Here’s a [link with an ampersand in the URL|http://example.com/?foo=1&bar=2]. Here’s a link with an ampersand in the link text: [AT&T|http://att.com/]. h2. Autolinks With an ampersand: http://example.com/?foo=1&bar=2 * In a list? * http://example.com/ * It should. An e-mail address: mailto:nobody@nowhere.invalid bq. Blockquoted: http://example.com/ {code:java} Autolink should not occur here: {code} ---- h1. Images From "Voyage dans la Lune" by Georges Melies (1902): !lalune.jpg! Here is a movie !movie.jpg! icon. ================================================ FILE: test/jira-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "" , [] , [] ) [ Span ( "headers" , [] , [] ) [] , Str "Headers" ] , Header 2 ( "" , [] , [] ) [ Span ( "level-2-with-an-embedded-link" , [] , [] ) [] , Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "https://test.example/url" , "" ) ] , Header 3 ( "" , [] , [] ) [ Span ( "level-3-with-emphasis" , [] , [] ) [] , Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 4 ( "" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Header 5 ( "" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 6 ( "" , [] , [] ) [ Str "Level" , Space , Str "6" ] , Para [ Str "h0." , Space , Str "this" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "header." ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , Space , Str "*" , Space , Str "criminey." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "hard" , Space , Str "line" , Space , Str "break" , LineBreak , Str "here." ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , Space , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , BlockQuote [ Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [ "java" ] , [] ) "sub status {\n print \"working\";\n}\n" , Para [ Str "An" , Space , Str "enumeration:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "item" , Space , Str "one" ] ] , [ Para [ Str "item" , Space , Str "two" ] ] ] ] , Para [ Str "A" , Space , Str "following" , Space , Str "paragraph." ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [ "java" ] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n" , Para [ Str "And:" ] , CodeBlock ( "" , [ "java" ] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{\n" , HorizontalRule , Header 1 ( "" , [] , [] ) [ Span ( "lists" , [] , [] ) [] , Str "Lists" ] , Header 2 ( "" , [] , [] ) [ Span ( "unordered" , [] , [] ) [] , Str "Unordered" ] , Para [ Str "Asterisks:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Minuses:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Header 2 ( "" , [] , [] ) [ Str "Ordered" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "Linebreak" , Space , Str "in" , Space , Str "paragraph:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "line" , Space , Str "one." , LineBreak , Str "Item" , Space , Str "1." , Space , Str "line" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog\8217s" , Space , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Header 2 ( "" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "another:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "Nested" , Space , Str "enumerations:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Essential" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Important" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Relevant" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Insignificant" ] ] ] ] ] ] ] ] ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Linebreaks" , Space , Str "and" , Space , Str "Markup" , Space , Str "in" , Space , Str "Lists" ] , BulletList [ [ Para [ Strong [ Str "apple" ] , LineBreak , Str "red" , Space , Str "fruit" ] ] , [ Para [ Strong [ Str "orange" ] , LineBreak , Str "orange" , Space , Str "fruit" ] ] , [ Para [ Strong [ Str "banana" ] , LineBreak , Str "yellow" , Space , Str "fruit" ] ] ] , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "with" , Space , Str "italics:" ] , BulletList [ [ Para [ Strong [ Emph [ Str "apple" ] ] , LineBreak , Str "red" , Space , Str "fruit" , LineBreak , Str "contains" , Space , Str "seeds," , Space , Str "crisp," , Space , Str "pleasant" , Space , Str "to" , Space , Str "taste" ] ] , [ Para [ Strong [ Emph [ Str "orange" ] ] , LineBreak , Str "orange" , Space , Str "fruit" ] , CodeBlock ( "" , [ "java" ] , [] ) "{ orange code block }\n" , BlockQuote [ Para [ Str "orange" , Space , Str "block" , Space , Str "quote" ] ] ] ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Colored" , Space , Str "Text" , Space , Str "Blocks" ] , Div ( "" , [] , [ ( "color" , "red" ) ] ) [ Para [ LineBreak , Str "This" , Space , Str "is" , Space , Str "red." ] ] , Header 2 ( "" , [] , [] ) [ Str "Eiffel" , Space , Str "65" ] , Div ( "" , [] , [ ( "color" , "blue" ) ] ) [ Para [ LineBreak , Str "da" , Space , Str "ba" , Space , Str "dee" ] ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "An" , Space , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , Space , Str "link" ] ( "https://my.example/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , Space , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , Space , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Strikeout [ Str "This" , Space , Str "is" , Space , Emph [ Str "strikeout" ] , Str "." ] ] , Para [ Str "Superscripts:" , Space , Str "a" , Superscript [ Str "bc" ] , Str "d" , Space , Str "a" , Superscript [ Emph [ Str "hello" ] ] , Space , Str "a" , Superscript [ Str "hello\160there" ] , Str "." ] , Para [ Str "Subscripts:" , Space , Str "H" , Subscript [ Str "2" ] , Str "O," , Space , Str "C" , Subscript [ Str "6" ] , Str "H" , Subscript [ Str "12" ] , Str "O" , Subscript [ Str "6" ] , Str "," , Space , Str "C" , Subscript [ Str "\160n\160" ] , Str "H" , Subscript [ Emph [ Str "2n" ] ] , Str "O" , Subscript [ Str "n" ] , Str "." ] , Para [ Str "These" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "superscripts" , Space , Str "or" , Space , Str "subscripts," , Space , Str "because" , Space , Str "of" , Space , Str "markers" , Space , Str "used" , Space , Str "within" , Space , Str "words:" , Space , Str "a^b" , Space , Str "c^d," , Space , Str "a~b" , Space , Str "c~d." ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Dashes," , Space , Str "and" , Space , Str "emoticons" ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one" , Space , Str "\8211" , Space , Str "two" , Space , Str "\8212" , Space , Str "three." ] , Para [ Str "Sure" , Space , Str "\10004" , LineBreak , Str "Nope" , Space , Str "\10060" ] , Para [ Str "Nice" , Space , Str "\128515" ] , Para [ Str "Capital" , Space , Str "d:D" ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Math" ] , BulletList [ [ Para [ Str "2\8197+\8197\&2\8196=\8196\&4" ] ] , [ Para [ Emph [ Str "x" ] , Str "\8196\8712\8196" , Emph [ Str "y" ] ] ] , [ Para [ Emph [ Str "\945" ] , Str "\8197\8743\8197" , Emph [ Str "\969" ] ] ] , [ Para [ Emph [ Str "p" ] , Str "-Tree" ] ] , [ Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "more:" , Space , Emph [ Str "\945" ] , Str "\8197+\8197" , Emph [ Str "\969" ] , Str "\8197\215\8197" , Emph [ Str "x" ] , Superscript [ Str "2" ] , Str "." ] ] ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Para [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Para [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Para [ Str "section:" , Space , Str "\167" ] ] , [ Para [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Para [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Links" ] , Header 2 ( "" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "https://example.org/url" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "File" , Space , Str "URL" ] ( "file://some/file/name/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "IRC" , Space , Str "link" ] ( "irc://example.org/pandoc" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "Email" , Space , Str "link" ] ( "mailto:nobody@nowhere.invalid" , "" ) ] , Para [ Str "[Not" , Space , Str "a" , Space , Str "link|not" , Space , Str "a" , Space , Str "URL]." ] , Header 2 ( "" , [] , [] ) [ Str "Reference" ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "[brackets]" ] ( "https://example.net/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "https://pandoc.org" ] ( "https://pandoc.org" , "" ) , Space , Str "by" , Space , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Header 2 ( "" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "http://att.com/" , "" ) , Str "." ] , Header 2 ( "" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , Space , Link ( "" , [] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Para [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Para [ Link ( "" , [] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Para [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , Space , Link ( "" , [] , [] ) [ Str "mailto:nobody@nowhere.invalid" ] ( "mailto:nobody@nowhere.invalid" , "" ) ] , BlockQuote [ Para [ Str "Blockquoted:" , Space , Link ( "" , [] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , CodeBlock ( "" , [ "java" ] , [] ) "Autolink should not occur here: \n" , HorizontalRule , Header 1 ( "" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Str "\"Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune\"" , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Para [ Image ( "" , [] , [] ) [] ( "lalune.jpg" , "" ) ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [] ( "movie.jpg" , "" ) , Space , Str "icon." ] ] ================================================ FILE: test/latex-reader.latex ================================================ \documentclass{article} \usepackage[utf8x]{inputenc} \usepackage{hyperref} \usepackage[normalem]{ulem} \usepackage{enumerate} \usepackage{setspace} \usepackage{fancyvrb} \usepackage{graphicx} \VerbatimFootnotes % allows verbatim text in footnotes \title{Pandoc Test Suite} \author{John MacFarlane \and Anonymous} \date{July 17, 2006} \begin{document} \maketitle This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. \begin{center}\rule{3in}{0.4pt}\end{center} \section{Headers} \subsection{Level 2 with an \href{/url}{embedded link}} \subsubsection{Level 3 with \emph{emphasis}} Level 4 Level 5 \section[alt title ignored]{Level 1} \subsection{Level 2 with \emph{emphasis}} \subsubsection{Level 3} with no blank line \subsection{Level 2} with no blank line \begin{center}\rule{3in}{0.4pt}\end{center} \section{Paragraphs} Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break\\here. \begin{center}\rule{3in}{0.4pt}\end{center} \section{Block Quotes} E-mail style: \begin{quote} This is a block quote. It is pretty short. \end{quote} \begin {quote} Code in a block quote: \begin{verbatim} sub status { print "working"; } \end{verbatim} A list: \begin{enumerate}[1.] \item item one \item item two \end{enumerate} Nested block quotes: \begin{quote} nested \end{quote} \begin{quote} nested \end{quote} \end{quote} This should not be a block quote: 2 \textgreater{} 1. Box-style: \begin{quote} Example: \begin{verbatim} sub status { print "working"; } \end{verbatim} \end{quote} \begin{quote} \begin{enumerate}[1.] \item do laundry \item take out the trash \end{enumerate} \end{quote} Here's a nested one: \begin{quote} Joe said: \begin{quote} Don't quote me. \end{quote} \end{quote} And a following paragraph. \begin{center}\rule{3in}{0.4pt}\end{center} \section{Code Blocks} Code: \begin{verbatim} ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab \end{verbatim} And: \begin{verbatim} this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ \end{verbatim} \begin{obeylines} this has \emph{two lines} \end{obeylines} \begin{center}\rule{3in}{0.4pt}\end{center} \section{Lists} \subsection{Unordered} Asterisks tight: \begin{itemize} \item asterisk 1 \item asterisk 2 \item asterisk 3 \end{itemize} Asterisks loose: \begin{itemize} \item asterisk 1 \item asterisk 2 \item asterisk 3 \end{itemize} Pluses tight: \begin{itemize} \item Plus 1 \item Plus 2 \item Plus 3 \end{itemize} Pluses loose: \begin{itemize} \item Plus 1 \item Plus 2 \item Plus 3 \end{itemize} Minuses tight: \begin{itemize} \item Minus 1 \item Minus 2 \item Minus 3 \end{itemize} Minuses loose: \begin{itemize} \item Minus 1 \item Minus 2 \item Minus 3 \end{itemize} \subsection{Ordered} Tight: \begin{enumerate}[1.] \item First \item Second \item Third \end{enumerate} and: \begin{enumerate}[1.] \item One \item Two \item Three \end{enumerate} Loose using tabs: \begin{enumerate}[1.] \item First \item Second \item Third \end{enumerate} and using spaces: \begin{enumerate}[1.] \item One \item Two \item Three \end{enumerate} Multiple paragraphs: \begin{enumerate}[1.] \item Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. \item Item 2. \item Item 3. \end{enumerate} \subsection{Nested} \begin{itemize} \item Tab \begin{itemize} \item Tab \begin{itemize} \item Tab \end{itemize} \end{itemize} \end{itemize} Here's another: \begin{enumerate}[1.] \item First \item Second: \begin{itemize} \item Fee \item Fie \item Foe \end{itemize} \item Third \end{enumerate} Same thing but with paragraphs: \begin{enumerate}[1.] \item First \item Second: \begin{itemize} \item Fee \item Fie \item Foe \end{itemize} \item Third \end{enumerate} \subsection{Tabs and spaces} \begin{itemize} \item this is a list item indented with tabs \item this is a list item indented with spaces \begin{itemize} \item this is an example list item indented with tabs \item this is an example list item indented with spaces \end{itemize} \end{itemize} \subsection{Fancy list markers} \begin{enumerate}[(1)] \setcounter{enumi}{1} \item begins with 2 \item and now 3 with a continuation \begin{enumerate}[i.] \setcounter{enumii}{3} \item sublist with roman numerals, starting with 4 \item more items \begin{enumerate}[(A)] \item a subsublist \item a subsublist \end{enumerate} \end{enumerate} \end{enumerate} Nesting: \begin{enumerate}[A.] \item Upper Alpha \begin{enumerate}[I.] \item Upper Roman. \begin{enumerate}[(1)] \setcounter{enumiii}{5} \item Decimal start with 6 \begin{enumerate}[a)] \setcounter{enumiv}{2} \item Lower alpha with paren \end{enumerate} \end{enumerate} \end{enumerate} \end{enumerate} Autonumbering: \begin{enumerate} \item Autonumber. \item More. \begin{enumerate} \item Nested. \end{enumerate} \end{enumerate} Should not be a list item: M.A. 2007 B. Williams \begin{center}\rule{3in}{0.4pt}\end{center} \section{Definition Lists} Tight using spaces: \begin{description} \item[apple] red fruit \item[orange] orange fruit \item[banana] yellow fruit \end{description} Tight using tabs: \begin{description} \item[apple] red fruit \item[orange] orange fruit \item[banana] yellow fruit \end{description} Loose: \begin{description} \item[apple] red fruit \item[orange] orange fruit \item[banana] yellow fruit \end{description} Multiple blocks with italics: \begin{description} \item[\emph{apple}] red fruit contains seeds, crisp, pleasant to taste \item[\emph{orange}] orange fruit \begin{verbatim} { orange code block } \end{verbatim} \begin{quote} orange block quote \end{quote} \end{description} \section{HTML Blocks} Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is \emph{emphasized} And this is \textbf{strong} Here's a simple block: foo This should be a code block, though: \begin{verbatim}
                foo
                \end{verbatim} As should this: \begin{verbatim}
                foo
                \end{verbatim} Now, nested: foo This should just be an HTML comment: Multiline: Code block: \begin{verbatim} \end{verbatim} Just plain comment, with trailing spaces on the line: Code: \begin{verbatim}
                \end{verbatim} Hr's: \begin{center}\rule{3in}{0.4pt}\end{center} \section{Inline Markup} This is \emph{emphasized}, and so \emph{is this}. This is \textbf{strong}, and so \textbf{is this}. An \emph{\href{/url}{emphasized link}}. \textbf{\emph{This is strong and em.}} So is \textbf{\emph{this}} word. \textbf{\emph{This is strong and em.}} So is \textbf{\emph{this}} word. This is code: \verb!>!, \verb!$!, \verb!\!, \verb!\$!, \verb!!. \sout{This is \emph{strikeout}.} Superscripts: a\textsuperscript{bc}d a\textsuperscript{\emph{hello}} a\textsuperscript{hello there}. Subscripts: H\textsubscript{2}O, H\textsubscript{23}O, H\textsubscript{many of them}O. These should not be superscripts or subscripts, because of the unescaped spaces: a\^{}b c\^{}d, a\ensuremath{\sim}b c\ensuremath{\sim}d. \begin{center}\rule{3in}{0.4pt}\end{center} \section{Smart quotes, ellipses, dashes} ``Hello,'' said the spider. ``\,`Shelob' is my name.'' `A', `B', and `C' are letters. `Oak,' `elm,' and `beech' are names of trees. So is `pine.' `He said, ``I want to go.''\,' Were you alive in the 70's? Here is some quoted `\verb!code!' and a ``\href{http://example.com/?foo=1&bar=2}{quoted link}''. Some dashes: one---two---three---four---five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses\ldots{}and\ldots{}and\ldots{}. \begin{center}\rule{3in}{0.4pt}\end{center} \section{LaTeX} \begin{itemize} \item \cite[22-23]{smith.1899} \item \doublespacing \item $2+2=4$ \item $x \in y$ \item $\alpha \wedge \omega$ \item $223$ \item $p$-Tree \item $\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$ \item Here's one that has a line break in it: $\alpha + \omega \times x^2$. \end{itemize} These shouldn't be math: \begin{itemize} \item To get the famous equation, write \verb!$e = mc^2$!. \item \$22,000 is a \emph{lot} of money. So is \$34,000. (It worked if ``lot'' is emphasized.) \item Escaped \verb!$!: \$73 \emph{this should be emphasized} 23\$. \end{itemize} Here's a LaTeX table: \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} A table with one column: \begin{tabular}{c} Animal \\ Vegetable \end{tabular} \begin{center}\rule{3in}{0.4pt}\end{center} \section{Special Characters} Here is some unicode: \begin{itemize} \item I hat: Î \item o umlaut: ö \item section: § \item set membership: ∈ \item copyright: © \end{itemize} AT\&T has an ampersand in their name. AT\&T is another way to write it. This \& that. 4 \textless{} 5. 6 \textgreater{} 5. Backslash: \textbackslash{} Backtick: ` Asterisk: * Underscore: \_ Left brace: \{ Right brace: \} Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: \textgreater{} Hash: \# Period: . Bang: ! Plus: + Minus: - \begin{center}\rule{3in}{0.4pt}\end{center} \section{Links} \subsection{Explicit} Just a \href{/url/}{URL}. \href{/url/}{URL and title}. \href{/url/}{URL and title}. \href{/url/}{URL and title}. \href{/url/}{URL and title} \href{/url/}{URL and title} \href{/url/with_underscore}{with\_underscore} \href{mailto:nobody@nowhere.net}{Email link} \href{}{Empty}. \subsection{Reference} Foo \href{/url/}{bar}. Foo \href{/url/}{bar}. Foo \href{/url/}{bar}. With \href{/url/}{embedded [brackets]}. \href{/url/}{b} by itself should be a link. Indented \href{/url}{once}. Indented \href{/url}{twice}. Indented \href{/url}{thrice}. This should [not][] be a link. \begin{verbatim} [not]: /url \end{verbatim} Foo \href{/url/}{bar}. Foo \href{/url/}{biz}. \subsection{With ampersands} Here's a \href{http://example.com/?foo=1&bar=2}{link with an ampersand in the URL}. Here's a link with an amersand in the link text: \href{http://att.com/}{AT\&T}. Here's an \href{/script?foo=1&bar=2}{inline link}. Here's an \href{/script?foo=1&bar=2}{inline link in pointy braces}. \subsection{Autolinks} With an ampersand: \url{http://example.com/?foo=1&bar=2} \begin{itemize} \item In a list? \item \url{http://example.com/} \item It should. \end{itemize} An e-mail address: \href{mailto:nobody@nowhere.net}{nobody@nowhere.net} \begin{quote} Blockquoted: \url{http://example.com/} \end{quote} Auto-links should not occur here: \verb!! \begin{verbatim} or here: \end{verbatim} \begin{center}\rule{3in}{0.4pt}\end{center} \section{Images} From ``Voyage dans la Lune'' by Georges Melies (1902): \includegraphics{lalune.jpg} Here is a movie \includegraphics{movie.jpg} icon. \begin{center}\rule{3in}{0.4pt}\end{center} \section{Footnotes} Here is a footnote reference,\footnote{ Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. } and another.\footnote{ Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). \begin{Verbatim} { } \end{Verbatim} If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. } This should \emph{not} be a footnote reference, because it contains a space.[\^{}my note] Here is an inline note.\footnote{ This is \emph{easier} to type. Inline notes may contain \href{http://google.com}{links} and \verb!]! verbatim characters, as well as [bracketed text]. } \begin{quote} Notes can go in quotes.\footnote{ In quote. } \end{quote} \begin{enumerate}[1.] \item And in list items.\footnote{ In list. } \end{enumerate} This paragraph should not be part of the note, as it is not indented. \section{Escaped characters} \$ \% \& \# \_ \{ \} \end{document} ================================================ FILE: test/latex-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "John" , Space , Str "MacFarlane" ] , MetaInlines [ Str "Anonymous" ] ] ) , ( "date" , MetaInlines [ Str "July" , Space , Str "17," , Space , Str "2006" ] ) , ( "title" , MetaInlines [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] ) ] } [ RawBlock (Format "latex") "\\maketitle" , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , SoftBreak , Str "John" , Space , Str "Gruber\8217s" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "headers" , [] , [] ) [ Str "Headers" ] , Header 2 ( "level-2-with-an-embedded-link" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "/url" , "" ) ] , Header 3 ( "level-3-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Para [ Str "Level" , Space , Str "4" ] , Para [ Str "Level" , Space , Str "5" ] , Header 1 ( "level-1" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Header 2 ( "level-2-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 3 ( "level-3" , [] , [] ) [ Str "Level" , Space , Str "3" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 2 ( "level-2" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , Space , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , SoftBreak , Str "list" , Space , Str "item." , Space , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , Space , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , SoftBreak , Str "looked" , Space , Str "like" , Space , Str "a" , Space , Str "list" , Space , Str "item." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , Space , Str "*" , Space , Str "criminey." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "hard" , Space , Str "line" , Space , Str "break" , LineBreak , Str "here." ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , Space , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , BlockQuote [ Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "item" , Space , Str "one" ] ] , [ Para [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] ] , BlockQuote [ Para [ Str "nested" ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" , Space , Str "2" , Space , Str ">" , Space , Str "1." ] , Para [ Str "Box-style:" ] , BlockQuote [ Para [ Str "Example:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" ] , BlockQuote [ OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "do" , Space , Str "laundry" ] ] , [ Para [ Str "take" , Space , Str "out" , Space , Str "the" , Space , Str "trash" ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "nested" , Space , Str "one:" ] , BlockQuote [ Para [ Str "Joe" , Space , Str "said:" ] , BlockQuote [ Para [ Str "Don\8217t" , Space , Str "quote" , Space , Str "me." ] ] ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n\nthis code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" , Para [ Str "this" , Space , Str "has" , Space , Emph [ Str "two" , LineBreak , Str "lines" ] ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "tight:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "tight:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Loose" , Space , Str "using" , Space , Str "tabs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and" , Space , Str "using" , Space , Str "spaces:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog\8217s" , SoftBreak , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Header 2 ( "nested" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "another:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing" , Space , Str "but" , Space , Str "with" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "tabs-and-spaces" , [] , [] ) [ Str "Tabs" , Space , Str "and" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , Space , Str "indented" , Space , Str "with" , Space , Str "spaces" ] ] ] ] ] , Header 2 ( "fancy-list-markers" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , OrderedList ( 2 , Decimal , TwoParens ) [ [ Para [ Str "begins" , Space , Str "with" , Space , Str "2" ] ] , [ Para [ Str "and" , Space , Str "now" , Space , Str "3" ] , Para [ Str "with" , Space , Str "a" , Space , Str "continuation" ] , OrderedList ( 4 , LowerRoman , Period ) [ [ Para [ Str "sublist" , Space , Str "with" , Space , Str "roman" , Space , Str "numerals," , Space , Str "starting" , Space , Str "with" , Space , Str "4" ] ] , [ Para [ Str "more" , Space , Str "items" ] , OrderedList ( 1 , UpperAlpha , TwoParens ) [ [ Para [ Str "a" , Space , Str "subsublist" ] ] , [ Para [ Str "a" , Space , Str "subsublist" ] ] ] ] ] ] ] , Para [ Str "Nesting:" ] , OrderedList ( 1 , UpperAlpha , Period ) [ [ Para [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , Period ) [ [ Para [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , TwoParens ) [ [ Para [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , OneParen ) [ [ Para [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Autonumber." ] ] , [ Para [ Str "More." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Nested." ] ] ] ] ] , Para [ Str "Should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "list" , Space , Str "item:" ] , Para [ Str "M.A." , Space , Str "2007" ] , Para [ Str "B." , Space , Str "Williams" ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "definition-lists" , [] , [] ) [ Str "Definition" , Space , Str "Lists" ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "spaces:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Para [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "tabs:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Para [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Para [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "with" , Space , Str "italics:" ] , DefinitionList [ ( [ Emph [ Str "apple" ] ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "contains" , Space , Str "seeds," , Space , Str "crisp," , Space , Str "pleasant" , Space , Str "to" , Space , Str "taste" ] ] ] ) , ( [ Emph [ Str "orange" ] ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , CodeBlock ( "" , [] , [] ) "{ orange code block }" , BlockQuote [ Para [ Str "orange" , Space , Str "block" , Space , Str "quote" ] ] ] ] ) ] , Header 1 ( "html-blocks" , [] , [] ) [ Str "HTML" , Space , Str "Blocks" ] , Para [ Str "Simple" , Space , Str "block" , Space , Str "on" , Space , Str "one" , Space , Str "line:" ] , Para [ Str "foo" , SoftBreak , Str "And" , Space , Str "nested" , Space , Str "without" , Space , Str "indentation:" ] , Para [ Str "foo" , SoftBreak , Str "bar" , SoftBreak , Str "Interpreted" , Space , Str "markdown" , Space , Str "in" , Space , Str "a" , Space , Str "table:" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , SoftBreak , Str "And" , Space , Str "this" , Space , Str "is" , Space , Strong [ Str "strong" ] , SoftBreak , Str "Here\8217s" , Space , Str "a" , Space , Str "simple" , Space , Str "block:" ] , Para [ Str "foo" , SoftBreak , Str "This" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "code" , Space , Str "block," , Space , Str "though:" ] , CodeBlock ( "" , [] , [] ) "
                \n foo\n
                " , Para [ Str "As" , Space , Str "should" , Space , Str "this:" ] , CodeBlock ( "" , [] , [] ) "
                foo
                " , Para [ Str "Now," , Space , Str "nested:" ] , Para [ Str "foo" , SoftBreak , Str "This" , Space , Str "should" , Space , Str "just" , Space , Str "be" , Space , Str "an" , Space , Str "HTML" , Space , Str "comment:" ] , Para [ Str "Multiline:" ] , Para [ Str "Code" , Space , Str "block:" ] , CodeBlock ( "" , [] , [] ) "" , Para [ Str "Just" , Space , Str "plain" , Space , Str "comment," , Space , Str "with" , Space , Str "trailing" , Space , Str "spaces" , Space , Str "on" , Space , Str "the" , Space , Str "line:" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "
                " , Para [ Str "Hr\8217s:" ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "An" , Space , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , Space , Str "link" ] ( "/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , Space , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , SoftBreak , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Strikeout [ Str "This" , Space , Str "is" , Space , Emph [ Str "strikeout" ] , Str "." ] ] , Para [ Str "Superscripts:" , Space , Str "a" , Superscript [ Str "bc" ] , Str "d" , SoftBreak , Str "a" , Superscript [ Emph [ Str "hello" ] ] , Space , Str "a" , Superscript [ Str "hello" , Space , Str "there" ] , Str "." ] , Para [ Str "Subscripts:" , Space , Str "H" , Subscript [ Str "2" ] , Str "O," , Space , Str "H" , Subscript [ Str "23" ] , Str "O," , SoftBreak , Str "H" , Subscript [ Str "many" , Space , Str "of" , Space , Str "them" ] , Str "O." ] , Para [ Str "These" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "superscripts" , Space , Str "or" , Space , Str "subscripts," , Space , Str "because" , Space , Str "of" , Space , Str "the" , SoftBreak , Str "unescaped" , Space , Str "spaces:" , Space , Str "a^b" , Space , Str "c^d," , Space , Str "a" , Math InlineMath "\\sim" , Str "b" , SoftBreak , Str "c" , Math InlineMath "\\sim" , Str "d." ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "smart-quotes-ellipses-dashes" , [] , [] ) [ Str "Smart" , Space , Str "quotes," , Space , Str "ellipses," , Space , Str "dashes" ] , Para [ Quoted DoubleQuote [ Str "Hello," ] , Space , Str "said" , Space , Str "the" , Space , Str "spider." , Space , Quoted DoubleQuote [ Str "\8198" , Quoted SingleQuote [ Str "Shelob" ] , Space , Str "is" , Space , Str "my" , Space , Str "name." ] ] , Para [ Quoted SingleQuote [ Str "A" ] , Str "," , Space , Quoted SingleQuote [ Str "B" ] , Str "," , Space , Str "and" , Space , Quoted SingleQuote [ Str "C" ] , Space , Str "are" , Space , Str "letters." ] , Para [ Quoted SingleQuote [ Str "Oak," ] , Space , Quoted SingleQuote [ Str "elm," ] , Space , Str "and" , Space , Quoted SingleQuote [ Str "beech" ] , Space , Str "are" , Space , Str "names" , Space , Str "of" , Space , Str "trees." , Space , Str "So" , Space , Str "is" , Space , Quoted SingleQuote [ Str "pine." ] ] , Para [ Quoted SingleQuote [ Str "He" , Space , Str "said," , Space , Quoted DoubleQuote [ Str "I" , Space , Str "want" , Space , Str "to" , Space , Str "go." ] , Str "\8198" ] , Space , Str "Were" , Space , Str "you" , Space , Str "alive" , Space , Str "in" , Space , Str "the" , Space , Str "70\8217s?" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "quoted" , Space , Quoted SingleQuote [ Code ( "" , [] , [] ) "code" ] , Space , Str "and" , Space , Str "a" , SoftBreak , Quoted DoubleQuote [ Link ( "" , [] , [] ) [ Str "quoted" , Space , Str "link" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , Str "." ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one\8212two\8212three\8212four\8212five." ] , Para [ Str "Dashes" , Space , Str "between" , Space , Str "numbers:" , Space , Str "5\8211\&7," , Space , Str "255\8211\&66," , Space , Str "1987\8211\&1999." ] , Para [ Str "Ellipses\8230and\8230and\8230." ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "latex" , [] , [] ) [ Str "LaTeX" ] , BulletList [ [ Para [ Cite [ Citation { citationId = "smith.1899" , citationPrefix = [] , citationSuffix = [ Str "22-23" ] , citationMode = NormalCitation , citationNoteNum = 0 , citationHash = 0 } ] [ RawInline (Format "latex") "\\cite[22-23]{smith.1899}" ] ] ] , [ RawBlock (Format "latex") "\\doublespacing" ] , [ Para [ Math InlineMath "2+2=4" ] ] , [ Para [ Math InlineMath "x \\in y" ] ] , [ Para [ Math InlineMath "\\alpha \\wedge \\omega" ] ] , [ Para [ Math InlineMath "223" ] ] , [ Para [ Math InlineMath "p" , Str "-Tree" ] ] , [ Para [ Math InlineMath "\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)-f(x)}{h}" ] ] , [ Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "that" , Space , Str "has" , Space , Str "a" , Space , Str "line" , Space , Str "break" , Space , Str "in" , Space , Str "it:" , SoftBreak , Math InlineMath "\\alpha + \\omega \\times x^2" , Str "." ] ] ] , Para [ Str "These" , Space , Str "shouldn\8217t" , Space , Str "be" , Space , Str "math:" ] , BulletList [ [ Para [ Str "To" , Space , Str "get" , Space , Str "the" , Space , Str "famous" , Space , Str "equation," , Space , Str "write" , Space , Code ( "" , [] , [] ) "$e = mc^2$" , Str "." ] ] , [ Para [ Str "$22,000" , Space , Str "is" , Space , Str "a" , Space , Emph [ Str "lot" ] , Space , Str "of" , Space , Str "money." , Space , Str "So" , Space , Str "is" , Space , Str "$34,000." , Space , Str "(It" , Space , Str "worked" , Space , Str "if" , SoftBreak , Quoted DoubleQuote [ Str "lot" ] , Space , Str "is" , Space , Str "emphasized.)" ] ] , [ Para [ Str "Escaped" , Space , Code ( "" , [] , [] ) "$" , Str ":" , Space , Str "$73" , Space , Emph [ Str "this" , Space , Str "should" , Space , Str "be" , Space , Str "emphasized" ] , Space , Str "23$." ] ] ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "LaTeX" , Space , Str "table:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Animal" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Number" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Dog" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cat" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "A" , Space , Str "table" , Space , Str "with" , Space , Str "one" , Space , Str "column:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Animal" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Vegetable" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "special-characters" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Para [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Para [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Para [ Str "section:" , Space , Str "\167" ] ] , [ Para [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Para [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "\8216" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Header 2 ( "explicit" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "with_underscore" ] ( "/url/with_underscore" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Email" , Space , Str "link" ] ( "mailto:nobody@nowhere.net" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Empty" ] ( "" , "" ) , Str "." ] , Header 2 ( "reference" , [] , [] ) [ Str "Reference" ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "[brackets]" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "b" ] ( "/url/" , "" ) , Space , Str "by" , Space , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "once" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "twice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "thrice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "This" , Space , Str "should" , Space , Str "[not][]" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , CodeBlock ( "" , [] , [] ) "[not]: /url" , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "biz" ] ( "/url/" , "" ) , Str "." ] , Header 2 ( "with-ampersands" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here\8217s" , Space , Str "a" , SoftBreak , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , SoftBreak , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "http://att.com/" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , SoftBreak , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" , Space , Str "in" , Space , Str "pointy" , Space , Str "braces" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Header 2 ( "autolinks" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Para [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Para [ Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Para [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , SoftBreak , Link ( "" , [] , [] ) [ Str "nobody@nowhere.net" ] ( "mailto:nobody@nowhere.net" , "" ) ] , BlockQuote [ Para [ Str "Blockquoted:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , Para [ Str "Auto-links" , Space , Str "should" , Space , Str "not" , Space , Str "occur" , Space , Str "here:" , Space , Code ( "" , [] , [] ) "" ] , CodeBlock ( "" , [] , [] ) "or here: " , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Quoted DoubleQuote [ Str "Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune" ] , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Para [ Image ( "" , [] , [] ) [ Str "image" ] ( "lalune.jpg" , "" ) ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "image" ] ( "movie.jpg" , "" ) , Space , Str "icon." ] , Div ( "" , [ "center" ] , [] ) [ HorizontalRule ] , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote" , SoftBreak , Str "reference," , Note [ Para [ Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "footnote." , Space , Str "It" , Space , Str "can" , Space , Str "go" , Space , Str "anywhere" , Space , Str "after" , Space , Str "the" , Space , Str "footnote" , SoftBreak , Str "reference." , Space , Str "It" , Space , Str "need" , Space , Str "not" , Space , Str "be" , Space , Str "placed" , Space , Str "at" , Space , Str "the" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "document." ] ] , SoftBreak , Str "and" , SoftBreak , Str "another." , Note [ Para [ Str "Here\8217s" , Space , Str "the" , Space , Str "long" , Space , Str "note." , Space , Str "This" , Space , Str "one" , Space , Str "contains" , Space , Str "multiple" , Space , Str "blocks." ] , Para [ Str "Subsequent" , Space , Str "blocks" , Space , Str "are" , Space , Str "indented" , Space , Str "to" , Space , Str "show" , Space , Str "that" , Space , Str "they" , Space , Str "belong" , Space , Str "to" , Space , Str "the" , SoftBreak , Str "footnote" , Space , Str "(as" , Space , Str "with" , Space , Str "list" , Space , Str "items)." ] , CodeBlock ( "" , [] , [] ) " { }" , Para [ Str "If" , Space , Str "you" , Space , Str "want," , Space , Str "you" , Space , Str "can" , Space , Str "indent" , Space , Str "every" , Space , Str "line," , Space , Str "but" , Space , Str "you" , Space , Str "can" , Space , Str "also" , Space , Str "be" , Space , Str "lazy" , SoftBreak , Str "and" , Space , Str "just" , Space , Str "indent" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "of" , Space , Str "each" , Space , Str "block." ] ] , SoftBreak , Str "This" , Space , Str "should" , Space , Emph [ Str "not" ] , Space , Str "be" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Space , Str "because" , Space , Str "it" , Space , Str "contains" , SoftBreak , Str "a" , Space , Str "space.[^my" , Space , Str "note]" , Space , Str "Here" , Space , Str "is" , Space , Str "an" , Space , Str "inline" , SoftBreak , Str "note." , Note [ Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "easier" ] , Space , Str "to" , Space , Str "type." , Space , Str "Inline" , Space , Str "notes" , Space , Str "may" , Space , Str "contain" , SoftBreak , Link ( "" , [] , [] ) [ Str "links" ] ( "http://google.com" , "" ) , Space , Str "and" , Space , Code ( "" , [] , [] ) "]" , Space , Str "verbatim" , Space , Str "characters," , SoftBreak , Str "as" , Space , Str "well" , Space , Str "as" , Space , Str "[bracketed" , Space , Str "text]." ] ] ] , BlockQuote [ Para [ Str "Notes" , Space , Str "can" , Space , Str "go" , Space , Str "in" , Space , Str "quotes." , Note [ Para [ Str "In" , Space , Str "quote." ] ] ] ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "And" , Space , Str "in" , Space , Str "list" , Space , Str "items." , Note [ Para [ Str "In" , Space , Str "list." ] ] ] ] ] , Para [ Str "This" , Space , Str "paragraph" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "part" , Space , Str "of" , Space , Str "the" , Space , Str "note," , Space , Str "as" , Space , Str "it" , Space , Str "is" , Space , Str "not" , SoftBreak , Str "indented." ] , Header 1 ( "escaped-characters" , [] , [] ) [ Str "Escaped" , Space , Str "characters" ] , Para [ Str "$" , Space , Str "%" , Space , Str "&" , Space , Str "#" , Space , Str "_" , Space , Str "{" , Space , Str "}" ] ] ================================================ FILE: test/lhs-test-markdown.native ================================================ [ Header 1 ( "lhs-test" , [] , [] ) [ Str "lhs" , Space , Str "test" ] , Para [ Code ( "" , [] , [] ) "unsplit" , Space , Str "is" , Space , Str "an" , Space , Str "arrow" , Space , Str "that" , Space , Str "takes" , Space , Str "a" , Space , Str "pair" , Space , Str "of" , Space , Str "values" , Space , Str "and" , Space , Str "combines" , Space , Str "them" , Space , Str "to" , SoftBreak , Str "return" , Space , Str "a" , Space , Str "single" , Space , Str "value:" ] , CodeBlock ( "" , [ "haskell" , "literate" ] , [] ) "unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d\nunsplit = arr . uncurry\n -- arr (\\op (x,y) -> x `op` y)" , Para [ Code ( "" , [] , [] ) "(***)" , Space , Str "combines" , Space , Str "two" , Space , Str "arrows" , Space , Str "into" , Space , Str "a" , Space , Str "new" , Space , Str "arrow" , Space , Str "by" , Space , Str "running" , Space , Str "the" , Space , Str "two" , Space , Str "arrows" , Space , Str "on" , Space , Str "a" , SoftBreak , Str "pair" , Space , Str "of" , Space , Str "values" , Space , Str "(one" , Space , Str "arrow" , Space , Str "on" , Space , Str "the" , Space , Str "first" , Space , Str "item" , Space , Str "of" , Space , Str "the" , Space , Str "pair" , Space , Str "and" , Space , Str "one" , Space , Str "arrow" , Space , Str "on" , Space , Str "the" , SoftBreak , Str "second" , Space , Str "item" , Space , Str "of" , Space , Str "the" , Space , Str "pair)." ] , CodeBlock ( "" , [] , [] ) "f *** g = first f >>> second g" , Para [ Str "Block" , Space , Str "quote:" ] , BlockQuote [ Para [ Str "foo" , Space , Str "bar" ] ] ] ================================================ FILE: test/lhs-test.fragment.html+lhs ================================================

                lhs test

                unsplit is an arrow that takes a pair of values and combines them to return a single value:

                > unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d
                > unsplit = arr . uncurry
                > -- arr (\op (x,y) -> x `op` y)

                (***) combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair).

                f *** g = first f >>> second g

                Block quote:

                foo bar

                ================================================ FILE: test/lhs-test.html ================================================

                lhs test

                unsplit is an arrow that takes a pair of values and combines them to return a single value:

                unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d
                unsplit = arr . uncurry
                          -- arr (\op (x,y) -> x `op` y)

                (***) combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair).

                f *** g = first f >>> second g

                Block quote:

                foo bar

                ================================================ FILE: test/lhs-test.html+lhs ================================================

                lhs test

                unsplit is an arrow that takes a pair of values and combines them to return a single value:

                > unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d
                > unsplit = arr . uncurry
                >           -- arr (\op (x,y) -> x `op` y)

                (***) combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair).

                f *** g = first f >>> second g

                Block quote:

                foo bar

                ================================================ FILE: test/lhs-test.latex ================================================ \section{lhs test}\label{lhs-test} \texttt{unsplit} is an arrow that takes a pair of values and combines them to return a single value: \begin{Shaded} \begin{Highlighting}[] \OtherTok{unsplit ::}\NormalTok{ (}\DataTypeTok{Arrow}\NormalTok{ a) }\OtherTok{=\textgreater{}}\NormalTok{ (b }\OtherTok{{-}\textgreater{}}\NormalTok{ c }\OtherTok{{-}\textgreater{}}\NormalTok{ d) }\OtherTok{{-}\textgreater{}}\NormalTok{ a (b, c) d} \NormalTok{unsplit }\OtherTok{=}\NormalTok{ arr }\OperatorTok{.} \FunctionTok{uncurry} \CommentTok{{-}{-} arr (\textbackslash{}op (x,y) {-}\textgreater{} x \textasciigrave{}op\textasciigrave{} y)} \end{Highlighting} \end{Shaded} \texttt{(***)} combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair). \begin{verbatim} f *** g = first f >>> second g \end{verbatim} Block quote: \begin{quote} foo bar \end{quote} ================================================ FILE: test/lhs-test.latex+lhs ================================================ \section{lhs test}\label{lhs-test} \texttt{unsplit} is an arrow that takes a pair of values and combines them to return a single value: \begin{code} unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d unsplit = arr . uncurry -- arr (\op (x,y) -> x `op` y) \end{code} \texttt{(***)} combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair). \begin{verbatim} f *** g = first f >>> second g \end{verbatim} Block quote: \begin{quote} foo bar \end{quote} ================================================ FILE: test/lhs-test.markdown ================================================ lhs test ======== `unsplit` is an arrow that takes a pair of values and combines them to return a single value: ``` {.haskell .literate} unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d unsplit = arr . uncurry -- arr (\op (x,y) -> x `op` y) ``` `(***)` combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair). f *** g = first f >>> second g Block quote: > foo bar ================================================ FILE: test/lhs-test.markdown+lhs ================================================ lhs test ======== `unsplit` is an arrow that takes a pair of values and combines them to return a single value: > unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d > unsplit = arr . uncurry > -- arr (\op (x,y) -> x `op` y) `(***)` combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair). f *** g = first f >>> second g Block quote: > foo bar ================================================ FILE: test/lhs-test.native ================================================ [ Header 1 ( "lhs-test" , [] , [] ) [ Str "lhs" , Space , Str "test" ] , Para [ Code ( "" , [] , [] ) "unsplit" , Space , Str "is" , Space , Str "an" , Space , Str "arrow" , Space , Str "that" , Space , Str "takes" , Space , Str "a" , Space , Str "pair" , Space , Str "of" , Space , Str "values" , Space , Str "and" , Space , Str "combines" , Space , Str "them" , Space , Str "to" , SoftBreak , Str "return" , Space , Str "a" , Space , Str "single" , Space , Str "value:" ] , CodeBlock ( "" , [ "haskell" , "literate" ] , [] ) "unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d\nunsplit = arr . uncurry\n -- arr (\\op (x,y) -> x `op` y)" , Para [ Code ( "" , [] , [] ) "(***)" , Space , Str "combines" , Space , Str "two" , Space , Str "arrows" , Space , Str "into" , Space , Str "a" , Space , Str "new" , Space , Str "arrow" , Space , Str "by" , Space , Str "running" , Space , Str "the" , Space , Str "two" , Space , Str "arrows" , Space , Str "on" , Space , Str "a" , SoftBreak , Str "pair" , Space , Str "of" , Space , Str "values" , Space , Str "(one" , Space , Str "arrow" , Space , Str "on" , Space , Str "the" , Space , Str "first" , Space , Str "item" , Space , Str "of" , Space , Str "the" , Space , Str "pair" , Space , Str "and" , Space , Str "one" , Space , Str "arrow" , Space , Str "on" , Space , Str "the" , SoftBreak , Str "second" , Space , Str "item" , Space , Str "of" , Space , Str "the" , Space , Str "pair)." ] , CodeBlock ( "" , [] , [] ) "f *** g = first f >>> second g" , Para [ Str "Block" , Space , Str "quote:" ] , BlockQuote [ Para [ Str "foo" , Space , Str "bar" ] ] ] ================================================ FILE: test/lhs-test.rst ================================================ lhs test ======== ``unsplit`` is an arrow that takes a pair of values and combines them to return a single value: .. code:: haskell unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d unsplit = arr . uncurry -- arr (\op (x,y) -> x `op` y) ``(***)`` combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair). :: f *** g = first f >>> second g Block quote: foo bar ================================================ FILE: test/lhs-test.rst+lhs ================================================ lhs test ======== ``unsplit`` is an arrow that takes a pair of values and combines them to return a single value: > unsplit :: (Arrow a) => (b -> c -> d) -> a (b, c) d > unsplit = arr . uncurry > -- arr (\op (x,y) -> x `op` y) ``(***)`` combines two arrows into a new arrow by running the two arrows on a pair of values (one arrow on the first item of the pair and one arrow on the second item of the pair). :: f *** g = first f >>> second g Block quote: foo bar ================================================ FILE: test/man-reader.man ================================================ .TH "PANDOC-MAN-TESTS" "1" "Oct 17, 2018" "v1.0.1" "Pandoc Tests" .PP This is a set of tests for pandoc. .PP * * * * * .SH Headers .SH Level 1 .SS Level 2 * * * * * .SH Paragraphs .PP Here's a regular paragraph. .PP Another paragraph 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 a list item. .PP There should be a hard line break .PD 0 .P .PD here. .PP * * * * * .SH Block Quotes Code in a block quote: .RS .nf \f[C] sub\ status\ { \ \ \ \ print\ "working"; } \f[] .fi .RE .PP A list: .IP "1." 3 item one .IP "2." 3 item two .PP .SH Code Blocks .PP Code: .IP .nf \f[C] \-\-\-\- (should be four hyphens) sub status { print "working"; } \f[] .fi .PP And: .IP .nf \f[C] \tthis code line is indented by one tab \f[] .fi .PP * * * * * .SH Lists .SS Unordered .PP Asterisks: .IP \[bu] 2 asterisk 1 .IP \[bu] 2 asterisk 2 .IP \[bu] 2 asterisk 3 .PP .SS Ordered .IP "1." 3 First .IP "2." 3 Second .IP "3." 3 Third .PP .SS Nested .IP \[bu] 2 Tab .RS 2 .IP \[bu] 2 Tab .RS 2 .IP \[bu] 2 Tab .RE .RE .PP Here's another: .IP "1." 3 First .IP "2." 3 Second: .RS 4 .IP \[bu] 2 Fee .IP \[bu] 2 Fie .IP \[bu] 2 Foe .RE .IP "3." 3 Third .PP Same thing: .IP "1." 3 First .IP "2." 3 Second: .RS 4 .IP \[bu] 2 Fee .IP \[bu] 2 Fie .IP \[bu] 2 Foe .RE .IP "3." 3 Third .SS different styles: .IP "A." 3 Upper Alpha .RS 4 .IP "I." 3 Upper Roman. .RS 4 .IP "(6)" 4 Decimal start with 6 .RS 4 .IP "c)" 3 Lower alpha with paren .RE .RE .RE .SS Ordered Definition lists .TP .B term1 definition 1 .RS .PP continued .RE .TP .B term2 definition 2 * * * * * .SH Special Characters AT&T has an ampersand in their name. .PP 4 < 5. .PP 6 > 5. .PP Backslash: \\ .PP Backtick: ` .PP Asterisk: * .PP Underscore: _ .PP Left brace: { .PP Right brace: } .PP Left bracket: [ .PP Right bracket: ] .PP Left paren: ( .PP Right paren: ) .PP Greater\-than: > .PP Hash: # .PP Period: . .PP Bang: ! .PP Plus: + .PP Minus: \- .PP .SH Links .PP .UR http://\:example.com some random site .UE . .PP .MT me@example.com my email address .ME . .SH Macros .de au .B Me Myself .. .de auth !! .I The author is \\$1. .!! .au and I. .auth "John Jones" .ds me The \f[B]Author\f[R] It's \*(me. .SH Tables .TS tab(@); r l c l. T{ Right T}@T{ Left T}@T{ Center T}@T{ Default T} _ T{ 12 T}@T{ 12 T}@T{ 12 T}@T{ 12 T} T{ 123 T}@T{ 123 T}@T{ 123 T}@T{ 123 T} T{ 1 T}@T{ 1 T}@T{ 1 T}@T{ 1 T} .TE .TS allbox tab(;); rlcl. T{ Right T};T{ Left T};T{ Center T};T{ Left \f[I]more\f[R] T} _ T{ 12 T};T{ 12 T};T{ 12 T};T{ 12 T} T{ 123 T};T{ 123 T};T{ 123 T};T{ 123 T} T{ 1 T};T{ 1 T};T{ 1 T};T{ 1 T} .TE .TS tab(@); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). T{ Centered Header T}@T{ Left Aligned T}@T{ Right Aligned T}@T{ Default aligned T} _ T{ First T}@T{ row T}@T{ 12.0 T}@T{ Example of a row that spans multiple lines. T} T{ Second T}@T{ row T}@T{ 5.0 T}@T{ Here\[cq]s another one. Note the blank line between rows. T} .TE .PP Table without column headers: .PP .TS tab(@); r l c r. T{ 12 T}@T{ 12 T}@T{ 12 T}@T{ 12 T} T{ 123 T}@T{ 123 T}@T{ 123 T}@T{ 123 T} T{ 1 T}@T{ 1 T}@T{ 1 T}@T{ 1 T} .TE .TS tab(@); rl. a@b T{ .PP one .PP two T}@T{ .nf some code .fi T} .TE ================================================ FILE: test/man-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "date" , MetaInlines [ Str "Oct" , Space , Str "17," , Space , Str "2018" ] ) , ( "footer" , MetaInlines [ Str "v1.0.1" ] ) , ( "header" , MetaInlines [ Str "Pandoc" , Space , Str "Tests" ] ) , ( "section" , MetaInlines [ Str "1" ] ) , ( "title" , MetaInlines [ Str "PANDOC-MAN-TESTS" ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." ] , Para [ Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" ] , Header 1 ( "" , [] , [] ) [ Str "Headers" ] , Header 1 ( "" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Header 2 ( "" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" ] , Header 1 ( "" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here's" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "Another" , Space , Str "paragraph" , Space , Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , Space , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , Space , Str "item." , Space , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , Space , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , Space , Str "a" , Space , Str "list" , Space , Str "item." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "hard" , Space , Str "line" , Space , Str "break" ] , Para [ Str "here." ] , Para [ Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" ] , Header 1 ( "" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , BlockQuote [ CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" ] , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "item" , Space , Str "one" ] ] , [ Para [ Str "item" , Space , Str "two" ] ] ] , Header 1 ( "" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) "\tthis code line is indented by one tab" , Para [ Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" ] , Header 1 ( "" , [] , [] ) [ Str "Lists" ] , Header 2 ( "" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Header 2 ( "" , [] , [] ) [ Str "Ordered" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] , BulletList [ [ Para [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here's" , Space , Str "another:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Para [ Str "Fee" ] ] , [ Para [ Str "Fie" ] ] , [ Para [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "" , [] , [] ) [ Str "different" , Space , Str "styles:" ] , OrderedList ( 1 , UpperAlpha , Period ) [ [ Para [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , Period ) [ [ Para [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , TwoParens ) [ [ Para [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , OneParen ) [ [ Para [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Header 2 ( "" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Definition" , Space , Str "lists" ] , DefinitionList [ ( [ Strong [ Str "term1" ] ] , [ [ Para [ Str "definition" , Space , Str "1" ] , Para [ Str "continued" ] ] ] ) , ( [ Strong [ Str "term2" ] ] , [ [ Para [ Str "definition" , Space , Str "2" , Space , Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" , Space , Str "*" ] ] ] ) ] , Header 1 ( "" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , Header 1 ( "" , [] , [] ) [ Str "Links" ] , Para [ Link ( "" , [] , [] ) [ Str "some" , Space , Str "randomsite" ] ( "http://example.com" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "my" , Space , Str "email" , Space , Str "address" ] ( "mailto:me@example.com" , "" ) , Str "." ] , Header 1 ( "" , [] , [] ) [ Str "Macros" ] , Para [ Strong [ Str "Me" , Space , Str "Myself" ] , Space , Str "and" , Space , Str "I." , Space , Emph [ Str "The" , Space , Str "author" , Space , Str "is" , Space , Str "John" , Space , Str "Jones." ] , Space , Str "It's" , Space , Str "The" , Space , Strong [ Str "Author" ] , Str "." ] , Header 1 ( "" , [] , [] ) [ Str "Tables" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , Space , Emph [ Str "more" ] ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , Space , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , Space , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , Space , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , Space , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , Space , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidth 0.5 ) , ( AlignLeft , ColWidth 0.5 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "one" ] , Para [ Str "two" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "some\n code" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/markdown-citations.native ================================================ [ Header 1 ( "pandoc-with-citeproc-hs" , [] , [] ) [ Str "Pandoc" , Space , Str "with" , Space , Str "citeproc-hs" ] , BulletList [ [ Para [ Cite [ Citation { citationId = "nonexistent" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 1 , citationHash = 0 } ] [ Str "[@nonexistent]" ] ] ] , [ Para [ Cite [ Citation { citationId = "nonexistent" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 2 , citationHash = 0 } ] [ Str "@nonexistent" ] ] ] , [ Para [ Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 3 , citationHash = 0 } ] [ Str "@item1" ] , Space , Str "says" , Space , Str "blah." ] ] , [ Para [ Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [ Str "p.\160\&30" ] , citationMode = AuthorInText , citationNoteNum = 4 , citationHash = 0 } ] [ Str "@item1" , Space , Str "[p." , Space , Str "30]" ] , Space , Str "says" , Space , Str "blah." ] ] , [ Para [ Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [ Str "p.\160\&30," , Space , Str "with" , Space , Str "suffix" ] , citationMode = AuthorInText , citationNoteNum = 5 , citationHash = 0 } ] [ Str "@item1" , Space , Str "[p." , Space , Str "30," , Space , Str "with" , Space , Str "suffix]" ] , Space , Str "says" , Space , Str "blah." ] ] , [ Para [ Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 6 , citationHash = 0 } , Citation { citationId = "item2" , citationPrefix = [] , citationSuffix = [ Space , Str "p.\160\&30" ] , citationMode = SuppressAuthor , citationNoteNum = 6 , citationHash = 0 } , Citation { citationId = "\1087\1091\1085\1082\1090\&3" , citationPrefix = [ Str "see" , Space , Str "also" ] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 6 , citationHash = 0 } ] [ Str "@item1" , Space , Str "[-@item2" , Space , Str "p." , Space , Str "30;" , Space , Str "see" , Space , Str "also" , Space , Str "@\1087\1091\1085\1082\1090\&3]" ] , Space , Str "says" , Space , Str "blah." ] ] , [ Para [ Str "In" , Space , Str "a" , Space , Str "note." , Note [ Para [ Cite [ Citation { citationId = "\1087\1091\1085\1082\1090\&3" , citationPrefix = [] , citationSuffix = [ Str "p.\160\&12" ] , citationMode = AuthorInText , citationNoteNum = 7 , citationHash = 0 } ] [ Str "@\1087\1091\1085\1082\1090\&3" , Space , Str "[p." , Space , Str "12]" ] , Space , Str "and" , Space , Str "a" , Space , Str "citation" , Space , Str "without" , Space , Str "locators" , Space , Cite [ Citation { citationId = "\1087\1091\1085\1082\1090\&3" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 7 , citationHash = 0 } ] [ Str "[@\1087\1091\1085\1082\1090\&3]" ] , Str "." ] ] ] ] , [ Para [ Str "A" , Space , Str "citation" , Space , Str "group" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [ Str "see" ] , citationSuffix = [ Space , Str "chap.\160\&3" ] , citationMode = NormalCitation , citationNoteNum = 8 , citationHash = 0 } , Citation { citationId = "\1087\1091\1085\1082\1090\&3" , citationPrefix = [ Str "also" ] , citationSuffix = [ Space , Str "p.\160\&34-35" ] , citationMode = NormalCitation , citationNoteNum = 8 , citationHash = 0 } ] [ Str "[see" , Space , Str "@item1" , Space , Str "chap." , Space , Str "3;" , Space , Str "also" , Space , Str "@\1087\1091\1085\1082\1090\&3" , Space , Str "p." , Space , Str "34-35]" ] , Str "." ] ] , [ Para [ Str "Another" , Space , Str "one" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [ Str "see" ] , citationSuffix = [ Space , Str "p.\160\&34-35" ] , citationMode = NormalCitation , citationNoteNum = 9 , citationHash = 0 } ] [ Str "[see" , Space , Str "@item1" , Space , Str "p." , Space , Str "34-35]" ] , Str "." ] ] , [ Para [ Str "And" , Space , Str "another" , Space , Str "one" , Space , Str "in" , Space , Str "a" , Space , Str "note." , Note [ Para [ Str "Some" , Space , Str "citations" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [ Str "see" ] , citationSuffix = [ Space , Str "chap.\160\&3" ] , citationMode = NormalCitation , citationNoteNum = 10 , citationHash = 0 } , Citation { citationId = "\1087\1091\1085\1082\1090\&3" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 10 , citationHash = 0 } , Citation { citationId = "item2" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 10 , citationHash = 0 } ] [ Str "[see" , Space , Str "@item1" , Space , Str "chap." , Space , Str "3;" , Space , Str "@\1087\1091\1085\1082\1090\&3;" , Space , Str "@item2]" ] , Str "." ] ] ] ] , [ Para [ Str "Citation" , Space , Str "with" , Space , Str "a" , Space , Str "suffix" , Space , Str "and" , Space , Str "locator" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [ Space , Str "pp.\160\&33," , Space , Str "35-37," , Space , Str "and" , Space , Str "nowhere" , Space , Str "else" ] , citationMode = NormalCitation , citationNoteNum = 11 , citationHash = 0 } ] [ Str "[@item1" , Space , Str "pp." , Space , Str "33," , Space , Str "35-37," , Space , Str "and" , Space , Str "nowhere" , Space , Str "else]" ] , Str "." ] ] , [ Para [ Str "Citation" , Space , Str "with" , Space , Str "suffix" , Space , Str "only" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [ Space , Str "and" , Space , Str "nowhere" , Space , Str "else" ] , citationMode = NormalCitation , citationNoteNum = 12 , citationHash = 0 } ] [ Str "[@item1" , Space , Str "and" , Space , Str "nowhere" , Space , Str "else]" ] , Str "." ] ] , [ Para [ Str "Now" , Space , Str "some" , Space , Str "modifiers." , Note [ Para [ Str "Like" , Space , Str "a" , Space , Str "citation" , Space , Str "without" , Space , Str "author:" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [] , citationSuffix = [] , citationMode = SuppressAuthor , citationNoteNum = 13 , citationHash = 0 } ] [ Str "[-@item1]" ] , Str "," , Space , Str "and" , Space , Str "now" , Space , Str "Doe" , Space , Str "with" , Space , Str "a" , Space , Str "locator" , Space , Cite [ Citation { citationId = "item2" , citationPrefix = [] , citationSuffix = [ Space , Str "p.\160\&44" ] , citationMode = SuppressAuthor , citationNoteNum = 13 , citationHash = 0 } ] [ Str "[-@item2" , Space , Str "p." , Space , Str "44]" ] , Str "." ] ] ] ] , [ Para [ Str "With" , Space , Str "some" , Space , Str "markup" , Space , Cite [ Citation { citationId = "item1" , citationPrefix = [ Emph [ Str "see" ] ] , citationSuffix = [ Space , Str "p.\160" , Strong [ Str "32" ] ] , citationMode = NormalCitation , citationNoteNum = 14 , citationHash = 0 } ] [ Str "[*see*" , Space , Str "@item1" , Space , Str "p." , Space , Str "**32**]" ] , Str "." ] ] ] , Header 1 ( "references" , [] , [] ) [ Str "References" ] ] ================================================ FILE: test/markdown-citations.txt ================================================ Pandoc with citeproc-hs ======================= - [@nonexistent] - @nonexistent - @item1 says blah. - @item1 [p. 30] says blah. - @item1 [p. 30, with suffix] says blah. - @item1 [-@item2 p. 30; see also @пункт3] says blah. - In a note.[^1] - A citation group [see @item1 chap. 3; also @пункт3 p. 34-35]. - Another one [see @item1 p. 34-35]. - And another one in a note.[^2] - Citation with a suffix and locator [@item1 pp. 33, 35-37, and nowhere else]. - Citation with suffix only [@item1 and nowhere else]. - Now some modifiers.[^3] - With some markup [*see* @item1 p. **32**]. References ========== [^1]: @пункт3 [p. 12] and a citation without locators [@пункт3]. [^2]: Some citations [see @item1 chap. 3; @пункт3; @item2]. [^3]: Like a citation without author: [-@item1], and now Doe with a locator [-@item2 p. 44]. ================================================ FILE: test/markdown-reader-more.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "Author" , Space , Str "One" ] , MetaInlines [ Str "Author" , Space , Str "Two" ] , MetaInlines [ Str "Author" , Space , Str "Three" ] , MetaInlines [ Str "Author" , Space , Str "Four" ] ] ) , ( "title" , MetaInlines [ Str "Title" , SoftBreak , Str "spanning" , Space , Str "multiple" , Space , Str "lines" ] ) ] } [ Header 1 ( "additional-markdown-reader-tests" , [] , [] ) [ Str "Additional" , Space , Str "markdown" , Space , Str "reader" , Space , Str "tests" ] , Header 2 ( "blank-line-before-url-in-link-reference" , [] , [] ) [ Str "Blank" , Space , Str "line" , Space , Str "before" , Space , Str "URL" , Space , Str "in" , Space , Str "link" , Space , Str "reference" ] , Para [ Link ( "" , [] , [] ) [ Str "foo" ] ( "/url" , "" ) , Space , Str "and" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url" , "title" ) ] , Header 2 ( "raw-context-environments" , [] , [] ) [ Str "Raw" , Space , Str "ConTeXt" , Space , Str "environments" ] , RawBlock (Format "tex") "\\placeformula \\startformula" , Para [ Str "L_{1}" , Space , Str "=" , Space , Str "L_{2}" , SoftBreak , RawInline (Format "tex") "\\stopformula" ] , RawBlock (Format "tex") "\\start[a2]\n\\start[a2]\n\\stop[a2]\n\\stop[a2]" , Header 2 ( "raw-latex-environments" , [] , [] ) [ Str "Raw" , Space , Str "LaTeX" , Space , Str "environments" ] , RawBlock (Format "tex") "\\begin{center}\n\\begin{tikzpicture}[baseline={([yshift=+-.5ex]current bounding box.center)}, level distance=24pt]\n\\Tree [.{S} [.NP John\\index{i} ] [.VP [.V likes ] [.NP himself\\index{i,*j} ]]]\n\\end{tikzpicture}\n\\end{center}" , Header 2 ( "urls-with-spaces-and-punctuation" , [] , [] ) [ Str "URLs" , Space , Str "with" , Space , Str "spaces" , Space , Str "and" , Space , Str "punctuation" ] , Para [ Link ( "" , [] , [] ) [ Str "foo" ] ( "/bar%20and%20baz" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "foo" ] ( "/bar%20and%20baz" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "foo" ] ( "/bar%20and%20baz" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "foo" ] ( "bar%20baz" , "title" ) ] , Para [ Link ( "" , [] , [] ) [ Str "baz" ] ( "/foo%20foo" , "" ) , Space , Link ( "" , [] , [] ) [ Str "bam" ] ( "/foo%20fee" , "" ) , Space , Link ( "" , [] , [] ) [ Str "bork" ] ( "/foo/zee%20zob" , "title" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Ward\8217s" , Space , Str "method." ] ( "http://en.wikipedia.org/wiki/Ward's_method" , "" ) ] , Header 2 ( "horizontal-rules-with-spaces-at-end" , [] , [] ) [ Str "Horizontal" , Space , Str "rules" , Space , Str "with" , Space , Str "spaces" , Space , Str "at" , Space , Str "end" ] , HorizontalRule , HorizontalRule , Header 2 ( "raw-html-before-header" , [] , [] ) [ Str "Raw" , Space , Str "HTML" , Space , Str "before" , Space , Str "header" ] , Para [ RawInline (Format "html") "" , RawInline (Format "html") "" ] , Header 3 ( "my-header" , [] , [] ) [ Str "my" , Space , Str "header" ] , Header 2 ( "in-math" , [] , [] ) [ Str "$" , Space , Str "in" , Space , Str "math" ] , Para [ Math InlineMath "\\$2 + \\$3" ] , Para [ Math InlineMath "x = \\text{the $n$th root of $y$}" ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "math:" ] , Para [ Str "$PATH" , Space , Str "90" , Space , Str "$PATH" ] , Header 2 ( "commented-out-list-item" , [] , [] ) [ Str "Commented-out" , Space , Str "list" , Space , Str "item" ] , BulletList [ [ Plain [ Str "one" , SoftBreak , Str "" ] ] , [ Plain [ Str "three" ] ] ] , Header 2 ( "indented-code-at-beginning-of-list" , [] , [] ) [ Str "Indented" , Space , Str "code" , Space , Str "at" , Space , Str "beginning" , Space , Str "of" , Space , Str "list" ] , BulletList [ [ CodeBlock ( "" , [] , [] ) "code\ncode" , OrderedList ( 1 , Decimal , Period ) [ [ CodeBlock ( "" , [] , [] ) "code\ncode" ] , [ CodeBlock ( "" , [] , [] ) "code\ncode" ] ] , BulletList [ [ CodeBlock ( "" , [] , [] ) "code\ncode" ] , [ Plain [ Str "no" , Space , Str "code" ] ] ] ] ] , Header 2 ( "backslash-newline" , [] , [] ) [ Str "Backslash" , Space , Str "newline" ] , Para [ Str "hi" , LineBreak , Str "there" ] , Header 2 ( "code-spans" , [] , [] ) [ Str "Code" , Space , Str "spans" ] , Para [ Code ( "" , [] , [] ) "hi\\" ] , Para [ Code ( "" , [] , [] ) "hi there" ] , Para [ Code ( "" , [] , [] ) "hi````there" ] , Para [ Str "`hi" ] , Para [ Str "there`" ] , Header 2 ( "multilingual-urls" , [] , [] ) [ Str "Multilingual" , Space , Str "URLs" ] , Para [ Link ( "" , [ "uri" ] , [] ) [ Str "http://\27979.com?\27979=\27979" ] ( "http://\27979.com?\27979=\27979" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "foo" ] ( "/bar/\27979?x=\27979" , "title" ) ] , Para [ Link ( "" , [ "email" ] , [] ) [ Str "\27979@foo.\27979.baz" ] ( "mailto:\27979@foo.\27979.baz" , "" ) ] , Header 2 ( "numbered-examples" , [] , [] ) [ Str "Numbered" , Space , Str "examples" ] , OrderedList ( 1 , Example , TwoParens ) [ [ Plain [ Str "First" , Space , Str "example." ] ] , [ Plain [ Str "Second" , Space , Str "example." ] ] ] , Para [ Str "Explanation" , Space , Str "of" , Space , Str "examples" , Space , Str "(2)" , Space , Str "and" , Space , Str "(3)." ] , OrderedList ( 3 , Example , TwoParens ) [ [ Plain [ Str "Third" , Space , Str "example." ] ] ] , Header 2 ( "macros" , [] , [] ) [ Str "Macros" ] , RawBlock (Format "tex") "\\newcommand{\\tuple}[1]{\\langle #1 \\rangle}" , Para [ Math InlineMath "\\langle x,y \\rangle" ] , Header 2 ( "case-insensitive-references" , [] , [] ) [ Str "Case-insensitive" , Space , Str "references" ] , Para [ Link ( "" , [] , [] ) [ Str "Fum" ] ( "/fum" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "FUM" ] ( "/fum" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "bat" ] ( "/bat" , "" ) ] , Header 2 ( "curly-smart-quotes" , [] , [] ) [ Str "Curly" , Space , Str "smart" , Space , Str "quotes" ] , Para [ Str "\8220Hi\8221" ] , Para [ Str "\8216Hi\8217" ] , Header 2 ( "consecutive-lists" , [] , [] ) [ Str "Consecutive" , Space , Str "lists" ] , BulletList [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] ] ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] ] ] , OrderedList ( 1 , LowerAlpha , Period ) [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] ] ] , Header 2 ( "implicit-header-references" , [] , [] ) [ Str "Implicit" , Space , Str "header" , Space , Str "references" ] , Header 3 ( "my-header-1" , [] , [] ) [ Str "My" , Space , Str "header" ] , Header 3 ( "my-other-header" , [] , [] ) [ Str "My" , Space , Str "other" , Space , Str "header" ] , Para [ Str "A" , Space , Str "link" , Space , Str "to" , Space , Link ( "" , [] , [] ) [ Str "My" , Space , Str "header" ] ( "#my-header" , "" ) , Str "." ] , Para [ Str "Another" , Space , Str "link" , Space , Str "to" , Space , Link ( "" , [] , [] ) [ Str "it" ] ( "#my-header" , "" ) , Str "." ] , Para [ Str "Should" , Space , Str "be" , Space , Link ( "" , [] , [] ) [ Str "case" , Space , Str "insensitive" ] ( "#my-header" , "" ) , Str "." ] , Para [ Str "Link" , Space , Str "to" , Space , Link ( "" , [] , [] ) [ Str "Explicit" , Space , Str "header" , Space , Str "attributes" ] ( "#foobar" , "" ) , Str "." ] , Para [ Str "But" , Space , Str "this" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "link" , Space , Str "to" , Space , Link ( "" , [] , [] ) [ Str "My" , Space , Str "other" , Space , Str "header" ] ( "/foo" , "" ) , Str "," , Space , Str "since" , Space , Str "the" , Space , Str "reference" , Space , Str "is" , Space , Str "defined." ] , Header 2 ( "foobar" , [ "baz" ] , [ ( "key" , "val" ) ] ) [ Str "Explicit" , Space , Str "header" , Space , Str "attributes" ] , BlockQuote [ Header 2 ( "foobar" , [ "baz" ] , [ ( "key" , "val" ) ] ) [ Str "Header" , Space , Str "attributes" , Space , Str "inside" , Space , Str "block" , Space , Str "quote" ] ] , Header 2 ( "line-blocks" , [] , [] ) [ Str "Line" , Space , Str "blocks" ] , LineBlock [ [ Str "But" , Space , Str "can" , Space , Str "a" , Space , Str "bee" , Space , Str "be" , Space , Str "said" , Space , Str "to" , Space , Str "be" ] , [ Str "\160\160\160\160or" , Space , Str "not" , Space , Str "to" , Space , Str "be" , Space , Str "an" , Space , Str "entire" , Space , Str "bee," ] , [ Str "\160\160\160\160\160\160\160\160when" , Space , Str "half" , Space , Str "the" , Space , Str "bee" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "bee," ] , [ Str "\160\160\160\160\160\160\160\160\160\160\160\160due" , Space , Str "to" , Space , Str "some" , Space , Str "ancient" , Space , Str "injury?" ] , [] , [ Str "Continuation" , Space , Str "line" ] , [ Str "\160\160and" , Space , Str "another" ] ] , Header 2 ( "grid-tables" , [] , [] ) [ Str "Grid" , Space , Str "Tables" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.2638888888888889 ) , ( AlignDefault , ColWidth 0.16666666666666666 ) , ( AlignDefault , ColWidth 0.18055555555555555 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" , SoftBreak , Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" , SoftBreak , Str "b" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Headless" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.2638888888888889 ) , ( AlignDefault , ColWidth 0.16666666666666666 ) , ( AlignDefault , ColWidth 0.18055555555555555 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" , SoftBreak , Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" , SoftBreak , Str "b" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "With" , Space , Str "alignments" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidth 0.2638888888888889 ) , ( AlignLeft , ColWidth 0.16666666666666666 ) , ( AlignCenter , ColWidth 0.18055555555555555 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" , SoftBreak , Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" , SoftBreak , Str "b" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Headless" , Space , Str "with" , Space , Str "alignments" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidth 0.2638888888888889 ) , ( AlignLeft , ColWidth 0.16666666666666666 ) , ( AlignCenter , ColWidth 0.18055555555555555 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" , SoftBreak , Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" , SoftBreak , Str "b" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Spaces" , Space , Str "at" , Space , Str "ends" , Space , Str "of" , Space , Str "lines" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.2638888888888889 ) , ( AlignDefault , ColWidth 0.16666666666666666 ) , ( AlignDefault , ColWidth 0.18055555555555555 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" , SoftBreak , Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" , SoftBreak , Str "b" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "in" , Space , Str "a" , Space , Str "cell" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.2638888888888889 ) , ( AlignDefault , ColWidth 0.16666666666666666 ) , ( AlignDefault , ColWidth 0.18055555555555555 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Header 1 ( "col-1" , [] , [] ) [ Str "col" , Space , Str "1" ] , Para [ Str "col" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Header 1 ( "col-2" , [] , [] ) [ Str "col" , Space , Str "2" ] , Para [ Str "col" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Header 1 ( "col-3" , [] , [] ) [ Str "col" , Space , Str "3" ] , Para [ Str "col" , Space , Str "3" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "r1" , Space , Str "a" ] , Para [ Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ BulletList [ [ Plain [ Str "b" ] ] , [ Plain [ Str "b" , Space , Str "2" ] ] , [ Plain [ Str "b" , Space , Str "2" ] ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" , SoftBreak , Str "c" , Space , Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "East" , Space , Str "Asian" , Space , Str "characters" , Space , Str "have" , Space , Str "double" , Space , Str "width" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 4.1666666666666664e-2 ) , ( AlignDefault , ColWidth 6.944444444444445e-2 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "\39770" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "fish" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Zero-width" , Space , Str "space" , Space , Str "in" , Space , Str "German" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.1111111111111111 ) , ( AlignDefault , ColWidth 0.1111111111111111 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "German" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "English" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Auf\8204lage" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "edition" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Zero-width" , Space , Str "non-joiner" , Space , Str "in" , Space , Str "Persian" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.1111111111111111 ) , ( AlignDefault , ColWidth 0.1388888888888889 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "\1605\1740\8204\1582\1608\1575\1607\1605" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "I" , Space , Str "want" , Space , Str "to" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Empty" , Space , Str "cells" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 5.555555555555555e-2 ) , ( AlignDefault , ColWidth 5.555555555555555e-2 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "with" , Space , Str "cells" , Space , Str "spanning" , Space , Str "multiple" , Space , Str "rows" , Space , Str "or" , Space , Str "columns:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.19444444444444445 ) , ( AlignDefault , ColWidth 0.1111111111111111 ) , ( AlignDefault , ColWidth 0.1527777777777778 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Property" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Earth" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 1) [ Plain [ Str "Temperature" , SoftBreak , Str "1961-1990" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "min" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-89.2" , Space , Str "\176C" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "mean" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "14" , Space , Str "\176C" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "min" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "56.7" , Space , Str "\176C" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "with" , Space , Str "complex" , Space , Str "header:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.3055555555555556 ) , ( AlignDefault , ColWidth 0.1111111111111111 ) , ( AlignDefault , ColWidth 0.1111111111111111 ) , ( AlignDefault , ColWidth 0.1111111111111111 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "Location" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Temperature" , Space , Str "1961-1990" , SoftBreak , Str "in" , Space , Str "degree" , Space , Str "Celsius" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "min" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "mean" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "max" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Antarctica" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-89.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "N/A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "19.8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Earth" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-89.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "14" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "56.7" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "entities-in-links-and-titles" , [] , [] ) [ Str "Entities" , Space , Str "in" , Space , Str "links" , Space , Str "and" , Space , Str "titles" ] , Para [ Link ( "" , [] , [] ) [ Str "link" ] ( "/\252rl" , "\246\246!" ) ] , Para [ Link ( "" , [ "uri" ] , [] ) [ Str "http://g\246\246gle.com" ] ( "http://g\246\246gle.com" , "" ) ] , Para [ Link ( "" , [ "email" ] , [] ) [ Str "me@ex\228mple.com" ] ( "mailto:me@ex\228mple.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "foobar" ] ( "/\252rl" , "\246\246!" ) ] , Header 2 ( "parentheses-in-urls" , [] , [] ) [ Str "Parentheses" , Space , Str "in" , Space , Str "URLs" ] , Para [ Link ( "" , [] , [] ) [ Str "link" ] ( "/hi(there)" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "link" ] ( "/hithere)" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "linky" ] ( "hi_(there_(nested))" , "" ) ] , Header 2 ( "backslashes-in-link-references" , [] , [] ) [ Str "Backslashes" , Space , Str "in" , Space , Str "link" , Space , Str "references" ] , Para [ Link ( "" , [] , [] ) [ Str "*" , RawInline (Format "tex") "\\a" ] ( "b" , "" ) ] , Header 2 ( "reference-link-fallbacks" , [] , [] ) [ Str "Reference" , Space , Str "link" , Space , Str "fallbacks" ] , Para [ Str "[" , Emph [ Str "not" , Space , Str "a" , Space , Str "link" ] , Str "]" , Space , Str "[" , Emph [ Str "nope" ] , Str "]\8230" ] , Header 2 ( "reference-link-followed-by-a-citation" , [] , [] ) [ Str "Reference" , Space , Str "link" , Space , Str "followed" , Space , Str "by" , Space , Str "a" , Space , Str "citation" ] , Para [ Str "MapReduce" , Space , Str "is" , Space , Str "a" , Space , Str "paradigm" , Space , Str "popularized" , Space , Str "by" , Space , Link ( "" , [] , [] ) [ Str "Google" ] ( "http://google.com" , "" ) , Space , Cite [ Citation { citationId = "mapreduce" , citationPrefix = [] , citationSuffix = [] , citationMode = NormalCitation , citationNoteNum = 2 , citationHash = 0 } ] [ Str "[@mapreduce]" ] , Space , Str "as" , Space , Str "its" , SoftBreak , Str "most" , Space , Str "vocal" , Space , Str "proponent." ] , Header 2 ( "empty-reference-links" , [] , [] ) [ Str "Empty" , Space , Str "reference" , Space , Str "links" ] , Para [ Str "bar" ] , Para [ Link ( "" , [] , [] ) [ Str "foo2" ] ( "" , "" ) ] , Header 2 ( "wrapping-shouldnt-introduce-new-list-items" , [] , [] ) [ Str "Wrapping" , Space , Str "shouldn\8217t" , Space , Str "introduce" , Space , Str "new" , Space , Str "list" , Space , Str "items" ] , BulletList [ [ Plain [ Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "blah" , Space , Str "2015." ] ] ] , Header 2 ( "bracketed-spans" , [] , [] ) [ Str "Bracketed" , Space , Str "spans" ] , Para [ Span ( "id" , [ "class" ] , [ ( "key" , "val" ) ] ) [ Emph [ Str "foo" ] , Space , Str "bar" , Space , Str "baz" , Space , Link ( "" , [] , [] ) [ Str "link" ] ( "url" , "" ) ] ] ] ================================================ FILE: test/markdown-reader-more.txt ================================================ % Title spanning multiple lines % Author One Author Two; Author Three; Author Four # Additional markdown reader tests ## Blank line before URL in link reference [foo] and [bar] [foo]: /url [bar]: /url "title" ## Raw ConTeXt environments \placeformula \startformula L_{1} = L_{2} \stopformula \start[a2] \start[a2] \stop[a2] \stop[a2] ## Raw LaTeX environments \begin{center} \begin{tikzpicture}[baseline={([yshift=+-.5ex]current bounding box.center)}, level distance=24pt] \Tree [.{S} [.NP John\index{i} ] [.VP [.V likes ] [.NP himself\index{i,*j} ]]] \end{tikzpicture} \end{center} ## URLs with spaces and punctuation [foo](/bar and baz) [foo](/bar and baz ) [foo]( /bar and baz ) [foo](bar baz "title" ) [baz][] [bam][] [bork][] [baz]: /foo foo [bam]: /foo fee [bork]: /foo/zee zob (title) [Ward's method.](http://en.wikipedia.org/wiki/Ward's_method) ## Horizontal rules with spaces at end * * * * * -- - -- -- - ## Raw HTML before header ### my header ## $ in math $\$2 + \$3$ $x = \text{the $n$th root of $y$}$ This should not be math: $PATH 90 $PATH ## Commented-out list item - one - three ## Indented code at beginning of list - code code 1. code code 12345678. code code - code code - no code ## Backslash newline hi\ there ## Code spans `hi\` `hi there` `` hi````there `` `hi there` ## Multilingual URLs [foo](/bar/测?x=测 "title") <测@foo.测.baz> ## Numbered examples (@) First example. (@foo) Second example. Explanation of examples (@foo) and (@bar). (@bar) Third example. ## Macros \newcommand{\tuple}[1]{\langle #1 \rangle} $\tuple{x,y}$ ## Case-insensitive references [Fum] [FUM] [bat] [fum]: /fum [BAT]: /bat ## Curly smart quotes “Hi” ‘Hi’ ## Consecutive lists - one - two 1. one 2. two a. one b. two ## Implicit header references ### My header ### My other header A link to [My header]. Another link to [it][My header]. Should be [case insensitive][my header]. Link to [Explicit header attributes]. [my other header]: /foo But this is not a link to [My other header], since the reference is defined. ## Explicit header attributes {#foobar .baz key="val"} > ## Header attributes inside block quote {#foobar .baz key="val"} ## Line blocks | But can a bee be said to be | or not to be an entire bee, | when half the bee is not a bee, | due to some ancient injury? | | Continuation line | and another ## Grid Tables +------------------+-----------+------------+ | col 1 | col 2 | col 3 | +==================+===========+============+ | r1 a | b | c | | r1 bis | b 2 | c 2 | +------------------+-----------+------------+ | r2 d | e | f | +------------------+-----------+------------+ Headless +------------------+-----------+------------+ | r1 a | b | c | | r1 bis | b 2 | c 2 | +------------------+-----------+------------+ | r2 d | e | f | +------------------+-----------+------------+ With alignments +------------------+-----------+------------+ | col 1 | col 2 | col 3 | +=================:+:==========+:==========:+ | r1 a | b | c | | r1 bis | b 2 | c 2 | +------------------+-----------+------------+ | r2 d | e | f | +------------------+-----------+------------+ Headless with alignments +-----------------:+:----------+:----------:+ | r1 a | b | c | | r1 bis | b 2 | c 2 | +------------------+-----------+------------+ | r2 d | e | f | +------------------+-----------+------------+ Spaces at ends of lines +------------------+-----------+------------+ | r1 a | b | c | | r1 bis | b 2 | c 2 | +------------------+-----------+------------+ | r2 d | e | f | +------------------+-----------+------------+ Multiple blocks in a cell +------------------+-----------+------------+ | # col 1 | # col 2 | # col 3 | | col 1 | col 2 | col 3 | +------------------+-----------+------------+ | r1 a | - b | c | | | - b 2 | c 2 | | r1 bis | - b 2 | c 2 | +------------------+-----------+------------+ East Asian characters have double width +--+----+ |魚|fish| +--+----+ Zero-width space in German +-------+-------+ |German |English| +-------+-------+ |Auf‌lage|edition| +-------+-------+ Zero-width non-joiner in Persian +-------+---------+ |می‌خواهم|I want to| +-------+---------+ Empty cells +---+---+ | | | +---+---+ Table with cells spanning multiple rows or columns: +---------------------+----------+ | Property | Earth | +=============+=======+==========+ | | min | -89.2 °C | | Temperature +-------+----------+ | 1961-1990 | mean | 14 °C | | +-------+----------+ | | min | 56.7 °C | +-------------+-------+----------+ Table with complex header: +---------------------+-----------------------+ | Location | Temperature 1961-1990 | | | in degree Celsius | | +-------+-------+-------+ | | min | mean | max | +=====================+=======+=======+=======+ | Antarctica | -89.2 | N/A | 19.8 | +---------------------+-------+-------+-------+ | Earth | -89.2 | 14 | 56.7 | +---------------------+-------+-------+-------+ ## Entities in links and titles [link](/ürl "öö!") [foobar] [foobar]: /ürl "öö!" ## Parentheses in URLs [link](/hi(there)) [link](/hithere\)) [linky] [linky]: hi_(there_(nested)) ## Backslashes in link references [\*\a](b) ## Reference link fallbacks [*not a link*] [*nope*]... ## Reference link followed by a citation MapReduce is a paradigm popularized by [Google] [@mapreduce] as its most vocal proponent. [Google]: http://google.com ## Empty reference links [foo2]: bar [foo2] ## Wrapping shouldn't introduce new list items - blah blah blah blah blah blah blah blah blah blah blah blah blah blah 2015. ## Bracketed spans [*foo* bar baz [link](url)]{.class #id key=val} ================================================ FILE: test/mediawiki-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "header" , [] , [] ) [ Str "header" ] , Header 2 ( "header_level_two" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "two" ] , Header 3 ( "header_level_3" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "3" ] , Header 4 ( "header_level_four" , [] , [] ) [ Str "header" , Space , Emph [ Str "level" ] , Space , Str "four" ] , Header 5 ( "header_level_5" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "5" ] , Header 6 ( "header_level_6" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "6" ] , Para [ Str "=======" , Space , Str "not" , Space , Str "a" , Space , Str "header" , Space , Str "========" ] , Para [ Code ( "" , [] , [] ) "==\160not\160a\160header\160==" ] , Header 2 ( "emph_and_strong" , [] , [] ) [ Str "emph" , Space , Str "and" , Space , Str "strong" ] , Para [ Emph [ Str "emph" ] , Space , Strong [ Str "strong" ] ] , Para [ Strong [ Emph [ Str "strong" , Space , Str "and" , Space , Str "emph" ] ] ] , Para [ Strong [ Emph [ Str "emph" , Space , Str "inside" ] , Space , Str "strong" ] ] , Para [ Strong [ Str "strong" , Space , Str "with" , Space , Emph [ Str "emph" ] ] ] , Para [ Emph [ Strong [ Str "strong" , Space , Str "inside" ] , Space , Str "emph" ] ] , Header 2 ( "horizontal_rule" , [] , [] ) [ Str "horizontal" , Space , Str "rule" ] , Para [ Str "top" ] , HorizontalRule , Para [ Str "bottom" ] , HorizontalRule , Header 2 ( "nowiki" , [] , [] ) [ Str "nowiki" ] , Para [ Str "''not" , Space , Str "emph''" ] , Header 2 ( "strikeout" , [] , [] ) [ Str "strikeout" ] , Para [ Strikeout [ Str "This" , Space , Str "is" , Space , Emph [ Str "struck" , Space , Str "out" ] ] ] , Header 2 ( "entities" , [] , [] ) [ Str "entities" ] , Para [ Str "hi" , Space , Str "&" , Space , Str "low" ] , Para [ Str "hi" , Space , Str "&" , Space , Str "low" ] , Para [ Str "G\246del" ] , Para [ Str "\777\2730" ] , Header 2 ( "comments" , [] , [] ) [ Str "comments" ] , Para [ Str "inline" , Space , Str "comment" ] , Para [ Str "between" , Space , Str "blocks" ] , Header 2 ( "linebreaks" , [] , [] ) [ Str "linebreaks" ] , Para [ Str "hi" , LineBreak , Str "there" ] , Para [ Str "hi" , LineBreak , Str "there" ] , Header 2 ( "indents" , [] , [] ) [ Str ":" , Space , Str "indents" ] , Para [ Str "hi" ] , DefinitionList [ ( [] , [ [ Plain [ Str "there" ] ] ] ) ] , Para [ Str "bud" ] , Para [ Str "hi" ] , DefinitionList [ ( [] , [ [ DefinitionList [ ( [] , [ [ Plain [ Str "there" ] ] ] ) ] ] ] ) ] , Para [ Str "bud" ] , Header 2 ( "p_tags" , [] , [] ) [ Str "p" , Space , Str "tags" ] , Para [ Str "hi" , Space , Str "there" ] , Para [ Str "bud" ] , Para [ Str "another" ] , Header 2 ( "raw_html" , [] , [] ) [ Str "raw" , Space , Str "html" ] , Para [ Str "hi" , Space , RawInline (Format "html") "" , Emph [ Str "there" ] , RawInline (Format "html") "" , Str "." ] , Para [ RawInline (Format "html") "" , Str "inserted" , RawInline (Format "html") "" ] , RawBlock (Format "html") "
                " , Para [ Str "hi" , Space , Emph [ Str "there" ] ] , RawBlock (Format "html") "
                " , Header 2 ( "sup_sub_del" , [] , [] ) [ Str "sup," , Space , Str "sub," , Space , Str "del" ] , Para [ Str "H" , Subscript [ Str "2" ] , Str "O" , Space , Str "base" , Superscript [ Emph [ Str "exponent" ] ] , SoftBreak , Strikeout [ Str "hello" ] ] , Header 2 ( "inline_code" , [] , [] ) [ Str "inline" , Space , Str "code" ] , Para [ Code ( "" , [] , [] ) "*\8594*" , Space , Code ( "" , [] , [] ) "typed" , Space , Code ( "" , [ "haskell" ] , [] ) ">>=" ] , Header 2 ( "code_blocks" , [] , [] ) [ Str "code" , Space , Str "blocks" ] , CodeBlock ( "" , [] , [] ) "case xs of\n (_:_) -> reverse xs\n [] -> ['*']" , CodeBlock ( "" , [ "haskell" ] , [] ) "case xs of\n (_:_) -> reverse xs\n [] -> ['*']" , CodeBlock ( "" , [ "ruby" , "numberLines" ] , [ ( "startFrom" , "100" ) ] ) "widgets.each do |w|\n print w.price\nend" , Header 2 ( "block_quotes" , [] , [] ) [ Str "block" , Space , Str "quotes" ] , Para [ Str "Regular" , Space , Str "paragraph" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." ] , Para [ Str "With" , Space , Str "two" , Space , Str "paragraphs." ] ] , Para [ Str "Nother" , Space , Str "paragraph." ] , Header 2 ( "external_links" , [] , [] ) [ Str "external" , Space , Str "links" ] , Para [ Link ( "" , [] , [] ) [ Emph [ Str "Google" ] , Space , Str "search" , Space , Str "engine" ] ( "http://google.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "http://pandoc.org" ] ( "http://pandoc.org" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "1" ] ( "http://google.com" , "" ) , Space , Link ( "" , [] , [] ) [ Str "2" ] ( "http://yahoo.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "email" , Space , Str "me" ] ( "mailto:info@example.org" , "" ) ] , Header 2 ( "internal_links" , [] , [] ) [ Str "internal" , Space , Str "links" ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "Help" ] ( "Help" , "Help" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "the" , Space , Str "help" , Space , Str "page" ] ( "Help" , "the help page" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "Helpers" ] ( "Help" , "Help" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "Help" ] ( "Help" , "Help" ) , Str "ers" ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "Contents" ] ( "Help:Contents" , "Contents" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "#My" , Space , Str "anchor" ] ( "#My_anchor" , "#My anchor" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "and" , Space , Str "text" ] ( "Page#with_anchor" , "and text" ) ] , Header 2 ( "images" , [] , [] ) [ Str "images" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "caption" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "example.jpg" , "caption" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "the" , Space , Emph [ Str "caption" ] , Space , Str "with" , Space , Link ( "" , [] , [] ) [ Str "external" , Space , Str "link" ] ( "http://google.com" , "" ) ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "example.jpg" , "the caption with external link" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "caption" ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "30" ) , ( "height" , "40" ) ] ) [] ( "example.jpg" , "caption" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "caption" ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "30" ) ] ) [] ( "example.jpg" , "caption" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "caption" ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "30" ) ] ) [] ( "example.jpg" , "caption" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "example.jpg" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "example.jpg" , "example.jpg" ) ] ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "example_es.jpg" ] ]) [ Plain [ Image ( "" , [] , [] ) [] ( "example_es.jpg" , "example_es.jpg" ) ] ] , Header 2 ( "lists" , [] , [] ) [ Str "lists" ] , BulletList [ [ Plain [ Str "Start" , Space , Str "each" , Space , Str "line" ] ] , [ Plain [ Str "with" , Space , Str "an" , Space , Str "asterisk" , Space , Str "(*)." ] , BulletList [ [ Plain [ Str "More" , Space , Str "asterisks" , Space , Str "gives" , Space , Str "deeper" ] , BulletList [ [ Plain [ Str "and" , Space , Str "deeper" , Space , Str "levels." ] ] ] ] ] ] , [ Plain [ Str "Line" , Space , Str "breaks" , LineBreak , Str "don't" , Space , Str "break" , Space , Str "levels." ] , BulletList [ [ BulletList [ [ Plain [ Str "But" , Space , Str "jumping" , Space , Str "levels" , Space , Str "creates" , Space , Str "empty" , Space , Str "space." ] ] ] ] ] ] ] , Para [ Str "Any" , Space , Str "other" , Space , Str "start" , Space , Str "ends" , Space , Str "the" , Space , Str "list." ] , BulletList [ [ BulletList [ [ Plain [ Str "two" ] ] ] ] , [ Plain [ Str "one" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Start" , Space , Str "each" , Space , Str "line" ] ] , [ Plain [ Str "with" , Space , Str "a" , Space , Str "number" , Space , Str "sign" , Space , Str "(#)." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "More" , Space , Str "number" , Space , Str "signs" , Space , Str "gives" , Space , Str "deeper" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "and" , Space , Str "deeper" ] ] , [ Plain [ Str "levels." ] ] ] ] ] ] , [ Plain [ Str "Line" , Space , Str "breaks" , LineBreak , Str "don't" , Space , Str "break" , Space , Str "levels." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "But" , Space , Str "jumping" , Space , Str "levels" , Space , Str "creates" , Space , Str "empty" , Space , Str "space." ] ] ] ] ] ] , [ Plain [ Str "Blank" , Space , Str "lines" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "end" , Space , Str "the" , Space , Str "list" , Space , Str "and" , Space , Str "start" , Space , Str "another." ] ] ] , Para [ Str "Any" , Space , Str "other" , Space , Str "start" , Space , Str "also" , SoftBreak , Str "ends" , Space , Str "the" , Space , Str "list." ] , DefinitionList [ ( [ Str "item" , Space , Str "1" ] , [ [ Plain [ Str "definition" , Space , Str "1" ] ] ] ) , ( [ Str "item" , Space , Str "2" ] , [ [ Plain [ Str "definition" , Space , Str "2-1" ] ] , [ Plain [ Str "definition" , Space , Str "2-2" ] ] ] ) ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] , BulletList [ [ Plain [ Str "two" , Space , Str "point" , Space , Str "one" ] ] , [ Plain [ Str "two" , Space , Str "point" , Space , Str "two" ] ] ] ] , [ Plain [ Str "three" ] , DefinitionList [ ( [ Str "three" , Space , Str "item" , Space , Str "one" ] , [ [ Plain [ Str "three" , Space , Str "def" , Space , Str "one" ] ] ] ) ] ] , [ Plain [ Str "four" ] , DefinitionList [ ( [] , [ [ Plain [ Str "four" , Space , Str "def" , Space , Str "one" ] ] , [ Plain [ Str "this" , Space , Str "looks" , Space , Str "like" , Space , Str "a" , Space , Str "continuation" ] ] , [ Plain [ Str "and" , Space , Str "is" , Space , Str "often" , Space , Str "used" ] ] , [ Plain [ Str "instead" , LineBreak , Str "of" , Space , Str "
                " ] ] ] ) ] ] , [ Plain [ RawInline (Format "mediawiki") "{{{template\n|author=John\n|title=My Book\n}}}" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "five" , Space , Str "sub" , Space , Str "1" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "five" , Space , Str "sub" , Space , Str "1" , Space , Str "sub" , Space , Str "1" ] ] ] ] , [ Plain [ Str "five" , Space , Str "sub" , Space , Str "2" ] ] ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "list" , Space , Str "item" , Space , Emph [ Str "emph" ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "list" , Space , Str "item" , Space , Str "B1" ] ] , [ Plain [ Str "list" , Space , Str "item" , Space , Str "B2" ] ] ] , Para [ Str "continuing" , Space , Str "list" , Space , Str "item" , Space , Str "A1" ] ] , [ Plain [ Str "list" , Space , Str "item" , Space , Str "A2" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "abc" ] ] , [ Plain [ Str "def" ] ] , [ Plain [ Str "ghi" ] ] ] , OrderedList ( 9 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Amsterdam" ] ] , [ Plain [ Str "Rotterdam" ] ] , [ Plain [ Str "The" , Space , Str "Hague" ] ] ] , Header 2 ( "math" , [] , [] ) [ Str "math" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Math InlineMath "x=\\frac{y^\\pi}{z}" , Str "." ] , Para [ Str "With" , Space , Str "spaces:" , Space , Math InlineMath "x=\\frac{y^\\pi}{z}" , Str "." ] , Header 2 ( "preformatted_blocks" , [] , [] ) [ Str "preformatted" , Space , Str "blocks" ] , Para [ Code ( "" , [] , [] ) "Start\160each\160line\160with\160a\160space." , LineBreak , Code ( "" , [] , [] ) "Text\160is\160" , Strong [ Code ( "" , [] , [] ) "preformatted" ] , Code ( "" , [] , [] ) "\160and" , LineBreak , Emph [ Code ( "" , [] , [] ) "markups" ] , Code ( "" , [] , [] ) "\160" , Strong [ Emph [ Code ( "" , [] , [] ) "can" ] ] , Code ( "" , [] , [] ) "\160be\160done." ] , Para [ Code ( "" , [] , [] ) "\160hell\160\160\160\160\160\160yeah" ] , Para [ Code ( "" , [] , [] ) "Start\160with\160a\160space\160in\160the\160first\160column," , LineBreak , Code ( "" , [] , [] ) "(before\160the\160)." , LineBreak , Code ( "" , [] , [] ) "" , LineBreak , Code ( "" , [] , [] ) "Then\160your\160block\160format\160will\160be" , LineBreak , Code ( "" , [] , [] ) "\160\160\160\160maintained." , LineBreak , Code ( "" , [] , [] ) "" , LineBreak , Code ( "" , [] , [] ) "This\160is\160good\160for\160copying\160in\160code\160blocks:" , LineBreak , Code ( "" , [] , [] ) "" , LineBreak , Code ( "" , [] , [] ) "def\160function():" , LineBreak , Code ( "" , [] , [] ) "\160\160\160\160\"\"\"documentation\160string\"\"\"" , LineBreak , Code ( "" , [] , [] ) "" , LineBreak , Code ( "" , [] , [] ) "\160\160\160\160if\160True:" , LineBreak , Code ( "" , [] , [] ) "\160\160\160\160\160\160\160\160print\160True" , LineBreak , Code ( "" , [] , [] ) "\160\160\160\160else:" , LineBreak , Code ( "" , [] , [] ) "\160\160\160\160\160\160\160\160print\160False" ] , Para [ Str "Not" ] , RawBlock (Format "html") "
                " , Para [ Str "preformatted" ] , Para [ Str "Don't" , Space , Str "need" ] , Para [ Code ( "" , [] , [] ) "a\160blank\160line" ] , Para [ Str "around" , Space , Str "a" , Space , Str "preformatted" , Space , Str "block." ] , Header 2 ( "templates" , [] , [] ) [ Str "templates" ] , RawBlock (Format "mediawiki") "{{Welcome}}" , RawBlock (Format "mediawiki") "{{Foo:Bar}}" , RawBlock (Format "mediawiki") "{{Thankyou|all your effort|Me}}" , Para [ Str "Written" , Space , RawInline (Format "mediawiki") "{{{date}}}" , Space , Str "by" , Space , RawInline (Format "mediawiki") "{{{name}}}" , Str "." ] , Header 2 ( "tables" , [] , [] ) [ Str "tables" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Apple" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Pie" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Butter" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Food" , Space , Str "complements" ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Apple" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Pie" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Butter" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Food" , Space , Str "complements" ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Apple" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Bread" ] , Para [ Str "and" , Space , Str "cheese" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Pie" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "apple" ] ] , [ Plain [ Str "carrot" ] ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Apple" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "more" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Pie" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "more" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Butter" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Ice" , Space , Str "cream" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "and" , Space , Str "more" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidth 0.25 ) , ( AlignRight , ColWidth 0.125 ) , ( AlignCenter , ColWidth 0.125 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [ ( "width" , "50%" ) ] ) AlignLeft (RowSpan 1) (ColSpan 1) [ Para [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignRight (RowSpan 1) (ColSpan 1) [ Para [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignCenter (RowSpan 1) (ColSpan 1) [ Para [ Str "Center" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "15.00" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "centered" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "more" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "more" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Apple" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "fruit" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "topping" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "apple" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Butter" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Orange" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Paragraph" , Space , Str "after" , Space , Str "the" , Space , Str "table." ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "fruit" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "topping" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "apple" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "notes" , [] , [] ) [ Str "notes" ] , Para [ Str "My" , Space , Str "note!" , Note [ Plain [ Str "This." ] ] ] , Para [ Str "URL" , Space , Str "note." , Note [ Plain [ Link ( "" , [] , [] ) [ Str "http://docs.python.org/library/functions.html#range" ] ( "http://docs.python.org/library/functions.html#range" , "" ) ] ] ] ] ================================================ FILE: test/mediawiki-reader.wiki ================================================ = header = == header level two == ===header level 3=== ====header ''level'' four==== ===== header level 5 ===== ====== header level 6 ====== ======= not a header ======== == not a header == == emph and strong == ''emph'' '''strong''' '''''strong and emph''''' '''''emph inside'' strong''' '''strong with ''emph''''' '''''strong inside''' emph'' == horizontal rule == top ---- bottom ---- == nowiki == ''not emph'' == strikeout == This is ''struck out'' == entities == hi & low hi & low Gödel ̉પ == comments == inline comment between blocks == linebreaks == hi
                there hi
                there == : indents == hi : there bud hi :: there bud == p tags == hi there

                bud

                another

                == raw html == hi ''there''. inserted
                hi ''there''
                == sup, sub, del == H2O base''exponent'' hello == inline code == *→* typed >>= == code blocks ==
                case xs of
                     (_:_) -> reverse xs
                     []    -> ['*']
                
                case xs of (_:_) -> reverse xs [] -> ['*'] widgets.each do |w| print w.price end == block quotes == Regular paragraph
                This is a block quote. With two paragraphs.
                Nother paragraph. == external links == [http://google.com ''Google'' search engine] http://pandoc.org [http://google.com] [http://yahoo.com] [mailto:info@example.org email me] == internal links == [[Help]] [[Help|the help page]] [[Help]]ers [[Help]]ers [[Help:Contents|]] [[#My anchor]] [[Page#with anchor|and text]] == images == [[File:example.jpg|caption]] [[File:example.jpg|border|the ''caption'' with [http://google.com external link]]] [[File:example.jpg|frameless|border|30x40px|caption]] [[File:example.jpg|frameless|border|30px|caption]] [[File:example.jpg|page=4|30px|border|caption]] [[File:example.jpg]] [[Archivo:example_es.jpg]] == lists == * Start each line * with an asterisk (*). ** More asterisks gives deeper *** and deeper levels. * Line breaks
                don't break levels. *** But jumping levels creates empty space. Any other start ends the list. ** two * one # Start each line # with a number sign (#). ## More number signs gives deeper ### and deeper ### levels. # Line breaks
                don't break levels. ### But jumping levels creates empty space. # Blank lines # end the list and start another. Any other start also ends the list. ;item 1 : definition 1 ;item 2 : definition 2-1 : definition 2-2 # one # two #* two point one #* two point two # three #; three item one #: three def one # four #: four def one #: this looks like a continuation #: and is often used #: instead
                of
                # {{{template |author=John |title=My Book }}} ## five sub 1 ### five sub 1 sub 1 ## five sub 2
                1. list item ''emph''
                  1. list item B1
                  2. list item B2
                  continuing list item A1
                2. list item A2
                  #abc #def #ghi
                1. Amsterdam
                2. Rotterdam
                3. The Hague
                == math == Here is some x=\frac{y^\pi}{z}. With spaces: x=\frac{y^\pi}{z} . == preformatted blocks == Start each line with a space. Text is '''preformatted''' and ''markups'' '''''can''''' be done. hell yeah Start with a space in the first column, (before the ). Then your block format will be maintained. This is good for copying in code blocks: def function(): """documentation string""" if True: print True else: print False Not
                preformatted Don't need a blank line around a preformatted block. == templates == {{Welcome}} {{Foo:Bar}} {{Thankyou|all your effort|Me}} Written {{{date}}} by {{{name}}}. == tables == {| |- |Orange |Apple |- |Bread |Pie |- |Butter |Ice cream |} {| |+Food complements !Orange !Apple |- |Bread |Pie |- !Butter |Ice cream |} {| |+Food complements !Orange !Apple |- |Bread and cheese |Pie # apple # carrot |} {| | Orange || Apple || more |- | Bread || Pie || more |- | Butter || Ice cream || and more |} {|width=50% ! align="left" width="50%"| Left ! align="right"|Right ! align="center"|Center |- | left || 15.00 || centered |- | more || 2.0 || more |} {| |- |Orange |Apple |- |Bread | {| !fruit !topping |- |apple |ice cream |} |- |Butter |Ice cream |} {| |Orange |}Paragraph after the table. {| !fruit !topping |- |apple |ice cream |} == notes == My note!This. URL note.http://docs.python.org/library/functions.html#range ================================================ FILE: test/odt/markdown/blockquote2.md ================================================ Paragraph > A blockquote. ================================================ FILE: test/odt/markdown/bold.md ================================================ Here comes **bold** text ================================================ FILE: test/odt/markdown/citation.md ================================================ Some text[@Ex] with a citation. ================================================ FILE: test/odt/markdown/endnote.md ================================================ Some text[^1] with an endnote. [^1]: Endnote text ================================================ FILE: test/odt/markdown/externalLink.md ================================================ Here comes an [external link](http://example.com/) to example.com. ================================================ FILE: test/odt/markdown/footnote.md ================================================ Some text[^1] with a footnote. [^1]: Footnote text ================================================ FILE: test/odt/markdown/formula.md ================================================ $$E = {m \cdot c^{2}}$$ ================================================ FILE: test/odt/markdown/headers.md ================================================ # A header (Lv 1) A paragraph ## Another header (Lv 2) Another paragraph # Back to Level 1 ================================================ FILE: test/odt/markdown/horizontalRule.md ================================================ --- ================================================ FILE: test/odt/markdown/image.md ================================================ ![](10000000000000FA000000FAD6A15225.jpg) ================================================ FILE: test/odt/markdown/imageIndex.md ================================================ # Abbildungsverzeichnis Abbildung 1: Image caption ![Abbildung 1: Image caption](10000000000000FA000000FAD6A15225.jpg) ================================================ FILE: test/odt/markdown/imageWithCaption.md ================================================ ![Abbildung 1: Image caption](10000000000000FA000000FAD6A15225.jpg) ================================================ FILE: test/odt/markdown/italic.md ================================================ Here comes *italic* text ================================================ FILE: test/odt/markdown/listBlocks.md ================================================ Indented text in a list. This is a numbered block.It contains several paragraphs of text.Like this.Next item. ================================================ FILE: test/odt/markdown/paragraph.md ================================================ This is a paragraph. This is another paragraph. This is a third one. ================================================ FILE: test/odt/markdown/strikeout.md ================================================ Here comes text that was ~~striken out~~. ================================================ FILE: test/odt/markdown/trackedChanges.md ================================================ Some text with and inserted text. ================================================ FILE: test/odt/markdown/underlined.md ================================================ Here comes *underlined* text ================================================ FILE: test/odt/native/blockquote.native ================================================ [Para [Str "Normal"],BlockQuote [Para [Str "Indented",Space,Str "(1cm)"]]] ================================================ FILE: test/odt/native/image.native ================================================ [Para [Image ("",[],[("width","5.292cm"),("height","5.292cm")]) [] ("Pictures/10000000000000FA000000FAD6A15225.jpg","")]] ================================================ FILE: test/odt/native/imageIndex.native ================================================ [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Image" , Space , Str "caption" ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "5.292cm" ) , ( "height" , "5.292cm" ) ] ) [ Str "Abbildung" , Space , Str "1:" , Space , Str "Image" , Space , Str "caption" ] ( "Pictures/10000000000000FA000000FAD6A15225.jpg" , "" ) ] ] ] ================================================ FILE: test/odt/native/imageRelative.native ================================================ [ Para [ Image ( "" , [] , [ ( "width" , "5.292cm" ) , ( "height" , "5.292cm" ) ] ) [] ( "../../lalune.jpg" , "" ) ] ] ================================================ FILE: test/odt/native/imageWithCaption.native ================================================ [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Image" , Space , Str "caption" ] ]) [ Plain [ Image ( "" , [] , [ ( "width" , "5.292cm" ) , ( "height" , "5.292cm" ) ] ) [ Str "Abbildung" , Space , Str "1:" , Space , Str "Image" , Space , Str "caption" ] ( "Pictures/10000000000000FA000000FAD6A15225.jpg" , "" ) ] ] ] ================================================ FILE: test/odt/native/inlinedCode.native ================================================ [Para [Str "Here",Space,Str "comes",Space,Code ("",[],[]) "inlined code",Space,Str "text",Space,Str "and",Space,Code ("",[],[]) "an another",Space,Str "one."]] ================================================ FILE: test/odt/native/listContinueNumbering.native ================================================ [ OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "Some" , Space , Str "text" , Space , Str "(1.)" ] ] ] , Para [] , Para [ Str "Some" , Space , Str "text" , Space , Str "in" , Space , Str "between." ] , Para [] , OrderedList ( 2 , Decimal , Period ) [ [ Plain [ Str "Some" , Space , Str "text" , Space , Str "(2.)" ] ] , [ Plain [ Str "Some" , Space , Str "text" , Space , Str "(3.)" ] ] ] , Para [] , Para [ Str "Some" , Space , Str "text" , Space , Str "in" , Space , Str "between." ] , Para [] , OrderedList ( 4 , Decimal , Period ) [ [ Plain [ Str "Some" , Space , Str "text" , Space , Str "(4.)" ] ] ] , Para [] , Para [ Str "Some" , Space , Str "text" , Space , Str "before" , Space , Str "starting" , Space , Str "new" , Space , Str "list" , Space , Str "from" , Space , Str "1." ] , Para [] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "Some" , Space , Str "text" , Space , Str "(1.)" ] ] ] , Para [] , Para [ Str "Some" , Space , Str "text" , Space , Str "in" , Space , Str "between." ] , Para [] , OrderedList ( 2 , Decimal , Period ) [ [ Plain [ Str "Some" , Space , Str "text" , Space , Str "(2.)" ] ] , [ Plain [ Str "Some" , Space , Str "text" , Space , Str "(3.)" ] ] ] ] ================================================ FILE: test/odt/native/listContinueNumbering2.native ================================================ [ OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "Top" , Space , Str "one" ] , OrderedList ( 1 , LowerAlpha , Period ) [[ Plain [ Str "Sub" , Space , Str "item" , Space , Str "1.a" ] ] , [ Plain [ Str "Sub" , Space , Str "item" , Space , Str "1.b" ] ] ] ] ] , Para [] , Para [ Str "Some" , Space , Str "text" , Space , Str "in" , Space , Str "between." ] , Para [] , OrderedList ( 2 , Decimal , Period ) [ [ Para [ Str "Top" , Space , Str "two" ] , OrderedList ( 1 , LowerAlpha , Period ) [ [ Plain [ Str "Sub" , Space , Str "item" , Space , Str "2.a" ] ] , [ Plain [ Str "Sub" , Space , Str "item" , Space , Str "2.b" ] ] ] ] ] , Para [] , Para [ Str "Some" , Space , Str "text" , Space , Str "in" , Space , Str "between." ] , Para [] , OrderedList ( 3 , Decimal , Period ) [ [ Para [ Str "Top" , Space , Str "three" ] , OrderedList ( 1 , LowerAlpha , Period ) [ [ Plain [ Str "Sub" , Space , Str "item" , Space , Str "3.a" ] ] , [ Plain [ Str "Sub" , Space , Str "item" , Space , Str "3.b" ] ] ] ] ] ] ================================================ FILE: test/odt/native/orderedListHeader.native ================================================ [ OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "A" , Space , Str "list" , Space , Str "item" , Space , Str "(list-header)" ] ] , [ Plain [ Str "A" , Space , Str "second" ] ] , [ Para [ Str "A" , Space , Str "third" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "New" , Space , Str "level!" , Space , Str "(list-header)" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "And" , Space , Str "another!" , Space , Str "(list-header)" ] ] , [ Plain [ Str "It's" , Space , Str "great" , Space , Str "up" , Space , Str "here!" ] ] ] ] , [ Plain [ Str "Oh" , Space , Str "noes" ] ] , [ Plain [ Str "We" , Space , Str "fell!" ] ] ] ] , [ Plain [ Str "Maybe" , Space , Str "someone" ] ] , [ Plain [ Str "Pushed" , Space , Str "us?" ] ] ] , Para [] , OrderedList ( 4 , Decimal , Period ) [ [ Plain [ Str "Start" , Space , Str "new" , Space , Str "list," , Space , Str "but" , Space , Str "a" , Space , Str "different" , Space , Str "starting" , Space , Str "point." , Space , Str "(list-header)" ] ] , [ Plain [ Str "Because" , Space , Str "we" , Space , Str "can." ] ] ] ] ================================================ FILE: test/odt/native/orderedListMixed.native ================================================ Pandoc (Meta {unMeta = fromList []}) [OrderedList (1,Decimal,Period) [[Plain [Str "A",Space,Str "list",Space,Str "item"]],[Plain [Str "A",Space,Str "second"]],[Para [Str "A",Space,Str "third"],OrderedList (1,Decimal,Period) [[Para [Str "New",Space,Str "level!"],OrderedList (1,LowerAlpha,OneParen) [[Plain [Str "And",Space,Str "another!"]],[Plain [Str "It's",Space,Str "great",Space,Str "up",Space,Str "here!"]]]],[Plain [Str "Oh",Space,Str "noes"]],[Plain [Str "We",Space,Str "fell!"]]]],[Plain [Str "Maybe",Space,Str "someone"]],[Plain [Str "Pushed",Space,Str "us?"]]],Para [],OrderedList (4,Decimal,Period) [[Plain [Str "Start",Space,Str "new",Space,Str "list,",Space,Str "but",Space,Str "a",Space,Str "different",Space,Str "starting",Space,Str "point."]] ,[Plain [Str "Because",Space,Str "we",Space,Str "can."]]]] ================================================ FILE: test/odt/native/orderedListRoman.native ================================================ Pandoc (Meta {unMeta = fromList []}) [OrderedList (1,UpperRoman,Period) [[Plain[Str "A",Space,Str "list",Space,Str "item"]],[Plain [Str "A",Space,Str "second"]],[Para [Str "A",Space,Str "third"],OrderedList (1,UpperRoman,Period) [[Para [Str "New",Space,Str "level!"],OrderedList (1,UpperRoman,Period) [[Plain [Str "And",Space,Str "another!"]],[Plain [Str "It's",Space,Str "great",Space,Str "up",Space,Str "here!"]]]],[Plain [Str "Oh",Space,Str "noes"]],[Plain [Str "We",Space,Str "fell!"]]]],[Plain [Str "Maybe",Space,Str "someone"]],[Plain [Str "Pushed",Space,Str "us?"]]],Para [],OrderedList (4,UpperRoman,Period) [[Plain [Str "Start",Space,Str "new",Space,Str "list,",Space,Str "but",Space,Str "a",Space,Str "different",Space,Str "starting",Space,Str "point."]] ,[Plain [Str "Because",Space,Str "we",Space,Str "can."]]]] ================================================ FILE: test/odt/native/orderedListSimple.native ================================================ Pandoc (Meta {unMeta = fromList []}) [OrderedList (1,Decimal,Period) [[Plain [Str "A",Space,Str "list",Space,Str "item"]],[Plain [Str "A",Space,Str "second"]],[Para [Str "A",Space,Str "third"],OrderedList (1,Decimal,Period) [[Para [Str "New",Space,Str "level!"],OrderedList (1,Decimal,Period) [[Plain [Str "And",Space,Str "another!"]],[Plain [Str "It's",Space,Str "great",Space,Str "up",Space,Str "here!"]]]],[Plain [Str "Oh",Space,Str "noes"]],[Plain [Str "We",Space,Str "fell!"]]]],[Plain [Str "Maybe",Space,Str "someone"]],[Plain [Str "Pushed",Space,Str "us?"]]],Para [],OrderedList (4,Decimal,Period) [[Plain [Str "Start",Space,Str "new",Space,Str "list,",Space,Str "but",Space,Str "a",Space,Str "different",Space,Str "starting",Space,Str "point."]] ,[Plain [Str "Because",Space,Str "we",Space,Str "can."]]]] ================================================ FILE: test/odt/native/preformattedText.native ================================================ [ CodeBlock ( "" , [] , [] ) "void main() {\n printf(\"Hello world\");\n}" ] ================================================ FILE: test/odt/native/preformattedTextParentStyle.native ================================================ [ CodeBlock ( "" , [] , [] ) "void main() {\n printf(\"Hello world\");\n}" ] ================================================ FILE: test/odt/native/referenceToChapter.native ================================================ [Header 1 ("a-chapter",[],[]) [Span ("anchor",[],[]) [],Str "A",Space,Str "chapter"],Para [Str "Some",Space,Str "text."],Header 1 ("another-chapter",[],[]) [Str "Another",Space,Str "chapter"],Para [Str "A",Space,Str "reference",Space,Str "to",Space,Str "."],Para [Str "A",Space,Str "reference",Space,Str "to",Space,Link ("",[],[]) [Str "A",Space,Str "chapter"] ("#anchor",""),Str "."]] ================================================ FILE: test/odt/native/referenceToListItem.native ================================================ [OrderedList (1,Decimal,Period) [[Plain [Span ("anchor",[],[]) [],Str "A",Space,Str "list",Space,Str "item"]],[Plain [Str "Another",Space,Str "list",Space,Str "item"]]],Para [Str "A",Space,Str "reference",Space,Str "to",Space,Str "list",Space,Str "item",Space,Link ("",[],[]) [Str "1."] ("#anchor",""),Str "."],Para [],Para []] ================================================ FILE: test/odt/native/referenceToText.native ================================================ [Para [Span ("an anchor",[],[]) [],Str "Some",Space,Str "text."],Para [Str "A",Space,Str "reference",Space,Str "to",Space,Link ("",[],[]) [Str "Some",Space,Str "text"] ("#an anchor",""),Str "."],Para [Str "Some",Space,Str "text",LineBreak,Str "Another",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "link",Span ("anchor",[],[]) []]] ================================================ FILE: test/odt/native/simpleTable.native ================================================ [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Content"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "More",Space,Str "content"]]]]] (TableFoot ("",[],[]) []),Para []] ================================================ FILE: test/odt/native/simpleTableWithCaption.native ================================================ [Table ("",[],[]) (Caption Nothing [Para [Str "Table",Space,Str "1:",Space,Str "Some",Space,Str "caption",Space,Str "for",Space,Str "a",Space,Str "table"]]) [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Content"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "More",Space,Str "content"]]]]] (TableFoot ("",[],[]) []),Para []] ================================================ FILE: test/odt/native/simpleTableWithHeader.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "C" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [] ] ================================================ FILE: test/odt/native/simpleTableWithMultipleHeaderRows.native ================================================ [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "C" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "I" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "II" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "II" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [] ] ================================================ FILE: test/odt/native/sourceText.native ================================================ [ Header 1 ( "test-foo" , [] , [] ) [ Span ( "anchor" , [] , [] ) [] , Str "Test" , Space , Code ( "" , [] , [] ) "foo" ] , Para [ Str "Hello" , Space , Code ( "" , [] , [] ) "world" ] ] ================================================ FILE: test/odt/native/tab.native ================================================ [Para [Str "Three",Space,Str "tabs",Space,Str "between",Space,Str "A",Space,Str "and",Space,Str "B",Space,Str "will",Space,Str "be",Space,Str "converted",Space,Str "to",Space,Str "one",Space,Str "Space:",Space,Str "A",Space,Str "B."]] ================================================ FILE: test/odt/native/tableWithContents.native ================================================ [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "A"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "B"]]],Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "C"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "D"]]]]] (TableFoot ("",[],[]) []),Para []] ================================================ FILE: test/odt/native/tableWithSpans.native ================================================ [ 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 [] ] ================================================ FILE: test/odt/native/textMixedStyles.native ================================================ [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "simple",Space,Str "text"] ,Para [] ,Para [Str "that",Space,Str "is",Space,Str "both",Space,Emph [Str "italic",Space],Strong [Str "bold",Space],Emph [Str "underlined",Space],Str "and",Space,Str "the",Space,Emph [Strong [Str "first",Space,Str "two"]],Space,Str "and",Space,Str "the",Space,Emph [Strong [Str "last",Space,Str "two",Space]],Space,Str "and",Space,Strong [Strikeout [Str "bold",Space,Str "and",Space,Str "line",Space,Str "through"]]] ,Para [] ,Para [Str "And",Space,Str "with",Space,Superscript [Emph [Str "superscripts"]]]] ================================================ FILE: test/odt/native/unicode.native ================================================ [Para [Str "\8220\8221\8217\231\1256\169\188\1074\1073\1060\1064\246\201\181"]] ================================================ FILE: test/odt/native/unorderedList.native ================================================ [BulletList [[Plain [Str "A",Space,Str "list",Space,Str "item"]],[Plain [Str "A",Space,Str "second"]],[Para [Str "A",Space,Str "third"],BulletList [[Para [Str "New",Space,Str "level!"],BulletList [[Plain [Str "And",Space,Str "another!"]],[Plain [Str "It's",Space,Str "great",Space,Str "up",Space,Str "here!"]]]],[Plain [Str "Oh",Space,Str "noes"]],[Plain [Str "We",Space,Str "fell!"]]]],[Plain [Str "Maybe",Space,Str "someone"]],[Plain [Str "Pushed",Space,Str "us?"]]]] ================================================ FILE: test/odt/native/unorderedListHeader.native ================================================ [ BulletList [ [ Plain [ Str "A" , Space , Str "list" , Space , Str "item" , Space , Str "(list-header)" ] ] , [ Plain [ Str "A" , Space , Str "second" ] ] , [ Para [ Str "A" , Space , Str "third" ] , BulletList [ [ Para [ Str "New" , Space , Str "level!" , Space , Str "(list-header)" ] , BulletList [ [ Plain [ Str "And" , Space , Str "another!" , Space , Str "(list-header)" ] ] , [ Plain [ Str "It's" , Space , Str "great" , Space , Str "up" , Space , Str "here!" ] ] ] ] , [ Plain [ Str "Oh" , Space , Str "noes" ] ] , [ Plain [ Str "We" , Space , Str "fell!" ] ] ] ] , [ Plain [ Str "Maybe" , Space , Str "someone" ] ] , [ Plain [ Str "Pushed" , Space , Str "us?" ] ] ] ] ================================================ FILE: test/opml-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "Dave" , Space , Str "Winer" ] ] ) , ( "date" , MetaInlines [ Str "Thu," , Space , Str "14" , Space , Str "Jul" , Space , Str "2005" , Space , Str "23:41:05" , Space , Str "GMT" ] ) , ( "title" , MetaInlines [ Str "States" ] ) ] } [ Header 1 ( "" , [] , [] ) [ Str "United" , Space , Str "States" ] , Header 2 ( "" , [] , [] ) [ Str "Far" , Space , Str "West" ] , Header 3 ( "" , [] , [] ) [ Str "Alaska" ] , Header 3 ( "" , [] , [] ) [ Str "California" ] , Header 3 ( "" , [] , [] ) [ Str "Hawaii" ] , Header 3 ( "" , [] , [] ) [ Strong [ Str "Nevada" ] ] , Para [ Str "I" , Space , Str "lived" , Space , Str "here" , Space , Emph [ Str "once" ] , Str "." ] , Para [ Str "Loved" , Space , Str "it." ] , Header 4 ( "" , [] , [] ) [ Link ( "" , [] , [] ) [ Str "Reno" ] ( "http://www.reno.gov" , "" ) ] , Header 4 ( "" , [] , [] ) [ Str "Las" , Space , Str "Vegas" ] , Header 4 ( "" , [] , [] ) [ Str "Ely" ] , Header 4 ( "" , [] , [] ) [ Str "Gerlach" ] , Header 3 ( "" , [] , [] ) [ Str "Oregon" ] , Header 3 ( "" , [] , [] ) [ Str "Washington" ] , Header 2 ( "" , [] , [] ) [ Str "Great" , Space , Str "Plains" ] , Header 3 ( "" , [] , [] ) [ Str "Kansas" ] , Header 3 ( "" , [] , [] ) [ Str "Nebraska" ] , Header 3 ( "" , [] , [] ) [ Str "North" , Space , Str "Dakota" ] , Header 3 ( "" , [] , [] ) [ Str "Oklahoma" ] , Header 3 ( "" , [] , [] ) [ Str "South" , Space , Str "Dakota" ] , Header 2 ( "" , [] , [] ) [ Str "Mid-Atlantic" ] , Header 3 ( "" , [] , [] ) [ Str "Delaware" ] , Header 3 ( "" , [] , [] ) [ Str "Maryland" ] , Header 3 ( "" , [] , [] ) [ Str "New" , Space , Str "Jersey" ] , Header 3 ( "" , [] , [] ) [ Str "New" , Space , Str "York" ] , Header 3 ( "" , [] , [] ) [ Str "Pennsylvania" ] , Header 2 ( "" , [] , [] ) [ Str "Midwest" ] , Header 3 ( "" , [] , [] ) [ Str "Illinois" ] , Header 3 ( "" , [] , [] ) [ Str "Indiana" ] , Header 3 ( "" , [] , [] ) [ Str "Iowa" ] , Header 3 ( "" , [] , [] ) [ Str "Kentucky" ] , Header 3 ( "" , [] , [] ) [ Str "Michigan" ] , Header 3 ( "" , [] , [] ) [ Str "Minnesota" ] , Header 3 ( "" , [] , [] ) [ Str "Missouri" ] , Header 3 ( "" , [] , [] ) [ Str "Ohio" ] , Header 3 ( "" , [] , [] ) [ Str "West" , Space , Str "Virginia" ] , Header 3 ( "" , [] , [] ) [ Str "Wisconsin" ] , Header 2 ( "" , [] , [] ) [ Str "Mountains" ] , Header 3 ( "" , [] , [] ) [ Str "Colorado" ] , Header 3 ( "" , [] , [] ) [ Str "Idaho" ] , Header 3 ( "" , [] , [] ) [ Str "Montana" ] , Header 3 ( "" , [] , [] ) [ Str "Utah" ] , Header 3 ( "" , [] , [] ) [ Str "Wyoming" ] , Header 2 ( "" , [] , [] ) [ Str "New" , Space , Str "England" ] , Header 3 ( "" , [] , [] ) [ Str "Connecticut" ] , Header 3 ( "" , [] , [] ) [ Str "Maine" ] , Header 3 ( "" , [] , [] ) [ Str "Massachusetts" ] , Header 3 ( "" , [] , [] ) [ Str "New" , Space , Str "Hampshire" ] , Header 3 ( "" , [] , [] ) [ Str "Rhode" , Space , Str "Island" ] , Header 3 ( "" , [] , [] ) [ Str "Vermont" ] , Header 2 ( "" , [] , [] ) [ Str "South" ] , Header 3 ( "" , [] , [] ) [ Str "Alabama" ] , Header 3 ( "" , [] , [] ) [ Str "Arkansas" ] , Header 3 ( "" , [] , [] ) [ Str "Florida" ] , Header 3 ( "" , [] , [] ) [ Str "Georgia" ] , Header 3 ( "" , [] , [] ) [ Str "Louisiana" ] , Header 3 ( "" , [] , [] ) [ Str "Mississippi" ] , Header 3 ( "" , [] , [] ) [ Str "North" , Space , Str "Carolina" ] , Header 3 ( "" , [] , [] ) [ Str "South" , Space , Str "Carolina" ] , Header 3 ( "" , [] , [] ) [ Str "Tennessee" ] , Header 3 ( "" , [] , [] ) [ Str "Virginia" ] , Header 2 ( "" , [] , [] ) [ Str "Southwest" ] , Header 3 ( "" , [] , [] ) [ Str "Arizona" ] , Header 3 ( "" , [] , [] ) [ Str "New" , Space , Str "Mexico" ] , Header 3 ( "" , [] , [] ) [ Str "Texas" ] ] ================================================ FILE: test/opml-reader.opml ================================================ States Thu, 14 Jul 2005 23:41:05 GMT Dave Winer ================================================ FILE: test/org-select-tags.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "will-appear-because-it-is-the-ancestor-of-something-tagged-yes" , [] , [] ) [ Str "Will" , Space , Str "appear" , Space , Str "because" , Space , Str "it" , Space , Str "is" , Space , Str "the" , Space , Str "ancestor" , Space , Str "of" , Space , Str "something" , Space , Str "tagged" , Space , Str "\"yes\"" ] , Header 2 ( "will-appear" , [] , [] ) [ Str "Will" , Space , Str "appear" , Space , Span ( "" , [ "tag" ] , [ ( "tag-name" , "yes" ) ] ) [ SmallCaps [ Str "yes" ] ] ] , Header 3 ( "will-appear-since-the-entire-subtree-of-something-selected-will-appear" , [] , [] ) [ Str "Will" , Space , Str "appear" , Space , Str "since" , Space , Str "the" , Space , Str "entire" , Space , Str "subtree" , Space , Str "of" , Space , Str "something" , Space , Str "selected" , Space , Str "will" , Space , Str "appear" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Will" , Space , Str "appear" ] ] ] , Header 2 ( "will-appear-because-it-is-the-ancestor-of-something-listed-in-select-tags" , [] , [] ) [ Str "Will" , Space , Str "appear" , Space , Str "because" , Space , Str "it" , Space , Str "is" , Space , Str "the" , Space , Str "ancestor" , Space , Str "of" , Space , Str "something" , Space , Str "listed" , Space , Str "in" , Space , Str "SELECT-TAGS" ] ] ================================================ FILE: test/org-select-tags.org ================================================ #+select_tags: yes no #+exclude_tags: no In a document containing one or more trees containing a tag listed in SELECT_TAGS, only those trees and their ancestor nodes will appear; this text and any other text preceding the first headline won't appear for such documents. * Will appear because it is the ancestor of something tagged "yes" ** Will appear :yes: *** Will appear since the entire subtree of something selected will appear **** Will appear *** Will not appear since this has tagged with something in EXCLUDE-TAGS :no: ** Will not appear since it's not an ancestor of listed in SELECT-TAGS ** Will appear because it is the ancestor of something listed in SELECT-TAGS *** Will not appear because it has an EXCLUDE-TAG, but since "no" is also listed as a SELECT-TAG, it will force its parent to appear :no: * Will not appear ================================================ FILE: test/pipe-tables.native ================================================ [ Para [ Str "Simplest" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Headerless" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "without" , Space , Str "sides:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Fruit" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Quantity" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "apple" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "17" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "pear" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "302" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "One-column:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "hi" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "lo" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Header-less" , Space , Str "one-column:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "hi" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Indented" , Space , Str "left" , Space , Str "column:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Number" , Space , Str "of" , Space , Str "siblings" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Salary" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "33" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "44" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Long" , Space , Str "pipe" , Space , Str "table" , Space , Str "with" , Space , Str "relative" , Space , Str "widths:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.15517241379310345 ) , ( AlignDefault , ColWidth 0.1724137931034483 ) , ( AlignDefault , ColWidth 0.6724137931034483 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "table" , Space , Str "cell" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "and" , Space , Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "really" , Space , Str "long" , Space , Str "table" , Space , Str "cell" , Space , Str "that" , Space , Str "will" , Space , Str "probably" , Space , Str "need" , Space , Str "wrapping" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Pipe" , Space , Str "table" , Space , Str "with" , Space , Str "no" , Space , Str "body:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Header" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Pipe" , Space , Str "table" , Space , Str "with" , Space , Str "tricky" , Space , Str "cell" , Space , Str "contents" , Space , Str "(see" , Space , Str "#2765):" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "IP_gene8-_1st" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "IP_gene8+_1st" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "IP_gene8-_1st" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1.0000000" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0.4357325" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "IP_gene8+_1st" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0.4357325" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1.0000000" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "foo" , Code ( "" , [] , [] ) "bar|baz" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "and|escaped" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3.0000000" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/pipe-tables.txt ================================================ Simplest table without caption: | Default1 | Default2 | Default3 | |----------|----------|----------| |12|12|12| |123|123|123| |1|1|1| Simple table with caption: | Right | Left | Default | Center | | ----: | :--- | ------- | :----: | | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | : Demonstration of simple table syntax. Simple table without caption: | Right | Left | Center | |------:|:-----|:------:| |12|12|12| |123|123|123| |1|1|1| Headerless table without caption: | | | | |------:|:-----|:------:| |12|12|12| |123|123|123| |1|1|1| Table without sides: Fruit |Quantity ------|-------: apple | 5 orange| 17 pear | 302 One-column: |hi| |--| |lo| Header-less one-column: | | |:-:| |hi| Indented left column: Number of siblings | Salary ------------------:|:------ 3 | 33 4 | 44 Long pipe table with relative widths: | Default1 | Default2 | Default3 | |---------|----------|---------------------------------------| |123|this is a table cell|and this is a really long table cell that will probably need wrapping| |123|123|123| Pipe table with no body: | Header | | ------ | Pipe table with tricky cell contents (see #2765): | | IP_gene8-_1st| IP_gene8+_1st| |:--------------|-------------:|-------------:| |IP_gene8-_1st | 1.0000000| 0.4357325| |IP_gene8+_1st | 0.4357325| 1.0000000| |foo`bar|baz` | and\|escaped | 3.0000000| ================================================ FILE: test/pod-reader.native ================================================ [ Header 1 ( "" , [] , [] ) [ Str "POD" , Space , Str "TEST" , Space , Str "SUITE" ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "test" , Space , Link ( "" , [] , [] ) [ Str "Pod" ] ( "https://perldoc.pl/perlpod" , "" ) , Space , Str "document" , Space , Str "for" , Space , Str "pandoc." ] , Header 2 ( "" , [] , [] ) [ Str "Head" , Space , Str "2" ] , Header 3 ( "" , [] , [] ) [ Str "Head" , Space , Str "3:" , Space , Emph [ Str "The>" , Space , Str "This is a raw block destined for the HTML format\n\n" , BulletList [ [ Para [ Str "Bulleted" , Space , Str "list" ] ] , [ Para [ Str "Ordered" , Space , Str "list" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Para [ Str "Here's" , Space , Str "a" , Space , Str "verbatim" , Space , Str "paragraph" , Space , Str "in" , Space , Str "this" , Space , Str "list" , Space , Str "item:" ] , CodeBlock ( "" , [] , [] ) " this is a code block\nthis is still part of the code block\n so is this.\nIt seems that the prefixed spaces in verbatim blocks in pod don't get stripped.\n\n This should continue the previous code block despite the intervening blank\n line, because the first line starts with a space\n\n\n\n the above blank lines with varying numbers of spaces should also be in\n the code block\n pod formatters should (but not must) expand tabs by default\nso we're not special casing pandoc's behavior there in any way\n" , Para [ Str "Wow," , Space , Str "that" , Space , Str "was" , Space , Str "fun." ] ] , [ Para [ Str "Definition" , Space , Str "list" ] , DefinitionList [ ( [ Span ( "" , [] , [] ) [ Str "Marvin" , Space , Str "the" , Space , Str "Martian" ] ] , [ [ Para [ Str "A" , Space , Str "cartoon" , Space , Str "alien" ] ] ] ) , ( [ Span ( "" , [] , [] ) [ Emph [ Str "The" , Space , Str "Sun" , Space , Str "Also" , Space , Str "Rises" ] ] ] , [ [ Para [ Str "A" , Space , Str "novel" , Space , Str "by" , Space , Str "Ernest" , Space , Str "Hemingway" ] ] ] ) , ( [ Span ( "" , [] , [] ) [ Code ( "" , [] , [] ) "undefined" ] ] , [ [] ] ) , ( [ Span ( "" , [] , [] ) [ Str "And" , Space , Str "now," , Space , Str "a" , Space , Str "quotation" ] ] , [ [ BlockQuote [ Para [ Str "Where's" , Space , Str "my" , Space , Str "space" , Space , Str "modulator?" ] ] ] ] ) ] ] , [ Para [ Str "And" , Space , Str "the" , Space , Str "list" , Space , Str "continues." ] ] ] ] , [ Para [ Str "And" , Space , Str "so" , Space , Str "does" , Space , Str "the" , Space , Str "other" , Space , Str "one," , Space , Str "even" , Space , Str "if" , Space , Str "I" , Space , Str "forget" , Space , Str "the" , Space , Str "asterisk." ] ] ] , Div ( "" , [ "neat" ] , [] ) [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "div" , Space , Str "for" , Space , Str "our" , Space , Str "purposes." ] , Para [ Str "It" , Space , Str "should" , Space , Str "parse" , Space , Strong [ Str "content" ] , Space , Str "inside" , Space , Str "of" , Space , Str "it." ] , BulletList [ [ Para [ Str "Like" , Space , Str "this" ] ] ] ] , Div ( "" , [ "excitement" ] , [] ) [ Para [ Str "this" , Space , Str "is" , Space , Str "its" , Space , Str "own" , Space , Str "div" ] ] , RawBlock (Format "html") "

                and this is its own raw block

                \n" , RawBlock (Format "html") "\n

                so is this

                \n" , Header 2 ( "" , [] , [] ) [ Code ( "" , [] , [] ) "=cut" , Space , Str "before" , Space , Str "any" , Space , Code ( "" , [] , [] ) "=item" , Space , Str "in" , Space , Code ( "" , [] , [] ) "=over" ] , BulletList [ [ Para [ Str "a" ] , Para [ Str "b" ] ] , [ Para [ Str "c" ] ] ] ] ================================================ FILE: test/pod-reader.pod ================================================ text before any Pod commands is not parsed, which is unusual for formats supported by pandoc, but =head1 POD TEST SUITE This is a test L document for pandoc. =encoding utf8 =head2 Head 2 =head3 Head 3: I<< The> > =head4 How to use the L command, an introduction =head5 C and his 52 assistants =head6 The =head5 and =head6 commands are newer and my syntax highlighting doesn't recognize them. In any case, it should be possible to have a very long paragraph in the heading. =head6 It should also be possible to start the heading paragraph on the next line =cut This doesn't get parsed at all. =begin html This is a raw block destined for the HTML format =end html =over =item * Bulleted list =item * Ordered list =over =item 1. Here's a verbatim paragraph in this list item: this is a code block this is still part of the code block so is this. It seems that the prefixed spaces in verbatim blocks in pod don't get stripped. This should continue the previous code block despite the intervening blank line, because the first line starts with a space the above blank lines with varying numbers of spaces should also be in the code block pod formatters should (but not must) expand tabs by default so we're not special casing pandoc's behavior there in any way Wow, that was fun. =item 2. Definition list =over =item Marvin the MZ<>artian A cartoon alien =item I A novel by Ernest Hemingway =item C =item And now, a quotation =over Where's my space modulator? =back =back =item 3. And the list continues. =back =item And so does the other one, even if I forget the asterisk. =back =begin :neat This is a div for our purposes. It should parse B<< content >> inside of it. =over =item Like this =back =end :neat =for :excitement this is its own div =for html

                and this is its own raw block

                =for html

                so is this

                =head2 C<=cut> before any C<=item> in C<=over> =over =cut blah =item * a =cut blah blah =pod b =item * c =back ================================================ FILE: test/pptx/background-image/input.native ================================================ [Header 1 ("section-header-with-background-image",[],[("background-image","movie.jpg")]) [Str "Section",Space,Str "Header",Space,Str "(with",Space,Str "background",Space,Str "image)"] ,Header 2 ("slide-1",[],[("background-image","lalune.jpg")]) [Str "Slide",Space,Str "1"] ,Para [Str "This",Space,Str "slide",Space,Str "has",Space,Str "a",Space,Str "moon",Space,Str "background."] ,Header 2 ("slide-2",[],[("background-image","movie.jpg")]) [Str "Slide",Space,Str "2"] ,Para [Str "This",Space,Str "slide",Space,Str "has",Space,Str "a",Space,Str "movie",Space,Str "background."] ,Header 2 ("slide-3",[],[("background-image","movie.jpg")]) [Str "Slide",Space,Str "3"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "Background",Space,Str "images",Space,Str "work",Space,Str "in",Space,Str "two-column",Space,Str "layout."]] ,Div ("",["column"],[]) [Para [Str "hello"]]] ,Header 2 ("slide-4",[],[("background-image","movie.jpg")]) [Str "Slide",Space,Str "4"] ,Para [Str "You",Space,Str "can",Space,Str "have",Space,Str "images",Space,Str "on",Space,Str "slides",Space,Str "that",Space,Str "have",Space,Str "background",Space,Str "images:"] ,Para [Image ("",[],[]) [Str "An",Space,Str "image"] ("lalune.jpg","fig:")] ,Header 2 ("section",[],[("background-image","lalune.jpg")]) [] ,Div ("",["notes"],[]) [Para [Str "Blank",Space,Str "slides",Space,Str "can",Space,Str "have",Space,Str "background",Space,Str "images."]]] ================================================ FILE: test/pptx/blanks/just-speaker-notes/input.native ================================================ [Header 1 ("first-slide",[],[]) [Str "First",Space,Str "slide"] ,Para [Str "Nothing",Space,Str "to",Space,Str "see",Space,Str "here"] ,Header 1 ("section",[],[]) [] ,Div ("",["notes"],[]) [Para [Str "Some",Space,Str "notes",Space,Str "here:",Space,Str "this",Space,Str "first",Space,Str "slide",Space,Str "should",Space,Str "use",Space,Str "the",Space,Str "Blank",Space,Str "template"]] ,Header 1 ("third-slide",[],[]) [Str "Third",Space,Str "slide"] ,Para [Str "The",Space,Str "second",Space,Str "slide",Space,Str "should",Space,Str "be",Space,Str "blank"]] ================================================ FILE: test/pptx/blanks/nbsp-in-body/input.native ================================================ [Header 1 ("first-slide",[],[]) [Str "First",Space,Str "slide"] ,Para [Str "Uninteresting,",Space,Str "normal"] ,Header 1 ("section",[],[]) [] ,Para [Str "\160"] ,Header 1 ("third-slide",[],[]) [Str "Third",Space,Str "slide"] ,Para [Str "Was",Space,Str "the",Space,Str "previous",Space,Str "one",Space,Str "blank?"]] ================================================ FILE: test/pptx/blanks/nbsp-in-heading/input.native ================================================ [Header 1 ("first-slide",[],[]) [Str "First",Space,Str "slide"] ,Para [Str "Uninteresting,",Space,Str "normal"] ,Header 1 ("section",[],[]) [Str "\160"] ,Header 1 ("third-slide",[],[]) [Str "Third",Space,Str "slide"] ,Para [Str "Was",Space,Str "the",Space,Str "previous",Space,Str "one",Space,Str "blank?"]] ================================================ FILE: test/pptx/code/input.native ================================================ [Header 1 ("header-with-inline-code",[],[]) [Str "Header",Space,Str "with",Space,Code ("",[],[]) "inline code"] ,CodeBlock ("",[],[]) "Code at level 0" ,BulletList [[Para [Str "Bullet",Space,Str "item",Space,Str "with",Space,Code ("",[],[]) "inline code"] ,CodeBlock ("",[],[]) "Code block at level 1" ,BulletList [[Para [Str "with",Space,Code ("",[],[]) "nested"] ,CodeBlock ("",[],[]) "lvl2\nlvl2\nlvl2" ,Header 2 ("second-heading-level-with-code",[],[]) [Str "Second",Space,Str "heading",Space,Str "level",Space,Str "with",Space,Code ("",[],[]) "code"]]]]] ,Header 1 ("syntax-highlighting",[],[]) [Str "Syntax",Space,Str "highlighting"] ,CodeBlock ("",["haskell"],[]) "id :: a -> a\nid x = x" ,BulletList [[Para [Str "Nested"] ,CodeBlock ("",["haskell"],[]) "g :: Int -> Int\ng x = x * 3"]] ,Header 1 ("two-column-slide",[],[]) [Str "Two",Space,Str "column",Space,Str "slide"] ,Div ("",["columns"],[]) [Div ("",["column"],[("width","50%")]) [BulletList [[Plain [Str "A",Space,Str "total",Space,Str "alternative",Space,Str "for",Space,Code ("",[],[]) "head"]]]] ,Div ("",["column"],[("width","50%")]) [CodeBlock ("",[],[]) "safeHead :: [a] -> Maybe a\nsafeHead [] = Nothing\nsafeHead (x:_) = Just x"]]] ================================================ FILE: test/pptx/comparison/both-columns/input.native ================================================ [Header 1 ("a-slide",[],[]) [Str "A",Space,Str "slide"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "A",Space,Str "paragraph",Space,Str "here"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.125) ,(AlignDefault,ColWidth 0.125)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "plus"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "a",Space,Str "table"]]]])] (TableFoot ("",[],[]) []) ,Para [Str "Then",Space,Str "some",Space,Str "more",Space,Str "text"]] ,Div ("",["column"],[]) [Para [Str "A",Space,Str "paragraph",Space,Str "here"] ,Para [Image ("",[],[]) [Str "Plus",Space,Str "an",Space,Str "image"] ("lalune.jpg","fig:")]]]] ================================================ FILE: test/pptx/comparison/extra-image/input.native ================================================ [Header 1 ("a-slide",[],[]) [Str "A",Space,Str "slide"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "A",Space,Str "paragraph",Space,Str "here"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.125) ,(AlignDefault,ColWidth 0.125)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "plus"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "a",Space,Str "table"]]]])] (TableFoot ("",[],[]) []) ,Para [Str "Then",Space,Str "some",Space,Str "more",Space,Str "text"]] ,Div ("",["column"],[]) [Para [Str "A",Space,Str "paragraph",Space,Str "here"] ,Para [Image ("",[],[]) [Str "Plus",Space,Str "an",Space,Str "image"] ("lalune.jpg","fig:")] ,Para [Image ("",[],[]) [Str "And",Space,Str "another",Space,Str "image"] ("lalune.jpg","fig:")]]]] ================================================ FILE: test/pptx/comparison/extra-text/input.native ================================================ [Header 1 ("a-slide",[],[]) [Str "A",Space,Str "slide"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "A",Space,Str "paragraph",Space,Str "here"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.125) ,(AlignDefault,ColWidth 0.125)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "plus"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "a",Space,Str "table"]]]])] (TableFoot ("",[],[]) []) ,Para [Str "Then",Space,Str "some",Space,Str "more",Space,Str "text"]] ,Div ("",["column"],[]) [Para [Str "A",Space,Str "paragraph",Space,Str "here"] ,Para [Image ("",[],[]) [Str "Plus",Space,Str "an",Space,Str "image"] ("lalune.jpg","fig:")]]]] ================================================ FILE: test/pptx/comparison/non-text-first/input.native ================================================ [Header 1 ("a-slide",[],[]) [Str "A",Space,Str "slide"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.125) ,(AlignDefault,ColWidth 0.125)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "a"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "table"]]]])] (TableFoot ("",[],[]) []) ,Para [Str "Plus",Space,Str "a",Space,Str "paragraph",Space,Str "here"]] ,Div ("",["column"],[]) [Para [Image ("",[],[]) [Str "Just",Space,Str "an",Space,Str "image",Space,Str "on",Space,Str "this",Space,Str "side"] ("lalune.jpg","fig:")]]]] ================================================ FILE: test/pptx/comparison/one-column/input.native ================================================ [Header 1 ("a-slide",[],[]) [Str "A",Space,Str "slide"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "A",Space,Str "paragraph",Space,Str "here"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 0.125) ,(AlignDefault,ColWidth 0.125)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "plus"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "a",Space,Str "table"]]]])] (TableFoot ("",[],[]) [])] ,Div ("",["column"],[]) [Para [Str "Only",Space,Str "a",Space,Str "paragraph",Space,Str "here"]]]] ================================================ FILE: test/pptx/content-with-caption/heading-text-image/input.native ================================================ [Header 1 ("a-slide",[],[]) [Str "A",Space,Str "slide"] ,Para [Str "Some",Space,Str "text",Space,Str "here"] ,Para [Image ("",[],[]) [Str "Followed",Space,Str "by",Space,Str "a",Space,Str "picture"] ("lalune.jpg","fig:")]] ================================================ FILE: test/pptx/content-with-caption/image-text/input.native ================================================ [Para [Image ("",[],[]) [Str "The",Space,Str "picture",Space,Str "first"] ("lalune.jpg","fig:")] ,Para [Str "Then",Space,Str "some",Space,Str "text",Space,Str "here"]] ================================================ FILE: test/pptx/content-with-caption/text-image/input.native ================================================ [Para [Str "Some",Space,Str "text",Space,Str "here"] ,Para [Image ("",[],[]) [Str "Followed",Space,Str "by",Space,Str "a",Space,Str "picture"] ("lalune.jpg","fig:")]] ================================================ FILE: test/pptx/document-properties/input.native ================================================ Pandoc (Meta {unMeta = fromList [("Company",MetaInlines [Str "My",Space,Str "Company"]),("Second Custom Property",MetaInlines [Str "Second",Space,Str "custom",Space,Str "property",Space,Str "value"]),("abstract",MetaBlocks [Plain [Str "Quite",Space,Str "a",Space,Str "long",Space,Str "description",SoftBreak,Str "spanning",Space,Str "several",Space,Str "lines"]]),("author",MetaList [MetaInlines [Str "A.",Space,Str "M."]]),("category",MetaInlines [Str "My",Space,Str "Category"]),("custom1",MetaInlines [Str "First",Space,Str "custom",Space,Str "property",Space,Str "value"]),("custom3",MetaInlines [Str "Escaping",Space,Str "amp",Space,Str "&",Space,Str "."]),("custom4",MetaInlines [Str "Escaping",Space,Str "LT,GT",Space,Str "<",Space,Str "asdf",Space,Str ">",Space,Str "<"]),("custom5",MetaInlines [Str "Escaping",Space,Str "html",Space,RawInline (Format "html") "",Str "asdf",RawInline (Format "html") ""]),("custom6",MetaInlines [Str "Escaping",Space,Emph [Str "MD"],Space,Str "\225",Space,Str "a"]),("custom9",MetaInlines [Str "Extended",Space,Str "chars:",Space,Str "\8364",Space,Str "\225",Space,Str "\233",Space,Str "\237",Space,Str "\243",Space,Str "\250",Space,Str "$"]),("description",MetaBlocks [Para [Str "Long",Space,Str "description",Space,Str "spanning",SoftBreak,Str "several",Space,Str "lines."],Plain [Str "This",Space,Str "is",Space,Str "\225",Space,Str "second",Space,RawInline (Format "html") "",Str "line",RawInline (Format "html") "",Str "."]]),("keywords",MetaList [MetaInlines [Str "keyword",Space,Str "1"],MetaInlines [Str "keyword",Space,Str "2"]]),("lang",MetaInlines [Str "en-US"]),("nested-custom",MetaList [MetaMap (fromList [("custom 7",MetaInlines [Str "Nested",Space,Str "Custom",Space,Str "value",Space,Str "7"])]),MetaMap (fromList [("custom 8",MetaInlines [Str "Nested",Space,Str "Custom",Space,Str "value",Space,Str "8"])])]),("subject",MetaInlines [Str "This",Space,Str "is",Space,Str "the",Space,Str "subject"]),("subtitle",MetaInlines [Str "This",Space,Str "is",Space,Str "a",Space,Str "subtitle"]),("title",MetaInlines [Str "Testing",Space,Str "custom",Space,Str "properties"])]}) [Para [Str "Testing",Space,Str "document",Space,Str "properties"]] ================================================ FILE: test/pptx/document-properties-short-desc/input.native ================================================ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "A.",Space,Str "M."]]),("description",MetaInlines [Str "Short",Space,RawInline (Format "html") "",Str "description",RawInline (Format "html") "",Space,Str "&."]),("keywords",MetaList [MetaInlines [Str "keyword",Space,Str "1"],MetaInlines [Str "keyword",Space,Str "2"]]),("subject",MetaInlines [Str "This",Space,Str "is",Space,Str "the",Space,Str "subject"]),("title",MetaInlines [Str "Testing",Space,Str "custom",Space,Str "properties"])]}) [Para [Str "Testing",Space,Str "document",Space,Str "properties"]] ================================================ FILE: test/pptx/endnotes/input.native ================================================ Pandoc (Meta {unMeta = fromList []}) [Para [Str "Here",Space,Str "is",Space,Str "one",Space,Str "note.",Note [Para [Str "Here",Space,Str "is",Space,Str "the",Space,Str "note."]],Space,Str "And",Space,Str "one",Space,Str "more",Space,Str "note.",Note [Para [Str "And",Space,Str "another",Space,Str "note."]]]] ================================================ FILE: test/pptx/footer/input.native ================================================ Pandoc (Meta {unMeta = fromList [("author",MetaInlines [Str "Me"]),("date",MetaInlines [Str "14/09/1995"]),("title",MetaInlines [Str "Slides"])]}) [Header 2 ("slide-1",[],[]) [Str "Slide",Space,Str "1"] ,Para [Str "Hello",Space,Str "there"] ,Header 1 ("layouts",[],[]) [Str "Layouts"] ,Header 2 ("slide-3",[],[]) [Str "Slide",Space,Str "3"] ,Para [Str "Does",Space,Str "it",Space,Str "work",Space,Str "on",Space,Str "other",Space,Str "layouts?"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 5.555555555555555e-2) ,(AlignDefault,ColWidth 5.555555555555555e-2)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2"]]]])] (TableFoot ("",[],[]) []) ,Header 2 ("slide-4",[],[]) [Str "Slide",Space,Str "4"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "hello",Space,Str "hello"]] ,Div ("",["column"],[]) [Para [Str "goood",Space,Str "bye"]]] ,Header 2 ("slide-5",[],[]) [Str "Slide",Space,Str "5"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "Hello",Space,Str "there"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 5.555555555555555e-2) ,(AlignDefault,ColWidth 5.555555555555555e-2)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2"]]]])] (TableFoot ("",[],[]) [])] ,Div ("",["column"],[]) [Para [Str "oh",Space,Str "wait"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 5.555555555555555e-2) ,(AlignDefault,ColWidth 5.555555555555555e-2)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2"]]]])] (TableFoot ("",[],[]) [])]] ,Header 2 ("section",[],[]) [] ,Div ("",["notes"],[]) [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "blank",Space,Str "slide:",Space,Str "does",Space,Str "it",Space,Str "have",Space,Str "a",Space,Str "footer?"]]] ================================================ FILE: test/pptx/images/input.native ================================================ Pandoc (Meta {unMeta = fromList []}) [Para [Image ("",[],[]) [] ("lalune.jpg","")] ,Para [Image ("",[],[]) [Str "The",Space,Str "Moon"] ("lalune.jpg","fig:")] ,Header 1 ("one-more",[],[]) [Str "One",Space,Str "More"] ,Para [Image ("",[],[]) [Str "The",Space,Str "Moon"] ("lalune.jpg","fig:")]] ================================================ FILE: test/pptx/incremental-lists/with-flag/input.native ================================================ [Header 1 ("slide-1-content",[],[]) [Str "Slide",Space,Str "1",Space,Str "(Content)"] ,BulletList [[Plain [Str "These"]] ,[Plain [Str "bullets"]] ,[Plain [Str "should"]] ,[Plain [Str "be"]] ,[Plain [Str "incremental"]]] ,Header 1 ("slide-2-content",[],[]) [Str "Slide",Space,Str "2",Space,Str "(Content)"] ,BulletList [[Para [Str "as"]] ,[Para [Str "should"]] ,[Para [Str "these"]]] ,Header 1 ("slide-3-content",[],[]) [Str "Slide",Space,Str "3",Space,Str "(Content)"] ,Para [Str "Even",Space,Str "with",Space,Str "some",Space,Str "text",Space,Str "first,",Space,Str "these",Space,Str "should:"] ,BulletList [[Plain [Str "also"]] ,[Plain [Str "be"]] ,[Plain [Str "incremental"]]] ,Header 1 ("slide-4-content",[],[]) [Str "Slide",Space,Str "4",Space,Str "(Content)"] ,OrderedList (1,Decimal,Period) [[Plain [Str "These"]] ,[Plain [Str "are"]] ,[Plain [Str "incremental"]]] ,Para [Str "But",Space,Str "this",Space,Str "text",Space,Str "isn\8217t"] ,BulletList [[Plain [Str "But"]] ,[Plain [Str "these"]] ,[Plain [Str "are"]]] ,Header 1 ("slide-5-two-content",[],[]) [Str "Slide",Space,Str "5",Space,Str "(Two",Space,Str "Content)"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "Incremental",Space,Str "on",Space,Str "the",Space,Str "left:"] ,BulletList [[Plain [Str "one"]] ,[Plain [Str "by"]] ,[Plain [Str "one"]]] ,Para [Str "With",Space,Str "something",Space,Str "below"]] ,Div ("",["column"],[]) [Para [Str "Incremental",Space,Str "on",Space,Str "the",Space,Str "right:"] ,BulletList [[Plain [Str "one"]] ,[Plain [Str "by"]] ,[Plain [Str "one"]]] ,Para [Str "With",Space,Str "something",Space,Str "else",Space,Str "below"]]] ,Header 1 ("slide-6-two-content-right",[],[]) [Str "Slide",Space,Str "6",Space,Str "(Two",Space,Str "Content",Space,Str "Right)"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Image ("",[],[]) [Str "an",Space,Str "image"] ("lalune.jpg","fig:")]] ,Div ("",["column"],[]) [BulletList [[Plain [Str "An"]] ,[Plain [Str "Incremental"]] ,[Plain [Str "List"]]]]] ,Header 1 ("slide-7-content-with-caption",[],[]) [Str "Slide",Space,Str "7",Space,Str "(Content",Space,Str "with",Space,Str "Caption)"] ,Para [Str "First,",Space,Str "we",Space,Str "have",Space,Str "some",Space,Str "incremental",Space,Str "bullets:"] ,BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]] ,Para [Str "Then,",Space,Str "a",Space,Str "picture:"] ,Para [Image ("",[],[]) [Str "An",Space,Str "image"] ("./lalune.jpg","fig:")] ,Header 1 ("slide-8-comparison",[],[]) [Str "Slide",Space,Str "8",Space,Str "(Comparison)"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]] ,Para [Image ("",[],[]) [Str "An",Space,Str "image"] ("./lalune.jpg","fig:")]] ,Div ("",["column"],[]) [OrderedList (1,Decimal,Period) [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 5.555555555555555e-2)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2"]]]])] (TableFoot ("",[],[]) [])]] ,Header 1 ("slide-9-content",[],[]) [Str "Slide",Space,Str "9",Space,Str "(Content)"] ,Div ("",["nonincremental"],[]) [BulletList [[Plain [Str "these"]] ,[Plain [Str "are"]] ,[Plain [Str "not"]] ,[Plain [Str "incremental"]]]] ,BulletList [[Plain [Str "these"]] ,[Plain [Str "are"]]] ,Header 1 ("slide-10-content",[],[]) [Str "Slide",Space,Str "10",Space,Str "(Content)"] ,Div ("",["incremental","nonincremental"],[]) [BulletList [[Plain [Str "these"]] ,[Plain [Str "are"]] ,[Plain [Str "incremental"]] ,[Plain [Str "(the",Space,Str "incremental",Space,Str "class",Space,Str "wins)"]]]] ,Header 1 ("slide-11-content",[],[]) [Str "Slide",Space,Str "11",Space,Str "(Content)"] ,Para [Str "These",Space,Str "bullets",Space,Str "are",Space,Str "not",Space,Str "incremental:"] ,BlockQuote [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]] ,Para [Str "These",Space,Str "are:"] ,Div ("",["nonincremental"],[]) [BlockQuote [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]]] ,Para [Str "These",Space,Str "are",Space,Str "not:"] ,BlockQuote [Div ("",["nonincremental"],[]) [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]]] ,Header 1 ("slide-12-content",[],[]) [Str "Slide",Space,Str "12",Space,Str "(Content)"] ,Div ("",["nonincremental"],[]) [Div ("",["incremental"],[]) [BulletList [[Plain [Str "these"]] ,[Plain [Str "are"]] ,[Plain [Str "incremental"]]]]] ,Div ("",["incremental"],[]) [Div ("",["nonincremental"],[]) [BulletList [[Plain [Str "these"]] ,[Plain [Str "are"]] ,[Plain [Str "not"]]]]]] ================================================ FILE: test/pptx/incremental-lists/without-flag/input.native ================================================ [Header 1 ("slide-1-content",[],[]) [Str "Slide",Space,Str "1",Space,Str "(Content)"] ,Div ("",["incremental"],[]) [BulletList [[Plain [Str "These"]] ,[Plain [Str "bullets"]] ,[Plain [Str "should"]] ,[Plain [Str "be"]] ,[Plain [Str "incremental"]]]] ,Header 1 ("slide-2-content",[],[]) [Str "Slide",Space,Str "2",Space,Str "(Content)"] ,BulletList [[Plain [Str "These"]] ,[Plain [Str "are"]] ,[Plain [Str "not"]]] ,Header 1 ("slide-3-content",[],[]) [Str "Slide",Space,Str "3",Space,Str "(Content)"] ,Para [Str "Even",Space,Str "with",Space,Str "some",Space,Str "text",Space,Str "first,",Space,Str "these",Space,Str "should:"] ,Div ("",["incremental"],[]) [BulletList [[Plain [Str "also"]] ,[Plain [Str "be"]] ,[Plain [Str "incremental"]]]] ,Header 1 ("slide-4-content",[],[]) [Str "Slide",Space,Str "4",Space,Str "(Content)"] ,Div ("",["incremental"],[]) [OrderedList (1,Decimal,Period) [[Plain [Str "These"]] ,[Plain [Str "are"]] ,[Plain [Str "incremental"]]]] ,Para [Str "But",Space,Str "this",Space,Str "text",Space,Str "isn\8217t"] ,Header 1 ("slide-5-two-content",[],[]) [Str "Slide",Space,Str "5",Space,Str "(Two",Space,Str "Content)"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "Incremental",Space,Str "on",Space,Str "the",Space,Str "left:"] ,Div ("",["incremental"],[]) [BulletList [[Plain [Str "one"]] ,[Plain [Str "by"]] ,[Plain [Str "one"]]]] ,Para [Str "With",Space,Str "something",Space,Str "below"]] ,Div ("",["column"],[]) [Para [Str "Incremental",Space,Str "on",Space,Str "the",Space,Str "right:"] ,Div ("",["incremental"],[]) [BulletList [[Plain [Str "one"]] ,[Plain [Str "by"]] ,[Plain [Str "one"]]]] ,BulletList [[Plain [Str "already"]] ,[Plain [Str "here"]] ,[Plain [Str "though"]]]]] ,Header 1 ("slide-6-two-content-right",[],[]) [Str "Slide",Space,Str "6",Space,Str "(Two",Space,Str "Content",Space,Str "Right)"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Image ("",[],[]) [Str "an",Space,Str "image"] ("lalune.jpg","fig:")]] ,Div ("",["column"],[]) [Div ("",["incremental"],[]) [BulletList [[Plain [Str "An"]] ,[Plain [Str "Incremental"]] ,[Plain [Str "List"]]]]]] ,Header 1 ("slide-7-content-with-caption",[],[]) [Str "Slide",Space,Str "7",Space,Str "(Content",Space,Str "with",Space,Str "Caption)"] ,Para [Str "First,",Space,Str "we",Space,Str "have",Space,Str "some",Space,Str "incremental",Space,Str "bullets:"] ,Div ("",["incremental"],[]) [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]] ,Para [Str "Then,",Space,Str "a",Space,Str "picture:"] ,Para [Image ("",[],[]) [Str "An",Space,Str "image"] ("./lalune.jpg","fig:")] ,Header 1 ("slide-8-comparison",[],[]) [Str "Slide",Space,Str "8",Space,Str "(Comparison)"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Div ("",["incremental"],[]) [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]] ,Para [Image ("",[],[]) [Str "An",Space,Str "image"] ("./lalune.jpg","fig:")]] ,Div ("",["column"],[]) [Div ("",["incremental"],[]) [OrderedList (1,Decimal,Period) [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 5.555555555555555e-2)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2"]]]])] (TableFoot ("",[],[]) [])]] ,Header 1 ("slide-10-content",[],[]) [Str "Slide",Space,Str "10",Space,Str "(Content)"] ,Div ("",["incremental","nonincremental"],[]) [BulletList [[Plain [Str "these"]] ,[Plain [Str "are"]] ,[Plain [Str "incremental"]] ,[Plain [Str "(the",Space,Str "incremental",Space,Str "class",Space,Str "wins)"]]]] ,Header 1 ("slide-11-content",[],[]) [Str "Slide",Space,Str "11",Space,Str "(Content)"] ,Para [Str "These",Space,Str "bullets",Space,Str "are",Space,Str "incremental:"] ,BlockQuote [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]] ,Para [Str "These",Space,Str "are",Space,Str "not:"] ,Div ("",["incremental"],[]) [BlockQuote [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]]] ,Para [Str "These",Space,Str "are:"] ,BlockQuote [Div ("",["incremental"],[]) [BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]] ,[Plain [Str "three"]]]]] ,Header 1 ("slide-12-content",[],[]) [Str "Slide",Space,Str "12",Space,Str "(Content)"] ,Div ("",["nonincremental"],[]) [Div ("",["incremental"],[]) [BulletList [[Plain [Str "these"]] ,[Plain [Str "are"]] ,[Plain [Str "incremental"]]]]] ,Div ("",["incremental"],[]) [Div ("",["nonincremental"],[]) [BulletList [[Plain [Str "these"]] ,[Plain [Str "are"]] ,[Plain [Str "not"]]]]]] ================================================ FILE: test/pptx/inline-formatting/input.native ================================================ [Para [Str "Here",Space,Str "are",Space,Str "examples",Space,Str "of",Space,Emph [Str "italics"],Str ",",Space,Strong [Str "bold"],Str ",",Space,Str "and",Space,Strong [Emph [Str "bold",Space,Str "italics"]],Str "."] ,Para [Str "Here",Space,Str "is",Space,Strikeout [Str "strook-three"],Space,Str "strike-through",Space,Str "and",Space,SmallCaps [Str "small",Space,Str "caps"],Str "."] ,Para [Str "Here",Space,Str "is",Space,Span ("",["underline"],[]) [Str "some",Space,Emph [Str "underlined"],Space,Strong [Str "text"]],Str "."] ,Para [Str "We",Space,Str "can",Space,Str "also",Space,Str "do",Space,Str "subscripts",Space,Str "(H",Subscript [Str "2"],Str "0)",Space,Str "and",Space,Str "super",Superscript [Str "script"],Str "."] ,RawBlock (Format "html") ""] ================================================ FILE: test/pptx/layouts/input.native ================================================ Pandoc (Meta {unMeta = fromList [("title",MetaInlines [Str "Testing",Space,Str "Layouts"])]}) [Header 2 ("slide-1",[],[]) [Str "Slide",Space,Str "1"] ,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "title",Space,Str "and",Space,Str "content",Space,Str "slide"] ,Header 2 ("slide-2",[],[]) [Str "Slide",Space,Str "2"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "This"]] ,Div ("",["column"],[]) [Para [Str "\8230is",Space,Str "a",Space,Str "two-column",Space,Str "slide"]]] ,Header 2 ("slide-3",[],[]) [Str "Slide",Space,Str "3"] ,Para [Str "This",Space,Str "slide",Space,Str "is",Space,Str "a",Space,Str "Content",Space,Str "with",Space,Str "Caption",Space,Str "slide"] ,Para [Image ("",[],[]) [Str "Content"] ("lalune.jpg","fig:")] ,Header 2 ("slide-4",[],[]) [Str "Slide",Space,Str "4"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "This",Space,Str "slide",Space,Str "is",Space,Str "a",Space,Str "Comparison",Space,Str "slide:"] ,Para [Image ("",[],[]) [Str "Content"] ("lalune.jpg","fig:")]] ,Div ("",["column"],[]) [Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "other",Space,Str "text"]]] ,Header 1 ("section-header",[],[]) [Str "Section",Space,Str "header"] ,Header 2 ("section",[],[]) [] ,Div ("",["notes"],[]) [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "blank",Space,Str "slide"]]] ================================================ FILE: test/pptx/list-level/input.native ================================================ [Header 1 ("slide",[],[]) [Str "Slide"] ,BulletList [[Para [Str "Top-level"] ,Para [Str "With",Space,Str "continuation",Space,Str "paragraph"]] ,[Para [Str "Then:"] ,BulletList [[Plain [Str "nested"]] ,[Plain [Str "list"]] ,[Plain [Str "items"]]]]] ,Header 1 ("slide-1",[],[]) [Str "Slide"] ,Para [Str "Paragraph."] ,OrderedList (1,Decimal,Period) [[Para [Str "Top-level"] ,Para [Str "Continuation"] ,OrderedList (1,Decimal,Period) [[Para [Str "Sub-list"] ,Para [Str "With",Space,Str "Continuation"]] ,[Para [Str "(still",Space,Str "sub-list)"]]]] ,[Para [Str "(back",Space,Str "to",Space,Str "top-level)"]]] ,Para [Str "Paragraph."]] ================================================ FILE: test/pptx/lists/input.native ================================================ [Header 1 ("lists",[],[]) [Str "Lists"] ,BulletList [[Para [Str "Bulleted",Space,Str "bulleted",Space,Str "lists."]] ,[Para [Str "And",Space,Str "go",Space,Str "to",Space,Str "arbitrary",Space,Str "depth."] ,BulletList [[Para [Str "Like",Space,Str "this"] ,BulletList [[Plain [Str "Or",Space,Str "this"]]]] ,[Para [Str "Back",Space,Str "to",Space,Str "here."]]]]] ,Header 1 ("lists-continued",[],[]) [Str "Lists",Space,Str "(continued)"] ,Para [Str "Lists",Space,Str "can",Space,Str "also",Space,Str "be",Space,Str "numbered:"] ,OrderedList (1,Decimal,Period) [[Para [Str "Tomatoes"]] ,[Para [Str "Potatoes",Space,Str "of",Space,Str "various",Space,Str "sorts"] ,OrderedList (1,LowerAlpha,Period) [[Para [Str "sweet",Space,Str "potatoes"]] ,[Para [Str "russet",Space,Str "potates"]]]] ,[Para [Str "Tornadoes,",Space,Str "for",Space,Str "the",Space,Str "rhyme."]]]] ================================================ FILE: test/pptx/metadata-speaker-notes/input.native ================================================ 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."]] ================================================ FILE: test/pptx/raw-ooxml/input.native ================================================ [Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "text,",Space,Str "written",Space,Str "as",Space,Str "a",Space,Str "raw",Space,Str "inline:",Space,RawInline (Format "openxml") "Here are examples of italics, bold"] ,HorizontalRule ,RawBlock (Format "openxml") "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Bulleted bulleted lists.\n \n \n \n \n \n \n And go to arbitrary depth.\n \n \n \n \n \n \n Like this\n \n \n \n \n \n \n Or this\n \n \n \n \n \n \n Back to here.\n \n \n \n "] ================================================ FILE: test/pptx/reference-no-slides/add-slides/input.native ================================================ [ Header 2 ( "first-slide" , [] , [] ) [ Str "First" , Space , Str "Slide" ] , Para [ Str "Title" ] , Header 2 ( "second-slide" , [] , [] ) [ Str "Second" , Space , Str "Slide" ] , BulletList [ [ Plain [ Str "First" , Space , Str "item" ] ] , [ Plain [ Str "Second" , Space , Str "item" ] ] ] ] ================================================ FILE: test/pptx/reference-no-slides/with-notes/input.native ================================================ [ Header 2 ( "first-slide" , [] , [] ) [ Str "First" , Space , Str "Slide" ] , Para [ Str "First" , Space , Str "slide" , Space , Str "with" , Space , Str "notes" ] , Div ( "" , [ "notes" ] , [] ) [ Para [ Str "Notes" , Space , Str "for" , Space , Str "the" , Space , Str "first" , Space , Str "slide" ] ] , Header 2 ( "second-slide" , [] , [] ) [ Str "Second" , Space , Str "Slide" ] , Para [ Str "Slide" , Space , Str "without" , Space , Str "notes" ] , Header 2 ( "third-slide" , [] , [] ) [ Str "Third" , Space , Str "Slide" ] , Para [ Str "Slide" , Space , Str "with" , Space , Str "notes" , Space , Str "again" ] , BulletList [ [ Plain [ Str "First" , Space , Str "item" ] ] , [ Plain [ Str "Second" , Space , Str "item" ] ] ] , Div ( "" , [ "notes" ] , [] ) [ Para [ Str "Notes" , Space , Str "for" , Space , Str "the" , Space , Str "third" , Space , Str "slides" ] ] ] ================================================ FILE: test/pptx/remove-empty-slides/input.native ================================================ [Para [Str "Content"] ,Para [Image ("",[],[]) [] ("lalune.jpg",""),Space,RawInline (Format "html") ""] ,HorizontalRule ,HorizontalRule ,Para [Str "More",Space,Str "content"]] ================================================ FILE: test/pptx/single-column/image/input.native ================================================ [ Header 1 ( "single-column" , [] , [] ) [ Str "Single" , Space , Str "column" ] , Div ( "" , [ "columns" ] , [] ) [ Div ( "" , [ "column" ] , [] ) [ Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "an" , Space , Str "image" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "an" , Space , Str "image" ] ( "lalune.jpg" , "" ) ] ] ] ] ] ================================================ FILE: test/pptx/single-column/text/input.native ================================================ [ Header 1 ( "single-column" , [] , [] ) [ Str "Single" , Space , Str "column" ] , Div ( "" , [ "columns" ] , [] ) [ Div ( "" , [ "column" ] , [] ) [ Para [ Str "One" , Space , Str "paragraph." ] , Para [ Str "Another" , Space , Str "paragraph." ] ] ] ] ================================================ FILE: test/pptx/slide-breaks/input.native ================================================ Pandoc (Meta {unMeta = fromList []}) [Para [Str "Break",Space,Str "with",Space,Str "a",Space,Str "new",Space,Str "section-level",Space,Str "header"] ,Header 1 ("below-section-level",[],[]) [Str "Below",Space,Str "section-level"] ,Header 2 ("section-level",[],[]) [Str "Section-level"] ,Para [Str "Third",Space,Str "slide",Space,Str "(with",Space,Str "a",Space,Str "section-level",Space,Str "of",Space,Str "2)"] ,HorizontalRule ,Para [Str "This",Space,Str "is",Space,Str "another",Space,Str "slide."]] ================================================ FILE: test/pptx/slide-level-0/h1-h2-with-table/input.native ================================================ [Header 1 ("hello",[],[]) [Str "Hello"] ,Header 2 ("there",[],[]) [Str "There"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 5.555555555555555e-2)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]]]])] (TableFoot ("",[],[]) [])] ================================================ FILE: test/pptx/slide-level-0/h1-with-image/input.native ================================================ [Header 1 ("hello",[],[]) [Str "Hello"] ,Para [Image ("",[],[]) [Str "An",Space,Str "image"] ("lalune.jpg","fig:")]] ================================================ FILE: test/pptx/slide-level-0/h1-with-table/input.native ================================================ [Header 1 ("hello",[],[]) [Str "Hello"] ,Table ("",[],[]) (Caption Nothing []) [(AlignDefault,ColWidth 5.555555555555555e-2)] (TableHead ("",[],[]) []) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]]]])] (TableFoot ("",[],[]) [])] ================================================ FILE: test/pptx/slide-level-0/h2-with-image/input.native ================================================ [Header 2 ("hello",[],[]) [Str "Hello"] ,Para [Image ("",[],[]) [Str "An",Space,Str "image"] ("lalune.jpg","fig:")]] ================================================ FILE: test/pptx/speaker-notes/input.native ================================================ [Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "slide."] ,Div ("",["notes"],[]) [Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "note."] ,Para [Str "Here",Space,Str "is",Space,Emph [Str "some"],Space,Strong [Str "other"],Space,Str "formatting."]] ,HorizontalRule ,Para [Str "A",Space,Str "page",Space,Str "with",Space,Str "no",Space,Str "speaker",Space,Str "notes"] ,HorizontalRule ,Div ("",["notes"],[]) [Para [Str "The",Space,Str "first",Space,Str "note",Space,Str "div"]] ,Para [Str "A",Space,Str "page",Space,Str "with",Space,Str "two",Space,Str "notes."] ,Div ("",["notes"],[]) [Para [Str "The",Space,Str "second",Space,Str "note",Space,Str "div"]] ,HorizontalRule ,Para [Str "Strip",Space,Str "links",Space,Str "and",Space,Str "footnotes."] ,Div ("",["notes"],[]) [Para [Str "No",Space,Link ("",[],[]) [Str "link"] ("https://www.google.com",""),Space,Str "here."] ,Para [Str "No",Space,Str "note",Space,Str "here.",Note [Para [Str "You\8217ll",Space,Str "never",Space,Str "read",Space,Str "this"]]]]] ================================================ FILE: test/pptx/speaker-notes-after-metadata/input.native ================================================ Pandoc (Meta {unMeta = fromList [("author",MetaInlines [Str "Jesse",Space,Str "Rosenthal"]),("title",MetaInlines [Str "Testing"])]}) [Div ("",["notes"],[]) [Para [Str "Some",Space,Str "speaker",Space,Str "notes"]] ,Header 1 ("a-header",[],[]) [Str "A",Space,Str "header"] ,Para [Str "And",Space,Str "a",Space,Str "new",Space,Str "slide."]] ================================================ FILE: test/pptx/speaker-notes-afterheader/input.native ================================================ [Header 1 ("here-is-a-single-header",[],[]) [Str "Here",Space,Str "is",Space,Str "a",Space,Str "single",Space,Str "header"] ,Div ("",["notes"],[]) [Para [Str "and",Space,Str "here",Space,Str "are",Space,Str "some",Space,Str "notes"]]] ================================================ FILE: test/pptx/speaker-notes-afterseps/input.native ================================================ [Para [Image ("",[],[]) [Str "The",Space,Str "moon"] ("lalune.jpg","fig:")] ,Div ("",["notes"],[]) [Para [Str "chicken",Space,Str "and",Space,Str "dumplings"]] ,Table ("",[],[]) (Caption Nothing [Para [Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax,",Space,Str "with",Space,Str "alignment"]]) [(AlignRight,ColWidthDefault) ,(AlignLeft,ColWidthDefault) ,(AlignCenter,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Right"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Left"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Center"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Default"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]]]])] (TableFoot ("",[],[]) []) ,Div ("",["notes"],[]) [Para [Str "foo",Space,Str "bar"]] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [BulletList [[Para [Str "some",Space,Str "stuff"]] ,[Para [Str "some",Space,Str "more",Space,Str "stuff"]]] ,Div ("",["notes"],[]) [Para [Str "Some",Space,Str "notes",Space,Str "inside",Space,Str "a",Space,Str "column"]]] ,Div ("",["column"],[]) [Para [Str "Some",Space,Str "other",Space,Emph [Str "stuff"]]]] ,Div ("",["notes"],[]) [Para [Str "Some",Space,Str "notes",Space,Str "outside",Space,Str "the",Space,Str "column"]]] ================================================ FILE: test/pptx/start-numbering-at/input.native ================================================ [Header 2 ("example-numbering-mwe",[],[]) [Str "Example",Space,Str "numbering",Space,Str "MWE"] ,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "slide",Space,Str "with",Space,Str "examples",Space,Str "in",Space,Str "(1)",Space,Str "and",Space,Str "(2)"] ,OrderedList (1,Example,TwoParens) [[Plain [Str "First"]] ,[Plain [Str "Second"]]] ,Header 2 ("a-second-slide",[],[]) [Str "A",Space,Str "second",Space,Str "slide"] ,Para [Str "This",Space,Str "second",Space,Str "slide",Space,Str "has",Space,Str "a",Space,Str "third",Space,Str "example",Space,Str "in",Space,Str "(3)."] ,OrderedList (3,Example,TwoParens) [[Plain [Str "Third"]]]] ================================================ FILE: test/pptx/tables/input.native ================================================ [Header 2 ("a-table-with-a-caption",[],[]) [Str "A",Space,Str "Table,",Space,Str "with",Space,Str "a",Space,Str "caption"] ,Table ("",[],[]) (Caption Nothing [Para [Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax,",Space,Str "with",Space,Str "alignment"]]) [(AlignRight,ColWidthDefault) ,(AlignLeft,ColWidthDefault) ,(AlignCenter,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Right"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Left"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Center"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Default"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]]]])] (TableFoot ("",[],[]) []) ,Table ("",[],[]) (Caption Nothing []) [(AlignRight,ColWidthDefault) ,(AlignLeft,ColWidthDefault) ,(AlignCenter,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Right"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Left"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Center"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Default"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "123"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]]]])] (TableFoot ("",[],[]) [])] ================================================ FILE: test/pptx/two-column/all-text/input.native ================================================ Pandoc (Meta {unMeta = fromList []}) [Header 1 ("two-column-layout",[],[]) [Str "Two-Column",Space,Str "Layout"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "One",Space,Str "paragraph."] ,Para [Str "Another",Space,Str "paragraph."]] ,Div ("",["column"],[]) [Para [Str "Second",Space,Str "column",Space,Str "paragraph."] ,Para [Str "Another",Space,Str "second",Space,Str "paragraph."]]]] ================================================ FILE: test/pptx/two-column/text-and-image/input.native ================================================ [Header 1 ("slide-1",[],[]) [Str "Slide",Space,Str "1"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Image ("",[],[]) [Str "an",Space,Str "image"] ("lalune.jpg","fig:")]] ,Div ("",["column"],[]) [Para [Str "This",Space,Str "should",Space,Str "use",Space,Str "Two",Space,Str "Content,",Space,Emph [Str "not"],Space,Str "Comparison!"]]] ,Header 1 ("slide-2",[],[]) [Str "Slide",Space,Str "2"] ,Div ("",["columns"],[]) [Div ("",["column"],[]) [Para [Str "This",Space,Str "should",Space,Str "also",Space,Str "use",Space,Str "Two",Space,Str "Content"]] ,Div ("",["column"],[]) [Para [Image ("",[],[]) [Str "an",Space,Str "image"] ("lalune.jpg","fig:")]]]] ================================================ FILE: test/pptx-reader/basic.native ================================================ [ Header 2 ( "slide-1" , [] , [] ) [ Str "LLMs" ] , BulletList [ [ Plain [ Str "Provider \61664 Available LLMs \8211 who manages? How?" ] ] , [ Plain [ Str "EW maintained list of \8220approved\8221 LLMs for Universal workers" ] ] , [ Plain [ Str "Rebuilding of UWs to the \8220Newgen\8221 thing completely" ] ] , [ Plain [ Str "Streaming support" ] ] , [ Plain [ Str "Multimodal (voice streaming) models?" ] ] ] , Header 2 ( "slide-2" , [] , [] ) [ Str "Everworker venn diagram" ] , Para [ Str "SKILLS" ] , Para [ Str "" ] , Para [ Str "Specialized Workers / Workflows:" ] , Para [ Str "" ] , Para [ Str "n8n, UI Path, " ] , Para [ Str "other RPA" ] , Para [ Str "BRAINS" ] , Para [ Str "" ] , Para [ Str "Universal Workers / AI Agents:" ] , Para [ Str "" ] , Para [ Str "openai , anthropic," ] , Para [ Str "Crew AI, other " ] , Para [ Str "\8220AI natives\8221" ] , Para [ Str "KNOWLEDGE " ] , Para [ Str "" ] , Para [ Str "Data / " ] , Para [ Str "RAG Pipelines" ] , Para [ Str "" ] , Para [ Str "Vector DBs, specialized data prep vendors, \8230" ] , Para [ Str "glean" ] , Para [ Str "EW" ] , Header 2 ( "slide-3" , [] , [] ) [ Str "Table" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Col1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Col2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Col3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Name" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Anton" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Antich" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Age" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "23" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "years" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Image ( "" , [] , [] ) [] ( "ppt/media/image1.png" , "Picture 6" ) ] , Header 2 ( "slide-4" , [] , [] ) [ Str "Smart Art" ] , Div ( "" , [ "smartart" , "chevron2" ] , [ ( "layout" , "chevron2" ) ] ) [ Para [ Strong [ Str "First" ] ] , BulletList [ [ Plain [ Str "another" ] ] , [ Plain [ Str "subtitle" ] ] ] , Para [ Strong [ Str "Second" ] ] , BulletList [ [ Plain [ Str "and yet again" ] ] , [ Plain [ Str "yet more" ] ] ] ] ] ================================================ FILE: test/rst-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "John" , Space , Str "MacFarlane" ] , MetaInlines [ Str "Anonymous" ] ] ) , ( "date" , MetaInlines [ Str "July" , Space , Str "17," , Space , Str "2006" ] ) , ( "revision" , MetaInlines [ Str "3" ] ) , ( "subtitle" , MetaInlines [ Str "Subtitle" ] ) , ( "title" , MetaInlines [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] ) ] } [ Header 1 ( "level-one-header" , [] , [] ) [ Str "Level" , Space , Str "one" , Space , Str "header" ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , SoftBreak , Str "John" , Space , Str "Gruber\8217s" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , Header 2 ( "level-two-header" , [] , [] ) [ Str "Level" , Space , Str "two" , Space , Str "header" ] , Header 3 ( "level-three" , [] , [] ) [ Str "Level" , Space , Str "three" ] , Header 4 ( "level-four-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "four" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 5 ( "level-five" , [] , [] ) [ Str "Level" , Space , Str "five" ] , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , SoftBreak , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , Space , Str "item." , SoftBreak , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , SoftBreak , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , Space , Str "a" , SoftBreak , Str "list" , Space , Str "item." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , SoftBreak , Str "*" , Space , Str "criminey." ] , Para [ Str "Horizontal" , Space , Str "rule:" ] , HorizontalRule , Para [ Str "Another:" ] , HorizontalRule , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , SoftBreak , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , Para [ Str "Here\8217s" , Space , Str "another," , Space , Str "differently" , Space , Str "indented:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , SoftBreak , Str "It\8217s" , Space , Str "indented" , Space , Str "with" , Space , Str "a" , Space , Str "tab." ] , Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" , Para [ Str "List" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "item" , Space , Str "one" ] ] , [ Plain [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] , BlockQuote [ Para [ Str "nested" ] ] ] ] , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}" , CodeBlock ( "" , [] , [] ) "this code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) "this block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" , Para [ Str "And:" ] , CodeBlock ( "" , [ "python" ] , [] ) "def my_function(x):\n return x + 1" , Para [ Str "If" , Space , Str "we" , Space , Str "use" , Space , Str "the" , Space , Str "highlight" , Space , Str "directive," , Space , Str "we" , Space , Str "can" , Space , Str "specify" , Space , Str "a" , Space , Str "default" , Space , Str "language" , SoftBreak , Str "for" , Space , Str "literate" , Space , Str "blocks." ] , CodeBlock ( "" , [ "haskell" ] , [] ) "-- this code is in haskell\ndata Tree = Leaf | Node Tree Tree" , CodeBlock ( "" , [ "haskell" ] , [] ) "-- this code is in haskell too\ndata Nat = Zero | Succ Nat" , CodeBlock ( "" , [ "javascript" ] , [] ) "-- this code is in javascript\nlet f = (x, y) => x + y" , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] , [ Plain [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] , [ Plain [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Plus" , Space , Str "1" ] ] , [ Plain [ Str "Plus" , Space , Str "2" ] ] , [ Plain [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "loose:" ] , BulletList [ [ Plain [ Str "Plus" , Space , Str "1" ] ] , [ Plain [ Str "Plus" , Space , Str "2" ] ] , [ Plain [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Minus" , Space , Str "1" ] ] , [ Plain [ Str "Minus" , Space , Str "2" ] ] , [ Plain [ Str "Minus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "loose:" ] , BulletList [ [ Plain [ Str "Minus" , Space , Str "1" ] ] , [ Plain [ Str "Minus" , Space , Str "2" ] ] , [ Plain [ Str "Minus" , Space , Str "3" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second" ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "and:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "One" ] ] , [ Plain [ Str "Two" ] ] , [ Plain [ Str "Three" ] ] ] , Para [ Str "Loose" , Space , Str "using" , Space , Str "tabs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second" ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "and" , Space , Str "using" , Space , Str "spaces:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "One" ] ] , [ Plain [ Str "Two" ] ] , [ Plain [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog\8217s" , SoftBreak , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Para [ Str "Nested:" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "another:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BlockQuote [ BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "fancy-list-markers" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , OrderedList ( 2 , Decimal , TwoParens ) [ [ Para [ Str "begins" , Space , Str "with" , Space , Str "2" ] ] , [ Para [ Str "and" , Space , Str "now" , Space , Str "3" ] , Para [ Str "with" , Space , Str "a" , Space , Str "continuation" ] , OrderedList ( 4 , LowerRoman , Period ) [ [ Plain [ Str "sublist" , Space , Str "with" , Space , Str "roman" , Space , Str "numerals," , Space , Str "starting" , Space , Str "with" , Space , Str "4" ] ] , [ Plain [ Str "more" , Space , Str "items" ] , OrderedList ( 1 , UpperAlpha , TwoParens ) [ [ Plain [ Str "a" , Space , Str "subsublist" ] ] , [ Plain [ Str "a" , Space , Str "subsublist" ] ] ] ] ] ] ] , Para [ Str "Nesting:" ] , OrderedList ( 1 , UpperAlpha , Period ) [ [ Plain [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , Period ) [ [ Plain [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , TwoParens ) [ [ Plain [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , OneParen ) [ [ Plain [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Autonumber." ] ] , [ Plain [ Str "More." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Nested." ] ] ] ] ] , Para [ Str "Autonumbering" , Space , Str "with" , Space , Str "explicit" , Space , Str "start:" ] , OrderedList ( 4 , LowerAlpha , TwoParens ) [ [ Plain [ Str "item" , Space , Str "1" ] ] , [ Plain [ Str "item" , Space , Str "2" ] ] ] , Header 2 ( "definition" , [] , [] ) [ Str "Definition" ] , DefinitionList [ ( [ Str "term" , Space , Str "1" ] , [ [ Para [ Str "Definition" , Space , Str "1." ] ] ] ) , ( [ Str "term" , Space , Str "2" ] , [ [ Para [ Str "Definition" , Space , Str "2," , Space , Str "paragraph" , Space , Str "1." ] , Para [ Str "Definition" , Space , Str "2," , Space , Str "paragraph" , Space , Str "2." ] ] ] ) , ( [ Str "term" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , [ [ Para [ Str "Definition" , Space , Str "3." ] ] ] ) ] , Header 1 ( "field-lists" , [] , [] ) [ Str "Field" , Space , Str "Lists" ] , BlockQuote [ DefinitionList [ ( [ Str "address" ] , [ [ Plain [ Str "61" , Space , Str "Main" , Space , Str "St." ] ] ] ) , ( [ Str "city" ] , [ [ Plain [ Emph [ Str "Nowhere" ] , Str "," , Space , Str "MA," , SoftBreak , Str "USA" ] ] ] ) , ( [ Str "phone" ] , [ [ Plain [ Str "123-4567" ] ] ] ) ] ] , DefinitionList [ ( [ Str "address" ] , [ [ Plain [ Str "61" , Space , Str "Main" , Space , Str "St." ] ] ] ) , ( [ Str "city" ] , [ [ Plain [ Emph [ Str "Nowhere" ] , Str "," , Space , Str "MA," , SoftBreak , Str "USA" ] ] ] ) , ( [ Str "phone" ] , [ [ Plain [ Str "123-4567" ] ] ] ) ] , Header 1 ( "html-blocks" , [] , [] ) [ Str "HTML" , Space , Str "Blocks" ] , Para [ Str "Simple" , Space , Str "block" , Space , Str "on" , Space , Str "one" , Space , Str "line:" ] , RawBlock (Format "html") "
                foo
                " , Para [ Str "Now," , Space , Str "nested:" ] , RawBlock (Format "html") "
                \n
                \n
                \n foo\n
                \n
                \n
                " , Header 1 ( "latex-block" , [] , [] ) [ Str "LaTeX" , Space , Str "Block" ] , RawBlock (Format "latex") "\\begin{tabular}{|l|l|}\\hline\nAnimal & Number \\\\ \\hline\nDog & 2 \\\\\nCat & 1 \\\\ \\hline\n\\end{tabular}" , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "." , Space , Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , Space , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , Space , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Str "This" , Space , Str "is" , Subscript [ Str "subscripted" ] , Space , Str "and" , Space , Str "this" , Space , Str "is" , Space , Superscript [ Str "superscripted" ] , Str "." ] , Header 1 ( "special-characters" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Plain [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Plain [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Plain [ Str "section:" , Space , Str "\167" ] ] , [ Plain [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Plain [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Para [ Str "Explicit:" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Explicit" , Space , Str "with" , Space , Str "no" , Space , Str "label:" , Space , Link ( "" , [] , [] ) [ Str "foo" ] ( "foo" , "" ) , Str "." ] , Para [ Str "Two" , Space , Str "anonymous" , Space , Str "links:" , Space , Link ( "" , [] , [] ) [ Str "the" , Space , Str "first" ] ( "/url1/" , "" ) , Space , Str "and" , Space , Link ( "" , [] , [] ) [ Str "the" , Space , Str "second" ] ( "/url2/" , "" ) ] , Para [ Str "Reference" , Space , Str "links:" , Space , Link ( "" , [] , [] ) [ Str "link1" ] ( "/url1/" , "" ) , Space , Str "and" , Space , Link ( "" , [] , [] ) [ Str "link2" ] ( "/url2/" , "" ) , Space , Str "and" , Space , Link ( "" , [] , [] ) [ Str "link1" ] ( "/url1/" , "" ) , Space , Str "again." ] , Para [ Str "Another" , Space , Link ( "" , [] , [] ) [ Str "style" , Space , Str "of" , Space , Str "reference" , Space , Str "link" ] ( "/url1/" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "Autolinks:" , Space , Link ( "" , [] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Space , Str "and" , Space , Link ( "" , [] , [] ) [ Str "nobody@nowhere.net" ] ( "mailto:nobody@nowhere.net" , "" ) , Str "." ] , Para [ Str "But" , Space , Str "not" , Space , Str "here:" ] , CodeBlock ( "" , [] , [] ) "http://example.com/" , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Quoted DoubleQuote [ Str "Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune" ] , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Para [ Image ( "" , [] , [] ) [ Str "image" ] ( "lalune.jpg" , "" ) ] , Para [ Image ( "" , [] , [ ( "height" , "2343px" ) ] ) [ Str "Voyage dans la Lune" ] ( "lalune.jpg" , "" ) ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "movie" ] ( "movie.jpg" , "" ) , Space , Str "icon." ] , Para [ Str "And" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Image ( "" , [] , [] ) [ Str "A movie" ] ( "movie.jpg" , "" ) ] ( "/url" , "" ) , Str "." ] , Header 1 ( "comments" , [] , [] ) [ Str "Comments" ] , Para [ Str "First" , Space , Str "paragraph" ] , Para [ Str "Another" , Space , Str "paragraph" ] , Para [ Str "A" , Space , Str "third" , Space , Str "paragraph" ] , Header 1 ( "line-blocks" , [] , [] ) [ Str "Line" , Space , Str "blocks" ] , LineBlock [ [ Str "But" , Space , Str "can" , Space , Str "a" , Space , Str "bee" , Space , Str "be" , Space , Str "said" , Space , Str "to" , Space , Str "be" ] , [ Str "\160\160\160\160or" , Space , Str "not" , Space , Str "to" , Space , Str "be" , Space , Str "an" , Space , Str "entire" , Space , Str "bee," ] , [ Str "\160\160\160\160\160\160\160\160when" , Space , Str "half" , Space , Str "the" , Space , Str "bee" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "bee," ] , [ Str "\160\160\160\160\160\160\160\160\160\160\160\160due" , Space , Str "to" , Space , Str "some" , Space , Str "ancient" , Space , Str "injury?" ] , [] , [ Str "Continuation" , Space , Str "line" ] , [ Str "\160\160and" , Space , Str "another" ] ] , Header 1 ( "simple-tables" , [] , [] ) [ Str "Simple" , Space , Str "Tables" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Headless" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 1 ( "grid-tables" , [] , [] ) [ Str "Grid" , Space , Str "Tables" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.2375 ) , ( AlignDefault , ColWidth 0.15 ) , ( AlignDefault , ColWidth 0.1625 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "col" , Space , Str "3" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" , SoftBreak , Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" , SoftBreak , Str "b" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Headless" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.2375 ) , ( AlignDefault , ColWidth 0.15 ) , ( AlignDefault , ColWidth 0.1625 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" , SoftBreak , Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" , SoftBreak , Str "b" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Spaces" , Space , Str "at" , Space , Str "ends" , Space , Str "of" , Space , Str "lines" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.2375 ) , ( AlignDefault , ColWidth 0.15 ) , ( AlignDefault , ColWidth 0.1625 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r1" , Space , Str "a" , SoftBreak , Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" , SoftBreak , Str "b" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "r2" , Space , Str "d" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "in" , Space , Str "a" , Space , Str "cell" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.2375 ) , ( AlignDefault , ColWidth 0.15 ) , ( AlignDefault , ColWidth 0.1625 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "r1" , Space , Str "a" ] , Para [ Str "r1" , Space , Str "bis" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ BulletList [ [ Plain [ Str "b" ] ] , [ Plain [ Str "b" , Space , Str "2" ] ] , [ Plain [ Str "b" , Space , Str "2" ] ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" , SoftBreak , Str "c" , Space , Str "2" , SoftBreak , Str "c" , Space , Str "2" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "with" , Space , Str "cells" , Space , Str "spanning" , Space , Str "multiple" , Space , Str "rows" , Space , Str "or" , Space , Str "columns:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.175 ) , ( AlignDefault , ColWidth 0.1 ) , ( AlignDefault , ColWidth 0.1375 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 2) [ Plain [ Str "Property" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Earth" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 3) (ColSpan 1) [ Plain [ Str "Temperature" , SoftBreak , Str "1961-1990" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "min" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-89.2" , Space , Str "\176C" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "mean" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "14" , Space , Str "\176C" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "min" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "56.7" , Space , Str "\176C" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "with" , Space , Str "complex" , Space , Str "header:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.275 ) , ( AlignDefault , ColWidth 0.1 ) , ( AlignDefault , ColWidth 0.1 ) , ( AlignDefault , ColWidth 0.1 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 2) (ColSpan 1) [ Plain [ Str "Location" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 3) [ Plain [ Str "Temperature" , Space , Str "1961-1990" , SoftBreak , Str "in" , Space , Str "degree" , Space , Str "Celsius" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "min" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "mean" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "max" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Antarctica" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-89.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "N/A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "19.8" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Earth" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-89.2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "14" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "56.7" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Note [ Para [ Str "Note" , Space , Str "with" , Space , Str "one" , Space , Str "line." ] ] ] , Para [ Note [ Para [ Str "Note" , Space , Str "with" , SoftBreak , Str "continuation" , Space , Str "line." ] ] ] , Para [ Note [ Para [ Str "Note" , Space , Str "with" ] , Para [ Str "continuation" , Space , Str "block." ] ] ] , Para [ Note [ Para [ Str "Note" , Space , Str "with" , SoftBreak , Str "continuation" , Space , Str "line" ] , Para [ Str "and" , Space , Str "a" , Space , Str "second" , Space , Str "para." ] ] ] , Para [ Str "Not" , Space , Str "in" , Space , Str "note." ] , Header 1 ( "math" , [] , [] ) [ Str "Math" ] , Para [ Str "Some" , Space , Str "inline" , Space , Str "math" , Space , Math InlineMath "E=mc^2" , Str "." , Space , Str "Now" , Space , Str "some" , SoftBreak , Str "display" , Space , Str "math:" ] , Para [ Math DisplayMath "E=mc^2" ] , Para [ Math DisplayMath "E = mc^2" ] , Para [ Math DisplayMath "E = mc^2" , Math DisplayMath "\\alpha = \\beta" ] , Para [ Span ( "" , [] , [ ( "label" , "hithere" ) , ( "nowrap" , "" ) ] ) [ Math DisplayMath "\\begin{aligned}\nE &= mc^2\\\\\nF &= \\pi E\n\\end{aligned}" , Math DisplayMath "F &= \\gamma \\alpha^2" ] ] , Para [ Str "All" , Space , Str "done." ] , Header 1 ( "default-role" , [] , [] ) [ Str "Default-Role" ] , Para [ Str "Try" , Space , Str "changing" , Space , Str "the" , Space , Str "default" , Space , Str "role" , Space , Str "to" , Space , Str "a" , Space , Str "few" , Space , Str "different" , Space , Str "things." ] , Header 2 ( "doesnt-break-title-parsing" , [] , [] ) [ Str "Doesn\8217t" , Space , Str "Break" , Space , Str "Title" , Space , Str "Parsing" ] , Para [ Str "Inline" , Space , Str "math:" , Space , Math InlineMath "E=mc^2" , Space , Str "or" , Space , Math InlineMath "E=mc^2" , Space , Str "or" , Space , Math InlineMath "E=mc^2" , Str "." , SoftBreak , Str "Other" , Space , Str "roles:" , Space , Superscript [ Str "super" ] , Str "," , Space , Subscript [ Str "sub" ] , Str "." ] , Para [ Math DisplayMath "\\alpha = beta" , Math DisplayMath "E = mc^2" ] , Para [ Str "Some" , Space , Superscript [ Str "of" ] , Space , Str "these" , Space , Superscript [ Str "words" ] , Space , Str "are" , Space , Str "in" , Space , Superscript [ Str "superscript" ] , Str "." ] , Para [ Str "Reset" , Space , Str "default-role" , Space , Str "to" , Space , Str "the" , Space , Str "default" , Space , Str "default." ] , Para [ Str "And" , Space , Str "now" , Space , Span ( "" , [ "title-ref" ] , [] ) [ Str "some-invalid-string-3231231" ] , Space , Str "is" , Space , Str "nonsense." ] , Para [ Str "And" , Space , Str "now" , Space , Str "with" , Space , RawInline (Format "html") "inline HTML" , Str "." ] , Para [ Str "And" , Space , Str "some" , Space , Str "inline" , Space , Str "haskell" , Space , Code ( "" , [ "haskell" ] , [] ) "fmap id [1,2..10]" , Str "." ] , Para [ Str "Indirect" , Space , Str "python" , Space , Str "role" , Space , Code ( "" , [ "py" , "python" , "indirect" ] , [] ) "[x*x for x in [1,2,3,4,5]]" , Str "." ] , Para [ Str "Different" , Space , Str "indirect" , Space , Str "C" , Space , Code ( "" , [ "c" , "different-indirect" ] , [] ) "int x = 15;" , Str "." ] , Header 2 ( "literal-symbols" , [] , [] ) [ Str "Literal" , Space , Str "symbols" ] , Para [ Str "2*2" , Space , Str "=" , Space , Str "4*1" ] ] ================================================ FILE: test/rst-reader.rst ================================================ Pandoc Test Suite ################# Subtitle ^^^^^^^^ :Authors: John MacFarlane; Anonymous :Date: July 17, 2006 :Revision: 3 Level one header ================ This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. Level two header ---------------- Level three +++++++++++ Level four with *emphasis* ~~~~~~~~~~~~~~~~~~~~~~~~~~ Level five '''''''''' Paragraphs ========== Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. Horizontal rule: ----- Another: **** Block Quotes ============ Here's a block quote: This is a block quote. It is pretty short. Here's another, differently indented: This is a block quote. It's indented with a tab. Code in a block quote:: sub status { print "working"; } List in a block quote: 1. item one 2. item two Nested block quotes: nested nested Code Blocks =========== Code: :: ---- (should be four hyphens) sub status { print "working"; } :: this code block is indented by one tab And:: this block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ And: .. code-block:: python def my_function(x): return x + 1 If we use the highlight directive, we can specify a default language for literate blocks. .. highlight:: haskell :: -- this code is in haskell data Tree = Leaf | Node Tree Tree :: -- this code is in haskell too data Nat = Zero | Succ Nat .. highlight:: javascript :: -- this code is in javascript let f = (x, y) => x + y .. highlight:: Lists ===== Unordered --------- Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: + Plus 1 + Plus 2 + Plus 3 Pluses loose: + Plus 1 + Plus 2 + Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 Ordered ------- Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. Nested: * Tab * Tab * Tab Here's another: 1. First 2. Second: * Fee * Fie * Foe 3. Third Fancy list markers ------------------ (2) begins with 2 (3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items (A) a subsublist (B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. (6) Decimal start with 6 c) Lower alpha with paren Autonumbering: #. Autonumber. #. More. #. Nested. Autonumbering with explicit start: (d) item 1 (#) item 2 Definition ---------- term 1 Definition 1. term 2 Definition 2, paragraph 1. Definition 2, paragraph 2. term with *emphasis* Definition 3. Field Lists =========== :address: 61 Main St. :city: *Nowhere*, MA, USA :phone: 123-4567 :address: 61 Main St. :city: *Nowhere*, MA, USA :phone: 123-4567 HTML Blocks =========== Simple block on one line: .. raw:: html
                foo
                Now, nested: .. raw:: html
                foo
                LaTeX Block =========== .. raw:: latex \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} Inline Markup ============= This is *emphasized*. This is **strong**. This is code: ``>``, ``$``, ``\``, ``\$``, ````. This is\ :sub:`subscripted` and this is :sup:`superscripted`\ . Special Characters ================== Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. This & that. 4 < 5. 6 > 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: \( Right paren: \) Greater-than: \> Hash: \# Period: \. Bang: \! Plus: \+ Minus: \- Links ===== Explicit: a `URL `_. Explicit with no label: ``_. Two anonymous links: `the first`__ and `the second`__ __ /url1/ __ /url2/ Reference links: `link1`_ and `link2`_ and link1_ again. .. _link1: /url1/ .. _`link2`: /url2/ Another `style of reference link `_. Here's a `link with an ampersand in the URL`_. Here's a link with an amersand in the link text: `AT&T `_. .. _link with an ampersand in the URL: http://example.com/?foo=1&bar=2 Autolinks: http://example.com/?foo=1&bar=2 and nobody@nowhere.net. But not here:: http://example.com/ Images ====== From "Voyage dans la Lune" by Georges Melies (1902): .. image:: lalune.jpg .. image:: lalune.jpg :height: 2343 :alt: Voyage dans la Lune Here is a movie |movie| icon. .. |movie| image:: movie.jpg And an |image with a link|. .. |image with a link| image:: movie.jpg :alt: A movie :target: /url Comments ======== First paragraph .. comment .. Comment block, should not appear in output as defined by reStructuredText Another paragraph .. Another comment block. This one spans several text elements. It doesn't end until indentation is restored to the preceding level. A third paragraph Line blocks =========== | But can a bee be said to be | or not to be an entire bee, | when half the bee is not a bee, | due to some ancient injury? | | Continuation line | and another Simple Tables ============= ================== =========== ========== col 1 col 2 col 3 ================== =========== ========== r1 a b c r2 d e f ================== =========== ========== Headless ================== =========== ========== r1 a b c r2 d e f ================== =========== ========== Grid Tables =========== +------------------+-----------+------------+ | col 1 | col 2 | col 3 | +==================+===========+============+ | r1 a | b | c | | r1 bis | b 2 | c 2 | +------------------+-----------+------------+ | r2 d | e | f | +------------------+-----------+------------+ Headless +------------------+-----------+------------+ | r1 a | b | c | | r1 bis | b 2 | c 2 | +------------------+-----------+------------+ | r2 d | e | f | +------------------+-----------+------------+ Spaces at ends of lines +------------------+-----------+------------+ | r1 a | b | c | | r1 bis | b 2 | c 2 | +------------------+-----------+------------+ | r2 d | e | f | +------------------+-----------+------------+ Multiple blocks in a cell +------------------+-----------+------------+ | r1 a | - b | c | | | - b 2 | c 2 | | r1 bis | - b 2 | c 2 | +------------------+-----------+------------+ Table with cells spanning multiple rows or columns: +---------------------+----------+ | Property | Earth | +=============+=======+==========+ | | min | -89.2 °C | | Temperature +-------+----------+ | 1961-1990 | mean | 14 °C | | +-------+----------+ | | min | 56.7 °C | +-------------+-------+----------+ Table with complex header: +---------------------+-----------------------+ | Location | Temperature 1961-1990 | | | in degree Celsius | | +-------+-------+-------+ | | min | mean | max | +=====================+=======+=======+=======+ | Antarctica | -89.2 | N/A | 19.8 | +---------------------+-------+-------+-------+ | Earth | -89.2 | 14 | 56.7 | +---------------------+-------+-------+-------+ Footnotes ========= [1]_ [#]_ [#]_ [*]_ .. [1] Note with one line. .. [#] Note with continuation line. .. [#] Note with continuation block. .. [*] Note with continuation line and a second para. Not in note. Math ==== Some inline math :math:`E=mc^2`\ . Now some display math: .. math:: E=mc^2 .. math:: E = mc^2 .. math:: E = mc^2 \alpha = \beta .. math:: :label: hithere :nowrap: E &= mc^2\\ F &= \pi E F &= \gamma \alpha^2 All done. Default-Role ============ Try changing the default role to a few different things. .. default-role:: math Doesn't Break Title Parsing --------------------------- Inline math: `E=mc^2` or :math:`E=mc^2` or `E=mc^2`:math:. Other roles: :sup:`super`, `sub`:sub:. .. math:: \alpha = beta E = mc^2 .. default-role:: sup Some `of` these :sup:`words` are in `superscript`:sup:. Reset default-role to the default default. .. default-role:: And now `some-invalid-string-3231231` is nonsense. .. role:: html(raw) :format: html And now with :html:`inline HTML`. .. role:: haskell(code) :language: haskell And some inline haskell :haskell:`fmap id [1,2..10]`. .. role:: indirect(code) .. role:: py(indirect) :language: python Indirect python role :py:`[x*x for x in [1,2,3,4,5]]`. .. role:: different-indirect(code) :language: c .. role:: c(different-indirect) Different indirect C :c:`int x = 15;`. Literal symbols --------------- 2*2 = 4*1 ================================================ FILE: test/rtf/accent.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Str "le" , Space , Str "caf\233" , Space , Str "o\249" , Space , Str "on" , Space , Str "ne" , Space , Str "fume" , Space , Str "pas" ] ] ================================================ FILE: test/rtf/accent.rtf ================================================ {\rtf1\ansi { le caf\'e9 o\'f9 on ne fume pas } } ================================================ FILE: test/rtf/bookmark.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Span ( "bookmark_1" , [] , [] ) [ Str "Bookmark_1" ] ] , Para [ Link ( "" , [] , [] ) [ Str "click" , Space , Str "me" ] ( "#bookmark_1" , "" ) ] ] ================================================ FILE: test/rtf/bookmark.rtf ================================================ {\rtf1\ansi \pard {\*\bkmkstart bookmark_1}Bookmark_1{\*\bkmkend bookmark_1} \par \pard {\field{\*\fldinst { HYPERLINK \\l "bookmark_1" }}{\fldrslt{click me}}} \par } ================================================ FILE: test/rtf/footnote.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Str "Mead's" , Space , Str "landmark" , Space , Str "study" , Space , Str "has" , Space , Str "been" , Space , Str "amply" , Space , Str "annotated." , Note [ Para [ Str "See" , Space , Str "Sahlins," , Space , Str "Bateson," , Space , Str "and" , Space , Str "Geertz" , Space , Str "for" , Space , Str "a" , Space , Str "complete" , Space , Str "bibliography." ] ] , Space , Str "It" , Space , Str "was" , Space , Str "her" , Space , Str "work" , Space , Str "in" , Space , Str "America" , Space , Str "during" , Space , Str "the" , Space , Str "Second" , Space , Str "World" , Space , Str "War," , Space , Str "however," , Space , Str "that" , Space , Str "forms" , Space , Str "the" , Space , Str "basis" , Space , Str "for" , Space , Str "the" , Space , Str "paper." , Space , Str "As" , Space , Str "others" , Space , Str "have" , Space , Str "noted," , Note [ Para [ Str "A" , Space , Str "complete" , Space , Str "bibliography" , Space , Str "will" , Space , Str "be" , Space , Str "found" , Space , Str "at" , Space , Str "the" , Space , Str "end" , Space , Str "of" , Space , Str "this" , Space , Str "chapter." ] ] , Space , Str "this" , Space , Str "period" , Space , Str "was" , Space , Str "a" , Space , Str "turning" , Space , Str "point" , Space , Str "for" , Space , Str "Margaret" , Space , Str "Mead." ] ] ================================================ FILE: test/rtf/footnote.rtf ================================================ {\rtf1\ansi \ftnbj\ftnrestart \sectd \linemod0\linex0\endnhere \pard\plain \ri1170 \fs20 {\pu6 Mead's landmark study has been amply annotated.\chftn {\footnote \pard\plain \s246 \fs20 {\up6\chftn } See Sahlins, Bateson, and Geertz for a complete bibliography.} It was her work in America during the Second World War, however, that forms the basis for the paper. As others have noted,\chftn {\footnote \pard\plain \s246 \fs20 {\up6\chftn} A complete bibliography will be found at the end of this chapter.} this period was a turning point for Margaret Mead.} \par} ================================================ FILE: test/rtf/formatting.native ================================================ Pandoc Meta { unMeta = fromList [ ( "operator" , MetaInlines [ Str "John" , Space , Str "MacFarlane" ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "test" , Space , Str "of" , Space , Str "FORMATTING." , Space , Str "This" , Space , Str "is" , Space , Str "hidden:" , Space , Str "." ] , Para [ SmallCaps [ Str "Small" , Space , Str "Caps" ] ] , Para [ Strong [ Str "bold" ] ] , Para [ Emph [ Str "italics" ] ] , Para [ Strong [ Str "bold" , Space , Emph [ Str "and" , Space , Str "italics" ] ] ] , Para [ Underline [ Str "underlined" ] ] , Para [ Strikeout [ Str "strikeout" ] ] , Para [ Str "x" , Superscript [ Str "superscript" ] ] , Para [ Str "x" , Subscript [ Str "subscript" ] ] ] ================================================ FILE: test/rtf/formatting.rtf ================================================ {\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f4\fbidi \fnil\fcharset0\fprq2{\*\panose 00000000000000000000}Helvetica;} {\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;} {\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} {\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1068\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f1069\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\f1071\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f1072\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f1073\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\f1074\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f1075\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f1076\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f1108\fbidi \fnil\fcharset238\fprq2 Helvetica CE;} {\f1109\fbidi \fnil\fcharset204\fprq2 Helvetica Cyr;}{\f1111\fbidi \fnil\fcharset161\fprq2 Helvetica Greek;}{\f1112\fbidi \fnil\fcharset162\fprq2 Helvetica Tur;}{\f1115\fbidi \fnil\fcharset186\fprq2 Helvetica Baltic;} {\f1116\fbidi \fnil\fcharset163\fprq2 Helvetica (Vietnamese);}{\f1408\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f1409\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f1411\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;} {\f1412\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f1415\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f1416\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);} {\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;} {\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);} {\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);} {\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} {\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} {\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;\red0\green0\blue0;\red0\green0\blue0;}{\*\defchp \fs24\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}}{\*\rsidtbl \rsid2168431\rsid14113975}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator John MacFarlane} {\creatim\yr2021\mo8\dy4\hr17\min1}{\revtim\yr2021\mo8\dy4\hr17\min2}{\version2}{\edmins1}{\nofpages1}{\nofwords21}{\nofchars120}{\nofcharsws140}{\vern6543}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}} \paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 \dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot2168431 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2 \pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6 \pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 This is a test of }{\rtlch\fcs1 \af4 \ltrch\fcs0 \caps\f4\insrsid14113975\charrsid2168431 \hich\af4\dbch\af31505\loch\f4 formatting}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 .}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid2168431 \hich\af4\dbch\af31505\loch\f4 This is hidden: }{\rtlch\fcs1 \af4 \ltrch\fcs0 \v\f4\insrsid2168431\charrsid2168431 \hich\af4\dbch\af31505\loch\f4 secret}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid2168431 .}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \par }{\rtlch\fcs1 \af4 \ltrch\fcs0 \scaps\f4\insrsid14113975\charrsid2168431 \hich\af4\dbch\af31505\loch\f4 Small Caps \par }{\rtlch\fcs1 \ab\af4 \ltrch\fcs0 \b\f4\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 bold}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \par }{\rtlch\fcs1 \ai\af4 \ltrch\fcs0 \i\f4\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 italics}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \par }{\rtlch\fcs1 \ab\af4 \ltrch\fcs0 \b\f4\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 bold }{\rtlch\fcs1 \ab\ai\af4 \ltrch\fcs0 \b\i\f4\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 and italics}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \par }{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\ul\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 underlined}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \par }{\rtlch\fcs1 \af4 \ltrch\fcs0 \strike\f4\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 strikeout \par }{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 x}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\super\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 superscript}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \par \hich\af4\dbch\af31505\loch\f4 x}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\sub\insrsid14113975 \hich\af4\dbch\af31505\loch\f4 subscript}{\rtlch\fcs1 \af4 \ltrch\fcs0 \f4\insrsid14113975 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b 4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b6f4679893070000c9200000160000007468656d652f7468656d652f 7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f2a24fcfda33b6b164873dd648a5eef2547789aad28cc56208de532e81c026e49085bd ed21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c99e191c3061463074977eefd5afde7bf5de53d5ddcf5e26d4bbc05c1096f6fcfa9d9aefe174ce16248d 7afeb3d9a4d2f13d2151ba4094a5b8e76fb0f03fbbf7eb5fdd454732c609f6403e1547a8e7c752ae8eaa5531876124eeb0154ee1bb25e30992f0caa3ea82a34b d09bd06aa3566b55134452df4b51026a1f2f97648ebd9952e9dfdb2a1f53784da5500373caa74a35b6243476715e5708b11143cabd0b447b3eccb3609733fc52 fa1e4542c2173dbfa6fffceabdbb5574940b517940d6909be8bf5c2e17589c37f49c3c3a2b260d823068f50bfd1a40e53e6edc1eb7c6ad429f06a0f91c569a71 b175b61bc320c71aa0ecd1a17bd41e35eb16ded0dfdce3dc0fd5c7c26b50a63fd8c34f2643b0a285d7a00c1feee1c3417730b2f56b50866fede1dbb5fe28685b fa3528a6243ddf43d7c25673b85d6d0159327aec8477c360d26ee4ca4b144443115d6a8a254be5a1584bd00bc6270050408a24493db959e1259a43140f112567 9c7827248a21f056286502866b8ddaa4d684ffea13e827ed5174849121ad780113b137a4f87862cec94af6fc07a0d537206f7ffef9cdeb1fdfbcfee9cd575fbd 79fdf77c6eadca923b466964cafdf2dd1ffef3cd6fbd7ffff0ed2f5fff319b7a172f4cfcbbbffdeedd3ffef93ef5b0e2d2146ffff4fdbb1fbf7ffbe7dfffebaf 5f3bb4f7393a33e1339260e13dc297de5396c0021dfcf119bf9ec42c46c494e8a791402952b338f48f656ca11f6d10450edc00db767cce21d5b880f7d72f2cc2 d398af2571687c182716f094313a60dc6985876a2ec3ccb3751ab927e76b13f714a10bd7dc43945a5e1eaf579063894be530c616cd2714a5124538c5d253dfb1 738c1dabfb8210cbaea764ce99604be97d41bc01224e93ccc899154da5d03149c02f1b1741f0b7659bd3e7de8051d7aa47f8c246c2de40d4417e86a965c6fb68 2d51e252394309350d7e8264ec2239ddf0b9891b0b099e8e3065de78818570c93ce6b05ec3e90f21cdb8dd7e4a37898de4929cbb749e20c64ce4889d0f6394ac 5cd829496313fbb938871045de13265df05366ef10f50e7e40e941773f27d872f787b3c133c8b026a53240d4376beef0e57dccacf89d6ee8126157aae9f3c44a b17d4e9cd131584756689f604cd1255a60ec3dfbdcc160c05696cd4bd20f62c82ac7d815580f901dabea3dc5027a25d5dcece7c91322ac909de2881de073bad9 493c1b9426881fd2fc08bc6eda7c0ca52e7105c0633a3f37818f08f480102f4ea33c16a0c308ee835a9fc4c82a60ea5db8e375c32dff5d658fc1be7c61d1b8c2 be04197c6d1948eca6cc7b6d3343d49aa00c9819822ec3956e41c4727f29a28aab165b3be596f6a62ddd00dd91d5f42424fd6007b4d3fb84ffbbde073a8cb77f f9c6b10f3e4ebfe3566c25ab6b763a8792c9f14e7f7308b7dbd50c195f904fbfa919a175fa04431dd9cf58b73dcd6d4fe3ffdff73487f6f36d2773a8dfb8ed64 7ce8306e3b99fc70e5e3743265f3027d8d3af0c80e7af4b14f72f0d46749289dca0dc527421ffc08f83db398c0a092d3279eb838055cc5f0a8ca1c4c60e1228e b48cc799fc0d91f134462b381daafb4a492472d591f0564cc0a1911e76ea5678ba4e4ed9223becacd7d5c16656590592e5782d2cc6e1a04a66e856bb3cc02bd4 6bb6913e68dd1250b2d721614c6693683a48b4b783ca48fa58178ce620a157f65158741d2c3a4afdd6557b2c805ae115f8c1edc1cff49e1f06200242701e07cd f942f92973f5d6bbda991fd3d3878c69450034d8db08283ddd555c0f2e4fad2e0bb52b78da2261849b4d425b46377822869fc17974aad1abd0b8aeafbba54b2d 7aca147a3e08ad9246bbf33e1637f535c8ede6069a9a9982a6de65cf6f35430899395af5fc251c1ac363b282d811ea3717a211dcbccc25cf36fc4d32cb8a0b39 4222ce0cae934e960d122231f728497abe5a7ee1069aea1ca2b9d51b90103e59725d482b9f1a3970baed64bc5ce2b934dd6e8c284b67af90e1b35ce1fc568bdf 1cac24d91adc3d8d1797de195df3a708422c6cd795011744c0dd413db3e682c0655891c8caf8db294c79da356fa3740c65e388ae62945714339967709dca0b3a faadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca2aaa6abab39835c3b60cecd8f26645de60b53531e434b3c2 67a97b37e576b7b96ea74f28aa0418bcb09fa3ea5ea12018d4cac92c6a8af17e1a56393b1fb56bc776811fa07695226164fdd656ed8edd8a1ae19c0e066f54f9 416e376a6168b9ed2bb5a5f5adb979b1cdce5e40f2184197bba6526857c2c92e47d0104d754f92a50dd8222f65be35e0c95b73d2f3bfac85fd60d80887955a27 1c57826650ab74c27eb3d20fc3667d1cd66ba341e31514161927f530bbb19fc00506dde4f7f67a7cefee3ed9ded1dc99b3a4caf4dd7c5513d777f7f5c6e1bb7b 8f40d2f9b2d598749bdd41abd26df627956034e854bac3d6a0326a0ddba3c9681876ba9357be77a1c141bf390c5ae34ea5551f0e2b41aba6e877ba9576d068f4 8376bf330efaaff23606569ea58fdc16605ecdebde7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d65 2f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d36 3f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e 3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d985 0528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000000000 0000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000 000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019020000 7468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b6f4679893070000c92000001600000000000000 000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000 000000000000000000009d0a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000980b00000000} {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} {\*\latentstyles\lsdstimax376\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text; \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2; \lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List; \lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1; \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision; \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1; \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1; \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2; \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2; \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3; \lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4; \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4; \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5; \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; \lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; \lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; \lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; \lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; \lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; \lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; \lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; \lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; \lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; \lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; \lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; \lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; \lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link;}}{\*\datastore }} ================================================ FILE: test/rtf/heading.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "" , [] , [] ) [ Str "Heading" , Space , Str "1" ] , Header 2 ( "" , [] , [] ) [ Str "Heading" , Space , Str "2" ] , Header 3 ( "" , [] , [] ) [ Str "Heading" , Space , Str "3" ] , Para [ Str "Paragraph" ] ] ================================================ FILE: test/rtf/heading.rtf ================================================ {\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31506\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;} {\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;} {\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} {\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f44\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f45\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\f47\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f48\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f49\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f50\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\f51\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f52\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f384\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f385\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;} {\f387\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f388\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f391\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f392\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);} {\f414\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f415\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f417\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f418\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} {\f419\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f420\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\f421\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f422\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);} {\f424\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\f425\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\f427\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\f428\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;} {\f429\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\f430\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\f431\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\f432\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);} {\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;} {\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);} {\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);} {\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} {\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} {\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} {\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;\red0\green0\blue0;\red0\green0\blue0;\caccentone\ctint255\cshade191\red47\green84\blue150; \caccentone\ctint255\cshade127\red31\green55\blue99;}{\*\defchp \f31506\fs22 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{ \s1\ql \li0\ri0\sb240\sl259\slmult1\keep\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31503\afs32\alang1025 \ltrch\fcs0 \fs32\cf19\lang1033\langfe1033\loch\f31502\hich\af31502\dbch\af31501\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink15 \sqformat \spriority9 \styrsid2496039 heading 1;}{\s2\ql \li0\ri0\sb40\sl259\slmult1 \keep\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31503\afs26\alang1025 \ltrch\fcs0\b\fs26\cf19\lang1033\langfe1033\loch\f31502\hich\af31502\dbch\af31501\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink16 \sunhideused \sqformat \spriority9 \styrsid2496039 heading 2;}{\s3\ql \li0\ri0\sb40\sl259\slmult1\keep\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel2\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31503\afs24\alang1025 \ltrch\fcs0 \fs24\cf20\lang1033\langfe1033\loch\f31502\hich\af31502\dbch\af31501\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink17 \sunhideused \sqformat \spriority9 \styrsid2496039 heading 3;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive \rtlch\fcs1 \af31503\afs32 \ltrch\fcs0 \fs32\cf19\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink1 \slocked \spriority9 \styrsid2496039 Heading 1 Char;}{\*\cs16 \additive \rtlch\fcs1 \af31503\afs26 \ltrch\fcs0 \fs26\cf19\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink2 \slocked \spriority9 \styrsid2496039 Heading 2 Char;}{\*\cs17 \additive \rtlch\fcs1 \af31503\afs24 \ltrch\fcs0 \fs24\cf20\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink3 \slocked \spriority9 \styrsid2496039 Heading 3 Char;}}{\*\rsidtbl \rsid1718450\rsid2496039\rsid2631807\rsid4616182\rsid10489260}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1 \mwrapIndent1440\mintLim0\mnaryLim1} {\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen \expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1440\dgvorigin1440\dghshow1\dgvshow1 \jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct \asianbrkrule\rsidroot2496039\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0 {\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}} \pard\plain \ltrpar\s1\ql \li0\ri0\sb240\sl259\slmult1\keep\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0\pararsid2496039 \rtlch\fcs1 \af31503\afs32\alang1025 \ltrch\fcs0 \fs32\cf19\lang1033\langfe1033\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31503 \ltrch\fcs0 \insrsid2496039 \hich\af31502\dbch\af31501\loch\f31502 H\hich\af31502\dbch\af31501\loch\f31502 eading 1}{\rtlch\fcs1 \af31503 \ltrch\fcs0 \insrsid2631807 \par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb40\sl259\slmult1\keep\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0\pararsid2496039 \rtlch\fcs1 \af31503\afs26\alang1025 \ltrch\fcs0 \fs26\cf19\lang1033\langfe1033\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31503 \ltrch\fcs0 \insrsid2496039 \hich\af31502\dbch\af31501\loch\f31502 H\hich\af31502\dbch\af31501\loch\f31502 eading }{\rtlch\fcs1 \af31503 \ltrch\fcs0 \insrsid2496039 \hich\af31502\dbch\af31501\loch\f31502 2}{\rtlch\fcs1 \af31503 \ltrch\fcs0 \insrsid2496039 \par }\pard\plain \ltrpar\s3\ql \li0\ri0\sb40\sl259\slmult1\keep\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel2\adjustright\rin0\lin0\itap0\pararsid2496039 \rtlch\fcs1 \af31503\afs24\alang1025 \ltrch\fcs0 \fs24\cf20\lang1033\langfe1033\loch\af31502\hich\af31502\dbch\af31501\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31503 \ltrch\fcs0 \insrsid2496039 \hich\af31502\dbch\af31501\loch\f31502 H\hich\af31502\dbch\af31501\loch\f31502 eading }{\rtlch\fcs1 \af31503 \ltrch\fcs0 \insrsid2496039 \hich\af31502\dbch\af31501\loch\f31502 3}{\rtlch\fcs1 \af31503 \ltrch\fcs0 \insrsid2496039 \par }\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid2496039 Paragraph \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b 4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100d3130843c40600008b1a0000160000007468656d652f7468656d652f 7468656d65312e786d6cec595d8bdb46147d2ff43f08bd3bfe92fcb1c41b6cd9ceb6d94d42eca4e4716c8fadc98e344633de8d0981923c160aa569e943037deb 43691b48a02fe9afd936a54d217fa17746b63c638fbb9b2585a5640d8b343af7ce997bafce1d4997afdc8fa87384134e58dc708b970aae83e3211b9178d2706f f7bbb99aeb7081e211a22cc60d778eb97b65f7c30f2ea31d11e2083b601ff31dd4704321a63bf93c1fc230e297d814c7706dcc920809384d26f951828ec16f44 f3a542a1928f10895d274611b8bd311e932176fad2a5bbbb74dea1701a0b2e078634e949d7d8b050d8d1615122f89c0734718e106db830cf881df7f17de13a14 7101171a6e41fdb9f9ddcb79b4b330a2628bad66d7557f0bbb85c1e8b0a4e64c26836c52cff3bd4a33f3af00546ce23ad54ea553c9fc29001a0e61a52917d367 b514780bac064a0f2dbedbd576b968e035ffe50dce4d5ffe0cbc02a5febd0d7cb71b40140dbc02a5787f03efb7eaadb6e95f81527c65035f2d34db5ed5f0af40 2125f1e106bae057cac172b51964cce89e155ef7bd6eb5b470be42413564d525a718b3586cabb508dd6349170012489120b123e6533c4643a8e20051324888b3 4f262114de14c58cc370a154e816caf05ffe3c75a4328a7630d2ac252f60c23786241f870f1332150df763f0ea6a90372f7f7cf3f2b973f2e8c5c9a35f4e1e3f 3e79f473eac8b0da43f144b77afdfd177f3ffdd4f9ebf977af9f7c65c7731dfffb4f9ffdf6eb977620ac741582575f3ffbe3c5b357df7cfee70f4f2cf0668206 3abc4f22cc9debf8d8b9c52258980a81c91c0f92b7b3e88788e816cd78c2518ce42c16ff1d111ae8eb73449105d7c26604ef24203136e0d5d93d83702f4c6682 583c5e0b230378c0186db1c41a856b722e2dccfd593cb14f9ecc74dc2d848e6c73072836f2db994d415b89cd65106283e64d8a62812638c6c291d7d821c696d5 dd25c488eb0119268cb3b170ee12a7858835247d3230aa6965b44722c8cbdc4610f26dc4e6e08ed362d4b6ea363e32917057206a21dfc7d408e355341328b2b9 eca388ea01df4722b491eccd93a18eeb7001999e60ca9cce08736eb3b991c07ab5a45f0379b1a7fd80ce231399087268f3b98f18d3916d761884289adab03d12 873af6237e08258a9c9b4cd8e007ccbc43e439e401c55bd37d876023dda7abc16d50569dd2aa40e4955962c9e555cc8cfaedcde91861253520fc869e47243e55 dcd764ddff6f651d84f4d5b74f2dabbaa882de4c88f58eda5b93f16db875f10e583222175fbbdb6816dfc470bb6c36b0f7d2fd5ebaddffbd746fbb9fdfbd60af 341ae45b6e15d3adbadab8475bf7ed6342694fcc29dee76aebcea1338dba3028edd4332bce9ee3a6211cca3b192630709304291b2761e21322c25e88a6b0bf2f bad2c9842f5c4fb833651cb6fd6ad8ea5be2e92c3a60a3f471b558948fa6a978702456e3053f1b87470d91a22bd5d52358e65eb19da847e5250169fb3624b4c9 4c12650b89ea725006493d9843d02c24d4cade098bba85454dba5fa66a830550cbb2025b2707365c0dd7f7c0048ce0890a513c92794a53bdccae4ae6bbccf4b6 601a1500fb886505ac325d975cb72e4fae2e2db53364da20a1959b49424546f5301ea2115e54a71c3d0b8db7cd757d9552839e0c859a0f4a6b45a35afb3716e7 cd35d8ad6b038d75a5a0b173dc702b651f4a6688a60d770c8ffd70184da176b8dcf2223a8177674391a437fc7994659a70d1463c4c03ae4427558388089c3894 440d572e3f4b038d9586286ec51208c28525570759b968e420e96692f1788c87424fbb3622239d9e82c2a75a61bdaacccf0f96966c06e9ee85a363674067c92d 0425e6578b328023c2e1ed4f318de688c0ebcc4cc856f5b7d69816b2abbf4f5435948e233a0dd1a2a3e8629ec295946774d4591603ed6cb16608a8169245231c 4c6483d5836a74d3ac6ba41cb676ddd38d64e434d15cf54c435564d7b4ab9831c3b20dacc5f27c4d5e63b50c31689adee153e95e97dcfa52ebd6f60959978080 67f1b374dd3334048dda6a32839a64bc29c352b317a366ef582ef0146a6769129aea57966ed7e296f508eb743078aece0f76eb550b43e3e5be52455a7df7d03f 4db0c13d108f36bc049e51c1552ae1c343826043d4537b925436e016b92f16b7061c39b38434dc0705bfe905253fc8156a7e27e795bd42aee637cbb9a6ef978b 1dbf5868b74a0fa1b188302afae937972ebc8aa2f3c5971735bef1f5255abe6dbb3464519ea9af2b79455c7d7d2996b67f7d710888ce834aa95b2fd75b955cbd dcece6bc76ab96ab079556ae5d09aaed6e3bf06bf5ee43d7395260af590ebc4aa796ab148320e7550a927ead9eab7aa552d3ab366b1daff970b18d8195a7f2b1 88058457f1dafd070000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468 656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969 bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38 a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbaba c4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f 54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72 656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000190200007468656d652f7468656d652f746865 6d654d616e616765722e786d6c504b01022d0014000600080000002100d3130843c40600008b1a00001600000000000000000000000000d60200007468656d65 2f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b0100002700000000000000000000000000ce0900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000c90a00000000} {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} {\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text; \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2; \lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List; \lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1; \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision; \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1; \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1; \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2; \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2; \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3; \lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4; \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4; \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5; \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; \lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; \lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; \lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; \lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; \lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; \lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; \lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; \lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; \lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; \lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; \lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; \lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; \lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000 4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000b002 c4a57123d401feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}} ================================================ FILE: test/rtf/image.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Image ( "" , [] , [ ( "width" , "2.0in" ) , ( "height" , "2.0in" ) ] ) [ Str "image" ] ( "f9d88c3dbe18f6a7f5670e994a947d51216cdf0e.jpg" , "" ) ] ] ================================================ FILE: test/rtf/image.rtf ================================================ {\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 \fmodern Courier;}} {\colortbl;\red255\green0\blue0;\red0\green0\blue255;} \widowctrl\hyphauto {\pard \ql \f0 \sa180 \li0 \fi0 {\pict\jpegblip\picw250\pich250\picwgoal2880\pichgoal2880 \bin ffd8ffe000104a46494600010101007800780000ffdb00430006040506050406060506070706080a100a0a09090a140e0f0c1017141818171416161a1d251f1a1b231c1616202c20232627292a29191f2d302d283025282928ffdb0043010707070a080a130a0a13281a161a2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828ffc000110800fa00fa03011100021101031101ffc4001c0000000701010000000000000000000000010203040506070008ffc4003e100002010303020404040502050500030001020300041105122106311322415107617181143291a1234252b1c115f016336272d1082443e1f1265382ffc40017010101010100000000000000000000000000010204ffc4001b11010101010003010000000000000000000001110212213141ffda000c03010002110311003f00dadd18a10a704f6a95ccc57e37750782b0d8d9ea0cd32e7c5446e07e9f4ad723119a7b89e61e348f260719278aad613cbb640002938c76a182b264fc87bd13009c0c019c76e3d68a072e1cf6f4cd502d330c28269a61bb39c923923d4fad44c08dccb95cfd28b8280769ee08a263891e1808739e4f1d8d149392172cc714050dbb9fde8960ed8c60b79b1ed44103b05c331dbdb1dc5026ac1946d20ff8140aa631c773ec738a0346a003bf93e9cf02801895e7b9a01886796c923bd0090a06393c76a0142003ce3d86680d8dd9392303f5341ccc1b3cf7a2c812c37e4923d381429757013209fa511c18146c9247a0f6a007900c0c671c6280854e086c673eb45c27c038fd68aedff2fda836ef881f136f25d5e6b7d1262964aa02b03f98fbf153131935edcc97576f35c33349212cc4f39f7ab26186dfce5b200f73451f7600dcb8cf27e7400c0b291914046c9e0718fde81371b8e7273f4ef4007691919240e714097f31f376e7b5008caee27807b0f5a02c8e1b3c6d27d33cd0201d839523144d1a149ae084b78da47638211771fd050d582c3a0faab5119b6d12f8ab1c06788a0fd4e2ac356fd1fe08754ddccaba849696309e598c9e2103fed1dcfdeadc44fea5ff00a7f956366d375e492403ca935bedcf1eea4ff6ac68a55efc1beb3b552574f8e7009ff933a927ec715bc82b3a8f4d6bba5ca1352d22fe061cf9a0383f71dea5119cc6c0baed3eaac0f1fad40897dcc3d81f7a052366c1007df3400f21edefc5008c28f30c9c5170ee4fc37830086395250a7c66770c18e78da31c0c63de8609b41f7c515c5172a30c3b76344a3e377cb2339cf7a242aea89808cce368272b8c1a2e107c672a49f5c1a181c9c7ae7da8a2119e7b1f5068099ffa68258a132062d8f9e0f34059502b61bf2824e681bb297ced2a71efda80f19c47b9c77fdbe74057c13e539cf3c1a0eeeb9c73f33405ddb4f18249c1e28062b79ae242902024465b9214614649e7bd0362a7249eddc513456e5720f38f5a1a98e96e95d6baa6ebc2d1ad1e65521649bb469f563534d6d7d31f04347d2a2fc5f535db6a0e83718906c887cbbe5a9a8bef44dce9f731ca9a2e89169d6d6f2184b1455dc07b11dcfeb4d16f119c649a681285b03d3e5500f87820ff006a0e098191de80ac9b8904647b55d11da9681a56a31f87a869f6970b8ffe4883629a289aefc16e92d441682da5b098f21ed9f033f353914d19b751fc08d66cc16d12fe2bf45ec92ff0df1fdbfb559ec667aff4eeb1a04db359d3ae6d40eccebe53f46ec7f5ab82263da7dcf3eb5174e5181076918c7de869503232491ee4515c1803824f03b51287f30e0e7d803449494832c157278a2e8c71fcb9f9d144639236824d01e142efb1768cfb9c7ef40512a818de78ff00a682518e7cc30ab9e00a02cce9953247bd41e467191ed9a04205ee99da4e4e3d283a524b0427b5026c18a8e082067db8341ce0e39ed409b6502907391edda80f2dfdc496f0c124ac6184b144cf0a4e338fd2894f7a7342d4ba9b568f4fd261f12571966270a8bfd47d8511bae85f02b47b7fc34bac5d5c5dc88a0c90ab6c8d9bedce3ef4d1ad691a6dae976a96d616d15b409f9638d70054a1dbc68ea51d4329f4619a8022b78e04548515117b05000a035c5c4702a995c26e3819f534047bcb68a458cce866719540724d02e41c0f7a012a40f6141cbc8e7bd01719e38e6838af1c0a04ca8206d3cd037bdb082fad9e0bd8a39e0718649141047d0d5d18f759fc0cd3af164b8e9999acae4e4f81236e898f7c0f55fed574615aee83a96817ef67abda3db4ebdb7f66f983d88a061bb8db9e3d45165076db83c1f950a53780d8247c80a2398f182724f1c7ad080c608cfa7a51a0062adc7afbd0130173bb9c5070f071cb37e82826106e8f615e01c9e680d6b35bc534be3c1e3831b2aa962bb188f2b71df1de819ae4b1048c7f57bd01a58268e332642ae0704f7cf6207af6a01b99e17b7b6416e227407c494139909ed9f4c0a04a4e501e0f1eb40d263e53dce7fa682c9d0bd13abf58dd6db18bc2b157c4975270ab8ef8f563f21447a73a03a1f4de8eb031582b497328066b97fcd21ff038edfde88b7e32703bd64188e7e6283864b73400cd804b67ca3268317eacea6d56ff005233592f8b6303f953fa4af7c2fa93417fe98b763e0ea171297bab98558068f695ce0e08fdbd281e5c6a57ba5e9d14d716ef7774f2ec112601da4f27ec2827ada74bab559a20e148fcae36b0f91140283729c77a0e0a7777a01dac68395719e39a029607cb901b19c501480ab9279f4a086ea8d0b48d76c0586b7143224a76c61ce1831fe93e86b43cd1f12fe19ea7d2533dcdbeebcd20b612651e68f9ece3fcf6fa5067cbcf20ff009a051724905411e94032799060723da8406d6c67e7ea68d0e1770fe5cfb5026c37039ef9a02eca098c91e6e01e71c500b1d8a49c12786cf6a06a4e256008e3d050119958007920e2801154e32fb4120927b014017eb1c523ac5209a356215c291b87be28957ef853f0d66eaa99352d515e1d190f947669ce7b0ffa7e74a8f4be996569a5d9c56b6704705b46bb5238d42851f2ac875712bc70b3c30f892019540704d01ad2669a0491936330c95ce7140b2e4939a03638c7e8680ae485e33bbe540d60d3ada162c90a02c7270a39340a4f28b68da4645007a8f6a069a746f73235ddcefc391e12b2e1916824948742c99382473c73404791c617695279dc0640f9502c578c9efeb4095cb4cb0830ba21cf999c6401f4f5a05061d430c8079a02e03b3004311c7d281b5e3cd676c65489ee594e4aafe6c7ae07a9a0a075bbea3a8ea96f047d3935ebc404f04ad29411e08e011d98fed416fd212ee5b05b4d5ad6300c615807f1171eaa49eff5ad418c7c55f8466dd66d57a521f20cbcd66a7247a9283dbe5418a63862479877cf1f6c5008e400bdf1ce684016fe53dfbd1a73794600e08ce3d6800377c8c7d6800a9cf75fd4503d91492460f7ee3d28247a7b459f5fd592d22711c206f9e563858a31f99cfd050583518ba75247b1d134f9aed21396d4669769931dc01c003f7a329c4d17458ac5b55d36c12e040a3f1da75c1cb04ede2447f7f6a94567ad7a66db4fbbb29ba7d65b8b4bd8ccd09c8231eaa07b8ab04a7c2cf87b3f53ea8d77abc72c1a5dabe2452bb5a561fc83e5ee7e541e988218ed2dd22b7855228d76a46a00000ec00a510bd4dd511f4fe84da95cc31f880022da4902b1e7d3e99ac86fd03d631f565b4ee6d4dbbc649009cab2e48c83f514165b8b94b6895c44f279c280839e78ce28178ae6de46748a789e453865570483ec6812d42e85a421fc37918b00a883924d024c6e99b7a2a966c0009e17dc9f9fed40f81c77e28139218e4ff9815b9cf23340a01c907b9140201038ed402fcafd28386464e4fd33c50272bc60032609cf00fbd024f722dc66f24822ddf972f8feff00e280f69b24844919cac9ce7de83a447f30ded823007b50459d6ecacb528349b979127651b1dc795f1f3f7a0990148054823dc5015d491c0a0c3be337c2ff00c489b5ee9c87172016b9b541c49ff5a8f7f71eb560c1fc43e0a47e1aa94277310431f91fa551c1727f29ed409b641c86e31839a3454805739c1f6a026f1fd740f64665fc8c31cfde82db79bb40e9e8f49b62eb7d7e8b717ec832c91ff247fa1dc7df2281bcc9369d671493c422b7911654c1215f92bb8827bf068624ba635392df5eb4b9924558ee5bc19b71cee43c6dc7cf34c657be8db0b0b9d0f51d2afe668934dbf9628ddb8c2b8c0073f3a80da37546a7d25174fd95dc125c69f7313ee5655dfc313bd483cf07b1f6a68d5b48d5ec758b612e9d7293211c8fe653f35ee2a084eb9e8bb1eafb3582fe496278f3e1c919fcb9f97ad03ee8dd017a6741b6d3229dae161057c5750a48249ec3eb4139238568f6a9e7b103b5037934cb3793c610a2cd9277a8da73f5140ee38f6280c4b11c65b934023006d50050030e4647de80c846de3b500fcf9fbd0197273ed4007b91400e580c8e45074a82400e72682b36fd2162b7f25ddc09af2766ceeb872db79cf00f6a0b3229550140e07007a50092db860673de818df473c862686dad6470d9cce3b7b63e740fa1de6252ebb5bd81cd00bee2d800d003a6464004763ce683ce9f1cbe1f1d3a67ea1d1a30b68edffba814708c7f9c63d0fafceaca31e6dc71e1f07daa82608c83819f7a1a11ce149238fd68d0a579ec682cfd27a7c3a86bd10bc38b3b756b8b93c1fe1a8c91f7381f7a034f752ea5aa5c5eb292f732128037619c018f6ec282e5a2cb047abda74d5ce9b6da80f136de4a496219b3e48c92000323ea73467519d3da5bb757dbda410ac90c77c23058f99007ee7ec31416882ee47d23acb5185caf8bab4691b1efe57fff0038a9457ee75a82f6e7429350466b482f2742c0f74241c80c38c64541a9cfa1e89ac0177d33ab3d8de28f2b5bca429f91140e2c7a9b5ae9fb85b6ea9b46b9b3c796fe040768f76ec0fafb1f9505df4ebdb3d4edd6e74db98ee216fe68ce47d280648f75e2485a44da385ddc13f4a025ddbdbea16a633286566ce55f9c8f6c502ad750db2c514f30dec428247e6340bbf04100b73402afb943ed2b9e30683836defe9403bc1e06734020e06280cafe8683a375941d841c77c1a0151b467b50159f00e4127e5402872371040c5046eb5aadbe9b1c02e2f6dad25b89047099c677b7b0140fe3f1010afc803f3018e68160c3041ee2823f5dba92d34db89a1d9e2843b03b6d05bd013560c1748d57aa2797c6b35bab78e6959dc47231580ff336dcfb03c1a58364b5bbb7d7fa7b7427f1f673830c8664285bd1815238fad20f2c7c41e979ba43aa2e2c1cb1b663bede438f3a13c7dc76fb5515e9065b851f7a02950002a09c51a1b83cf14176e90d3645e9ad7752752aac23b3439c066665c827e944d29a6410aea725c4567135bd840d3c88a723728c29c9efe6c50d3ee9545b0bf8ef2e6e3c2fc2c6f72f2920e5f19039ee4938a9a875d03278377acf52ddf867f036ef71923932bfe51fbd3475cdc369df0db4fb389d4ea37970fa9cc0b00511795ce7d4f181eb4cd2451755fc45ac16d637381b14ca36b641dfce723e4053170d6cb52bbb362f6d3cb19241f2b9029862f09f143549ba7e7d2eef6caf2797c66ee17fdfd69862d1a069da7dfdac579d17adcda5ea9e1a992376c4723e39e3b024fd7e94c458ac3e25dee8d31d3bae74e7b79002bf8b8549471db38f5f4ed4c165e943a06a328d4ba605b4b22a1523c420c64fbaf38a82d36f0ce7cf7463790729b53017e940e0b0ceceed8ce0500e1b70daa08f5c9ed41d271cd0132476e7d7ff00aa069797d2411168ed9a41fcc858211f73c5075acb25ca6fb82aa31e58a36c81f561dcd033d42169e158ac64b98151b3981c2966cf639f4a064c7a8ac55e4865b7d493701e1c8e52403ea3cbfda827e390ca3f2c914aa81991bd281cdacc2747215c60e0ee5c67e940cb51b0b2bcbd824bfb08ee1a252d1caea1821cfa67b1fa504982b2283ce08f518a08abb82f6dd0369a5662081e14ce40c7ae1b04fda82275cb0bfd4f4536f7114589a5412461f3e4ce4e0e060f63f6ab2893d3b4b5b5b78e22ed22aae3cc3cc7e64fad3449a22a461500007602a0cd7e3b74c26b5d2ad79147baf34eccca40e4a7f30ff3f6aba3cd0543267eb5427b86f1f4c76ef45d0eca1ad5ef224d13e1cf4fd9b22192fa67bc955f8c8c617fba9fb510d7a6ed3fd43a735e5b54964be658c048fb6cdd9e7eb8a186bac97d174e6d22e23437b7ac26b95e77c68bf950fa7279e2b22dba45b59e97a669fa4ea36aeff89cea9a90451fc355ff0096ad9f4ce3f41570675d4fa8c77da8de5cde5be26bc653171ca47dc1f6c9fed5562b97f70276808da7c24f0c1c63804e33fa8a2928c0e0383c8f7ed41d92a41393f4a2548595c2c37493db4cd04e8a08f139566edfef3445b6e7aeaf65d2df48ea2b11776ae02a93e564c772adef409f4ee8ba9a21d73a36fa579ad9f325afe599171ed9c30a960d5ba0fe2843abb47a6f510fc26a4c36890f9558fcc6783506a1147b510024e30339ce680d2c6ae9861eb9a009178a0205443b989e39cd01d8075e3047ce80563057ca381ed4011c4531e503d85013c91b804a21279c903341131cda8c3abdc8650f6d20c4321232adec3dc504bab2c113c9293bb1963df3408dd4b75e1efb2856463dbc43b4631fad047e9177ad4fe32ea16b1db4b8fe1aa92571f5f5a0916bc8e0895af5c46c17cd8c9ff7da80f6d736f7f6915c59cab35bc837238ed8a072a31c1ef4062870718a06f7702dc5b3c522ee4752ae0fa8230683c75d6ba3b74ef535fe984929149e4278ca9e47edfdab42058003763ed409f88ffd6dfad06b1f12ae612fa0c76e0b471e9916d23f973eb4158d3efeff004a984da5debc1295d8e4018dbf3145d583a2ad96f356bbd7f5d90dc59587f1e79a6392f28fcaa3ee47159444ea3aa5ddfc7acf50dcdc344d7a4dbc317f52641200f6000fdeb41b5ef51d8eab672ffa9e971c97c11638268e431a46000012a3b9a351567db823b11f3ef40948e428048207a8340ab48ae83cb83ee0f3428a982719edf3e68c9cc97d3fe15ad8c9be138f2bf38e7b8f6ef40f7a5f55bdd3f56b46d3649127f1405f08e7249c76f5fa50689d48ba5f545cdcbdb462cba9206411b2b055bb07d4fb1c73528d4fa8f52d62cf47b6b8d2e65fc458c49f8a818795c151939f977a823ba0fe253750eb7fe937b04293f9f6c90be41dbdc7ff006283473c1efc1a06f69776d73bbf0f2aca32572bc80470450284a46dfca19f819f5a04e799614def26c0bdce09cfd85045dc75769d12dc3c3e2491db0dd3c85195235f7c91cfd066ae0cdba9be31f4ec61a386c1ef9d4ee473e45cfb1cf34c101d3ff1ac9d481d46c628ed24751881880833f988e7b0fa5328dfed2f2def2ce2b9b79925b791772ca87208f7a60182ee2b95cc0c48f53823fbd40ac658b30f4f4a086d4ee1d75bb6b78f4e965596366fc5211b23238008fde81f43692da5bc30d97831a0397dc09e3d714087506bf61a2c4cd77324726d2caaec141f9fd2ae0c435bf8c57173ad7876f7a2daca10489121244cdf319ce3dbf5a834fe81f881a6f57bcb6ba7c53c72c11873e28cee1db391dbef4199ff00ea4348116a5a66a8a8a04aad04847a90723f6ad7d18c312ddc02a3815423ba0f63fa541687bd9efe1b533b3c9e0a78473e899e318a09bd0ba6eef543e3b2bd8e9b10064bd9e4da001dc81401aeeb29a984d0ba7d5e1d06d4e6594f06523bc8e7f5c0ac8af752dfc17d7090d9218ec6d9447129ee71fcc4fb9cd6842ab10dc0014f3e5a2c1704b671dfdc734525226dc939e283a362c7f940344a380393df144733e2276e38f5efcd01b4bbbfc3dda4a9298a44395902e4a9c70682660d4265d62de40b1bdc1545054f95c8fe627df141af7c3af8808f3dd68dd5d2a45765884b8908d8c3b6c27b7a77a945d7a5fa474bd2ba8e4d5748b28624955d5d8b13b79ee9e983d8d40a753758c7a46b96f6114725dc92279a2810b3a64f94900763cfafa503fd3b59b79ed84da34713c0cd890f0a158fa1f981de826e1b548959fc4dc5cee24b6467e59ed4101ff19e9f676baa5d6a72c50adb4ad1ac790ccc076200f7ad41e7df881d79a87576a5f87b0f161d381db1c2a36e7e6d8ff3416bf87ff082c6f208ef7a82f22b9761bd6d619785f6dc477fa53705ab57f83bd297ceb1e9caf67708db9c4526723e849e3e94f212dd25d117fd29a8c09a76b534fa39cf8b6b71ced38e36fb73417f52e64548e34007e673e9f21ef590a1c918c90718dc281a69b68f67118d9da5058b798f6fa7fe280daadd1b2d36eae70710c4d263df0a4d583cc7a668fd4bf11b5837d7c93dcd9a3146959822a0e781f4cfa55161e9dd7fa67a4f55b9e9aea3e9f81fc09ca0ba118998fcdb2338c7b528d39f4cd2ba76e2d357d292df4eb391809963420ce1b1b576fa1e7359119f1eb4e17dd033ca172d6b2a4df303383fdeb5c8f2eef3bce4e0e335684cb0c9f354160d36f64b0baf16072b91b5f03391f43c51aab23a5debe91c4fad4d73689e6fc3a290573ff4f03e59f4a3280d67581ce916567f84b58ce0a1fccec3d58fa9a084de08c90464e4d1a8e419059b201f4a05630a176918efc50176293872c17bf14042aa0125b03db14046c60b60123fde6827fa0c68edd5365ff11346ba6292ee64194240c807e59a32b7fc51bfe8bd5ed5db424860beb62b89218422ce09c11c01dbbd0660ae110bf1bf2154838dbebfefeb41a8f4cdac7f117458f4d9ecd2df53b4cf81a822808c47255c0f7c8e7fb54a2ec2cfabba3b4b4b8d3af12eedad40926b0f070a13f9b633649f7a82eba6ea4357d321d4ecad512daf20df26e016507fa4fbfaf3e98f9d067dd2bd2faac9aa4d72d72d1e9510ca46a7631c7a320e18f1dfd7bd059afb7da816d23de4ba5de211346a1e4785f190548e4648c63b64e6b43ce9d5baafe3b539c5b452dbda46c638a167cb281c73ee4ff9340e3a3fa5f5aea4ba58f4bb57dbfcf2b02a8bf7f7a0de3a5fa0b50d2a2d92eb3e048c0a97c867dbedcf6a944e5cf4f6b76d1b3d8ea42795066266c87c81c65b9cfaf15048e83af3cd64abac08edaf01546c38dae4e0657ee6826e5b892de3702292e2545ddb55700fd0fbfca81c4b3bc718716eef9eeaa402280d14ab3c0b2c65c06fe571823ed40df56b217fa6dd5ab9216689a33f2c8c558307e83b8d77a37aaa7d22f2512c28768800c9954671b3d33ebef568d0ef7a7749eb0b5bbbb162d657b32b46d2e1564c8ed9c5644d1b0b9bbd261d2a440af6cb0e2e5b1e7dbc1238e0f7a0375b696daa7496a3a4dac8a92cf078685b271db04d391e40d5ec4586a1716de2a49e0c8c85d3b120f715ba1899173ff305413070abd89cfe9f5a2d3ee9c8639fa874eb6b804c52dc46b20c9f302c3bd11e84d47e1af4d5dc6521d3e3b662c19a58721ff5a9a321f89bd27a374b456d158dccd34d333332c9b4b2afbe47a7cb1f7aa33d2bc0d8c0f1c8f6a2c14b6d501b39cf63450897380c319e3de8065031c038f7ed40d8faf1ce41e4d004876a8dc3cc7e743025c956c818028c904579e7f0e15695c9c0541924f6c00283d0bf07f42d6b48820b8d62d20b2b58d656404959e52f83c8ff00fcfafbd4a35bb06f12391a48dd55cee2b2f3818ed8f6a8158a159890f02242079147623e631c502b0db436d1ecb7458d4738038fb0a087d6eeb508f48bb7d32d95750752b6c26c905b3ddb6f61eb574794f5cb6b9d0fa9678b512b25d24bbe52b8c1638278fbd582c57ff12afaed45b5bc0d0d8a8c08a2731ee3eec5793f40450466a1d59af446293c186cd53ca0c36eab93dc649e49fbd048e89f1675ed35e301e293919dcbf9867b37cbe94a35fe94ea4d33aba6824306dc48015750d86c6e247b0cf63591a40b8dc23fc30f14138dcac3000f9d03687547f12e8dd5af816b13148dddbcd29039c0f6f6f7a0eb5d62caf5636825db70c9bc4328f0dc0271c8a090627d3073ce681acf6505ccf14d35bc2f2c2731bb28254fb8a075144a83ca806792400334049ee6281e2496408656d880ff0031f61fa50446bd76058ea1b9e21025a3bb48afe71df9c7b71de9c8f196a0de23ca7b827d4f7add117e0cbfd4b5059392369663ff004e71c51aa97e8f555eadd258f2bf8a889cff00dc28cbd0bf123a926e96d163bdb74490bca2321c678209ff001591e71eafd7a7d7ef45cde2c20aae144638033fb9ad2e1b5e69d058da431ccf21d4a5c3b4631b62523807feaf5c7a50222f2d648c25f5aeec8c2cd19dae3d3e87e944d3eb7e90d425b49ef2292de38224f1505c3f8724a9eeaa7bd0d57a60406059436306868a7803839c7ad1a158039c13f4a33a716767f8cb9b6b55e1ae2458813e9938cd07a9f42d0b4de99b4b7d1f41b58ff19b03c93ba06607fa8b1f5f619a5b8266d74a65d42da6ba90cce996dcdc8c9fff006a5a2c2635083b05ef83eb5028076341db4b1ed9f7a04651fc41db18ed419d75b744dbea335fcb0db0335f2057901c05da73c8f9d5d18a75174a3f4c47335e35da4ce418a489374254f707d463d33565d1529b569a489a17944b06ec8057d71dcd037b4b6b8bfba31584124b27e62a8a4f1ea68357f83da7ea5a76bfe0912453ccabb49194653cb60f6ce3dfda983d196cd108c2401711f9768e306b2297d73fc6d02773a8b591922693c5004bb9d72542fa2f6efde8314ff867aeeec27500b77bb5670e36ca19b1dff2e7f2fd2837ce8bd5dd348b78b552219022870d9c46e792a4f6c608a0b846c8e03232b29ec41cd0199f1410fd4b24b1e8f712c0a5e4452d851e6c639dbf3238a0afa42ba77475e4ba8470896681da45180b18da76af3c9029c8f26dc1df2b9c606e273e86b743331924f27f4a82c12280e59b008e79f6a2d4d74188ff00e30d203a82ad7519c1ff00b860d11ba7c5e86c9fa3afae6f4091e043e021270b21e01c7dcd6479ab4dd3aeb56be4b7b184cf2b301b57d07bfd2b4bad0fe25e9f6960ba7c7a55ac50cd750335ccaade7723b83b8f6e38a2207a5ba4e7ea3d93780cb616ca53781f99fbff009a0b675a5be9765d43a75a6ad3b25adb4185429b831c70303dfdfd2831eb8954ca48f3827819c71ed406b2b1b9bf9a5fc1c4ce2253238047957dc9345d122b792eee522811a495ce1157b93ed444ff0049f476b1aaf51c761345269d25be269259570c833c6077249e062a68f53e8ef0da69509d4ae225b92a04af232ab16f98cf1f4a5a266d4dbca8af13a329ecca723f51502d14f0cb9f05d1c8ee01c91f6a0393b4edfe63c8a031608859b38f97340d84d04e5846eae50f9829ce3eb400fb24466041f5e3d2823f56d22db57b192d6e61468a41c823ff0035651916bbf04ada7badda5cad6d1b72c09dc33f2a6875d25f0865d06fe2bc6d4c4d3282026cca8cfafcfd29a34cd234a10c768f711a78f1bb392a3001208e3ec69a26a58d640c832091c90706a084ea1d3eeafdadf4f86da3166c0b4b397c18f046140f5ce4d04f4702436e91c28a9122e028ed8a0a9f5a5b6a09a1bc5a135bc72cce048b3c5bc15c638f9d59043fc2db997481aa69dae49e1cb6bb643239211939e467818f97bd305965ebce9a10bc8da9dbaa2679dd9ce3d8530572cfac87566ab05ae9f1490692b9696e1f833738555f96793504df5f25945d2576b7ec16dc46792381c7b7ad5e60f234980e42f6c9c56a82ec3eff00bd4124e49700f1c646e3cd169ce9575f83d52cee324347323f6e3861ff008a23d47d4ba6a75074fdcd8ef317e2e2ff0098bdd4706a60c3f4ae8cd5f44eb8fc3e97248a638cbc73bf90483d463b373e9574685abf42a6b5649fea72bbdeac4a8d3b018cfae31f7a6895b6b29b41d30d8e9f6bbed9213e1b7a994e724fcbb5064bd7835a9752d3af75d8116354778f660788cbd9483dbb0a0cd20b2b8d43528ad2088bdcccf854039cff00e2827a0d34e9da1de896f2182492efc0976f998aa827d3d334113a74aa9a9298628e74570478bc0c7cf1da83724bb8246d3e6416b0384da61b5501c9f5c3704f152c037561a95f5fce61d3ad6db4ab950f34b331dd9f4191db8fdcd406d67a675ab4b6d325d1af248272a43430315ddec7038f6ad4b3f448bf47eb71cb69abddebf21d5e26896203846c30c8603b9c6452d9835901405660376319ac84bf13180779d8bb82827d4fb0a06f777367a75acd7170f1430a9f331200cfceae061a0cf6dac692d7365266191db0578f5edf3a6075a6c9278b3433188a467860d96fbd40fe540471409aa0c1140750001ed8ed4058e15133c983960077edf6a06faade5c5b7822d2d926766cb967da2341dd8f0727d85033d27597d62e2ee3163756915bbf8799d71e2f19dcbf2a092b88dd9a311950a0e5b70ce47fe6ac18af53f5b69da9752dd69da9ca906876e24465d9e69881c6ff005c679c0aa2bfd267a347512c93dbb5d42d90d3c800b68c9ce0ec3cfa528db74ad034db5905ee8be1ac728dc153984f3f980f4fb56453be2de8f647a6aff53796596f0aed46798ec033ce149c0fb0ad71479c9bb9dc3bf3c55a0b95f65a825150b481a407b93c0fda8a29c06671dc93803bd131eafd0af6dffe18d2ee25982a4b04603b7a9c631fad03bbe586381bc5945b96385718c827db3eb5288eb8ba934eb15fc3c535f05427796dcccdec7150637d5bd55d5362b7975aa4d0d919018adec8637807bb60723000e4f7cd58203538aefa8aeb478a7b9beba924547b8774cf8608036a80704639cf1f9855d1a7f4c68b67a03de5e5d59dad8d988c62e1c0f107a1e7fdf7a082ea6d07a6e3e99375a7cb6b3db093c727701e2360f7f53dfb50653d4130d42ee18f48b3f0232a15218936963ebf5fbd06dbf0cfa74855b9d46e04b730c6144691e12307d33ea7de8348ba6b5478a279a004f98c479247b81f5a9438805ac0be2e02e73c9ef8fbd40c2346d43578eefc40da7da1dc8b8eefea4fcb9fef419beabf12a4bcebcb2d2fa7ee0dc58492084b30236bb6467dce383f6a0b87556a67a7f4d95a0bbb5468816f0a69c78b2e072572719ce78357079dfab3af2e7a92c963b88d94abbbf91cedc93edf418aa2c5f0dbe2a6a9a0c90d8de34773a6a8da1186d6403fa48fec682d9adeb7a9b4c9d572dacc9a6c9700456e5ca910e000ecbd8e580352fb1ae7476bd6dd49a325e5a9f3025245fe961dea097523cc0919a032af039a006936c81423104649c703ef40dcce64bc3035a87b6f0c378f9fe7cf2b8fdf340a4b6e25962915d94a67807839f7a043586922b5636ec44c061063f31f6ab079d7fe19b7eb2d4efeef55d5d74f992e9a0fc3a441dcb13927b838c9aa2d4bf042c618e178efee6e18104870172318edf5c1e6945bbe1a748ea7d2315edbea3a99bcb190030c401010e4e783ee0fa56455be3d6b90c1a6268d69e17f1486900ee98ec29ccc183119419e7e55ba11f089f523ef5058363a8059fb8e31e9421b491056c01819e28d3d0ff07eea3d53a1e3b6b8db235aca63c139c0eea68ca47a9fa6ef757b83ff00bf68ad428c2f248c7a8f9fcea518df516adac74e7544f63a2ea172f1800291e6cee19ec78cd5826344e85b6bad25ba8fade5b99dae0ee11efc71e858f7e7d054a2d7d39a75ae8f7b6b00b8917f1516624b78429da327cec493db1db1d8540cf4aea28f58d6aeae2f6dd64b498082d880488d149fcea7d4939ab04175e6850a8d32de3b78e380c8de32c4db4e18e430fef543cf86fd0d691b4fabdf6648b3b2db69ce7dd87be68342d2f4fb9b5b891af1a28a2dc05bc5036d001e0eef7352884b961a2ea9aa7555c885e08d45b5bc52b61b686c120fb939fb541276bd4b61d53624d942e2f6200bc32290633e99f4233416dd3ad3f0d611c0c77b632e71dc9ef41156dd27a45addcb3dbd9c513b1dd941821bdc7b558333b9f873757fd69a85e3f813e9c7723b5d93265d872473c11544a68ff08fa75e290b42ec4e4124e70738fa5048e89f0f745d2aeadd2decedda6525c975121183c77f7a945c754d0e0d563682ed43425369403bff00bcd58308ba7d5fe13f5a05889974a9d8b46aede4914f707d88ff001528ddf4fd7edb54d1a1d56c312dab2e64dbc9418e78f5c541296d70b716d1cd6f8789977230ecc280cb7519b816f212b205de4e0843ce300f6cfcb39a0545c42cee88e0b458ddec33ee680eac92266360debc7b5056fad2d354b9b189745744be121daf27e550548ce3d4d58324d07a725e98ea2b7ff5381f5169a7579d021fe13904ee43ddfbe49038ab46e76cf05cc714f6d309232a4a98ce54fd6b2196bb7f2e9b631b2c427b891b6851db3eff002007341e5bf887aa2ea3d4f77378be381e42fdb711ed5a1554395caf1cd07617dcd04fc85402101da791421b49920331381c60d1a69bf02f56f03a925b12c162b98c955f775e47df19a32d99b518268e4491668704a79d4aeec7b7bd4a30feb7e9144d76e265697c3e2693631674273803e556087d76f35fd49859e9925ccf611141106f2e182e3241f727f5a94681a268f79ad1b1b8b9dd66b1c2b1ce9bb06361c1e7bf3fe6a096b0d3ba57488e485b52b40909c386901607d47bf7a0ae758eb69ac4d15be81a748f0a9c35cc90b2eff4c03c1c638a0b77405c5d5d45f87be5fe359a88b81b401dc1c7d38fb50586fed18ea3015790091591e447c1518c8c7a0a0a5754d8c9d49a8d9f4ee9dba0b3b5224b9692327728f627e7c6682f9a7e996b6b3a8b6b748a348820c7720761412c064e3041ce282b5d4bd73d3bd3f33daea97ac2e540dd0a292dc8cd043c1f15ba2a7923b65bb910371b9a12141f9d02edf13ba2ade56857551e5e77244c54fd0e280746ebfe99bbbd655d56dcdc9ce08465565f4ee3bfca82d53eb3a6dac3e25c5f5ba646402e33fa77a0aff5b74ad8757e9ca972844aa37c520fcca7d3f5ab067bd369a8f467544d626c98e9f7118f0200e4465c903049c8c9e6ad1b24334b6fa6249716cab20037c309dd83db03b5640dddac3764a4f02cb1103863919fa7a1a06d6f600c37162911b7b252b87dc773f1927393f4a079f868edae1ae6328a8b1ed38e30050226e85d5dc1f879011b3c47c2f604719f9d01eff4f4b83e3c6b18bb452b1cac9b8a83de80b16e8208a3b7b58e1407cc061427cc0ff1560ce7a8f592d69a97504cad2dac01a2b53900c3e9e51ea58f727d0551e73bfb86b99a49a46dcf21c96340dc13804f20f6f9501c0e3b8a0963316fcc4607007f57bd084d64dec01200ce483468ff0040d525d1755b4d4206ff0095207c11dc67ff0019a18f56584f6daad9dade4211e39104a8ded9152b235cd8c530613229c8c1c8ef50472e81690ee00322b0c100f0debdbb7ca8111ace856371358cba85aa5cc407891ccf83f2ef4048b4ad2e59bf116769672c72f99dd1437239078e2824a4d3e1b94559234110e781839fa0a0561b38ad532a12319c86c6307e740a9732a3a00cac870cd8e0faf0681be8b6db965bb909df3c85806eeabced5a09523647e6e0fef419a6adf116daf7ac34be9dd1da686e7f1ca2e243b76320ce57df9ff1560cc3e3f470ff00c78255b842b35bc6c4af9b6e323d3e95467b6365f8dbcf062beb68c119595d8aaff6e282422e9899b4f6bb5d46cda2562a76316c1078f4a0859e1b9b762c0bf94f0e84feb4125a57505c58ea70dd5d0174a986d92b1c13f6f5a0de7a5be366877260b5d42dee2d2423124a487507ebdf15289dd3fac7a7bab6feded74d61733473acdb5a162142ff00313d81ed505fa58fc6d809380c1b9f5c502e0e05040ea367a85dea454de2ff00a610375b04c16c7a16ef8340b5f6930dce9375636acf6a278f04c5dd4f1dbf4a084e91d06f3a52dee62bbd4a2b882494ced3c8a448063b63b638fde803ab7ae749b2b3096d792c93c8c109b55dcd18ce3710473ffdd043dc758dc5869044565aa5dda4c3c2b7bc78c1f14e3963db03e7c0ab066bf1327d41ba76ca6bc48ecad24c456f6b0c87cf8e4bbfa138c0fa9aa3297c83dd4e28395811cf2680a5b93c7ed413cd1ff0f3c797b8031406645236b0508406f30e68ba49c051b97d0e0e7d28ad57e19f575c5be8f269515dac772877c11bc464dea7ba8c739ce78f9d3193bb8ebfea2bcbd6b5b6b8b58bb7f13c2f0ce31cf0deb4c0f2dbad6d743d2ee99efae752d6c02a86e0054524f6383c7ff94c0b6af274c75149a46a5a82c46f1e1479fc3190bd8156fbe7f4a960bf74e9d253f1167a3ac09e0856610e3041ec7f6c540f67b892cee7f8d18368232ef2af2508c7047cf340ead5bf130accc9b1186541e723d09ffc5033bbb1b82d74d6b37f1244daa1b38073df3f4a08eeb1d3b50d4fa6a5b4d32f12cef1902ee73e523d476fde8306eb0d3fabfa4ba92c278af67bb7281606472f90bdd58558253a6afb40ea1d62283a8b461a66a6f931cf6a7c2466f7cfb939e7b5515fbed3747b5eb8f06eb78d35080a2ec9719c76f98049a0b4ebdd37d1bad869acbf0b03c5c16b29444adf50ded41995f68564a263a66b31cd02be152505493f51c1a088bab69ad1bc179e320f07c37c8a0692b46c4995f0381db39a0b2fc34e971d57d4705bc8db6c50ef9dd97b81fcbf7381528f5ae97a1d8e996d1db69b0c7648855b10a81b80f43c739c54134147b9a036063279fb5046e957726a0f2cfe04915b06db1788305ffeac7a0a00d72f8d9c491c06337533050ac7185cf2df6a06da2da4293488f34973328c34aea428c9ec3eded40ee7d2ad249448f6b133820ee2833df3fde819ea96897461b05b87815f2ee919e5d47704fa039ab079b3e326b70eafd4ef6d6650d8e9ebf868b69c8247723efebf2aa280eb9193ebedc5002a124e015340018fb8fde82cf32b1603d0678cd02406dce3008e47ce81b499c331383c9c9f5a2e9c69377369f7d6f796a4acf148acb83fb511e91b3b3d0bab745b7d45acedd8e3732b71b1fd73f7a5a19eafd09a56a0a96b1c705b49c48510761ce4fcfbd4d0d752826b2b583476fc3daacb295b79d768de8a32b1e71f989e49f6a7d14dd1af5ba275a45b8b093c054492ea769092373765c1da4679fbd306e36ba9595fe9f0dcdbcc92c33e1579cf27d0d409ea178f68521b6b76926ee8a7853f7ff140bc768d78f6f73748d1cd103b543f0091cf6efc502f7319dac194371d8b6326829f0c501d7ae6fa568d5d4942c806d4db81839f53c8cd59456fae6e628749b5bab8b1865b08e4726588ec11e7f2e49071c93da9a31dd4341d675e9a5bad374f9858162d0b4ac70e18f0573df35a0c759f87bd53a404f174f965dfff00f479b1ef570576e34bd4b4cc0bdb3b9b7258a00e846e3f2a94376475cee4914af7ca9150685f09ba61efb52fc7dd69bf8eb7c158d1d0b47bb38f37efde968d6eeb4eb5e91d6ad25b660aee59974db6881690918e31ce39279e062a5a34bb57b88adedd1e379679065d80036679e6a07e8391bce7e940c659ef5afe1286de3b16f2b8903094b7b2fa7ce81eb380c4260ed193f2a0cd6fa5d4a4ea0fc7bdadcce923158a3039db83c038e3ef41a0e96b2ad8a35e009291b8a939d9f227e43bd590436bbd6fa269202c974b7123602c76e779624e00e29833bf897d493e896525dbcb2a6b5a9c3e1456b91b6d60cf989c7f31f7a60c02490961ebcf3eb541308e39e067b507007b96007a67fb50178f97eb4165ce18165c2927b773fad023202d9c8dbb7818ff003408300b87f2f1c107d0d0c15a4f2e339c90467f6a18bdfc2cea8ff4bd561b4b9ba686d6e64552c4f954e7d7e46a60f4688d240af1b2bc6cbf5047ca960617ba658de1b792f2d94a5ab33461b18524633fa1a81b5c8b5168967369d23c0e0a24622dc981d81c76aba29df0bb48d660d52fceb88d069f04aeb69130037127f37b9c0ed9a8350781240bbc06da72323b1f7a0eb8816e633192ebc8c9472a78fa50349b4c83c068e24f0ddbcc1c13b837be4d055b57e98b996d4db5b4a893dc1e6620b6ccf2c467efc504ce97a38d3f4d874bf09af6d46e2f25cb82724e791db15650a43f878b51fc34f3da8f132b6d6a98c80a39ff007e99aba249631b58b9047239ec3e55368aeeb7d2da6f52c0eb7f16e87f2c6e836ba90724ab7a67b55d115ac7c3e8aed2182def4c56a14096368959a423d77e3229a27b41e9d8343b01069c8a8dc9660aa3713df3c64d4a1c695d3d6b67a8cba94b9b8d4e61869e4e4a8c636a7f4afcaa09a485519caae19b966f7a0435196582c656b74df3e308beec7b50629375775b691ac5bdb6b16d6378779f019b00a31cf391c9c2f1daae0b35ef52f5374de88d77aa45a6cd25ddc0108694ee2188c28007603d6a0d16c92430a4b2ed3230dc401855cfa0a0a07c45d7b5db9d462e9ee960b14b2ee134ef8c850012147ec78ab0670b643a2efae753d72686e6e2da211db42176079c8e768f65e39f7aa332d6f58bbd635096f6fe6692695b24f603d801e82823cb900ee00e68006460051dfd680ed9f0c905b713d8d006d5f5419fa505a18f94e40501b1b81a04186d62402c87be3d6810b81290aea8467201231cd1749dbc437f9f1e201db3de8ba07dc0175c027f28a335b17c26f888d6b6d0e8fabf892a29c453b1e547f49f7a946d6424f08236491c833ee0835073294888894120700f0280813c40a6711bc8843e00fca7d3ef40e41dc081f9a811681c6f7565329185623b7e9de812b3bcf11ff0b74563bd50494cf120071b97e5fda80d7577046c9019d5669dbc340324eec67fb734103d5da96bf67a7cf0e916f6e2765f25ddc4c11107ab1c8c647cce2816d3ec7f0f6564cae6e67da375e6d52cc4af2e4fb13ed4145f899d47b3499f4bb6d46cda49b69b92921565c3648e3dd40c81cd5c0ae89f13ec246d3e379ed2d6da180b5d34849da1780b128e49271c9f4a60ba685d5fa36bc42d95c324ec7090cc9b1d87b81ed50588958977cacaaa3b9341c655f12348d1dcb8dc1946540f99a019e2134454eeda7bed3839cd052fe2136b536b5d3563a1ca53c49da4b9507198940ce7e5c9fbe281c6b7a974cf4ee4ea7242d76dc784a3c595b3e9b464e3f6ad0ac745ccbd4fadcbabea42da56959a382ce7460d6b1a93c01f97272093ebf6a82f1ad6af0e9da5cb3cecf6902216919f82aa3818c7a9f4c530649a9f5269ba7429d472239bc991a1b0d3c3152880f0f23039e7bf3de90635ab6a377aa5ebdcdecef2c9239e59f3827e5ed54302195fcc38cd01245395c1f5f4a05b606538e483c501b6939e3b5077860f3914165754c33e549000dbe87de813b820c27fa7baafb8a04a69c98e281e42618c795338033df1f3a06c03e4e029247007ad010093c35674da71f977640340081a190658e41c820f141b0fc33f8926c218b4ed609366a02249bb2d19ce3ea4528dbece68eead926b7916689c643a9e0d643387521fea2f693c2d6efc786ee46d9bfed3eff2a04669edf5295ff057a60bd865309246d3bbbedc1efef41d63a8dec9ab4fa7dd4510fc3c69234ca186e2d9c003b7a67bd034bad0a5bb96e25d575267889cc1b54446d9bd0a37bfbfbd02925945a72cba8dddc48b3f87b1e58f23c623f292bdb7fa7cf38a0a5750f54ebba56a9a67fc516b6d6fd3970ea9234677c8dc7f38f6e4640ce282d1ac4b16bfa72c1d2bad430cd1ba822061865f5007d3daac19cf547c189357d561bbd3eee683c62cd786e9b73337b8c7bd512bd39f06adedec2de0d5ae639da372e6485363107f97767b505cee755d03a5e58ac228659af123184b7b733322e38c91dbf5a943ab3d52e64b49ee755d2e64950e238e35f10c884e17cbe87dc540e6d7509a4f110e9f7566a471712850abf6ce463e6280bacea36fd33a4497f773cf32c698dcc4beee33938edc7ad05534aea683ae61d64c4d2a69b6b88d16d5ca5c303f3e386cf61db140e27b5e99e96b64bdd562b6b30aa36c6c37cac7dc9eec6b42c1a66a962fa70beb6b516d0cbe76322f86c78f6c66831bf8b5d5d2dfa35a5dce60b1933b6ce3c788769f2b331ec1b8fd2831b79649984b239773c1dc68129725c1c640e73400ec781d8fb1a03c4a85d0c8582640257bd01e51fc42236263c9c67be280429c6037eb405dbf5fd682c2a0bb976cf07d3fc5009279ef8ec4e68193a9902b28e7dc71fb501d95b098e0f6dd9e0d0049131019b047b8390281bb292484e47c8500c4f242c3076bf704704739a0be7c3febfbae9a5daf23cd017c7e19fb107bb67d0f6fd6837cd2757d1fab34f4f05e37dde630b1c3a91edf4f7159103d6eb7da65fdbdf59dadb5e2460ac876ed9e1c8c060f9efe9c8a0a75cfc42d5742d2ee5a7e9dbb494b6d6b9bc930cce4f940e3cd81ed4160e98f88315e689fff0022b57664199cc5196f0c7a164ef8f98cd0589baffa68590985eb15c0db1985839f6c2919340b69d03f5285bdd6f4bf021424db4329cb153fccc3d09c76a090d3340d2f479a7bab3b38e2924e5e451cfd280d7dafe936f6c5e4bd89813b02a36589ce318a0358dc35cc4e17f9bf234cdbb78fa0c607a7340e6cec20b1596610c6934b8323226379edda81da8dcb9ec40a042f50b5bb21645473b58b11dbd7bfca8304f8add5d67a9ea8ba5d8ea72c1a4d8a952f10f2c920e368cf71e99fad5833dd0754d6ed66bdb2e9a91a5babb2a310465a57c1ddc1038e7bd5171d3748b8d46ee1ff005298dc6a764c27d4eeaee7fe1c01795881c9e7804fe940dbe287c4b6d75a1b1d1c986d62277c91bf131f4c0f6fad0663737135d3b497124924871f98e7803007d2811dc5b83903dc0a0333f03be7de8122df2c9a05a11e5f51f7a05ce7071804fca8033e5da0734020b0183bb23e5416269577ed0e5323078ceeefdff6a03dbcd62914c2f629a47c622689800879e4fbd046f9d8a2a03e31385c0e73da8b83ca590947dcaead8208e73da8849b3e19ce704f7c5015586d006431f5cf61f3a04ee586ff002481c8fe6191408f0011eb8e30682774dea9bbb5784492ca6385832e1ca95e3d2834be9df8c312e2db5eb4375080337000f1303d18763591a469baef4d755989ec6f6dae5a23e20b599406dd8e080ddbed41272f4dd8caf1c86d163910f9595882a3d718a035edd695d3b6a926b57d0ac65b10b4e06eedd863bf141077fd7af2782bd3fa26a1a8891d57c630948c0279393dcd04cf5875258f4de9f0cb7b7b6d66d2b81ba752d85f5214724fed41036bd79d2fe319d7a8f4b9c81e58de2f04827b9ce09a07a3acecf54b790685ace8697606009e52c377b7f2e682b097d7d36acf0f55df5edbdc0977412468cb6ce3be10af3c63b9c8a0b0751f505be9690dd5d752436f62a3fe4c6448f3b7b0c64e38f615734651f107e3045ac42b67a7693018633b965bc1bc838ee173807bf7cd33065baaeb1a95f2c11dfcd2bc51fe48880aa3e8a062a8b059f595df4ae9a74de9e9ec499d43c97b1427c6e47e525bb63e4282ad3ea3712893c599dbc4259c1627713c927de81043950df97db1406da8411c92063be280230839627078e79a0390a71b5bb5003a8c1232338e4507025573e9fbd02a0e41dc0e680c3691cf714053bb34160895dc16f291d98d02322aa39f3e14707ffaa06f202a5bb0c7201f4a343aca51090497efdfbfce89840c8db8e46573923ff14410b00490d9f5c1a0425dd8ce4673e873cd0265e4dbe7041ed814009b8cbb8e5863bd07163b7716e0607b6698060b96b7b9478dc8643918247ee39a60b7e97f133a8b4e0c63d4ee597380923970a3ef4c0e13e25dfcfab457ba95bdbddb212016501867b9cfbfda982f907c74b282da34874a9048aa479c83838edc62982b7ac7c42d235cd67f15ac5b40c366418a2cb0c1c81e6f5e31db14c160d035de8cd4a3f18ea96562cc37359dfe9cac887fef039fd6b39446f56ea5d13a9dca4579a922496b1975b8d22dfc3566cf9557230703be715ac1431d4d2275325d7fae6b4f6f182a93ef1e32a9f41938f6a60afeb1a95c5fea53dccf772cf2c8c489240031f627e7565c0d67bbf160487c1801073bc0c31f91f953420f2bc8c7c52cc540032738a809905c0f4fa501940208e73df34028e703938a0577ae013f4a001300db4f03b71407461ce0502dca8c9c7dcf6a003fafcbdcd0070bc1e0fd734070db467b8a04cb9c9e68274b93bcb1daa40200ed4099765249ec476cf340849b8b264823bf34689ee009cb671df1409bc8e8b8e770e3ec68984d9c953cf97be2860b248caaebc107dc67f7a184c31232db88fd451031ce50e76039f5c8045026efb8f93279e0fd680b239504f0483d88ef409ee25bb90c79c0e050151c6e21b39ce783eb40adbcc2cefa17bdb61322387781c950e3dbdf9c8a066f28790b22e013db3dbef54726081e63c6460d34191b1bb0720f634060e7600412c781502409c8e3d7b500ed71dbef8ed40243f181c0a02e5d4e0fad006f644e4819e3b501f7e3049e7b501bc43b4e391da80558003392d40a23305e320d02e64cf998e1bf5a032b0c927b8e68049c8c8e71c6280377043118f4e680bbff00de0504ddbb3128371c1c64668024e59f3cd02521254e4fad1a2107e48fe6a6809ddb9e78a029e1463d05027ffc744a6c3857c7b1a205ff00e637fbf4a02b12b1794e39f4a02024e7249ed409924720906800005173fd7404989698ee39e4f7a04cf723d07a501fff0097ed406ffe36a018ff00281e99a037f4d0731f3bfd28007e53400ff99a810248c0cf140bc60123233cff008a0557f9beb4056eff007a07109243e79a03778b27bfbd02b128c27039a054001b818a06609de793da815006070283ffd9}\par} } ================================================ FILE: test/rtf/link.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Link ( "" , [] , [] ) [ Str "pandoc" ] ( "http://pandoc.org" , "" ) ] ] ================================================ FILE: test/rtf/link.rtf ================================================ {\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 \fmodern Courier;}} {\colortbl;\red255\green0\blue0;\red0\green0\blue255;} \widowctrl\hyphauto {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "http://pandoc.org"}}{\fldrslt{pandoc}}} \par} } ================================================ FILE: test/rtf/list_complex.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaInlines [ Str "Cynthia" , Space , Str "Johnson" ] ) , ( "operator" , MetaInlines [ Str "John" , Space , Str "MacFarlane" ] ) , ( "title" , MetaInlines [ Str "Text" , Space , Str "before" , Space , Str "list" ] ) ] } [ OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] , OrderedList ( 1 , LowerAlpha , Period ) [ [ Para [ Str "Three" ] ] , [ Para [ Str "Four" ] , OrderedList ( 1 , LowerRoman , Period ) [ [ Para [ Str "Five" ] ] , [ Para [ Str "Six" ] , OrderedList ( 1 , UpperAlpha , Period ) [ [ Para [ Str "Seven" ] ] , [ Para [ Str "Eight" ] , OrderedList ( 1 , UpperRoman , Period ) [ [ Para [ Str "Nine" ] ] , [ Para [ Str "Ten" ] , BulletList [ [ Para [ Str "Eleven" ] ] , [ Para [ Str "Twelve" ] ] ] ] ] ] ] ] ] ] ] ] ] , Para [ Str "Out" , Space , Str "of" , Space , Str "list!" ] , Para [ Str "Start" , Space , Str "with" ] , OrderedList ( 7 , Decimal , Period ) [ [ Para [ Str "Seven" , Space , Str "Start" ] ] , [ Para [ Str "Eight" , Space , Str "Continue" ] ] ] ] ================================================ FILE: test/rtf/list_complex.rtf ================================================ {\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff45\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;} {\f3\fbidi \fdecor\fcharset2\fprq2{\*\panose 00000000000000000000}Symbol;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f45\fbidi \fnil\fcharset0\fprq2{\*\panose 00000000000000000000}Times;} {\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f52\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\f53\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f55\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f56\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f57\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\f58\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f59\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f60\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f72\fbidi \fmodern\fcharset238\fprq1 Courier New CE;} {\f73\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}{\f75\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f76\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f77\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);} {\f78\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f79\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f80\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f392\fbidi \froman\fcharset238\fprq2 Cambria Math CE;} {\f393\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f395\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f396\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f399\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;} {\f400\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\f502\fbidi \fnil\fcharset238\fprq2 Times CE;}{\f503\fbidi \fnil\fcharset204\fprq2 Times Cyr;}{\f505\fbidi \fnil\fcharset161\fprq2 Times Greek;} {\f506\fbidi \fnil\fcharset162\fprq2 Times Tur;}{\f509\fbidi \fnil\fcharset186\fprq2 Times Baltic;}{\f510\fbidi \fnil\fcharset163\fprq2 Times (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;} {\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);} {\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;} {\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} {\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} {\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;\red0\green0\blue0;\red0\green0\blue0;}{\*\defchp \fs24\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af45\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f45\hich\af45\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive Default Paragraph Font;}{\* \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}}{\*\listtable{\list\listtemplateid2032649{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 _;}{\levelnumbers;} \fs28\cf0\loch\af3\hich\af3\dbch\af0\fbias0\hres0\chhres0 \fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'04\'00.\'01.;}{\levelnumbers\'01\'03;} \rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-432\li792\jclisttab\tx792\lin792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'06\'00.\'01.\'02.;}{\levelnumbers\'01\'03\'05;} \rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-504\li1224\jclisttab\tx1440\lin1224 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'08\'00.\'01.\'02.\'03.;}{\levelnumbers \'01\'03\'05\'07;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-648\li1728\jclisttab\tx2160\lin1728 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \'0a\'00.\'01.\'02.\'03.\'04.;}{\levelnumbers\'01\'03\'05\'07\'09;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-792\li2232\jclisttab\tx2520\lin2232 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 {\leveltext\'0c\'00.\'01.\'02.\'03.\'04.\'05.;}{\levelnumbers\'01\'03\'05\'07\'09\'0b;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-936\li2736\jclisttab\tx3240\lin2736 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1 \levelspace0\levelindent0{\leveltext\'0e\'00.\'01.\'02.\'03.\'04.\'05.\'06.;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-1080\li3240\jclisttab\tx3600\lin3240 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 \leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'10\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07.;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-1224\li3744\jclisttab\tx4320\lin3744 } {\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'12\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07.\'08.;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f\'11;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-1440\li4320\jclisttab\tx4680\lin4320 }{\listname ;}\listid161162663}{\list\listtemplateid-1055851588{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;} \rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 } {\listlevel\levelnfc1\levelnfcn1\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace540\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li3420\lin3420 }{\listlevel\levelnfc23\levelnfcn23 \leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li4500\lin4500 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 \levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;} \rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid180975272}{\list\listtemplateid-1055851588\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \leveltemplateid67698703\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \leveltemplateid67698713\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \leveltemplateid67698715\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext \leveltemplateid67698709\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc1\levelnfcn1\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace540\levelindent0{\leveltext \leveltemplateid67698707\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li3420\lin3420 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext \leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li4500\lin4500 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698703 \'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698713 \'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698715 \'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid581336416}{\list\listtemplateid-1516889114\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1 \levelspace0\levelindent0{\leveltext\leveltemplateid67698703\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative \levelspace0\levelindent0{\leveltext\leveltemplateid67698713\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative \levelspace0\levelindent0{\leveltext\leveltemplateid67698715\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative \levelspace0\levelindent0{\leveltext\leveltemplateid67698703\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative \levelspace0\levelindent0{\leveltext\leveltemplateid67698713\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative \levelspace0\levelindent0{\leveltext\leveltemplateid67698715\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative \levelspace0\levelindent0{\leveltext\leveltemplateid67698703\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative \levelspace0\levelindent0{\leveltext\leveltemplateid67698713\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\lvltentative \levelspace0\levelindent0{\leveltext\leveltemplateid67698715\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid592708402}{\list\listtemplateid353931476{\listlevel\levelnfc0\levelnfcn0 \leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0 \levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0 {\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext\'02\'03.;}{\levelnumbers \'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc1\levelnfcn1\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace540\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li3420\lin3420 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 } {\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 \leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1 \levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid621182402}{\list\listtemplateid-37037068{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0 \levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1 \levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;} \rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 } {\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 \leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1 \levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid982349901}{\list\listtemplateid-2094603378{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0 \levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1 \levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;} \rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 } {\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 \leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1 \levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid1082678316}{\list\listtemplateid166376406\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0 \leveljcn0\levelfollow0\levelstartat7\levelspace0\levelindent0{\leveltext\leveltemplateid-1898031884\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698713\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698715\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698703\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698713\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698715\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698703\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698713\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2 \levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698715\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 }{\listname ;}\listid1306352145} {\list\listtemplateid2032649{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li360\jclisttab\tx360\lin360 } {\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'04\'00.\'01.;}{\levelnumbers\'01\'03;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-432\li792\jclisttab\tx792\lin792 }{\listlevel \levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'06\'00.\'01.\'02.;}{\levelnumbers\'01\'03\'05;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-504\li1224\jclisttab\tx1440\lin1224 }{\listlevel \levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'08\'00.\'01.\'02.\'03.;}{\levelnumbers\'01\'03\'05\'07;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-648\li1728\jclisttab\tx2160\lin1728 } {\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'0a\'00.\'01.\'02.\'03.\'04.;}{\levelnumbers\'01\'03\'05\'07\'09;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-792\li2232 \jclisttab\tx2520\lin2232 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'0c\'00.\'01.\'02.\'03.\'04.\'05.;}{\levelnumbers\'01\'03\'05\'07\'09\'0b;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-936\li2736\jclisttab\tx3240\lin2736 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'0e\'00.\'01.\'02.\'03.\'04.\'05.\'06.;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d;} \rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-1080\li3240\jclisttab\tx3600\lin3240 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \'10\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07.;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-1224\li3744\jclisttab\tx4320\lin3744 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0 \levelstartat1\levelspace0\levelindent0{\leveltext\'12\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07.\'08.;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f\'11;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-1440\li4320\jclisttab\tx4680\lin4320 }{\listname ;}\listid1517575639}{\list\listtemplateid-2069710980{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel \levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li2160\lin2160 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0 \levelfollow0\levelstartat1\levelspace360\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 \levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext \'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li4320\lin4320 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;} \rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc2\levelnfcn2\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-180\li6480\lin6480 } {\listname ;}\listid1762801537}}{\*\listoverridetable{\listoverride\listid1517575639\listoverridecount0\ls1}{\listoverride\listid161162663\listoverridecount0\ls2}{\listoverride\listid581336416\listoverridecount0\ls3}{\listoverride\listid1082678316 \listoverridecount0\ls4}{\listoverride\listid1762801537\listoverridecount0\ls5}{\listoverride\listid621182402\listoverridecount0\ls6}{\listoverride\listid592708402\listoverridecount0\ls7}{\listoverride\listid180975272\listoverridecount0\ls8} {\listoverride\listid1306352145\listoverridecount0\levelstartat7\ls9}{\listoverride\listid982349901\listoverridecount0\ls10}}{\*\rsidtbl \rsid329053\rsid3617154\rsid3749795\rsid9258424}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0 \mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\title Text before list}{\author Cynthia Johnson}{\operator John MacFarlane}{\creatim\yr2003\mo10\dy13\hr19\min55}{\revtim\yr2021\mo8\dy9\hr10\min28}{\version4}{\edmins6}{\nofpages1}{\nofwords15} {\nofchars87}{\nofcharsws101}{\vern4617}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\noxlattoyen \expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dghspace180\dgvspace180\dghorigin1701\dgvorigin1984\dghshow0\dgvshow0 \jexpand\viewkind1\viewscale154\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\rsidroot3749795 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect \linex0\endnhere\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4 \pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (} {\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 1.\tab}\pard\plain \ltrpar\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls3\adjustright\rin0\lin720\itap0\pararsid9258424 \rtlch\fcs1 \af45\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af45\hich\af45\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \hich\af45\dbch\af31505\loch\f45 One}{\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid329053 \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 2.\tab}}{\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \hich\af45\dbch\af31505\loch\f45 Two \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 a.\tab}}\pard \ltrpar \ql \fi-360\li1440\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls3\ilvl1\adjustright\rin0\lin1440\itap0\pararsid9258424 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \hich\af45\dbch\af31505\loch\f45 Three \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 b.\tab}\hich\af45\dbch\af31505\loch\f45 Four \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 i.\tab}}\pard \ltrpar \ql \fi-180\li2160\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls3\ilvl2\adjustright\rin0\lin2160\itap0\pararsid9258424 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \hich\af45\dbch\af31505\loch\f45 Five \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 ii.\tab}\hich\af45\dbch\af31505\loch\f45 Six \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 A.\tab}}\pard \ltrpar \ql \fi-360\li2880\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls3\ilvl3\adjustright\rin0\lin2880\itap0\pararsid9258424 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \hich\af45\dbch\af31505\loch\f45 Seven \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 B.\tab}\hich\af45\dbch\af31505\loch\f45 Eight \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 I.\tab}}\pard \ltrpar \ql \fi-180\li3420\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls3\ilvl4\adjustright\rin0\lin3420\itap0\pararsid9258424 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \hich\af45\dbch\af31505\loch\f45 Nine \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \f45\insrsid9258424 \hich\af45\dbch\af31505\loch\f45 II.\tab}\hich\af45\dbch\af31505\loch\f45 Ten \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af45 \ltrch\fcs0 \f2\insrsid9258424 \hich\af2\dbch\af31505\loch\f2 o\tab}}\pard \ltrpar\ql \fi-360\li4500\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls3\ilvl5\adjustright\rin0\lin4500\itap0\pararsid9258424 { \rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \hich\af45\dbch\af31505\loch\f45 Eleven \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af45 \ltrch\fcs0 \f2\insrsid9258424 \hich\af2\dbch\af31505\loch\f2 o\tab}\hich\af45\dbch\af31505\loch\f45 Twelve \par }\pard \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid9258424 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \hich\af45\dbch\af31505\loch\f45 Out of list! \par }\pard \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid3617154 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid3617154 \hich\af45\dbch\af31505\loch\f45 Start with \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af45 \ltrch\fcs0 \f45\insrsid3617154 \hich\af45\dbch\af31505\loch\f45 7.\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls9\adjustright\rin0\lin720\itap0\pararsid3617154 { \rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid3617154 \hich\af45\dbch\af31505\loch\f45 Seven Start \par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af45 \ltrch\fcs0 \f45\insrsid3617154 \hich\af45\dbch\af31505\loch\f45 8.\tab}\hich\af45\dbch\af31505\loch\f45 Eight Continue \par }\pard \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid3617154 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid3617154 \par }\pard \ltrpar\ql \li4140\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin4140\itap0\pararsid9258424 {\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid9258424 \par }{\rtlch\fcs1 \af45 \ltrch\fcs0 \insrsid3617154 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b 4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b6f4679893070000c9200000160000007468656d652f7468656d652f 7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f2a24fcfda33b6b164873dd648a5eef2547789aad28cc56208de532e81c026e49085bd ed21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c99e191c3061463074977eefd5afde7bf5de53d5ddcf5e26d4bbc05c1096f6fcfa9d9aefe174ce16248d 7afeb3d9a4d2f13d2151ba4094a5b8e76fb0f03fbbf7eb5fdd454732c609f6403e1547a8e7c752ae8eaa5531876124eeb0154ee1bb25e30992f0caa3ea82a34b d09bd06aa3566b55134452df4b51026a1f2f97648ebd9952e9dfdb2a1f53784da5500373caa74a35b6243476715e5708b11143cabd0b447b3eccb3609733fc52 fa1e4542c2173dbfa6fffceabdbb5574940b517940d6909be8bf5c2e17589c37f49c3c3a2b260d823068f50bfd1a40e53e6edc1eb7c6ad429f06a0f91c569a71 b175b61bc320c71aa0ecd1a17bd41e35eb16ded0dfdce3dc0fd5c7c26b50a63fd8c34f2643b0a285d7a00c1feee1c3417730b2f56b50866fede1dbb5fe28685b fa3528a6243ddf43d7c25673b85d6d0159327aec8477c360d26ee4ca4b144443115d6a8a254be5a1584bd00bc6270050408a24493db959e1259a43140f112567 9c7827248a21f056286502866b8ddaa4d684ffea13e827ed5174849121ad780113b137a4f87862cec94af6fc07a0d537206f7ffef9cdeb1fdfbcfee9cd575fbd 79fdf77c6eadca923b466964cafdf2dd1ffef3cd6fbd7ffff0ed2f5fff319b7a172f4cfcbbbffdeedd3ffef93ef5b0e2d2146ffff4fdbb1fbf7ffbe7dfffebaf 5f3bb4f7393a33e1339260e13dc297de5396c0021dfcf119bf9ec42c46c494e8a791402952b338f48f656ca11f6d10450edc00db767cce21d5b880f7d72f2cc2 d398af2571687c182716f094313a60dc6985876a2ec3ccb3751ab927e76b13f714a10bd7dc43945a5e1eaf579063894be530c616cd2714a5124538c5d253dfb1 738c1dabfb8210cbaea764ce99604be97d41bc01224e93ccc899154da5d03149c02f1b1741f0b7659bd3e7de8051d7aa47f8c246c2de40d4417e86a965c6fb68 2d51e252394309350d7e8264ec2239ddf0b9891b0b099e8e3065de78818570c93ce6b05ec3e90f21cdb8dd7e4a37898de4929cbb749e20c64ce4889d0f6394ac 5cd829496313fbb938871045de13265df05366ef10f50e7e40e941773f27d872f787b3c133c8b026a53240d4376beef0e57dccacf89d6ee8126157aae9f3c44a b17d4e9cd131584756689f604cd1255a60ec3dfbdcc160c05696cd4bd20f62c82ac7d815580f901dabea3dc5027a25d5dcece7c91322ac909de2881de073bad9 493c1b9426881fd2fc08bc6eda7c0ca52e7105c0633a3f37818f08f480102f4ea33c16a0c308ee835a9fc4c82a60ea5db8e375c32dff5d658fc1be7c61d1b8c2 be04197c6d1948eca6cc7b6d3343d49aa00c9819822ec3956e41c4727f29a28aab165b3be596f6a62ddd00dd91d5f42424fd6007b4d3fb84ffbbde073a8cb77f f9c6b10f3e4ebfe3566c25ab6b763a8792c9f14e7f7308b7dbd50c195f904fbfa919a175fa04431dd9cf58b73dcd6d4fe3ffdff73487f6f36d2773a8dfb8ed64 7ce8306e3b99fc70e5e3743265f3027d8d3af0c80e7af4b14f72f0d46749289dca0dc527421ffc08f83db398c0a092d3279eb838055cc5f0a8ca1c4c60e1228e b48cc799fc0d91f134462b381daafb4a492472d591f0564cc0a1911e76ea5678ba4e4ed9223becacd7d5c16656590592e5782d2cc6e1a04a66e856bb3cc02bd4 6bb6913e68dd1250b2d721614c6693683a48b4b783ca48fa58178ce620a157f65158741d2c3a4afdd6557b2c805ae115f8c1edc1cff49e1f06200242701e07cd f942f92973f5d6bbda991fd3d3878c69450034d8db08283ddd555c0f2e4fad2e0bb52b78da2261849b4d425b46377822869fc17974aad1abd0b8aeafbba54b2d 7aca147a3e08ad9246bbf33e1637f535c8ede6069a9a9982a6de65cf6f35430899395af5fc251c1ac363b282d811ea3717a211dcbccc25cf36fc4d32cb8a0b39 4222ce0cae934e960d122231f728497abe5a7ee1069aea1ca2b9d51b90103e59725d482b9f1a3970baed64bc5ce2b934dd6e8c284b67af90e1b35ce1fc568bdf 1cac24d91adc3d8d1797de195df3a708422c6cd795011744c0dd413db3e682c0655891c8caf8db294c79da356fa3740c65e388ae62945714339967709dca0b3a faadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca2aaa6abab39835c3b60cecd8f26645de60b53531e434b3c2 67a97b37e576b7b96ea74f28aa0418bcb09fa3ea5ea12018d4cac92c6a8af17e1a56393b1fb56bc776811fa07695226164fdd656ed8edd8a1ae19c0e066f54f9 416e376a6168b9ed2bb5a5f5adb979b1cdce5e40f2184197bba6526857c2c92e47d0104d754f92a50dd8222f65be35e0c95b73d2f3bfac85fd60d80887955a27 1c57826650ab74c27eb3d20fc3667d1cd66ba341e31514161927f530bbb19fc00506dde4f7f67a7cefee3ed9ded1dc99b3a4caf4dd7c5513d777f7f5c6e1bb7b 8f40d2f9b2d598749bdd41abd26df627956034e854bac3d6a0326a0ddba3c9681876ba9357be77a1c141bf390c5ae34ea5551f0e2b41aba6e877ba9576d068f4 8376bf330efaaff23606569ea58fdc16605ecdebde7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d65 2f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d36 3f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e 3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d985 0528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000000000 0000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000 000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019020000 7468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b6f4679893070000c92000001600000000000000 000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000 000000000000000000009d0a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000980b00000000} {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} {\*\latentstyles\lsdstimax376\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; \lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid; \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2; \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1; \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1; \lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; \lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; \lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; \lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; \lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; \lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; \lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; \lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; \lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; \lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; \lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; \lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; \lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; \lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; \lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link;}}{\*\datastore }} ================================================ FILE: test/rtf/list_simple.native ================================================ Pandoc Meta { unMeta = fromList [] } [ BulletList [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] , BulletList [ [ Para [ Str "sub" ] ] ] ] ] , BulletList [ [ Para [ Str "new" , Space , Str "list" ] ] ] ] ================================================ FILE: test/rtf/list_simple.rtf ================================================ {\rtf1\ansi\ansicpg1252\cocoartf2513 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{hyphen\}}{\leveltext\leveltemplateid2\'01\uc0\u8259 ;}{\levelnumbers;}\fi-360\li1440\lin1440 }{\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}} \margl1440\margr1440\vieww11520\viewh8400\viewkind0 \pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural\partightenfactor0 \ls1\ilvl0 \f0\fs24 \cf0 {\listtext \uc0\u8226 }one\ {\listtext \uc0\u8226 }two\ \pard\tx940\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li1440\fi-1440\pardirnatural\partightenfactor0 \ls1\ilvl1\cf0 {\listtext \uc0\u8259 }sub\ \pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural\partightenfactor0 \ls2\ilvl0\cf0 {\listtext \uc0\u8226 }new list\ } ================================================ FILE: test/rtf/table_error_codes.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Code" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Error" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocFailOnWarningError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocAppError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocTemplateError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocOptionError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "21" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocUnknownReaderError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "22" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocUnknownWriterError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "23" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocUnsupportedExtensionError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "24" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocCiteprocError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "31" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocEpubSubdirectoryError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "43" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocPDFError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "44" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocXMLError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "47" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocPDFProgramNotFoundError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "61" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocHttpError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "62" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocShouldNeverHappenError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "63" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocSomeError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "64" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocParseError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "65" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocParsecError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "66" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocMakePDFError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "67" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocSyntaxMapError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "83" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocFilterError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "91" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocMacroLoop" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "92" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocUTF8DecodingError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "93" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocIpynbDecodingError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "94" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocUnsupportedCharsetError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "97" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocCouldNotFindDataFileError" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "99" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "PandocResourceNotFound" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/rtf/table_error_codes.rtf ================================================ {\rtf1\ansi { \trowd \trgaph120 \clbrdrb\brdrs\cellx4320\clbrdrb\brdrs\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 Code\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Error\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 3\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocFailOnWarningError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 4\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocAppError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocTemplateError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 6\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocOptionError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 21\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocUnknownReaderError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 22\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocUnknownWriterError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 23\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocUnsupportedExtensionError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 24\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocCiteprocError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 31\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocEpubSubdirectoryError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 43\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocPDFError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 44\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocXMLError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 47\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocPDFProgramNotFoundError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 61\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocHttpError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 62\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocShouldNeverHappenError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 63\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocSomeError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 64\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocParseError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 65\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocParsecError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 66\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocMakePDFError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 67\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocSyntaxMapError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 83\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocFilterError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 91\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocMacroLoop\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 92\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocUTF8DecodingError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 93\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocIpynbDecodingError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 94\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocUnsupportedCharsetError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 97\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocCouldNotFindDataFileError\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx4320\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 99\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 PandocResourceNotFound\par} \cell} } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 \par} } ================================================ FILE: test/rtf/table_simple.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( 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" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "C" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "D" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "E" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "F" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "G" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "H" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/rtf/table_simple.rtf ================================================ {\rtf1\ansi \par \trowd \cellx10836 \pard \intbl A \cell \pard \intbl B \cell \pard \intbl C \cell \pard \intbl D \cell \row \trowd \cellx10836 \pard \intbl E \cell \pard \intbl F \cell \pard \intbl G \cell \pard \intbl H \cell \row \pard } ================================================ FILE: test/rtf/unicode.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Str "\8220hi\8221\8216hi\8217\61623\945\228" ] ] ================================================ FILE: test/rtf/unicode.rtf ================================================ {\rtf1\ansi \pard \'93hi\'94 \u8216\'93hi\u8217\'94 \u-3913? \u945a {\uc2\u228\'E8a} \par } ================================================ FILE: test/s5-basic.html ================================================ My S5 Document

                My S5 Document

                Sam Smith
                Jen Jones

                July 15, 2006

                First slide

                • first bullet
                • second bullet

                Math

                • $\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$
                ================================================ FILE: test/s5-fancy.html ================================================ My S5 Document

                My S5 Document

                Sam Smith
                Jen Jones

                July 15, 2006

                First slide

                • first bullet
                • second bullet

                Math

                • \(\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\)
                ================================================ FILE: test/s5-fragment.html ================================================

                First slide

                • first bullet
                • second bullet

                Math

                • $\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$
                ================================================ FILE: test/s5-inserts.html ================================================ My S5 Document STUFF INSERTED STUFF INSERTED

                First slide

                • first bullet
                • second bullet

                Math

                • $\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$
                STUFF INSERTED ================================================ FILE: test/s5.native ================================================ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "Sam",Space,Str "Smith"],MetaInlines [Str "Jen",Space,Str "Jones"]]),("date",MetaInlines [Str "July",Space,Str "15,",Space,Str "2006"]),("title",MetaInlines [Str "My",Space,Str "S5",Space,Str "Document"])]}) [Header 1 ("first-slide",[],[]) [Str "First",Space,Str "slide"] ,BulletList [[Plain [Str "first",Space,Str "bullet"]] ,[Plain [Str "second",Space,Str "bullet"]]] ,Header 1 ("math",[],[]) [Str "Math"] ,BulletList [[Plain [Math InlineMath "\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)-f(x)}{h}"]]]] ================================================ FILE: test/tables/nordics.html4 ================================================

                States belonging to the Nordics.

                Name Capital Population
                (in 2018)
                Area
                (in km2)
                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
                ================================================ FILE: test/tables/nordics.html5 ================================================

                States belonging to the Nordics.

                Name Capital Population
                (in 2018)
                Area
                (in km2)
                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
                ================================================ FILE: test/tables/nordics.jats_archiving ================================================

                States belonging to the Nordics.

                Name Capital Population(in 2018) Area(in km2)
                Total 27,376,022 1,258,336
                DenmarkCopenhagen 5,809,502 43,094
                FinlandHelsinki 5,537,364 338,145
                IcelandReykjavik 343,518 103,000
                NorwayOslo 5,372,191 323,802
                SwedenStockholm 10,313,447 450,295
                ================================================ FILE: test/tables/nordics.latex ================================================ \begin{longtable}[]{@{} >{\centering\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.3000}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.3000}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.2000}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.2000}}@{}} \caption[Nordic countries]{States belonging to the \emph{Nordics.}}\label{nordics}\tabularnewline \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Name \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Capital \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Population\\ (in 2018)\strut \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Area\\ (in km\textsuperscript{2})\strut \end{minipage} \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Name \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Capital \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Population\\ (in 2018)\strut \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Area\\ (in km\textsuperscript{2})\strut \end{minipage} \\ \midrule\noalign{} \endhead \midrule\noalign{} Total & & 27,376,022 & 1,258,336 \\ \bottomrule\noalign{} \endlastfoot 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 \\ \end{longtable} ================================================ FILE: test/tables/nordics.markdown ================================================ +-----------------------+-----------------------+---------------+---------------+ | 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"} ================================================ FILE: test/tables/nordics.mediawiki ================================================ {| id="nordics" class="wikitable" source="wikipedia" |+ States belonging to the ''Nordics.'' |- ! style="text-align: center;"| Name ! style="text-align: center;"| Capital ! style="text-align: center;"| Population
                (in 2018) ! style="text-align: center;"| Area
                (in km2) |- class="country" ! style="text-align: center;"| Denmark | style="text-align: left;"| Copenhagen | style="text-align: left;"| 5,809,502 | style="text-align: left;"| 43,094 |- class="country" ! style="text-align: center;"| Finland | style="text-align: left;"| Helsinki | style="text-align: left;"| 5,537,364 | style="text-align: left;"| 338,145 |- class="country" ! style="text-align: center;"| Iceland | style="text-align: left;"| Reykjavik | style="text-align: left;"| 343,518 | style="text-align: left;"| 103,000 |- class="country" ! style="text-align: center;"| Norway | style="text-align: left;"| Oslo | style="text-align: left;"| 5,372,191 | style="text-align: left;"| 323,802 |- class="country" ! style="text-align: center;"| Sweden | style="text-align: left;"| Stockholm | style="text-align: left;"| 10,313,447 | style="text-align: left;"| 450,295 |- id="summary" ! style="text-align: center;"| Total ! style="text-align: left;"| ! id="total-population" style="text-align: left;"| 27,376,022 ! id="total-area" style="text-align: left;"| 1,258,336 |} ================================================ FILE: test/tables/nordics.native ================================================ [Table ("nordics",[],[("source","wikipedia")]) (Caption (Just [Str "Nordic",Space,Str "countries"]) [Para [Str "States",Space,Str "belonging",Space,Str "to",Space,Str "the",Space,Emph [Str "Nordics."]]]) [(AlignCenter,ColWidth 0.3) ,(AlignLeft,ColWidth 0.3) ,(AlignLeft,ColWidth 0.2) ,(AlignLeft,ColWidth 0.2)] (TableHead ("",["simple-head"],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Plain [Str "Name"]] ,Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Plain [Str "Capital"]] ,Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Plain [Str "Population",LineBreak,Str "(in",Space,Str "2018)"]] ,Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Plain [Str "Area",LineBreak,Str "(in",Space,Str "km",Superscript [Str "2"],Str ")"]]]]) [(TableBody ("",["souvereign-states"],[]) (RowHeadColumns 1) [] [Row ("",["country"],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Denmark"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Copenhagen"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5,809,502"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "43,094"]]] ,Row ("",["country"],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Finland"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Helsinki"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5,537,364"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "338,145"]]] ,Row ("",["country"],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Iceland"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Reykjavik"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "343,518"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "103,000"]]] ,Row ("",["country"],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Norway"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Oslo"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5,372,191"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "323,802"]]] ,Row ("",["country"],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Sweden"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Stockholm"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "10,313,447"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "450,295"]]]])] (TableFoot ("",[],[]) [Row ("summary",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Total"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [] ,Cell ("total-population",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "27,376,022"]] ,Cell ("total-area",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1,258,336"]]]])] ================================================ FILE: test/tables/nordics.typst ================================================ #figure( align(center)[#table( columns: (30%, 30%, 20%, 20%), align: (center,left,left,left,), table.header(table.cell(align: center)[Name], table.cell(align: center)[Capital], table.cell(align: center)[Population \ (in 2018)], table.cell(align: center)[Area \ (in km#super[2])],), table.hline(), [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], table.hline(), table.footer([Total], [], [27,376,022], [1,258,336],), )] , caption: [States belonging to the #emph[Nordics.] ] , kind: table ) ================================================ FILE: test/tables/planets.html4 ================================================

                Data about the planets of our solar system.

                Name Mass (10^24kg) Diameter (km) Density (kg/m^3) Gravity (m/s^2) Length of day (hours) Distance from Sun (10^6km) Mean temperature (C) Number of moons Notes
                Terrestrial planets Mercury 0.330 4,879 5427 3.7 4222.6 57.9 167 0 Closest to the Sun
                Venus 4.87 12,104 5243 8.9 2802.0 108.2 464 0
                Earth 5.97 12,756 5514 9.8 24.0 149.6 15 1 Our world
                Mars 0.642 6,792 3933 3.7 24.7 227.9 -65 2 The red planet
                Jovian planets Gas giants Jupiter 1898 142,984 1326 23.1 9.9 778.6 -110 67 The largest planet
                Saturn 568 120,536 687 9.0 10.7 1433.5 -140 62
                Ice giants Uranus 86.8 51,118 1271 8.7 17.2 2872.5 -195 27
                Neptune 102 49,528 1638 11.0 16.1 4495.1 -200 14
                Dwarf planets Pluto 0.0146 2,370 2095 0.7 153.3 5906.4 -225 5 Declassified as a planet in 2006.
                ================================================ FILE: test/tables/planets.html5 ================================================

                Data about the planets of our solar system.

                Name Mass (10^24kg) Diameter (km) Density (kg/m^3) Gravity (m/s^2) Length of day (hours) Distance from Sun (10^6km) Mean temperature (C) Number of moons Notes
                Terrestrial planets Mercury 0.330 4,879 5427 3.7 4222.6 57.9 167 0 Closest to the Sun
                Venus 4.87 12,104 5243 8.9 2802.0 108.2 464 0
                Earth 5.97 12,756 5514 9.8 24.0 149.6 15 1 Our world
                Mars 0.642 6,792 3933 3.7 24.7 227.9 -65 2 The red planet
                Jovian planets Gas giants Jupiter 1898 142,984 1326 23.1 9.9 778.6 -110 67 The largest planet
                Saturn 568 120,536 687 9.0 10.7 1433.5 -140 62
                Ice giants Uranus 86.8 51,118 1271 8.7 17.2 2872.5 -195 27
                Neptune 102 49,528 1638 11.0 16.1 4495.1 -200 14
                Dwarf planets Pluto 0.0146 2,370 2095 0.7 153.3 5906.4 -225 5 Declassified as a planet in 2006.
                ================================================ FILE: test/tables/planets.jats_archiving ================================================

                Data about the planets of our solar system.

                Name Mass (10^24kg) Diameter (km) Density (kg/m^3) Gravity (m/s^2) Length of day (hours) Distance from Sun (10^6km) Mean temperature (C) Number of moons Notes
                Terrestrial planets Mercury0.330 4,879 5427 3.7 4222.6 57.9 167 0 Closest to the Sun
                Venus4.87 12,104 5243 8.9 2802.0 108.2 464 0
                Earth5.97 12,756 5514 9.8 24.0 149.6 15 1 Our world
                Mars0.642 6,792 3933 3.7 24.7 227.9 -65 2 The red planet
                Jovian planets Gas giants Jupiter1898 142,984 1326 23.1 9.9 778.6 -110 67 The largest planet
                Saturn568 120,536 687 9.0 10.7 1433.5 -140 62
                Ice giants Uranus86.8 51,118 1271 8.7 17.2 2872.5 -195 27
                Neptune102 49,528 1638 11.0 16.1 4495.1 -200 14
                Dwarf planets Pluto0.0146 2,370 2095 0.7 153.3 5906.4 -225 5 Declassified as a planet in 2006.
                ================================================ FILE: test/tables/planets.latex ================================================ \begin{longtable}[]{@{}cclrrrrrrrrl@{}} \caption{Data about the planets of our solar system.}\tabularnewline \toprule\noalign{} \multicolumn{2}{@{}c}{% } & Name & Mass (10\^{}24kg) & Diameter (km) & Density (kg/m\^{}3) & Gravity (m/s\^{}2) & Length of day (hours) & Distance from Sun (10\^{}6km) & Mean temperature (C) & Number of moons & Notes \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} \multicolumn{2}{@{}c}{% } & Name & Mass (10\^{}24kg) & Diameter (km) & Density (kg/m\^{}3) & Gravity (m/s\^{}2) & Length of day (hours) & Distance from Sun (10\^{}6km) & Mean temperature (C) & Number of moons & Notes \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \multicolumn{2}{@{}c}{% \multirow{4}{*}{\centering\arraybackslash Terrestrial planets}} & Mercury & 0.330 & 4,879 & 5427 & 3.7 & 4222.6 & 57.9 & 167 & 0 & Closest to the Sun \\ & & Venus & 4.87 & 12,104 & 5243 & 8.9 & 2802.0 & 108.2 & 464 & 0 & \\ & & Earth & 5.97 & 12,756 & 5514 & 9.8 & 24.0 & 149.6 & 15 & 1 & Our world \\ & & Mars & 0.642 & 6,792 & 3933 & 3.7 & 24.7 & 227.9 & -65 & 2 & The red planet \\ \multirow{4}{*}{\centering\arraybackslash Jovian planets} & \multirow{2}{*}{\centering\arraybackslash Gas giants} & Jupiter & 1898 & 142,984 & 1326 & 23.1 & 9.9 & 778.6 & -110 & 67 & The largest planet \\ & & Saturn & 568 & 120,536 & 687 & 9.0 & 10.7 & 1433.5 & -140 & 62 & \\ & \multirow{2}{*}{\centering\arraybackslash Ice giants} & Uranus & 86.8 & 51,118 & 1271 & 8.7 & 17.2 & 2872.5 & -195 & 27 & \\ & & Neptune & 102 & 49,528 & 1638 & 11.0 & 16.1 & 4495.1 & -200 & 14 & \\ \multicolumn{2}{@{}c}{% Dwarf planets} & Pluto & 0.0146 & 2,370 & 2095 & 0.7 & 153.3 & 5906.4 & -225 & 5 & Declassified as a planet in 2006. \\ \end{longtable} ================================================ FILE: test/tables/planets.markdown ================================================ +------------------+---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | | Name | Mass | Diameter | Density | Gravity | Length | Distance | Mean | Number | Notes | | | | (10\^24kg) | (km) | (kg/m\^3) | (m/s\^2) | of day | from Sun | temperature | of | | | | | | | | | (hours) | (10\^6km) | (C) | moons | | +:=======:+:======:+=========+===========:+=========:+==========:+=========:+========:+==========:+============:+=======:+==============+ | Terrestrial | Mercury | 0.330 | 4,879 | 5427 | 3.7 | 4222.6 | 57.9 | 167 | 0 | Closest to | | planets | | | | | | | | | | the Sun | | +---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | | Venus | 4.87 | 12,104 | 5243 | 8.9 | 2802.0 | 108.2 | 464 | 0 | | | +---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | | Earth | 5.97 | 12,756 | 5514 | 9.8 | 24.0 | 149.6 | 15 | 1 | Our world | | +---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | | Mars | 0.642 | 6,792 | 3933 | 3.7 | 24.7 | 227.9 | -65 | 2 | The red | | | | | | | | | | | | planet | +---------+--------+---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | Jovian | Gas | Jupiter | 1898 | 142,984 | 1326 | 23.1 | 9.9 | 778.6 | -110 | 67 | The largest | | planets | giants | | | | | | | | | | planet | | | +---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | | | Saturn | 568 | 120,536 | 687 | 9.0 | 10.7 | 1433.5 | -140 | 62 | | | +--------+---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | | Ice | Uranus | 86.8 | 51,118 | 1271 | 8.7 | 17.2 | 2872.5 | -195 | 27 | | | | giants | | | | | | | | | | | | | +---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | | | Neptune | 102 | 49,528 | 1638 | 11.0 | 16.1 | 4495.1 | -200 | 14 | | +---------+--------+---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ | Dwarf planets | Pluto | 0.0146 | 2,370 | 2095 | 0.7 | 153.3 | 5906.4 | -225 | 5 | Declassified | | | | | | | | | | | | as a planet | | | | | | | | | | | | in 2006. | +------------------+---------+------------+----------+-----------+----------+---------+-----------+-------------+--------+--------------+ : Data about the planets of our solar system. ================================================ FILE: test/tables/planets.mediawiki ================================================ {| class="wikitable" |+ Data about the planets of our solar system. |- ! colspan="2" style="text-align: center;"| ! Name ! style="text-align: right;"| Mass (10^24kg) ! style="text-align: right;"| Diameter (km) ! style="text-align: right;"| Density (kg/m^3) ! style="text-align: right;"| Gravity (m/s^2) ! style="text-align: right;"| Length of day (hours) ! style="text-align: right;"| Distance from Sun (10^6km) ! style="text-align: right;"| Mean temperature (C) ! style="text-align: right;"| Number of moons ! Notes |- ! rowspan="4" colspan="2" style="text-align: center;"| Terrestrial planets ! Mercury | style="text-align: right;"| 0.330 | style="text-align: right;"| 4,879 | style="text-align: right;"| 5427 | style="text-align: right;"| 3.7 | style="text-align: right;"| 4222.6 | style="text-align: right;"| 57.9 | style="text-align: right;"| 167 | style="text-align: right;"| 0 | Closest to the Sun |- ! Venus | style="text-align: right;"| 4.87 | style="text-align: right;"| 12,104 | style="text-align: right;"| 5243 | style="text-align: right;"| 8.9 | style="text-align: right;"| 2802.0 | style="text-align: right;"| 108.2 | style="text-align: right;"| 464 | style="text-align: right;"| 0 | |- ! Earth | style="text-align: right;"| 5.97 | style="text-align: right;"| 12,756 | style="text-align: right;"| 5514 | style="text-align: right;"| 9.8 | style="text-align: right;"| 24.0 | style="text-align: right;"| 149.6 | style="text-align: right;"| 15 | style="text-align: right;"| 1 | Our world |- ! Mars | style="text-align: right;"| 0.642 | style="text-align: right;"| 6,792 | style="text-align: right;"| 3933 | style="text-align: right;"| 3.7 | style="text-align: right;"| 24.7 | style="text-align: right;"| 227.9 | style="text-align: right;"| -65 | style="text-align: right;"| 2 | The red planet |- ! rowspan="4" style="text-align: center;"| Jovian planets ! rowspan="2" style="text-align: center;"| Gas giants ! Jupiter | style="text-align: right;"| 1898 | style="text-align: right;"| 142,984 | style="text-align: right;"| 1326 | style="text-align: right;"| 23.1 | style="text-align: right;"| 9.9 | style="text-align: right;"| 778.6 | style="text-align: right;"| -110 | style="text-align: right;"| 67 | The largest planet |- ! Saturn | style="text-align: right;"| 568 | style="text-align: right;"| 120,536 | style="text-align: right;"| 687 | style="text-align: right;"| 9.0 | style="text-align: right;"| 10.7 | style="text-align: right;"| 1433.5 | style="text-align: right;"| -140 | style="text-align: right;"| 62 | |- ! rowspan="2" style="text-align: center;"| Ice giants ! Uranus | style="text-align: right;"| 86.8 | style="text-align: right;"| 51,118 | style="text-align: right;"| 1271 | style="text-align: right;"| 8.7 | style="text-align: right;"| 17.2 | style="text-align: right;"| 2872.5 | style="text-align: right;"| -195 | style="text-align: right;"| 27 | |- ! Neptune | style="text-align: right;"| 102 | style="text-align: right;"| 49,528 | style="text-align: right;"| 1638 | style="text-align: right;"| 11.0 | style="text-align: right;"| 16.1 | style="text-align: right;"| 4495.1 | style="text-align: right;"| -200 | style="text-align: right;"| 14 | |- ! colspan="2" style="text-align: center;"| Dwarf planets ! Pluto | style="text-align: right;"| 0.0146 | style="text-align: right;"| 2,370 | style="text-align: right;"| 2095 | style="text-align: right;"| 0.7 | style="text-align: right;"| 153.3 | style="text-align: right;"| 5906.4 | style="text-align: right;"| -225 | style="text-align: right;"| 5 | Declassified as a planet in 2006. |} ================================================ FILE: test/tables/planets.native ================================================ [Table ("",[],[]) (Caption Nothing [Para [Str "Data",Space,Str "about",Space,Str "the",Space,Str "planets",Space,Str "of",Space,Str "our",Space,Str "solar",Space,Str "system."]]) [(AlignCenter,ColWidthDefault) ,(AlignCenter,ColWidthDefault) ,(AlignDefault,ColWidthDefault) ,(AlignRight,ColWidthDefault) ,(AlignRight,ColWidthDefault) ,(AlignRight,ColWidthDefault) ,(AlignRight,ColWidthDefault) ,(AlignRight,ColWidthDefault) ,(AlignRight,ColWidthDefault) ,(AlignRight,ColWidthDefault) ,(AlignRight,ColWidthDefault) ,(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) [] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Name"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Mass",Space,Str "(10^24kg)"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Diameter",Space,Str "(km)"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Density",Space,Str "(kg/m^3)"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Gravity",Space,Str "(m/s^2)"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Length",Space,Str "of",Space,Str "day",Space,Str "(hours)"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Distance",Space,Str "from",Space,Str "Sun",Space,Str "(10^6km)"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Mean",Space,Str "temperature",Space,Str "(C)"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Number",Space,Str "of",Space,Str "moons"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Notes"]]]]) [(TableBody ("",[],[]) (RowHeadColumns 3) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 4) (ColSpan 2) [Plain [Str "Terrestrial",Space,Str "planets"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Mercury"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "0.330"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "4,879"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5427"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "3.7"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "4222.6"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "57.9"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "167"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "0"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Closest",Space,Str "to",Space,Str "the",Space,Str "Sun"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Venus"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "4.87"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12,104"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5243"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "8.9"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2802.0"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "108.2"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "464"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "0"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) []] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Earth"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5.97"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "12,756"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5514"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "9.8"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "24.0"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "149.6"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "15"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Our",Space,Str "world"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Mars"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "0.642"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "6,792"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "3933"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "3.7"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "24.7"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "227.9"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "-65"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "The",Space,Str "red",Space,Str "planet"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 4) (ColSpan 1) [Plain [Str "Jovian",Space,Str "planets"]] ,Cell ("",[],[]) AlignDefault (RowSpan 2) (ColSpan 1) [Plain [Str "Gas",Space,Str "giants"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Jupiter"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1898"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "142,984"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1326"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "23.1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "9.9"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "778.6"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "-110"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "67"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "The",Space,Str "largest",Space,Str "planet"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Saturn"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "568"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "120,536"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "687"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "9.0"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "10.7"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1433.5"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "-140"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "62"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) []] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 2) (ColSpan 1) [Plain [Str "Ice",Space,Str "giants"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Uranus"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "86.8"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "51,118"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1271"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "8.7"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "17.2"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2872.5"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "-195"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "27"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) []] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Neptune"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "102"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "49,528"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "1638"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "11.0"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "16.1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "4495.1"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "-200"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "14"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) []] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) [Plain [Str "Dwarf",Space,Str "planets"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Pluto"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "0.0146"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2,370"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "2095"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "0.7"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "153.3"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5906.4"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "-225"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Declassified",Space,Str "as",Space,Str "a",Space,Str "planet",Space,Str "in",Space,Str "2006."]]]])] (TableFoot ("",[],[]) [])] ================================================ FILE: test/tables/planets.typst ================================================ #figure( align(center)[#table( columns: 12, align: (center,center,auto,right,right,right,right,right,right,right,right,auto,), table.header(table.cell(colspan: 2)[], [Name], [Mass (10^24kg)], [Diameter (km)], [Density (kg/m^3)], [Gravity (m/s^2)], [Length of day (hours)], [Distance from Sun (10^6km)], [Mean temperature (C)], [Number of moons], [Notes],), table.hline(), table.cell(rowspan: 4, colspan: 2)[Terrestrial planets], [Mercury], [0.330], [4,879], [5427], [3.7], [4222.6], [57.9], [167], [0], [Closest to the Sun], [Venus], [4.87], [12,104], [5243], [8.9], [2802.0], [108.2], [464], [0], [], [Earth], [5.97], [12,756], [5514], [9.8], [24.0], [149.6], [15], [1], [Our world], [Mars], [0.642], [6,792], [3933], [3.7], [24.7], [227.9], [-65], [2], [The red planet], table.cell(rowspan: 4)[Jovian planets], table.cell(rowspan: 2)[Gas giants], [Jupiter], [1898], [142,984], [1326], [23.1], [9.9], [778.6], [-110], [67], [The largest planet], [Saturn], [568], [120,536], [687], [9.0], [10.7], [1433.5], [-140], [62], [], table.cell(rowspan: 2)[Ice giants], [Uranus], [86.8], [51,118], [1271], [8.7], [17.2], [2872.5], [-195], [27], [], [Neptune], [102], [49,528], [1638], [11.0], [16.1], [4495.1], [-200], [14], [], table.cell(colspan: 2)[Dwarf planets], [Pluto], [0.0146], [2,370], [2095], [0.7], [153.3], [5906.4], [-225], [5], [Declassified as a planet in 2006.], )] , caption: [Data about the planets of our solar system. ] , kind: table ) ================================================ FILE: test/tables/students.html4 ================================================

                List of Students

                Student ID Name
                Computer Science
                3741255 Jones, Martha
                4077830 Pierce, Benjamin
                5151701 Kirk, James
                Russian Literature
                3971244 Nim, Victor
                Astrophysics
                4100332 Petrov, Alexandra
                4100332 Toyota, Hiroko
                ================================================ FILE: test/tables/students.html5 ================================================

                List of Students

                Student ID Name
                Computer Science
                3741255 Jones, Martha
                4077830 Pierce, Benjamin
                5151701 Kirk, James
                Russian Literature
                3971244 Nim, Victor
                Astrophysics
                4100332 Petrov, Alexandra
                4100332 Toyota, Hiroko
                ================================================ FILE: test/tables/students.jats_archiving ================================================

                List of Students

                Student ID Name
                Computer Science
                3741255 Jones, Martha
                4077830 Pierce, Benjamin
                5151701 Kirk, James
                Russian Literature
                3971244 Nim, Victor
                Astrophysics
                4100332 Petrov, Alexandra
                4100332 Toyota, Hiroko
                ================================================ FILE: test/tables/students.latex ================================================ \begin{longtable}[]{@{} >{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.5000}} >{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{0.5000}}@{}} \caption{List of Students}\label{students}\tabularnewline \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Student ID \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Name \end{minipage} \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Student ID \end{minipage} & \begin{minipage}[b]{\linewidth}\centering Name \end{minipage} \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \multicolumn{2}{@{}>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{1.0000} + 2\tabcolsep}@{}}{% Computer Science} \\ 3741255 & Jones, Martha \\ 4077830 & Pierce, Benjamin \\ 5151701 & Kirk, James \\ \multicolumn{2}{@{}>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{1.0000} + 2\tabcolsep}@{}}{% Russian Literature} \\ 3971244 & Nim, Victor \\ \multicolumn{2}{@{}>{\raggedright\arraybackslash}p{(\linewidth - 2\tabcolsep) * \real{1.0000} + 2\tabcolsep}@{}}{% Astrophysics} \\ 4100332 & Petrov, Alexandra \\ 4100332 & Toyota, Hiroko \\ \end{longtable} ================================================ FILE: test/tables/students.markdown ================================================ +-----------------------------------------+-----------------------------------------+ | Student ID | Name | +:========================================+:========================================+ | Computer Science | +-----------------------------------------+-----------------------------------------+ | 3741255 | Jones, Martha | +-----------------------------------------+-----------------------------------------+ | 4077830 | Pierce, Benjamin | +-----------------------------------------+-----------------------------------------+ | 5151701 | Kirk, James | +-----------------------------------------+-----------------------------------------+ | Russian Literature | +-----------------------------------------+-----------------------------------------+ | 3971244 | Nim, Victor | +-----------------------------------------+-----------------------------------------+ | Astrophysics | +-----------------------------------------+-----------------------------------------+ | 4100332 | Petrov, Alexandra | +-----------------------------------------+-----------------------------------------+ | 4100332 | Toyota, Hiroko | +-----------------------------------------+-----------------------------------------+ : List of Students {#students source="mdn"} ================================================ FILE: test/tables/students.mediawiki ================================================ {| id="students" class="wikitable" source="mdn" |+ List of Students |- ! style="text-align: center;"| Student ID ! style="text-align: center;"| Name |- ! colspan="2" style="text-align: left;"| Computer Science |- | style="text-align: left;"| 3741255 | style="text-align: left;"| Jones, Martha |- | style="text-align: left;"| 4077830 | style="text-align: left;"| Pierce, Benjamin |- | style="text-align: left;"| 5151701 | style="text-align: left;"| Kirk, James |- ! colspan="2" style="text-align: left;"| Russian Literature |- | style="text-align: left;"| 3971244 | style="text-align: left;"| Nim, Victor |- ! colspan="2" style="text-align: left;"| Astrophysics |- | style="text-align: left;"| 4100332 | style="text-align: left;"| Petrov, Alexandra |- | style="text-align: left;"| 4100332 | style="text-align: left;"| Toyota, Hiroko |} ================================================ FILE: test/tables/students.native ================================================ [Table ("students",[],[("source","mdn")]) (Caption Nothing [Para [Str "List",Space,Str "of",Space,Str "Students"]]) [(AlignLeft,ColWidth 0.5) ,(AlignLeft,ColWidth 0.5)] (TableHead ("",[],[]) [Row ("",[],[]) [Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Plain [Str "Student",Space,Str "ID"]] ,Cell ("",[],[]) AlignCenter (RowSpan 1) (ColSpan 1) [Plain [Str "Name"]]]]) [(TableBody ("",["souvereign-states"],[]) (RowHeadColumns 0) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) [Plain [Str "Computer",Space,Str "Science"]]]] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "3741255"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Jones,",Space,Str "Martha"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "4077830"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Pierce,",Space,Str "Benjamin"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "5151701"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Kirk,",Space,Str "James"]]]]) ,(TableBody ("",[],[]) (RowHeadColumns 0) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) [Plain [Str "Russian",Space,Str "Literature"]]]] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "3971244"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Nim,",Space,Str "Victor"]]]]) ,(TableBody ("",[],[]) (RowHeadColumns 0) [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 2) [Plain [Str "Astrophysics"]]]] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "4100332"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Petrov,",Space,Str "Alexandra"]]] ,Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "4100332"]] ,Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Toyota,",Space,Str "Hiroko"]]]])] (TableFoot ("",[],[]) [])] ================================================ FILE: test/tables/students.typst ================================================ #figure( align(center)[#table( columns: (50%, 50%), align: (left,left,), table.header(table.cell(align: center)[Student ID], table.cell(align: center)[Name],), table.hline(), table.cell(colspan: 2)[Computer Science], table.hline(), [3741255], [Jones, Martha], [4077830], [Pierce, Benjamin], [5151701], [Kirk, James], table.cell(colspan: 2)[Russian Literature], table.hline(), [3971244], [Nim, Victor], table.cell(colspan: 2)[Astrophysics], table.hline(), [4100332], [Petrov, Alexandra], [4100332], [Toyota, Hiroko], )] , caption: [List of Students ] , kind: table ) ================================================ FILE: test/tables-rstsubset.native ================================================ [ Para [ Str "Simple" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "indented" , Space , Str "two" , Space , Str "spaces:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Here\8217s" , Space , Str "the" , Space , Str "caption." , Space , Str "It" , Space , Str "may" , Space , Str "span" , Space , Str "multiple" , Space , Str "lines." ] ]) [ ( AlignDefault , ColWidth 0.15 ) , ( AlignDefault , ColWidth 0.1375 ) , ( AlignDefault , ColWidth 0.1625 ) , ( AlignDefault , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , SoftBreak , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , SoftBreak , Str "spans" , Space , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , SoftBreak , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.15 ) , ( AlignDefault , ColWidth 0.1375 ) , ( AlignDefault , ColWidth 0.1625 ) , ( AlignDefault , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , SoftBreak , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , SoftBreak , Str "spans" , Space , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , SoftBreak , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidth 0.15 ) , ( AlignDefault , ColWidth 0.1375 ) , ( AlignDefault , ColWidth 0.1625 ) , ( AlignDefault , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , SoftBreak , Str "spans" , Space , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , SoftBreak , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/tables.asciidoc ================================================ Simple table with caption: .Demonstration of simple table syntax. [cols=">,<,^,",options="header",] |=== |Right |Left |Center |Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 |=== Simple table without caption: [cols=">,<,^,",options="header",] |=== |Right |Left |Center |Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 |=== Simple table indented two spaces: .Demonstration of simple table syntax. [cols=">,<,^,",options="header",] |=== |Right |Left |Center |Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 |=== Multiline table with caption: .Here’s the caption. It may span multiple lines. [width="80%",cols="^20%,<17%,>20%,<43%",options="header",] |=== |Centered Header |Left Aligned |Right Aligned |Default aligned |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. |=== Multiline table without caption: [width="80%",cols="^20%,<17%,>20%,<43%",options="header",] |=== |Centered Header |Left Aligned |Right Aligned |Default aligned |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. |=== Table without column headers: [cols=">,<,^,>",] |=== |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 |=== Multiline table without column headers: [width="80%",cols="^20%,<17%,>20%,43%",] |=== |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. |=== ================================================ FILE: test/tables.asciidoc_legacy ================================================ Simple table with caption: .Demonstration of simple table syntax. [cols=">,<,^,",options="header",] |=== |Right |Left |Center |Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 |=== Simple table without caption: [cols=">,<,^,",options="header",] |=== |Right |Left |Center |Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 |=== Simple table indented two spaces: .Demonstration of simple table syntax. [cols=">,<,^,",options="header",] |=== |Right |Left |Center |Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 |=== Multiline table with caption: .Here’s the caption. It may span multiple lines. [width="80%",cols="^20%,<17%,>20%,<43%",options="header",] |=== |Centered Header |Left Aligned |Right Aligned |Default aligned |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. |=== Multiline table without caption: [width="80%",cols="^20%,<17%,>20%,<43%",options="header",] |=== |Centered Header |Left Aligned |Right Aligned |Default aligned |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. |=== Table without column headers: [cols=">,<,^,>",] |=== |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 |=== Multiline table without column headers: [width="80%",cols="^20%,<17%,>20%,43%",] |=== |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. |=== ================================================ FILE: test/tables.bbcode ================================================ Simple table with caption: Demonstration of simple table syntax. [table] [tr][th]Right[/th][th]Left[/th][th]Center[/th][th]Default[/th][/tr] [tr][td]12[/td][td]12[/td][td]12[/td][td]12[/td][/tr] [tr][td]123[/td][td]123[/td][td]123[/td][td]123[/td][/tr] [tr][td]1[/td][td]1[/td][td]1[/td][td]1[/td][/tr] [/table] Simple table without caption: [table] [tr][th]Right[/th][th]Left[/th][th]Center[/th][th]Default[/th][/tr] [tr][td]12[/td][td]12[/td][td]12[/td][td]12[/td][/tr] [tr][td]123[/td][td]123[/td][td]123[/td][td]123[/td][/tr] [tr][td]1[/td][td]1[/td][td]1[/td][td]1[/td][/tr] [/table] Simple table indented two spaces: Demonstration of simple table syntax. [table] [tr][th]Right[/th][th]Left[/th][th]Center[/th][th]Default[/th][/tr] [tr][td]12[/td][td]12[/td][td]12[/td][td]12[/td][/tr] [tr][td]123[/td][td]123[/td][td]123[/td][td]123[/td][/tr] [tr][td]1[/td][td]1[/td][td]1[/td][td]1[/td][/tr] [/table] Multiline table with caption: Here's the caption. It may span multiple lines. [table] [tr][th]Centered Header[/th][th]Left Aligned[/th][th]Right Aligned[/th][th]Default aligned[/th][/tr] [tr][td]First[/td][td]row[/td][td]12.0[/td][td]Example of a row that spans multiple lines.[/td][/tr] [tr][td]Second[/td][td]row[/td][td]5.0[/td][td]Here's another one. Note the blank line between rows.[/td][/tr] [/table] Multiline table without caption: [table] [tr][th]Centered Header[/th][th]Left Aligned[/th][th]Right Aligned[/th][th]Default aligned[/th][/tr] [tr][td]First[/td][td]row[/td][td]12.0[/td][td]Example of a row that spans multiple lines.[/td][/tr] [tr][td]Second[/td][td]row[/td][td]5.0[/td][td]Here's another one. Note the blank line between rows.[/td][/tr] [/table] Table without column headers: [table] [tr][td]12[/td][td]12[/td][td]12[/td][td]12[/td][/tr] [tr][td]123[/td][td]123[/td][td]123[/td][td]123[/td][/tr] [tr][td]1[/td][td]1[/td][td]1[/td][td]1[/td][/tr] [/table] Multiline table without column headers: [table] [tr][td]First[/td][td]row[/td][td]12.0[/td][td]Example of a row that spans multiple lines.[/td][/tr] [tr][td]Second[/td][td]row[/td][td]5.0[/td][td]Here's another one. Note the blank line between rows.[/td][/tr] [/table] ================================================ FILE: test/tables.context ================================================ Simple table with caption: \startplacetable[title={Demonstration of simple table syntax.}] \startxtable \startxtablehead[head] \startxrowgroup[lastrow] \startxrow \startxcell[align=left] Right \stopxcell \startxcell[align=right] Left \stopxcell \startxcell[align=middle] Center \stopxcell \startxcell Default \stopxcell \stopxrow \stopxrowgroup \stopxtablehead \startxtablebody[body] \startxrow \startxcell[align=left] 12 \stopxcell \startxcell[align=right] 12 \stopxcell \startxcell[align=middle] 12 \stopxcell \startxcell 12 \stopxcell \stopxrow \startxrow \startxcell[align=left] 123 \stopxcell \startxcell[align=right] 123 \stopxcell \startxcell[align=middle] 123 \stopxcell \startxcell 123 \stopxcell \stopxrow \startxrowgroup[lastrow] \startxrow \startxcell[align=left] 1 \stopxcell \startxcell[align=right] 1 \stopxcell \startxcell[align=middle] 1 \stopxcell \startxcell 1 \stopxcell \stopxrow \stopxrowgroup \stopxtablebody \startxtablefoot[foot] \stopxtablefoot \stopxtable \stopplacetable Simple table without caption: \startplacetable[location=none] \startxtable \startxtablehead[head] \startxrowgroup[lastrow] \startxrow \startxcell[align=left] Right \stopxcell \startxcell[align=right] Left \stopxcell \startxcell[align=middle] Center \stopxcell \startxcell Default \stopxcell \stopxrow \stopxrowgroup \stopxtablehead \startxtablebody[body] \startxrow \startxcell[align=left] 12 \stopxcell \startxcell[align=right] 12 \stopxcell \startxcell[align=middle] 12 \stopxcell \startxcell 12 \stopxcell \stopxrow \startxrow \startxcell[align=left] 123 \stopxcell \startxcell[align=right] 123 \stopxcell \startxcell[align=middle] 123 \stopxcell \startxcell 123 \stopxcell \stopxrow \startxrowgroup[lastrow] \startxrow \startxcell[align=left] 1 \stopxcell \startxcell[align=right] 1 \stopxcell \startxcell[align=middle] 1 \stopxcell \startxcell 1 \stopxcell \stopxrow \stopxrowgroup \stopxtablebody \startxtablefoot[foot] \stopxtablefoot \stopxtable \stopplacetable Simple table indented two spaces: \startplacetable[title={Demonstration of simple table syntax.}] \startxtable \startxtablehead[head] \startxrowgroup[lastrow] \startxrow \startxcell[align=left] Right \stopxcell \startxcell[align=right] Left \stopxcell \startxcell[align=middle] Center \stopxcell \startxcell Default \stopxcell \stopxrow \stopxrowgroup \stopxtablehead \startxtablebody[body] \startxrow \startxcell[align=left] 12 \stopxcell \startxcell[align=right] 12 \stopxcell \startxcell[align=middle] 12 \stopxcell \startxcell 12 \stopxcell \stopxrow \startxrow \startxcell[align=left] 123 \stopxcell \startxcell[align=right] 123 \stopxcell \startxcell[align=middle] 123 \stopxcell \startxcell 123 \stopxcell \stopxrow \startxrowgroup[lastrow] \startxrow \startxcell[align=left] 1 \stopxcell \startxcell[align=right] 1 \stopxcell \startxcell[align=middle] 1 \stopxcell \startxcell 1 \stopxcell \stopxrow \stopxrowgroup \stopxtablebody \startxtablefoot[foot] \stopxtablefoot \stopxtable \stopplacetable Multiline table with caption: \startplacetable[title={Here's the caption. It may span multiple lines.}] \startxtable \startxtablehead[head] \startxrowgroup[lastrow] \startxrow \startxcell[align=middle,width={0.15\textwidth}] Centered Header \stopxcell \startxcell[align=right,width={0.14\textwidth}] Left Aligned \stopxcell \startxcell[align=left,width={0.16\textwidth}] Right Aligned \stopxcell \startxcell[align=right,width={0.35\textwidth}] Default aligned \stopxcell \stopxrow \stopxrowgroup \stopxtablehead \startxtablebody[body] \startxrow \startxcell[align=middle,width={0.15\textwidth}] First \stopxcell \startxcell[align=right,width={0.14\textwidth}] row \stopxcell \startxcell[align=left,width={0.16\textwidth}] 12.0 \stopxcell \startxcell[align=right,width={0.35\textwidth}] Example of a row that spans multiple lines. \stopxcell \stopxrow \startxrowgroup[lastrow] \startxrow \startxcell[align=middle,width={0.15\textwidth}] Second \stopxcell \startxcell[align=right,width={0.14\textwidth}] row \stopxcell \startxcell[align=left,width={0.16\textwidth}] 5.0 \stopxcell \startxcell[align=right,width={0.35\textwidth}] Here's another one. Note the blank line between rows. \stopxcell \stopxrow \stopxrowgroup \stopxtablebody \startxtablefoot[foot] \stopxtablefoot \stopxtable \stopplacetable Multiline table without caption: \startplacetable[location=none] \startxtable \startxtablehead[head] \startxrowgroup[lastrow] \startxrow \startxcell[align=middle,width={0.15\textwidth}] Centered Header \stopxcell \startxcell[align=right,width={0.14\textwidth}] Left Aligned \stopxcell \startxcell[align=left,width={0.16\textwidth}] Right Aligned \stopxcell \startxcell[align=right,width={0.35\textwidth}] Default aligned \stopxcell \stopxrow \stopxrowgroup \stopxtablehead \startxtablebody[body] \startxrow \startxcell[align=middle,width={0.15\textwidth}] First \stopxcell \startxcell[align=right,width={0.14\textwidth}] row \stopxcell \startxcell[align=left,width={0.16\textwidth}] 12.0 \stopxcell \startxcell[align=right,width={0.35\textwidth}] Example of a row that spans multiple lines. \stopxcell \stopxrow \startxrowgroup[lastrow] \startxrow \startxcell[align=middle,width={0.15\textwidth}] Second \stopxcell \startxcell[align=right,width={0.14\textwidth}] row \stopxcell \startxcell[align=left,width={0.16\textwidth}] 5.0 \stopxcell \startxcell[align=right,width={0.35\textwidth}] Here's another one. Note the blank line between rows. \stopxcell \stopxrow \stopxrowgroup \stopxtablebody \startxtablefoot[foot] \stopxtablefoot \stopxtable \stopplacetable Table without column headers: \startplacetable[location=none] \startxtable \startxtablehead[head] \stopxtablehead \startxtablebody[body] \startxrow \startxcell[align=left] 12 \stopxcell \startxcell[align=right] 12 \stopxcell \startxcell[align=middle] 12 \stopxcell \startxcell[align=left] 12 \stopxcell \stopxrow \startxrow \startxcell[align=left] 123 \stopxcell \startxcell[align=right] 123 \stopxcell \startxcell[align=middle] 123 \stopxcell \startxcell[align=left] 123 \stopxcell \stopxrow \startxrowgroup[lastrow] \startxrow \startxcell[align=left] 1 \stopxcell \startxcell[align=right] 1 \stopxcell \startxcell[align=middle] 1 \stopxcell \startxcell[align=left] 1 \stopxcell \stopxrow \stopxrowgroup \stopxtablebody \startxtablefoot[foot] \stopxtablefoot \stopxtable \stopplacetable Multiline table without column headers: \startplacetable[location=none] \startxtable \startxtablehead[head] \stopxtablehead \startxtablebody[body] \startxrow \startxcell[align=middle,width={0.15\textwidth}] First \stopxcell \startxcell[align=right,width={0.14\textwidth}] row \stopxcell \startxcell[align=left,width={0.16\textwidth}] 12.0 \stopxcell \startxcell[width={0.35\textwidth}] Example of a row that spans multiple lines. \stopxcell \stopxrow \startxrowgroup[lastrow] \startxrow \startxcell[align=middle,width={0.15\textwidth}] Second \stopxcell \startxcell[align=right,width={0.14\textwidth}] row \stopxcell \startxcell[align=left,width={0.16\textwidth}] 5.0 \stopxcell \startxcell[width={0.35\textwidth}] Here's another one. Note the blank line between rows. \stopxcell \stopxrow \stopxrowgroup \stopxtablebody \startxtablefoot[foot] \stopxtablefoot \stopxtable \stopplacetable ================================================ FILE: test/tables.djot ================================================ Simple table with caption: | Right | Left | Center | Default | |------:|:-----|:------:|-------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | ^ Demonstration of simple table syntax. Simple table without caption: | Right | Left | Center | Default | |------:|:-----|:------:|-------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Simple table indented two spaces: | Right | Left | Center | Default | |------:|:-----|:------:|-------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | ^ Demonstration of simple table syntax. Multiline table with caption: | Centered Header | Left Aligned | Right Aligned | Default aligned | |:---------------:|:-------------|--------------:|:------------------------------------------------------| | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here's another one. Note the blank line between rows. | ^ Here's the caption. It may span multiple lines. Multiline table without caption: | Centered Header | Left Aligned | Right Aligned | Default aligned | |:---------------:|:-------------|--------------:|:------------------------------------------------------| | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here's another one. Note the blank line between rows. | Table without column headers: |----:|:----|:---:|----:| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Multiline table without column headers: |:------:|:----|-----:|-----------------------------------------------------| | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here's another one. Note the blank line between rows. | ================================================ FILE: test/tables.docbook4 ================================================ Simple table with caption: Demonstration of simple table syntax. Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1
                Simple table without caption: Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1 Simple table indented two spaces: Demonstration of simple table syntax. Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1
                Multiline table with caption: Here’s the caption. It may span multiple lines. Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows.
                Multiline table without caption: Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. Table without column headers: 12 12 12 12 123 123 123 123 1 1 1 1 Multiline table without column headers: First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. ================================================ FILE: test/tables.docbook5 ================================================ Simple table with caption: Demonstration of simple table syntax. Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1
                Simple table without caption: Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1 Simple table indented two spaces: Demonstration of simple table syntax. Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1
                Multiline table with caption: Here’s the caption. It may span multiple lines. Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows.
                Multiline table without caption: Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. Table without column headers: 12 12 12 12 123 123 123 123 1 1 1 1 Multiline table without column headers: First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. ================================================ FILE: test/tables.dokuwiki ================================================ Simple table with caption: Demonstration of simple table syntax. ^ Right^Left ^ Center ^Default^ | 12|12 | 12 |12 | | 123|123 | 123 |123 | | 1|1 | 1 |1 | Simple table without caption: ^ Right^Left ^ Center ^Default^ | 12|12 | 12 |12 | | 123|123 | 123 |123 | | 1|1 | 1 |1 | Simple table indented two spaces: Demonstration of simple table syntax. ^ Right^Left ^ Center ^Default^ | 12|12 | 12 |12 | | 123|123 | 123 |123 | | 1|1 | 1 |1 | Multiline table with caption: Here’s the caption. It may span multiple lines. ^ Centered Header ^Left Aligned ^ Right Aligned^Default aligned ^ | First |row | 12.0|Example of a row that spans multiple lines. | | Second |row | 5.0|Here’s another one. Note the blank line between rows. | Multiline table without caption: ^ Centered Header ^Left Aligned ^ Right Aligned^Default aligned ^ | First |row | 12.0|Example of a row that spans multiple lines. | | Second |row | 5.0|Here’s another one. Note the blank line between rows. | Table without column headers: | 12|12 | 12 | 12| | 123|123 | 123 | 123| | 1|1 | 1 | 1| Multiline table without column headers: | First |row | 12.0|Example of a row that spans multiple lines. | | Second |row | 5.0|Here’s another one. Note the blank line between rows.| ================================================ FILE: test/tables.fb2 ================================================ unrecognisedpandoc<p />

                Simple table with caption:

                RightLeftCenterDefault
                12121212
                123123123123
                1111

                Demonstration of simple table syntax.

                Simple table without caption:

                RightLeftCenterDefault
                12121212
                123123123123
                1111

                Simple table indented two spaces:

                RightLeftCenterDefault
                12121212
                123123123123
                1111

                Demonstration of simple table syntax.

                Multiline table with caption:

                Centered HeaderLeft AlignedRight AlignedDefault aligned
                Firstrow12.0Example of a row that spans multiple lines.
                Secondrow5.0Here’s another one. Note the blank line between rows.

                Here’s the caption. It may span multiple lines.

                Multiline table without caption:

                Centered HeaderLeft AlignedRight AlignedDefault aligned
                Firstrow12.0Example of a row that spans multiple lines.
                Secondrow5.0Here’s another one. Note the blank line between rows.

                Table without column headers:

                12121212
                123123123123
                1111

                Multiline table without column headers:

                Firstrow12.0Example of a row that spans multiple lines.
                Secondrow5.0Here’s another one. Note the blank line between rows.

                ================================================ FILE: test/tables.haddock ================================================ Simple table with caption: +-------+------+--------+---------+ | Right | Left | Center | Default | +======:+:=====+:======:+=========+ | 12 | 12 | 12 | 12 | +-------+------+--------+---------+ | 123 | 123 | 123 | 123 | +-------+------+--------+---------+ | 1 | 1 | 1 | 1 | +-------+------+--------+---------+ Demonstration of simple table syntax. Simple table without caption: +-------+------+--------+---------+ | Right | Left | Center | Default | +======:+:=====+:======:+=========+ | 12 | 12 | 12 | 12 | +-------+------+--------+---------+ | 123 | 123 | 123 | 123 | +-------+------+--------+---------+ | 1 | 1 | 1 | 1 | +-------+------+--------+---------+ Simple table indented two spaces: +-------+------+--------+---------+ | Right | Left | Center | Default | +======:+:=====+:======:+=========+ | 12 | 12 | 12 | 12 | +-------+------+--------+---------+ | 123 | 123 | 123 | 123 | +-------+------+--------+---------+ | 1 | 1 | 1 | 1 | +-------+------+--------+---------+ Demonstration of simple table syntax. Multiline table with caption: +-----------+----------+------------+---------------------------+ | Centered | Left | Right | Default aligned | | Header | Aligned | Aligned | | +:=========:+:=========+===========:+:==========================+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ Here’s the caption. It may span multiple lines. Multiline table without caption: +-----------+----------+------------+---------------------------+ | Centered | Left | Right | Default aligned | | Header | Aligned | Aligned | | +:=========:+:=========+===========:+:==========================+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ Table without column headers: +----:+:----+:---:+----:+ | 12 | 12 | 12 | 12 | +-----+-----+-----+-----+ | 123 | 123 | 123 | 123 | +-----+-----+-----+-----+ | 1 | 1 | 1 | 1 | +-----+-----+-----+-----+ Multiline table without column headers: +:---------:+:---------+-----------:+---------------------------+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ ================================================ FILE: test/tables.html4 ================================================

                Simple table with caption:

                Demonstration of simple table syntax.
                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table without caption:

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table indented two spaces:

                Demonstration of simple table syntax.
                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table with caption:

                Here’s the caption. It may span multiple lines.
                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Multiline table without caption:

                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Table without column headers:

                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table without column headers:

                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.
                ================================================ FILE: test/tables.html5 ================================================

                Simple table with caption:

                Demonstration of simple table syntax.
                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table without caption:

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table indented two spaces:

                Demonstration of simple table syntax.
                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table with caption:

                Here’s the caption. It may span multiple lines.
                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Multiline table without caption:

                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Table without column headers:

                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table without column headers:

                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.
                ================================================ FILE: test/tables.icml ================================================ Simple table with caption:
                Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1
                Demonstration of simple table syntax.
                Simple table without caption:
                Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1

                Simple table indented two spaces:
                Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1
                Demonstration of simple table syntax.
                Multiline table with caption:
                Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows.
                Here’s the caption. It may span multiple lines.
                Multiline table without caption:
                Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows.

                Table without column headers:
                12 12 12 12 123 123 123 123 1 1 1 1

                Multiline table without column headers:
                First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows.
                ================================================ FILE: test/tables.jats_archiving ================================================

                Simple table with caption:

                Demonstration of simple table syntax.

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table without caption:

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table indented two spaces:

                Demonstration of simple table syntax.

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table with caption:

                Here’s the caption. It may span multiple lines.

                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Multiline table without caption:

                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Table without column headers:

                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table without column headers:

                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.
                ================================================ FILE: test/tables.jats_articleauthoring ================================================

                Simple table with caption:

                Demonstration of simple table syntax.

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table without caption:

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table indented two spaces:

                Demonstration of simple table syntax.

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table with caption:

                Here’s the caption. It may span multiple lines.

                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Multiline table without caption:

                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Table without column headers:

                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table without column headers:

                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.
                ================================================ FILE: test/tables.jats_publishing ================================================

                Simple table with caption:

                Demonstration of simple table syntax.

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table without caption:

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Simple table indented two spaces:

                Demonstration of simple table syntax.

                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table with caption:

                Here’s the caption. It may span multiple lines.

                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Multiline table without caption:

                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.

                Table without column headers:

                12 12 12 12
                123 123 123 123
                1 1 1 1

                Multiline table without column headers:

                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here’s another one. Note the blank line between rows.
                ================================================ FILE: test/tables.jira ================================================ Simple table with caption: || Right || Left || Center || Default || | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Simple table without caption: || Right || Left || Center || Default || | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Simple table indented two spaces: || Right || Left || Center || Default || | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Multiline table with caption: || Centered Header || Left Aligned || Right Aligned || Default aligned || | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here’s another one. Note the blank line between rows. | Multiline table without caption: || Centered Header || Left Aligned || Right Aligned || Default aligned || | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here’s another one. Note the blank line between rows. | Table without column headers: | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Multiline table without column headers: | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here’s another one. Note the blank line between rows. | ================================================ FILE: test/tables.latex ================================================ Simple table with caption: \begin{longtable}[]{@{}rlcl@{}} \caption{Demonstration of simple table syntax.}\tabularnewline \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot 12 & 12 & 12 & 12 \\ 123 & 123 & 123 & 123 \\ 1 & 1 & 1 & 1 \\ \end{longtable} Simple table without caption: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}rlcl@{}} \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot 12 & 12 & 12 & 12 \\ 123 & 123 & 123 & 123 \\ 1 & 1 & 1 & 1 \\ \end{longtable} } Simple table indented two spaces: \begin{longtable}[]{@{}rlcl@{}} \caption{Demonstration of simple table syntax.}\tabularnewline \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} Right & Left & Center & Default \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot 12 & 12 & 12 & 12 \\ 123 & 123 & 123 & 123 \\ 1 & 1 & 1 & 1 \\ \end{longtable} Multiline table with caption: \begin{longtable}[]{@{} >{\centering\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1500}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1375}} >{\raggedleft\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1625}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.3500}}@{}} \caption{Here's the caption. It may span multiple lines.}\tabularnewline \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Centered Header \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright Left Aligned \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedleft Right Aligned \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright Default aligned \end{minipage} \\ \midrule\noalign{} \endfirsthead \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Centered Header \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright Left Aligned \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedleft Right Aligned \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright Default aligned \end{minipage} \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot First & row & 12.0 & Example of a row that spans multiple lines. \\ Second & row & 5.0 & Here's another one. Note the blank line between rows. \\ \end{longtable} Multiline table without caption: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{} >{\centering\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1500}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1375}} >{\raggedleft\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1625}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.3500}}@{}} \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Centered Header \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright Left Aligned \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedleft Right Aligned \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright Default aligned \end{minipage} \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot First & row & 12.0 & Example of a row that spans multiple lines. \\ Second & row & 5.0 & Here's another one. Note the blank line between rows. \\ \end{longtable} } Table without column headers: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}rlcr@{}} \toprule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot 12 & 12 & 12 & 12 \\ 123 & 123 & 123 & 123 \\ 1 & 1 & 1 & 1 \\ \end{longtable} } Multiline table without column headers: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{} >{\centering\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1500}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1375}} >{\raggedleft\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.1625}} >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.3500}}@{}} \toprule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot First & row & 12.0 & Example of a row that spans multiple lines. \\ Second & row & 5.0 & Here's another one. Note the blank line between rows. \\ \end{longtable} } ================================================ FILE: test/tables.man ================================================ .PP Simple table with caption: .PP Demonstration of simple table syntax. .TS tab(@); r l c l. T{ Right T}@T{ Left T}@T{ Center T}@T{ Default T} _ T{ 12 T}@T{ 12 T}@T{ 12 T}@T{ 12 T} T{ 123 T}@T{ 123 T}@T{ 123 T}@T{ 123 T} T{ 1 T}@T{ 1 T}@T{ 1 T}@T{ 1 T} .TE .PP Simple table without caption: .PP .TS tab(@); r l c l. T{ Right T}@T{ Left T}@T{ Center T}@T{ Default T} _ T{ 12 T}@T{ 12 T}@T{ 12 T}@T{ 12 T} T{ 123 T}@T{ 123 T}@T{ 123 T}@T{ 123 T} T{ 1 T}@T{ 1 T}@T{ 1 T}@T{ 1 T} .TE .PP Simple table indented two spaces: .PP Demonstration of simple table syntax. .TS tab(@); r l c l. T{ Right T}@T{ Left T}@T{ Center T}@T{ Default T} _ T{ 12 T}@T{ 12 T}@T{ 12 T}@T{ 12 T} T{ 123 T}@T{ 123 T}@T{ 123 T}@T{ 123 T} T{ 1 T}@T{ 1 T}@T{ 1 T}@T{ 1 T} .TE .PP Multiline table with caption: .PP Here\(cqs the caption. It may span multiple lines. .TS tab(@); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). T{ Centered Header T}@T{ Left Aligned T}@T{ Right Aligned T}@T{ Default aligned T} _ T{ First T}@T{ row T}@T{ 12.0 T}@T{ Example of a row that spans multiple lines. T} T{ Second T}@T{ row T}@T{ 5.0 T}@T{ Here\(cqs another one. Note the blank line between rows. T} .TE .PP Multiline table without caption: .PP .TS tab(@); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). T{ Centered Header T}@T{ Left Aligned T}@T{ Right Aligned T}@T{ Default aligned T} _ T{ First T}@T{ row T}@T{ 12.0 T}@T{ Example of a row that spans multiple lines. T} T{ Second T}@T{ row T}@T{ 5.0 T}@T{ Here\(cqs another one. Note the blank line between rows. T} .TE .PP Table without column headers: .PP .TS tab(@); r l c r. T{ 12 T}@T{ 12 T}@T{ 12 T}@T{ 12 T} T{ 123 T}@T{ 123 T}@T{ 123 T}@T{ 123 T} T{ 1 T}@T{ 1 T}@T{ 1 T}@T{ 1 T} .TE .PP Multiline table without column headers: .PP .TS tab(@); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). T{ First T}@T{ row T}@T{ 12.0 T}@T{ Example of a row that spans multiple lines. T} T{ Second T}@T{ row T}@T{ 5.0 T}@T{ Here\(cqs another one. Note the blank line between rows. T} .TE ================================================ FILE: test/tables.markdown ================================================ Simple table with caption: Right Left Center Default ------- ------ -------- --------- 12 12 12 12 123 123 123 123 1 1 1 1 : Demonstration of simple table syntax. Simple table without caption: Right Left Center Default ------- ------ -------- --------- 12 12 12 12 123 123 123 123 1 1 1 1 Simple table indented two spaces: Right Left Center Default ------- ------ -------- --------- 12 12 12 12 123 123 123 123 1 1 1 1 : Demonstration of simple table syntax. Multiline table with caption: --------------------------------------------------------------- Centered Left Right Default aligned Header Aligned Aligned ----------- ---------- ------------ --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. --------------------------------------------------------------- : Here's the caption. It may span multiple lines. Multiline table without caption: --------------------------------------------------------------- Centered Left Right Default aligned Header Aligned Aligned ----------- ---------- ------------ --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. --------------------------------------------------------------- Table without column headers: ----- ----- ----- ----- 12 12 12 12 123 123 123 123 1 1 1 1 ----- ----- ----- ----- Multiline table without column headers: ----------- ---------- ------------ --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. ----------- ---------- ------------ --------------------------- ================================================ FILE: test/tables.markua ================================================ Simple table with caption: | Right | Left | Center | Default | |------:|:-----|:------:|---------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Demonstration of simple table syntax. Simple table without caption: | Right | Left | Center | Default | |------:|:-----|:------:|---------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Simple table indented two spaces: | Right | Left | Center | Default | |------:|:-----|:------:|---------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Demonstration of simple table syntax. Multiline table with caption: | Centered Header | Left Aligned | Right Aligned | Default aligned | |:--:|:---|---:|:---| | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here’s another one. Note the blank line between rows. | Here’s the caption. It may span multiple lines. Multiline table without caption: | Centered Header | Left Aligned | Right Aligned | Default aligned | |:--:|:---|---:|:---| | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here’s another one. Note the blank line between rows. | Table without column headers: | | | | | |----:|:----|:---:|----:| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Multiline table without column headers: | | | | | |:------:|:----|-----:|-------------------------------------------------------| | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here’s another one. Note the blank line between rows. | ================================================ FILE: test/tables.mediawiki ================================================ Simple table with caption: {| class="wikitable" |+ Demonstration of simple table syntax. |- ! style="text-align: right;"| Right ! style="text-align: left;"| Left ! style="text-align: center;"| Center ! Default |- | style="text-align: right;"| 12 | style="text-align: left;"| 12 | style="text-align: center;"| 12 | 12 |- | style="text-align: right;"| 123 | style="text-align: left;"| 123 | style="text-align: center;"| 123 | 123 |- | style="text-align: right;"| 1 | style="text-align: left;"| 1 | style="text-align: center;"| 1 | 1 |} Simple table without caption: {| class="wikitable" |- ! style="text-align: right;"| Right ! style="text-align: left;"| Left ! style="text-align: center;"| Center ! Default |- | style="text-align: right;"| 12 | style="text-align: left;"| 12 | style="text-align: center;"| 12 | 12 |- | style="text-align: right;"| 123 | style="text-align: left;"| 123 | style="text-align: center;"| 123 | 123 |- | style="text-align: right;"| 1 | style="text-align: left;"| 1 | style="text-align: center;"| 1 | 1 |} Simple table indented two spaces: {| class="wikitable" |+ Demonstration of simple table syntax. |- ! style="text-align: right;"| Right ! style="text-align: left;"| Left ! style="text-align: center;"| Center ! Default |- | style="text-align: right;"| 12 | style="text-align: left;"| 12 | style="text-align: center;"| 12 | 12 |- | style="text-align: right;"| 123 | style="text-align: left;"| 123 | style="text-align: center;"| 123 | 123 |- | style="text-align: right;"| 1 | style="text-align: left;"| 1 | style="text-align: center;"| 1 | 1 |} Multiline table with caption: {| class="wikitable" |+ Here’s the caption. It may span multiple lines. |- ! style="text-align: center;"| Centered Header ! style="text-align: left;"| Left Aligned ! style="text-align: right;"| Right Aligned ! style="text-align: left;"| Default aligned |- | style="text-align: center;"| First | style="text-align: left;"| row | style="text-align: right;"| 12.0 | style="text-align: left;"| Example of a row that spans multiple lines. |- | style="text-align: center;"| Second | style="text-align: left;"| row | style="text-align: right;"| 5.0 | style="text-align: left;"| Here’s another one. Note the blank line between rows. |} Multiline table without caption: {| class="wikitable" |- ! style="text-align: center;"| Centered Header ! style="text-align: left;"| Left Aligned ! style="text-align: right;"| Right Aligned ! style="text-align: left;"| Default aligned |- | style="text-align: center;"| First | style="text-align: left;"| row | style="text-align: right;"| 12.0 | style="text-align: left;"| Example of a row that spans multiple lines. |- | style="text-align: center;"| Second | style="text-align: left;"| row | style="text-align: right;"| 5.0 | style="text-align: left;"| Here’s another one. Note the blank line between rows. |} Table without column headers: {| class="wikitable" |- | style="text-align: right;"| 12 | style="text-align: left;"| 12 | style="text-align: center;"| 12 | style="text-align: right;"| 12 |- | style="text-align: right;"| 123 | style="text-align: left;"| 123 | style="text-align: center;"| 123 | style="text-align: right;"| 123 |- | style="text-align: right;"| 1 | style="text-align: left;"| 1 | style="text-align: center;"| 1 | style="text-align: right;"| 1 |} Multiline table without column headers: {| class="wikitable" |- | style="text-align: center;"| First | style="text-align: left;"| row | style="text-align: right;"| 12.0 | Example of a row that spans multiple lines. |- | style="text-align: center;"| Second | style="text-align: left;"| row | style="text-align: right;"| 5.0 | Here’s another one. Note the blank line between rows. |} ================================================ FILE: test/tables.ms ================================================ .LP Simple table with caption: .PP Demonstration of simple table syntax. .na .TS delim(@@) tab( ); r l c l. T{ Right T} T{ Left T} T{ Center T} T{ Default T} _ T{ 12 T} T{ 12 T} T{ 12 T} T{ 12 T} T{ 123 T} T{ 123 T} T{ 123 T} T{ 123 T} T{ 1 T} T{ 1 T} T{ 1 T} T{ 1 T} .TE .ad .LP Simple table without caption: .PP .na .TS delim(@@) tab( ); r l c l. T{ Right T} T{ Left T} T{ Center T} T{ Default T} _ T{ 12 T} T{ 12 T} T{ 12 T} T{ 12 T} T{ 123 T} T{ 123 T} T{ 123 T} T{ 123 T} T{ 1 T} T{ 1 T} T{ 1 T} T{ 1 T} .TE .ad .LP Simple table indented two spaces: .PP Demonstration of simple table syntax. .na .TS delim(@@) tab( ); r l c l. T{ Right T} T{ Left T} T{ Center T} T{ Default T} _ T{ 12 T} T{ 12 T} T{ 12 T} T{ 12 T} T{ 123 T} T{ 123 T} T{ 123 T} T{ 123 T} T{ 1 T} T{ 1 T} T{ 1 T} T{ 1 T} .TE .ad .LP Multiline table with caption: .PP Here\(cqs the caption. It may span multiple lines. .na .nr LLold \n[LL] .TS delim(@@) tab( ); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). T{ Centered Header T} T{ Left Aligned T} T{ Right Aligned T} T{ Default aligned T} _ T{ .nr LL 10.5n First T} T{ .nr LL 9.6n row T} T{ .nr LL 11.4n 12.0 T} T{ .nr LL 24.5n Example of a row that spans multiple lines. T} T{ .nr LL 10.5n Second T} T{ .nr LL 9.6n row T} T{ .nr LL 11.4n 5.0 T} T{ .nr LL 24.5n Here\(cqs another one. Note the blank line between rows. T} .TE .nr LL \n[LLold] .ad .LP Multiline table without caption: .PP .na .nr LLold \n[LL] .TS delim(@@) tab( ); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). T{ Centered Header T} T{ Left Aligned T} T{ Right Aligned T} T{ Default aligned T} _ T{ .nr LL 10.5n First T} T{ .nr LL 9.6n row T} T{ .nr LL 11.4n 12.0 T} T{ .nr LL 24.5n Example of a row that spans multiple lines. T} T{ .nr LL 10.5n Second T} T{ .nr LL 9.6n row T} T{ .nr LL 11.4n 5.0 T} T{ .nr LL 24.5n Here\(cqs another one. Note the blank line between rows. T} .TE .nr LL \n[LLold] .ad .LP Table without column headers: .PP .na .TS delim(@@) tab( ); r l c r. T{ 12 T} T{ 12 T} T{ 12 T} T{ 12 T} T{ 123 T} T{ 123 T} T{ 123 T} T{ 123 T} T{ 1 T} T{ 1 T} T{ 1 T} T{ 1 T} .TE .ad .LP Multiline table without column headers: .PP .na .nr LLold \n[LL] .TS delim(@@) tab( ); cw(10.5n) lw(9.6n) rw(11.4n) lw(24.5n). T{ .nr LL 10.5n First T} T{ .nr LL 9.6n row T} T{ .nr LL 11.4n 12.0 T} T{ .nr LL 24.5n Example of a row that spans multiple lines. T} T{ .nr LL 10.5n Second T} T{ .nr LL 9.6n row T} T{ .nr LL 11.4n 5.0 T} T{ .nr LL 24.5n Here\(cqs another one. Note the blank line between rows. T} .TE .nr LL \n[LLold] .ad ================================================ FILE: test/tables.muse ================================================ Simple table with caption: Right || Left || Center || Default 12 | 12 | 12 | 12 123 | 123 | 123 | 123 1 | 1 | 1 | 1 |+ Demonstration of simple table syntax. +| Simple table without caption: Right || Left || Center || Default 12 | 12 | 12 | 12 123 | 123 | 123 | 123 1 | 1 | 1 | 1 Simple table indented two spaces: Right || Left || Center || Default 12 | 12 | 12 | 12 123 | 123 | 123 | 123 1 | 1 | 1 | 1 |+ Demonstration of simple table syntax. +| Multiline table with caption: +:---------:+:---------+-----------:+:--------------------------+ | Centered | Left | Right | Default aligned | | Header | Aligned | Aligned | | +-----------+----------+------------+---------------------------+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ Multiline table without caption: +:---------:+:---------+-----------:+:--------------------------+ | Centered | Left | Right | Default aligned | | Header | Aligned | Aligned | | +-----------+----------+------------+---------------------------+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ Table without column headers: 12 | 12 | 12 | 12 123 | 123 | 123 | 123 1 | 1 | 1 | 1 Multiline table without column headers: +:---------:+:---------+-----------:+---------------------------+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ ================================================ FILE: test/tables.native ================================================ [ Para [ Str "Simple" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Simple" , Space , Str "table" , Space , Str "indented" , Space , Str "two" , Space , Str "spaces:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Demonstration" , Space , Str "of" , Space , Str "simple" , Space , Str "table" , Space , Str "syntax." ] ]) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Center" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "with" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "Here\8217s" , Space , Str "the" , Space , Str "caption." , SoftBreak , Str "It" , Space , Str "may" , Space , Str "span" , Space , Str "multiple" , Space , Str "lines." ] ]) [ ( AlignCenter , ColWidth 0.15 ) , ( AlignLeft , ColWidth 0.1375 ) , ( AlignRight , ColWidth 0.1625 ) , ( AlignLeft , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , SoftBreak , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , SoftBreak , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "without" , Space , Str "caption:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidth 0.15 ) , ( AlignLeft , ColWidth 0.1375 ) , ( AlignRight , ColWidth 0.1625 ) , ( AlignLeft , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Centered" , SoftBreak , Str "Header" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Left" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Right" , SoftBreak , Str "Aligned" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Default" , Space , Str "aligned" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , SoftBreak , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "123" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "Multiline" , Space , Str "table" , Space , Str "without" , Space , Str "column" , Space , Str "headers:" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidth 0.15 ) , ( AlignLeft , ColWidth 0.1375 ) , ( AlignRight , ColWidth 0.1625 ) , ( AlignDefault , ColWidth 0.35 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "First" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "12.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "row" , Space , Str "that" , Space , Str "spans" , SoftBreak , Str "multiple" , Space , Str "lines." ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Second" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "row" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Here\8217s" , Space , Str "another" , Space , Str "one." , Space , Str "Note" , SoftBreak , Str "the" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "rows." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/tables.opendocument ================================================ Simple table with caption: Demonstration of simple table syntax. Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1 Simple table without caption: Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1 Simple table indented two spaces: Demonstration of simple table syntax. Right Left Center Default 12 12 12 12 123 123 123 123 1 1 1 1 Multiline table with caption: Here’s the caption. It may span multiple lines. Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. Multiline table without caption: Centered Header Left Aligned Right Aligned Default aligned First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. Table without column headers: 12 12 12 12 123 123 123 123 1 1 1 1 Multiline table without column headers: First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. ================================================ FILE: test/tables.org ================================================ Simple table with caption: | Right | Left | Center | Default | |-------+------+--------+---------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | #+caption: Demonstration of simple table syntax. Simple table without caption: | Right | Left | Center | Default | |-------+------+--------+---------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Simple table indented two spaces: | Right | Left | Center | Default | |-------+------+--------+---------| | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | #+caption: Demonstration of simple table syntax. Multiline table with caption: | Centered Header | Left Aligned | Right Aligned | Default aligned | |-----------------+--------------+---------------+-------------------------------------------------------| | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here's another one. Note the blank line between rows. | #+caption: Here's the caption. It may span multiple lines. Multiline table without caption: | Centered Header | Left Aligned | Right Aligned | Default aligned | |-----------------+--------------+---------------+-------------------------------------------------------| | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here's another one. Note the blank line between rows. | Table without column headers: | 12 | 12 | 12 | 12 | | 123 | 123 | 123 | 123 | | 1 | 1 | 1 | 1 | Multiline table without column headers: | First | row | 12.0 | Example of a row that spans multiple lines. | | Second | row | 5.0 | Here's another one. Note the blank line between rows. | ================================================ FILE: test/tables.plain ================================================ Simple table with caption: Right Left Center Default ------- ------ -------- --------- 12 12 12 12 123 123 123 123 1 1 1 1 : Demonstration of simple table syntax. Simple table without caption: Right Left Center Default ------- ------ -------- --------- 12 12 12 12 123 123 123 123 1 1 1 1 Simple table indented two spaces: Right Left Center Default ------- ------ -------- --------- 12 12 12 12 123 123 123 123 1 1 1 1 : Demonstration of simple table syntax. Multiline table with caption: --------------------------------------------------------------- Centered Left Right Default aligned Header Aligned Aligned ----------- ---------- ------------ --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. --------------------------------------------------------------- : Here’s the caption. It may span multiple lines. Multiline table without caption: --------------------------------------------------------------- Centered Left Right Default aligned Header Aligned Aligned ----------- ---------- ------------ --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. --------------------------------------------------------------- Table without column headers: ----- ----- ----- ----- 12 12 12 12 123 123 123 123 1 1 1 1 ----- ----- ----- ----- Multiline table without column headers: ----------- ---------- ------------ --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here’s another one. Note the blank line between rows. ----------- ---------- ------------ --------------------------- ================================================ FILE: test/tables.rst ================================================ Simple table with caption: .. table:: Demonstration of simple table syntax. ===== ==== ====== ======= Right Left Center Default ===== ==== ====== ======= 12 12 12 12 123 123 123 123 1 1 1 1 ===== ==== ====== ======= Simple table without caption: ===== ==== ====== ======= Right Left Center Default ===== ==== ====== ======= 12 12 12 12 123 123 123 123 1 1 1 1 ===== ==== ====== ======= Simple table indented two spaces: .. table:: Demonstration of simple table syntax. ===== ==== ====== ======= Right Left Center Default ===== ==== ====== ======= 12 12 12 12 123 123 123 123 1 1 1 1 ===== ==== ====== ======= Multiline table with caption: .. table:: Here’s the caption. It may span multiple lines. +-----------+----------+------------+---------------------------+ | Centered | Left | Right | Default aligned | | Header | Aligned | Aligned | | +===========+==========+============+===========================+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ Multiline table without caption: +-----------+----------+------------+---------------------------+ | Centered | Left | Right | Default aligned | | Header | Aligned | Aligned | | +===========+==========+============+===========================+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ Table without column headers: === === === === 12 12 12 12 123 123 123 123 1 1 1 1 === === === === Multiline table without column headers: +-----------+----------+------------+---------------------------+ | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-----------+----------+------------+---------------------------+ | Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-----------+----------+------------+---------------------------+ ================================================ FILE: test/tables.rtf ================================================ {\pard \ql \f0 \sa180 \li0 \fi0 Simple table with caption:\par} { \trowd \trgaph120 \clbrdrb\brdrs\cellx2160\clbrdrb\brdrs\cellx4320\clbrdrb\brdrs\cellx6480\clbrdrb\brdrs\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 Right\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Left\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 Center\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Default\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 12\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 123\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 1\par} \cell} } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 Demonstration of simple table syntax.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Simple table without caption:\par} { \trowd \trgaph120 \clbrdrb\brdrs\cellx2160\clbrdrb\brdrs\cellx4320\clbrdrb\brdrs\cellx6480\clbrdrb\brdrs\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 Right\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Left\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 Center\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Default\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 12\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 123\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 1\par} \cell} } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 \par} {\pard \ql \f0 \sa180 \li0 \fi0 Simple table indented two spaces:\par} { \trowd \trgaph120 \clbrdrb\brdrs\cellx2160\clbrdrb\brdrs\cellx4320\clbrdrb\brdrs\cellx6480\clbrdrb\brdrs\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 Right\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Left\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 Center\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Default\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 12\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 123\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 1\par} \cell} } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 Demonstration of simple table syntax.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiline table with caption:\par} { \trowd \trgaph120 \clbrdrb\brdrs\cellx1296\clbrdrb\brdrs\cellx2484\clbrdrb\brdrs\cellx3888\clbrdrb\brdrs\cellx6912 \trkeep\intbl { {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 Centered Header\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Left Aligned\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 Right Aligned\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Default aligned\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx1296\cellx2484\cellx3888\cellx6912 \trkeep\intbl { {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 First\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 row\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 12.0\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Example of a row that spans multiple lines.\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx1296\cellx2484\cellx3888\cellx6912 \trkeep\intbl { {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 Second\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 row\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par} \cell} } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's the caption. It may span multiple lines.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiline table without caption:\par} { \trowd \trgaph120 \clbrdrb\brdrs\cellx1296\clbrdrb\brdrs\cellx2484\clbrdrb\brdrs\cellx3888\clbrdrb\brdrs\cellx6912 \trkeep\intbl { {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 Centered Header\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Left Aligned\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 Right Aligned\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Default aligned\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx1296\cellx2484\cellx3888\cellx6912 \trkeep\intbl { {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 First\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 row\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 12.0\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Example of a row that spans multiple lines.\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx1296\cellx2484\cellx3888\cellx6912 \trkeep\intbl { {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 Second\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 row\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par} \cell} } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 \par} {\pard \ql \f0 \sa180 \li0 \fi0 Table without column headers:\par} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 12\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 12\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 123\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 123\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx2160\cellx4320\cellx6480\cellx8640 \trkeep\intbl { {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 1\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 1\par} \cell} } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 \par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiline table without column headers:\par} { \trowd \trgaph120 \cellx1296\cellx2484\cellx3888\cellx6912 \trkeep\intbl { {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 First\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 row\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 12.0\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Example of a row that spans multiple lines.\par} \cell} } \intbl\row} { \trowd \trgaph120 \cellx1296\cellx2484\cellx3888\cellx6912 \trkeep\intbl { {{\pard\intbl \qc \f0 \sa0 \li0 \fi0 Second\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 row\par} \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par} \cell} {{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par} \cell} } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 \par} ================================================ FILE: test/tables.tei ================================================

                Simple table with caption:

                Right

                Left

                Center

                Default

                12

                12

                12

                12

                123

                123

                123

                123

                1

                1

                1

                1

                Simple table without caption:

                Right

                Left

                Center

                Default

                12

                12

                12

                12

                123

                123

                123

                123

                1

                1

                1

                1

                Simple table indented two spaces:

                Right

                Left

                Center

                Default

                12

                12

                12

                12

                123

                123

                123

                123

                1

                1

                1

                1

                Multiline table with caption:

                Centered Header

                Left Aligned

                Right Aligned

                Default aligned

                First

                row

                12.0

                Example of a row that spans multiple lines.

                Second

                row

                5.0

                Here’s another one. Note the blank line between rows.

                Multiline table without caption:

                Centered Header

                Left Aligned

                Right Aligned

                Default aligned

                First

                row

                12.0

                Example of a row that spans multiple lines.

                Second

                row

                5.0

                Here’s another one. Note the blank line between rows.

                Table without column headers:

                12

                12

                12

                12

                123

                123

                123

                123

                1

                1

                1

                1

                Multiline table without column headers:

                First

                row

                12.0

                Example of a row that spans multiple lines.

                Second

                row

                5.0

                Here’s another one. Note the blank line between rows.

                ================================================ FILE: test/tables.texinfo ================================================ @node Top @top Top Simple table with caption: @float Table @multitable {Right} {Left} {Center} {Default} @headitem Right @tab Left @tab Center @tab Default @item 12 @tab 12 @tab 12 @tab 12 @item 123 @tab 123 @tab 123 @tab 123 @item 1 @tab 1 @tab 1 @tab 1 @end multitable @caption{Demonstration of simple table syntax.} @end float Simple table without caption: @multitable {Right} {Left} {Center} {Default} @headitem Right @tab Left @tab Center @tab Default @item 12 @tab 12 @tab 12 @tab 12 @item 123 @tab 123 @tab 123 @tab 123 @item 1 @tab 1 @tab 1 @tab 1 @end multitable Simple table indented two spaces: @float Table @multitable {Right} {Left} {Center} {Default} @headitem Right @tab Left @tab Center @tab Default @item 12 @tab 12 @tab 12 @tab 12 @item 123 @tab 123 @tab 123 @tab 123 @item 1 @tab 1 @tab 1 @tab 1 @end multitable @caption{Demonstration of simple table syntax.} @end float Multiline table with caption: @float Table @multitable @columnfractions 0.15 0.14 0.16 0.35 @headitem Centered Header @tab Left Aligned @tab Right Aligned @tab Default aligned @item First @tab row @tab 12.0 @tab Example of a row that spans multiple lines. @item Second @tab row @tab 5.0 @tab Here's another one. Note the blank line between rows. @end multitable @caption{Here's the caption. It may span multiple lines.} @end float Multiline table without caption: @multitable @columnfractions 0.15 0.14 0.16 0.35 @headitem Centered Header @tab Left Aligned @tab Right Aligned @tab Default aligned @item First @tab row @tab 12.0 @tab Example of a row that spans multiple lines. @item Second @tab row @tab 5.0 @tab Here's another one. Note the blank line between rows. @end multitable Table without column headers: @multitable {123} {123} {123} {123} @item 12 @tab 12 @tab 12 @tab 12 @item 123 @tab 123 @tab 123 @tab 123 @item 1 @tab 1 @tab 1 @tab 1 @end multitable Multiline table without column headers: @multitable @columnfractions 0.15 0.14 0.16 0.35 @item First @tab row @tab 12.0 @tab Example of a row that spans multiple lines. @item Second @tab row @tab 5.0 @tab Here's another one. Note the blank line between rows. @end multitable ================================================ FILE: test/tables.textile ================================================ Simple table with caption:
                Demonstration of simple table syntax.
                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1
                Simple table without caption: |_. Right|_. Left|_. Center|_. Default| |>. 12|<. 12|=. 12|12| |>. 123|<. 123|=. 123|123| |>. 1|<. 1|=. 1|1| Simple table indented two spaces:
                Demonstration of simple table syntax.
                Right Left Center Default
                12 12 12 12
                123 123 123 123
                1 1 1 1
                Multiline table with caption:
                Here's the caption. It may span multiple lines.
                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here's another one. Note the blank line between rows.
                Multiline table without caption:
                Centered Header Left Aligned Right Aligned Default aligned
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here's another one. Note the blank line between rows.
                Table without column headers: |>. 12|<. 12|=. 12|>. 12| |>. 123|<. 123|=. 123|>. 123| |>. 1|<. 1|=. 1|>. 1| Multiline table without column headers:
                First row 12.0 Example of a row that spans multiple lines.
                Second row 5.0 Here's another one. Note the blank line between rows.
                ================================================ FILE: test/tables.txt ================================================ Simple table with caption: Right Left Center Default ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 Table: Demonstration of simple table syntax. Simple table without caption: Right Left Center Default ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 Simple table indented two spaces: Right Left Center Default ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 : Demonstration of simple table syntax. Multiline table with caption: : Here's the caption. It may span multiple lines. --------------------------------------------------------------- Centered Left Right Header Aligned Aligned Default aligned ---------- --------- ----------- --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. --------------------------------------------------------------- Multiline table without caption: --------------------------------------------------------------- Centered Left Right Header Aligned Aligned Default aligned ---------- --------- ----------- --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. --------------------------------------------------------------- Table without column headers: ------- ------ ---------- ------- 12 12 12 12 123 123 123 123 1 1 1 1 ------- ------ ---------- ------- Multiline table without column headers: ---------- --------- ----------- --------------------------- First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. ---------- --------- ----------- --------------------------- ================================================ FILE: test/tables.typst ================================================ Simple table with caption: #figure( align(center)[#table( columns: 4, align: (right,left,center,auto,), table.header([Right], [Left], [Center], [Default],), table.hline(), [12], [12], [12], [12], [123], [123], [123], [123], [1], [1], [1], [1], )] , caption: [Demonstration of simple table syntax.] , kind: table ) Simple table without caption: #figure( align(center)[#table( columns: 4, align: (right,left,center,auto,), table.header([Right], [Left], [Center], [Default],), table.hline(), [12], [12], [12], [12], [123], [123], [123], [123], [1], [1], [1], [1], )] , kind: table ) Simple table indented two spaces: #figure( align(center)[#table( columns: 4, align: (right,left,center,auto,), table.header([Right], [Left], [Center], [Default],), table.hline(), [12], [12], [12], [12], [123], [123], [123], [123], [1], [1], [1], [1], )] , caption: [Demonstration of simple table syntax.] , kind: table ) Multiline table with caption: #figure( align(center)[#table( columns: (15%, 13.75%, 16.25%, 35%), align: (center,left,right,left,), table.header([Centered Header], [Left Aligned], [Right Aligned], [Default aligned],), table.hline(), [First], [row], [12.0], [Example of a row that spans multiple lines.], [Second], [row], [5.0], [Here's another one. Note the blank line between rows.], )] , caption: [Here's the caption. It may span multiple lines.] , kind: table ) Multiline table without caption: #figure( align(center)[#table( columns: (15%, 13.75%, 16.25%, 35%), align: (center,left,right,left,), table.header([Centered Header], [Left Aligned], [Right Aligned], [Default aligned],), table.hline(), [First], [row], [12.0], [Example of a row that spans multiple lines.], [Second], [row], [5.0], [Here's another one. Note the blank line between rows.], )] , kind: table ) Table without column headers: #figure( align(center)[#table( columns: 4, align: (right,left,center,right,), [12], [12], [12], [12], [123], [123], [123], [123], [1], [1], [1], [1], )] , kind: table ) Multiline table without column headers: #figure( align(center)[#table( columns: (15%, 13.75%, 16.25%, 35%), align: (center,left,right,auto,), [First], [row], [12.0], [Example of a row that spans multiple lines.], [Second], [row], [5.0], [Here's another one. Note the blank line between rows.], )] , kind: table ) ================================================ FILE: test/tables.vimdoc ================================================ Simple table with caption: Right Left Center Default ~ 12 12 12 12 123 123 123 123 1 1 1 1 Demonstration of simple table syntax. Simple table without caption: Right Left Center Default ~ 12 12 12 12 123 123 123 123 1 1 1 1 Simple table indented two spaces: Right Left Center Default ~ 12 12 12 12 123 123 123 123 1 1 1 1 Demonstration of simple table syntax. Multiline table with caption: Centered Left Right Default aligned Header Aligned Aligned ~ First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. Here's the caption. It may span multiple lines. Multiline table without caption: Centered Left Right Default aligned Header Aligned Aligned ~ First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. Table without column headers: 12 12 12 12 123 123 123 123 1 1 1 1 Multiline table without column headers: First row 12.0 Example of a row that spans multiple lines. Second row 5.0 Here's another one. Note the blank line between rows. ================================================ FILE: test/tables.xwiki ================================================ Simple table with caption: |=Right |=Left |=Center |=Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 Simple table without caption: |=Right |=Left |=Center |=Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 Simple table indented two spaces: |=Right |=Left |=Center |=Default |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 Multiline table with caption: |=Centered Header |=Left Aligned |=Right Aligned |=Default aligned |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. Multiline table without caption: |=Centered Header |=Left Aligned |=Right Aligned |=Default aligned |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. Table without column headers: |= |= |= |= |12 |12 |12 |12 |123 |123 |123 |123 |1 |1 |1 |1 Multiline table without column headers: |= |= |= |= |First |row |12.0 |Example of a row that spans multiple lines. |Second |row |5.0 |Here’s another one. Note the blank line between rows. ================================================ FILE: test/tables.zimwiki ================================================ Simple table with caption: Demonstration of simple table syntax. |Right|Left |Center |Default| |----:|:----|:-----:|-------| | 12|12 | 12 |12 | | 123|123 | 123 |123 | | 1|1 | 1 |1 | Simple table without caption: |Right|Left |Center |Default| |----:|:----|:-----:|-------| | 12|12 | 12 |12 | | 123|123 | 123 |123 | | 1|1 | 1 |1 | Simple table indented two spaces: Demonstration of simple table syntax. |Right|Left |Center |Default| |----:|:----|:-----:|-------| | 12|12 | 12 |12 | | 123|123 | 123 |123 | | 1|1 | 1 |1 | Multiline table with caption: Here’s the caption. It may span multiple lines. |Centered Header|Left Aligned|Right Aligned|Default aligned | |:-------------:|:-----------|------------:|:------------------------------------------------------| | First |row | 12.0|Example of a row that spans multiple lines. | | Second |row | 5.0|Here’s another one. Note the blank line between rows. | Multiline table without caption: |Centered Header|Left Aligned|Right Aligned|Default aligned | |:-------------:|:-----------|------------:|:------------------------------------------------------| | First |row | 12.0|Example of a row that spans multiple lines. | | Second |row | 5.0|Here’s another one. Note the blank line between rows. | Table without column headers: | 12|12 | 12 | 12| |----:|:----|:-----:|----:| | 12|12 | 12 | 12| | 123|123 | 123 | 123| | 1|1 | 1 | 1| Multiline table without column headers: | First |row | 12.0|Example of a row that spans multiple lines. | |:--------:|:----|-----:|-----------------------------------------------------| | First |row | 12.0|Example of a row that spans multiple lines. | | Second |row | 5.0|Here’s another one. Note the blank line between rows.| ================================================ FILE: test/test-pandoc.hs ================================================ module Main (main) where import System.Environment (getArgs, getExecutablePath) import qualified Control.Exception as E import Text.Pandoc.App (convertWithOpts, handleOptInfo, defaultOpts, options, parseOptionsFromArgs) import Text.Pandoc.Error (handleError) import Text.Pandoc.Scripting (noEngine) import GHC.IO.Encoding import Test.Tasty import qualified Tests.Command import qualified Tests.Old import qualified Tests.Readers.Creole import qualified Tests.Readers.Docx import qualified Tests.Readers.Pptx import qualified Tests.Readers.Xlsx import qualified Tests.Readers.DokuWiki import qualified Tests.Readers.EPUB import qualified Tests.Readers.FB2 import qualified Tests.Readers.HTML import qualified Tests.Readers.JATS import qualified Tests.Readers.Jira import qualified Tests.Readers.LaTeX import qualified Tests.Readers.Markdown import qualified Tests.Readers.Muse import qualified Tests.Readers.ODT import qualified Tests.Readers.Org import qualified Tests.Readers.RST import qualified Tests.Readers.RTF import qualified Tests.Readers.Txt2Tags import qualified Tests.Readers.Man import qualified Tests.Readers.Mdoc import qualified Tests.Readers.Pod import qualified Tests.Shared import qualified Tests.Writers.AsciiDoc import qualified Tests.Writers.ConTeXt import qualified Tests.Writers.DocBook import qualified Tests.Writers.Docx import qualified Tests.Writers.FB2 import qualified Tests.Writers.HTML import qualified Tests.Writers.JATS import qualified Tests.Writers.Jira import qualified Tests.Writers.LaTeX import qualified Tests.Writers.Markdown import qualified Tests.Writers.Ms import qualified Tests.Writers.Muse import qualified Tests.Writers.Native import qualified Tests.Writers.Org import qualified Tests.Writers.Plain import qualified Tests.Writers.Powerpoint import qualified Tests.Writers.RST import qualified Tests.Writers.AnnotatedTable import qualified Tests.Writers.TEI import qualified Tests.Writers.Markua import qualified Tests.Writers.BBCode import qualified Tests.XML import qualified Tests.MediaBag import Text.Pandoc.Shared (inDirectory) tests :: FilePath -> TestTree tests pandocPath = testGroup "pandoc tests" [ Tests.Command.tests , testGroup "Old" (Tests.Old.tests pandocPath) , testGroup "Shared" Tests.Shared.tests , testGroup "MediaBag" Tests.MediaBag.tests , testGroup "XML" Tests.XML.tests , testGroup "Writers" [ testGroup "Native" Tests.Writers.Native.tests , testGroup "ConTeXt" Tests.Writers.ConTeXt.tests , testGroup "LaTeX" Tests.Writers.LaTeX.tests , testGroup "HTML" Tests.Writers.HTML.tests , testGroup "JATS" Tests.Writers.JATS.tests , testGroup "Jira" Tests.Writers.Jira.tests , testGroup "Docbook" Tests.Writers.DocBook.tests , testGroup "Markdown" Tests.Writers.Markdown.tests , testGroup "Org" Tests.Writers.Org.tests , testGroup "Plain" Tests.Writers.Plain.tests , testGroup "AsciiDoc" Tests.Writers.AsciiDoc.tests , testGroup "Docx" Tests.Writers.Docx.tests , testGroup "RST" Tests.Writers.RST.tests , testGroup "TEI" Tests.Writers.TEI.tests , testGroup "markua" Tests.Writers.Markua.tests , testGroup "Muse" Tests.Writers.Muse.tests , testGroup "FB2" Tests.Writers.FB2.tests , testGroup "PowerPoint" Tests.Writers.Powerpoint.tests , testGroup "Ms" Tests.Writers.Ms.tests , testGroup "AnnotatedTable" Tests.Writers.AnnotatedTable.tests , testGroup "BBCode" Tests.Writers.BBCode.tests ] , testGroup "Readers" [ testGroup "LaTeX" Tests.Readers.LaTeX.tests , testGroup "Markdown" Tests.Readers.Markdown.tests , testGroup "HTML" Tests.Readers.HTML.tests , testGroup "JATS" Tests.Readers.JATS.tests , testGroup "Jira" Tests.Readers.Jira.tests , testGroup "Org" Tests.Readers.Org.tests , testGroup "RST" Tests.Readers.RST.tests , testGroup "RTF" Tests.Readers.RTF.tests , testGroup "Docx" Tests.Readers.Docx.tests , testGroup "Pptx" Tests.Readers.Pptx.tests , testGroup "Xlsx" Tests.Readers.Xlsx.tests , testGroup "ODT" Tests.Readers.ODT.tests , testGroup "Txt2Tags" Tests.Readers.Txt2Tags.tests , testGroup "EPUB" Tests.Readers.EPUB.tests , testGroup "Muse" Tests.Readers.Muse.tests , testGroup "Creole" Tests.Readers.Creole.tests , testGroup "Man" Tests.Readers.Man.tests , testGroup "Mdoc" Tests.Readers.Mdoc.tests , testGroup "FB2" Tests.Readers.FB2.tests , testGroup "DokuWiki" Tests.Readers.DokuWiki.tests , testGroup "Pod" Tests.Readers.Pod.tests ] ] main :: IO () main = do setLocaleEncoding utf8 args <- getArgs case args of "--emulate":args' -> -- emulate pandoc executable E.catch (do res <- parseOptionsFromArgs options defaultOpts "pandoc" args' case res of Left e -> handleOptInfo noEngine e Right opts -> convertWithOpts noEngine opts) (handleError . Left) _ -> inDirectory "test" $ do fp <- getExecutablePath -- putStrLn $ "Using pandoc executable at " ++ fp defaultMain $ tests fp ================================================ FILE: test/testsuite.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "John" , Space , Str "MacFarlane" ] , MetaInlines [ Str "Anonymous" ] ] ) , ( "date" , MetaInlines [ Str "July" , Space , Str "17," , Space , Str "2006" ] ) , ( "title" , MetaInlines [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , SoftBreak , Str "John" , Space , Str "Gruber\8217s" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , HorizontalRule , Header 1 ( "headers" , [] , [] ) [ Str "Headers" ] , Header 2 ( "level-2-with-an-embedded-link" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "/url" , "" ) ] , Header 3 ( "level-3-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 4 ( "level-4" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Header 5 ( "level-5" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 1 ( "level-1" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Header 2 ( "level-2-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 3 ( "level-3" , [] , [] ) [ Str "Level" , Space , Str "3" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 2 ( "level-2" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , HorizontalRule , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , SoftBreak , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , Space , Str "item." , SoftBreak , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , SoftBreak , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , Space , Str "a" , SoftBreak , Str "list" , Space , Str "item." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , SoftBreak , Str "*" , Space , Str "criminey." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "hard" , Space , Str "line" , Space , Str "break" , LineBreak , Str "here." ] , HorizontalRule , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , SoftBreak , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , BlockQuote [ Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "item" , Space , Str "one" ] ] , [ Plain [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] ] , BlockQuote [ Para [ Str "nested" ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" , Space , Str "2" , SoftBreak , Str ">" , Space , Str "1." ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , HorizontalRule , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n\nthis code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" , HorizontalRule , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] , [ Plain [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Plus" , Space , Str "1" ] ] , [ Plain [ Str "Plus" , Space , Str "2" ] ] , [ Plain [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Minus" , Space , Str "1" ] ] , [ Plain [ Str "Minus" , Space , Str "2" ] ] , [ Plain [ Str "Minus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second" ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "and:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "One" ] ] , [ Plain [ Str "Two" ] ] , [ Plain [ Str "Three" ] ] ] , Para [ Str "Loose" , Space , Str "using" , Space , Str "tabs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and" , Space , Str "using" , Space , Str "spaces:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog\8217s" , SoftBreak , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Header 2 ( "nested" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "another:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing" , Space , Str "but" , Space , Str "with" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "tabs-and-spaces" , [] , [] ) [ Str "Tabs" , Space , Str "and" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "spaces" ] ] ] ] ] , Header 2 ( "fancy-list-markers" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , OrderedList ( 2 , Decimal , TwoParens ) [ [ Para [ Str "begins" , Space , Str "with" , Space , Str "2" ] ] , [ Para [ Str "and" , Space , Str "now" , Space , Str "3" ] , Para [ Str "with" , Space , Str "a" , Space , Str "continuation" ] , OrderedList ( 4 , LowerRoman , Period ) [ [ Plain [ Str "sublist" , Space , Str "with" , Space , Str "roman" , Space , Str "numerals," , SoftBreak , Str "starting" , Space , Str "with" , Space , Str "4" ] ] , [ Plain [ Str "more" , Space , Str "items" ] , OrderedList ( 1 , UpperAlpha , TwoParens ) [ [ Plain [ Str "a" , Space , Str "subsublist" ] ] , [ Plain [ Str "a" , Space , Str "subsublist" ] ] ] ] ] ] ] , Para [ Str "Nesting:" ] , OrderedList ( 1 , UpperAlpha , Period ) [ [ Plain [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , Period ) [ [ Plain [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , TwoParens ) [ [ Plain [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , OneParen ) [ [ Plain [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Autonumber." ] ] , [ Plain [ Str "More." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Nested." ] ] ] ] ] , Para [ Str "Should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "list" , Space , Str "item:" ] , Para [ Str "M.A.\160\&2007" ] , Para [ Str "B." , Space , Str "Williams" ] , HorizontalRule , Header 1 ( "definition-lists" , [] , [] ) [ Str "Definition" , Space , Str "Lists" ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "spaces:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Plain [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "tabs:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Plain [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Para [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "with" , Space , Str "italics:" ] , DefinitionList [ ( [ Emph [ Str "apple" ] ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "contains" , Space , Str "seeds," , SoftBreak , Str "crisp," , Space , Str "pleasant" , Space , Str "to" , Space , Str "taste" ] ] ] ) , ( [ Emph [ Str "orange" ] ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , CodeBlock ( "" , [] , [] ) "{ orange code block }" , BlockQuote [ Para [ Str "orange" , Space , Str "block" , Space , Str "quote" ] ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "tight:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] , [ Plain [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] , [ Plain [ Str "bank" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] , [ Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] , [ Para [ Str "bank" ] ] ] ) ] , Para [ Str "Blank" , Space , Str "line" , Space , Str "after" , Space , Str "term," , Space , Str "indented" , Space , Str "marker," , Space , Str "alternate" , Space , Str "markers:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] , [ Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "sublist" ] ] , [ Plain [ Str "sublist" ] ] ] ] ] ) ] , Header 1 ( "html-blocks" , [] , [] ) [ Str "HTML" , Space , Str "Blocks" ] , Para [ Str "Simple" , Space , Str "block" , Space , Str "on" , Space , Str "one" , Space , Str "line:" ] , Div ( "" , [] , [] ) [ Plain [ Str "foo" ] ] , Para [ Str "And" , Space , Str "nested" , Space , Str "without" , Space , Str "indentation:" ] , Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "bar" ] ] ] , Para [ Str "Interpreted" , Space , Str "markdown" , Space , Str "in" , Space , Str "a" , Space , Str "table:" ] , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "
                " , Plain [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] ] , RawBlock (Format "html") "" , Plain [ Str "And" , Space , Str "this" , Space , Str "is" , Space , Strong [ Str "strong" ] ] , RawBlock (Format "html") "
                " , RawBlock (Format "html") "" , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "simple" , Space , Str "block:" ] , Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "code" , Space , Str "block," , Space , Str "though:" ] , CodeBlock ( "" , [] , [] ) "
                \n foo\n
                " , Para [ Str "As" , Space , Str "should" , Space , Str "this:" ] , CodeBlock ( "" , [] , [] ) "
                foo
                " , Para [ Str "Now," , Space , Str "nested:" ] , Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "foo" ] ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "just" , Space , Str "be" , Space , Str "an" , Space , Str "HTML" , Space , Str "comment:" ] , RawBlock (Format "html") "" , Para [ Str "Multiline:" ] , RawBlock (Format "html") "" , RawBlock (Format "html") "" , Para [ Str "Code" , Space , Str "block:" ] , CodeBlock ( "" , [] , [] ) "" , Para [ Str "Just" , Space , Str "plain" , Space , Str "comment," , Space , Str "with" , Space , Str "trailing" , Space , Str "spaces" , Space , Str "on" , Space , Str "the" , Space , Str "line:" ] , RawBlock (Format "html") "" , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "
                " , Para [ Str "Hr\8217s:" ] , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , HorizontalRule , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "An" , Space , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , Space , Str "link" ] ( "/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , Space , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , Space , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Strikeout [ Str "This" , Space , Str "is" , Space , Emph [ Str "strikeout" ] , Str "." ] ] , Para [ Str "Superscripts:" , Space , Str "a" , Superscript [ Str "bc" ] , Str "d" , Space , Str "a" , Superscript [ Emph [ Str "hello" ] ] , Space , Str "a" , Superscript [ Str "hello\160there" ] , Str "." ] , Para [ Str "Subscripts:" , Space , Str "H" , Subscript [ Str "2" ] , Str "O," , Space , Str "H" , Subscript [ Str "23" ] , Str "O," , Space , Str "H" , Subscript [ Str "many\160of\160them" ] , Str "O." ] , Para [ Str "These" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "superscripts" , Space , Str "or" , Space , Str "subscripts," , SoftBreak , Str "because" , Space , Str "of" , Space , Str "the" , Space , Str "unescaped" , Space , Str "spaces:" , Space , Str "a^b" , Space , Str "c^d," , Space , Str "a~b" , Space , Str "c~d." ] , HorizontalRule , Header 1 ( "smart-quotes-ellipses-dashes" , [] , [] ) [ Str "Smart" , Space , Str "quotes," , Space , Str "ellipses," , Space , Str "dashes" ] , Para [ Quoted DoubleQuote [ Str "Hello," ] , Space , Str "said" , Space , Str "the" , Space , Str "spider." , Space , Quoted DoubleQuote [ Quoted SingleQuote [ Str "Shelob" ] , Space , Str "is" , Space , Str "my" , Space , Str "name." ] ] , Para [ Quoted SingleQuote [ Str "A" ] , Str "," , Space , Quoted SingleQuote [ Str "B" ] , Str "," , Space , Str "and" , Space , Quoted SingleQuote [ Str "C" ] , Space , Str "are" , Space , Str "letters." ] , Para [ Quoted SingleQuote [ Str "Oak," ] , Space , Quoted SingleQuote [ Str "elm," ] , Space , Str "and" , Space , Quoted SingleQuote [ Str "beech" ] , Space , Str "are" , Space , Str "names" , Space , Str "of" , Space , Str "trees." , SoftBreak , Str "So" , Space , Str "is" , Space , Quoted SingleQuote [ Str "pine." ] ] , Para [ Quoted SingleQuote [ Str "He" , Space , Str "said," , Space , Quoted DoubleQuote [ Str "I" , Space , Str "want" , Space , Str "to" , Space , Str "go." ] ] , Space , Str "Were" , Space , Str "you" , Space , Str "alive" , Space , Str "in" , Space , Str "the" , SoftBreak , Str "70\8217s?" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "quoted" , Space , Quoted SingleQuote [ Code ( "" , [] , [] ) "code" ] , Space , Str "and" , Space , Str "a" , Space , Quoted DoubleQuote [ Link ( "" , [] , [] ) [ Str "quoted" , Space , Str "link" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , Str "." ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one\8212two" , Space , Str "\8212" , Space , Str "three\8212four" , Space , Str "\8212" , Space , Str "five." ] , Para [ Str "Dashes" , Space , Str "between" , Space , Str "numbers:" , Space , Str "5\8211\&7," , Space , Str "255\8211\&66," , Space , Str "1987\8211\&1999." ] , Para [ Str "Ellipses\8230and\8230and\8230." ] , HorizontalRule , Header 1 ( "latex" , [] , [] ) [ Str "LaTeX" ] , BulletList [ [ Plain [ RawInline (Format "tex") "\\cite[22-23]{smith.1899}" ] ] , [ Plain [ Math InlineMath "2+2=4" ] ] , [ Plain [ Math InlineMath "x \\in y" ] ] , [ Plain [ Math InlineMath "\\alpha \\wedge \\omega" ] ] , [ Plain [ Math InlineMath "223" ] ] , [ Plain [ Math InlineMath "p" , Str "-Tree" ] ] , [ Plain [ Str "Here\8217s" , Space , Str "some" , Space , Str "display" , Space , Str "math:" , SoftBreak , Math DisplayMath "\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)-f(x)}{h}" ] ] , [ Plain [ Str "Here\8217s" , Space , Str "one" , Space , Str "that" , Space , Str "has" , Space , Str "a" , Space , Str "line" , Space , Str "break" , Space , Str "in" , Space , Str "it:" , Space , Math InlineMath "\\alpha + \\omega \\times x^2" , Str "." ] ] ] , Para [ Str "These" , Space , Str "shouldn\8217t" , Space , Str "be" , Space , Str "math:" ] , BulletList [ [ Plain [ Str "To" , Space , Str "get" , Space , Str "the" , Space , Str "famous" , Space , Str "equation," , Space , Str "write" , Space , Code ( "" , [] , [] ) "$e = mc^2$" , Str "." ] ] , [ Plain [ Str "$22,000" , Space , Str "is" , Space , Str "a" , Space , Emph [ Str "lot" ] , Space , Str "of" , Space , Str "money." , Space , Str "So" , Space , Str "is" , Space , Str "$34,000." , SoftBreak , Str "(It" , Space , Str "worked" , Space , Str "if" , Space , Quoted DoubleQuote [ Str "lot" ] , Space , Str "is" , Space , Str "emphasized.)" ] ] , [ Plain [ Str "Shoes" , Space , Str "($20)" , Space , Str "and" , Space , Str "socks" , Space , Str "($5)." ] ] , [ Plain [ Str "Escaped" , Space , Code ( "" , [] , [] ) "$" , Str ":" , Space , Str "$73" , Space , Emph [ Str "this" , Space , Str "should" , Space , Str "be" , Space , Str "emphasized" ] , Space , Str "23$." ] ] ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "LaTeX" , Space , Str "table:" ] , RawBlock (Format "tex") "\\begin{tabular}{|l|l|}\\hline\nAnimal & Number \\\\ \\hline\nDog & 2 \\\\\nCat & 1 \\\\ \\hline\n\\end{tabular}" , HorizontalRule , Header 1 ( "special-characters" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Plain [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Plain [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Plain [ Str "section:" , Space , Str "\167" ] ] , [ Plain [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Plain [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , HorizontalRule , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Header 2 ( "explicit" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by two spaces" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by a tab" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title with \"quotes\" in it" ) ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title with single quotes" ) ] , Para [ Link ( "" , [] , [] ) [ Str "with_underscore" ] ( "/url/with_underscore" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Email" , Space , Str "link" ] ( "mailto:nobody@nowhere.net" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Empty" ] ( "" , "" ) , Str "." ] , Header 2 ( "reference" , [] , [] ) [ Str "Reference" ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "[brackets]" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "b" ] ( "/url/" , "" ) , Space , Str "by" , Space , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "once" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "twice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "thrice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "This" , Space , Str "should" , Space , Str "[not][]" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , CodeBlock ( "" , [] , [] ) "[not]: /url" , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "Title with \"quotes\" inside" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "biz" ] ( "/url/" , "Title with \"quote\" inside" ) , Str "." ] , Header 2 ( "with-ampersands" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "http://att.com/" , "AT&T" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" , Space , Str "in" , Space , Str "pointy" , Space , Str "braces" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Header 2 ( "autolinks" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Plain [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Plain [ Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Plain [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , Space , Link ( "" , [ "email" ] , [] ) [ Str "nobody@nowhere.net" ] ( "mailto:nobody@nowhere.net" , "" ) ] , BlockQuote [ Para [ Str "Blockquoted:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , Para [ Str "Auto-links" , Space , Str "should" , Space , Str "not" , Space , Str "occur" , Space , Str "here:" , Space , Code ( "" , [] , [] ) "" ] , CodeBlock ( "" , [] , [] ) "or here: " , HorizontalRule , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Quoted DoubleQuote [ Str "Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune" ] , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "lalune" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "lalune" ] ( "lalune.jpg" , "Voyage dans la Lune" ) ] ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "movie" ] ( "movie.jpg" , "" ) , Space , Str "icon." ] , HorizontalRule , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Note [ Para [ Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "footnote." , Space , Str "It" , Space , Str "can" , Space , Str "go" , Space , Str "anywhere" , Space , Str "after" , Space , Str "the" , Space , Str "footnote" , SoftBreak , Str "reference." , Space , Str "It" , Space , Str "need" , Space , Str "not" , Space , Str "be" , Space , Str "placed" , Space , Str "at" , Space , Str "the" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "document." ] ] , Space , Str "and" , Space , Str "another." , Note [ Para [ Str "Here\8217s" , Space , Str "the" , Space , Str "long" , Space , Str "note." , Space , Str "This" , Space , Str "one" , Space , Str "contains" , Space , Str "multiple" , SoftBreak , Str "blocks." ] , Para [ Str "Subsequent" , Space , Str "blocks" , Space , Str "are" , Space , Str "indented" , Space , Str "to" , Space , Str "show" , Space , Str "that" , Space , Str "they" , Space , Str "belong" , Space , Str "to" , Space , Str "the" , SoftBreak , Str "footnote" , Space , Str "(as" , Space , Str "with" , Space , Str "list" , Space , Str "items)." ] , CodeBlock ( "" , [] , [] ) " { }" , Para [ Str "If" , Space , Str "you" , Space , Str "want," , Space , Str "you" , Space , Str "can" , Space , Str "indent" , Space , Str "every" , Space , Str "line," , Space , Str "but" , Space , Str "you" , Space , Str "can" , Space , Str "also" , Space , Str "be" , SoftBreak , Str "lazy" , Space , Str "and" , Space , Str "just" , Space , Str "indent" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "of" , Space , Str "each" , Space , Str "block." ] ] , SoftBreak , Str "This" , Space , Str "should" , Space , Emph [ Str "not" ] , Space , Str "be" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Space , Str "because" , Space , Str "it" , SoftBreak , Str "contains" , Space , Str "a" , Space , Str "space.[^my" , Space , Str "note]" , Space , Str "Here" , Space , Str "is" , Space , Str "an" , Space , Str "inline" , Space , Str "note." , Note [ Para [ Str "This" , SoftBreak , Str "is" , Space , Emph [ Str "easier" ] , Space , Str "to" , Space , Str "type." , Space , Str "Inline" , Space , Str "notes" , Space , Str "may" , Space , Str "contain" , SoftBreak , Link ( "" , [] , [] ) [ Str "links" ] ( "http://google.com" , "" ) , Space , Str "and" , Space , Code ( "" , [] , [] ) "]" , Space , Str "verbatim" , Space , Str "characters," , SoftBreak , Str "as" , Space , Str "well" , Space , Str "as" , Space , Str "[bracketed" , Space , Str "text]." ] ] ] , BlockQuote [ Para [ Str "Notes" , Space , Str "can" , Space , Str "go" , Space , Str "in" , Space , Str "quotes." , Note [ Para [ Str "In" , Space , Str "quote." ] ] ] ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "And" , Space , Str "in" , Space , Str "list" , Space , Str "items." , Note [ Para [ Str "In" , Space , Str "list." ] ] ] ] ] , Para [ Str "This" , Space , Str "paragraph" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "part" , Space , Str "of" , Space , Str "the" , Space , Str "note," , Space , Str "as" , Space , Str "it" , Space , Str "is" , Space , Str "not" , Space , Str "indented." ] ] ================================================ FILE: test/testsuite.txt ================================================ % Pandoc Test Suite % John MacFarlane; Anonymous % July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. ----- # Headers ## Level 2 with an [embedded link](/url) ### Level 3 with *emphasis* #### Level 4 ##### Level 5 Level 1 ======= Level 2 with *emphasis* ----------------------- ### Level 3 with no blank line Level 2 ------- with no blank line ---------- # Paragraphs Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break here. --- # Block Quotes E-mail style: > This is a block quote. > It is pretty short. > Code in a block quote: > > sub status { > print "working"; > } > > A list: > > 1. item one > 2. item two > > Nested block quotes: > > > nested > >> nested > This should not be a block quote: 2 > 1. And a following paragraph. * * * * # Code Blocks Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ ___________ # Lists ## Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: + Plus 1 + Plus 2 + Plus 3 Pluses loose: + Plus 1 + Plus 2 + Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 ## Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. ## Nested * Tab * Tab * Tab Here's another: 1. First 2. Second: * Fee * Fie * Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: * Fee * Fie * Foe 3. Third ## Tabs and spaces + this is a list item indented with tabs + this is a list item indented with spaces + this is an example list item indented with tabs + this is an example list item indented with spaces ## Fancy list markers (2) begins with 2 (3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items (A) a subsublist (B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. (6) Decimal start with 6 c) Lower alpha with paren Autonumbering: #. Autonumber. #. More. #. Nested. Should not be a list item: M.A. 2007 B. Williams * * * * * # Definition Lists Tight using spaces: apple : red fruit orange : orange fruit banana : yellow fruit Tight using tabs: apple : red fruit orange : orange fruit banana : yellow fruit Loose: apple : red fruit orange : orange fruit banana : yellow fruit Multiple blocks with italics: *apple* : red fruit contains seeds, crisp, pleasant to taste *orange* : orange fruit { orange code block } > orange block quote Multiple definitions, tight: apple : red fruit : computer orange : orange fruit : bank Multiple definitions, loose: apple : red fruit : computer orange : orange fruit : bank Blank line after term, indented marker, alternate markers: apple ~ red fruit ~ computer orange ~ orange fruit 1. sublist 2. sublist # HTML Blocks Simple block on one line:
                foo
                And nested without indentation:
                foo
                bar
                Interpreted markdown in a table:
                This is *emphasized* And this is **strong**
                Here's a simple block:
                foo
                This should be a code block, though:
                foo
                As should this:
                foo
                Now, nested:
                foo
                This should just be an HTML comment: Multiline: Code block: Just plain comment, with trailing spaces on the line: Code:
                Hr's:








                ----- # Inline Markup This is *emphasized*, and so _is this_. This is **strong**, and so __is this__. An *[emphasized link](/url)*. ***This is strong and em.*** So is ***this*** word. ___This is strong and em.___ So is ___this___ word. This is code: `>`, `$`, `\`, `\$`, ``. ~~This is *strikeout*.~~ Superscripts: a^bc^d a^*hello*^ a^hello\ there^. Subscripts: H~2~O, H~23~O, H~many\ of\ them~O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ----- # Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70's? Here is some quoted '`code`' and a "[quoted link][1]". Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses...and...and.... ----- # LaTeX - \cite[22-23]{smith.1899} - $2+2=4$ - $x \in y$ - $\alpha \wedge \omega$ - $223$ - $p$-Tree - Here's some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ - Here's one that has a line break in it: $\alpha + \omega \times x^2$. These shouldn't be math: - To get the famous equation, write `$e = mc^2$`. - $22,000 is a *lot* of money. So is $34,000. (It worked if "lot" is emphasized.) - Shoes ($20) and socks ($5). - Escaped `$`: $73 *this should be emphasized* 23\$. Here's a LaTeX table: \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} * * * * * # Special Characters Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: \( Right paren: \) Greater-than: \> Hash: \# Period: \. Bang: \! Plus: \+ Minus: \- - - - - - - - - - - - - - # Links ## Explicit Just a [URL](/url/). [URL and title](/url/ "title"). [URL and title](/url/ "title preceded by two spaces"). [URL and title](/url/ "title preceded by a tab"). [URL and title](/url/ "title with "quotes" in it") [URL and title](/url/ 'title with single quotes') [with\_underscore](/url/with_underscore) [Email link](mailto:nobody@nowhere.net) [Empty](). ## Reference Foo [bar][a]. [a]: /url/ With [embedded [brackets]][b]. [b] by itself should be a link. Indented [once][]. Indented [twice][]. Indented [thrice][]. This should [not][] be a link. [once]: /url [twice]: /url [thrice]: /url [not]: /url [b]: /url/ Foo [bar][]. Foo [biz](/url/ "Title with "quote" inside"). [bar]: /url/ "Title with "quotes" inside" ## With ampersands Here's a [link with an ampersand in the URL][1]. Here's a link with an amersand in the link text: [AT&T][2]. Here's an [inline link](/script?foo=1&bar=2). Here's an [inline link in pointy braces](). [1]: http://example.com/?foo=1&bar=2 [2]: http://att.com/ "AT&T" ## Autolinks With an ampersand: * In a list? * * It should. An e-mail address: > Blockquoted: Auto-links should not occur here: `` or here: ---- # Images From "Voyage dans la Lune" by Georges Melies (1902): ![lalune][] [lalune]: lalune.jpg "Voyage dans la Lune" Here is a movie ![movie](movie.jpg) icon. ---- # Footnotes Here is a footnote reference,[^1] and another.[^longnote] This should *not* be a footnote reference, because it contains a space.[^my note] Here is an inline note.^[This is *easier* to type. Inline notes may contain [links](http://google.com) and `]` verbatim characters, as well as [bracketed text].] > Notes can go in quotes.^[In quote.] 1. And in list items.^[In list.] [^longnote]: Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). { } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. This paragraph should not be part of the note, as it is not indented. [^1]: Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. ================================================ FILE: test/textile-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc" , Space , Str "Textile" , Space , Str "Reader." , Space , Str "Part" , Space , Str "of" , Space , Str "it" , Space , Str "comes" , LineBreak , Str "from" , Space , Str "John" , Space , Str "Gruber\8217s" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , HorizontalRule , Header 1 ( "headers" , [] , [] ) [ Str "Headers" ] , Header 2 ( "level-2-with-an-embedded-link" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "http://www.example.com" , "" ) ] , Header 3 ( "level-3-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Strong [ Str "emphasis" ] ] , Header 4 ( "level-4" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Header 5 ( "level-5" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 6 ( "level-6" , [] , [] ) [ Str "Level" , Space , Str "6" ] , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "Line" , Space , Str "breaks" , Space , Str "are" , Space , Str "preserved" , Space , Str "in" , Space , Str "textile," , Space , Str "so" , Space , Str "you" , Space , Str "can" , Space , Str "not" , Space , Str "wrap" , Space , Str "your" , Space , Str "very" , LineBreak , Str "long" , Space , Str "paragraph" , Space , Str "with" , Space , Str "your" , Space , Str "favourite" , Space , Str "text" , Space , Str "editor" , Space , Str "and" , Space , Str "have" , Space , Str "it" , Space , Str "rendered" , LineBreak , Str "with" , Space , Str "no" , Space , Str "break." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." ] , BulletList [ [ Plain [ Str "criminey." ] ] ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "paragraph" , Space , Str "break" , Space , Str "between" , Space , Str "here" ] , Para [ Str "and" , Space , Str "here." ] , Para [ Str "pandoc" , Space , Str "converts" , Space , Str "textile." ] , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "famous" , Space , Str "quote" , Space , Str "from" , Space , Str "somebody." , Space , Str "He" , Space , Str "had" , Space , Str "a" , Space , Str "lot" , Space , Str "of" , Space , Str "things" , Space , Str "to" , LineBreak , Str "say," , Space , Str "so" , Space , Str "the" , Space , Str "text" , Space , Str "is" , Space , Str "really" , Space , Str "really" , Space , Str "long" , Space , Str "and" , Space , Str "spans" , Space , Str "on" , Space , Str "multiple" , Space , Str "lines." ] ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) " ---- (should be four hyphens)\n\n sub status {\n print \"working\";\n }\n\n this code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) " this code block is indented by two tabs\n\n These should not be escaped: \\$ \\\\ \\> \\[ \\{" , CodeBlock ( "" , [] , [] ) "Code block with .bc\n continued\n @" , Str "," , Space , Code ( "" , [] , [] ) "@" , Str "." ] , Header 1 ( "notextile" , [] , [] ) [ Str "Notextile" ] , Para [ Str "A" , Space , Str "block" , Space , Str "of" , Space , Str "text" , Space , Str "can" , Space , Str "be" , Space , Str "protected" , Space , Str "with" , Space , Str "notextile" , Space , Str ":" ] , Para [ Str "\nNo *bold* and\n* no bullet\n" ] , Para [ Str "and" , Space , Str "inlines" , Space , Str "can" , Space , Str "be" , Space , Str "protected" , Space , Str "with" , Space , Str "double *equals (=)* markup." ] , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] , [ Plain [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "With" , Space , Str "line" , Space , Str "breaks:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" , LineBreak , Str "newline" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second" ] ] , [ Plain [ Str "Third" ] ] ] , Header 2 ( "nested" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Plain [ Str "ui" , Space , Str "1" ] , BulletList [ [ Plain [ Str "ui" , Space , Str "1.1" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "oi" , Space , Str "1.1.1" ] ] , [ Plain [ Str "oi" , Space , Str "1.1.2" ] ] ] ] , [ Plain [ Str "ui" , Space , Str "1.2" ] ] ] ] , [ Plain [ Str "ui" , Space , Str "2" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "oi" , Space , Str "2.1" ] , BulletList [ [ Plain [ Str "ui" , Space , Str "2.1.1" ] ] , [ Plain [ Str "ui" , Space , Str "2.1.2" ] ] ] ] ] ] ] , Header 2 ( "issue-1500" , [] , [] ) [ Str "Issue" , Space , Str "#1500" ] , BulletList [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" , LineBreak , Str "->" , Space , Str "and" , Space , Str "more" ] ] ] , Header 2 ( "issue-1513" , [] , [] ) [ Str "Issue" , Space , Str "#1513" ] , Para [ Str "List:" ] , BulletList [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] ] ] , Header 2 ( "definition-list" , [] , [] ) [ Str "Definition" , Space , Str "List" ] , DefinitionList [ ( [ Str "coffee" ] , [ [ Plain [ Str "Hot" , Space , Str "and" , Space , Str "black" ] ] ] ) , ( [ Str "tea" ] , [ [ Plain [ Str "Also" , Space , Str "hot," , Space , Str "but" , Space , Str "a" , Space , Str "little" , Space , Str "less" , Space , Str "black" ] ] ] ) , ( [ Str "milk" ] , [ [ Para [ Str "Nourishing" , Space , Str "beverage" , Space , Str "for" , Space , Str "baby" , Space , Str "cows." ] , Para [ Str "Cold" , Space , Str "drink" , Space , Str "that" , Space , Str "goes" , Space , Str "great" , Space , Str "with" , Space , Str "cookies." ] ] ] ) , ( [ Str "beer" ] , [ [ Plain [ Str "fresh" , Space , Str "and" , Space , Str "bitter" ] ] ] ) ] , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , Space , Str "this" ] , Str "." , LineBreak , Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Strong [ Str "is" , Space , Str "this" ] , Str "." , LineBreak , Str "This" , Space , Str "is" , Space , Underline [ Str "inserted" ] , Str "," , Space , Str "and" , Space , Str "this" , Space , Str "is" , Space , Strikeout [ Str "deleted" ] , Str "." , LineBreak , Str "Hyphenated-words-are-ok," , Space , Str "as" , Space , Str "well" , Space , Str "as" , Space , Str "strange_underscore_notation." , LineBreak , Str "A" , Space , Link ( "" , [] , [] ) [ Strong [ Str "strong" , Space , Str "link" ] ] ( "http://www.foobar.com" , "" ) , Str "." ] , Para [ Emph [ Strong [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] , LineBreak , Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word" , Space , Str "and" , Space , Emph [ Strong [ Str "that" , Space , Str "one" ] ] , Str "." , LineBreak , Strikeout [ Str "This" , Space , Str "is" , Space , Str "strikeout" , Space , Str "and" , Space , Strong [ Str "strong" ] ] ] , Para [ Str "Superscripts:" , Space , Str "a" , Superscript [ Str "bc" ] , Str "d" , Space , Str "a" , Space , Superscript [ Strong [ Str "hello" ] ] , Space , Str "a" , Superscript [ Str "hello" , Space , Str "there" ] , Str "." , LineBreak , Str "Subscripts:" , Space , Subscript [ Str "here" ] , Space , Str "H" , Space , Subscript [ Str "2" ] , Str "O," , Space , Str "H" , Space , Subscript [ Str "23" ] , Str "O," , Space , Str "H" , Space , Subscript [ Str "many" , Space , Str "of" , Space , Str "them" ] , Str "O." ] , Para [ Str "Dashes" , Space , Str ":" , Space , Str "How" , Space , Str "cool" , Space , Str "\8212" , Space , Str "automatic" , Space , Str "dashes." ] , Para [ Str "Ellipses" , Space , Str ":" , Space , Str "He" , Space , Str "thought" , Space , Str "and" , Space , Str "thought" , Space , Str "\8230" , Space , Str "and" , Space , Str "then" , Space , Str "thought" , Space , Str "some" , Space , Str "more." ] , Para [ Str "Quotes" , Space , Str "and" , Space , Str "apostrophes" , Space , Str ":" , Space , Quoted DoubleQuote [ Str "I\8217d" , Space , Str "like" , Space , Str "to" , Space , Str "thank" , Space , Str "you" ] , Space , Str "for" , Space , Str "example." ] , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Header 2 ( "explicit" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "url" ] ( "http://www.url.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Email" , Space , Str "link" ] ( "mailto:nobody@nowhere.net" , "" ) ] , Para [ Quoted DoubleQuote [ Str "not" , Space , Str "a" , Space , Str "link" ] , Str ":" , Space , Str "foo" ] , Para [ Str "Automatic" , Space , Str "linking" , Space , Str "to" , Space , Link ( "" , [] , [] ) [ Str "http://www.example.com" ] ( "http://www.example.com" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "Example" ] ( "http://www.example.com/" , "" ) , Str ":" , Space , Str "Example" , Space , Str "of" , Space , Str "a" , Space , Str "link" , Space , Str "followed" , Space , Str "by" , Space , Str "a" , Space , Str "colon." ] , Para [ Str "A" , Space , Str "link" , Link ( "" , [] , [] ) [ Str "with" , Space , Str "brackets" ] ( "http://www.example.com" , "" ) , Str "and" , Space , Str "no" , Space , Str "spaces." ] , Para [ Str "A" , Space , Link ( "" , [] , [] ) [ Str "link" ] ( "ftp://example.com" , "" ) , Space , Str "to" , Space , Str "a" , Space , Str "named" , Space , Str "target" ] , Para [ Str "A" , Space , Link ( "" , [] , [] ) [ Str "link" ] ( "missing" , "" ) , Space , Str "to" , Space , Str "a" , Space , Str "missing" , Space , Str "target" ] , Header 1 ( "tables" , [] , [] ) [ Str "Tables" ] , Para [ Str "Textile" , Space , Str "allows" , Space , Str "tables" , Space , Str "with" , Space , Str "and" , Space , Str "without" , Space , Str "headers" , Space , Str ":" ] , Header 2 ( "without-headers" , [] , [] ) [ Str "Without" , Space , Str "headers" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "name" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "age" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "sex" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "joan" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "24" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "archie" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "29" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "m" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "bella" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "45" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "and" , Space , Str "some" , Space , Str "text" , Space , Str "following" , Space , Str "\8230" ] , Header 2 ( "with-headers" , [] , [] ) [ Str "With" , Space , Str "headers" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "name" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "age" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "sex" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "joan" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "24" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "archie" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "29" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "m" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "bella" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "45" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "Textile" , Space , Str "inline" , Space , Str "image" , Space , Str "syntax," , Space , Str "like" , LineBreak , Str "here" , Space , Image ( "" , [] , [] ) [ Str "this is the alt text" ] ( "this_is_an_image.png" , "this is the alt text" ) , LineBreak , Str "and" , Space , Str "here" , Space , Image ( "" , [] , [] ) [ Str "" ] ( "this_is_an_image.png" , "" ) , Str "." ] , Header 1 ( "attributes" , [] , [] ) [ Str "Attributes" ] , Header 2 ( "ident" , [ "bar" , "foo" ] , [ ( "style" , "color:red;" ) , ( "lang" , "en" ) ] ) [ Str "HTML" , Space , Str "and" , Space , Str "CSS" , Space , Str "attributes" , Space , Str "are" , Space , Str "parsed" , Space , Str "in" , Space , Str "headers." ] , Header 2 ( "centered" , [] , [ ( "style" , "text-align:center;" ) ] ) [ Str "Centered" ] , Header 2 ( "right" , [] , [ ( "style" , "text-align:right;" ) ] ) [ Str "Right" ] , Header 2 ( "justified" , [] , [ ( "lang" , "en" ) , ( "style" , "color:blue;text-align:justify;" ) ] ) [ Str "Justified" ] , Para [ Str "as" , Space , Str "well" , Space , Str "as" , Space , Strong [ Span ( "" , [ "foo" ] , [] ) [ Str "inline" , Space , Str "attributes" ] ] , Space , Str "of" , Space , Span ( "" , [] , [ ( "style" , "color:red;" ) ] ) [ Str "all" , Space , Str "kind" ] ] , Div ( "" , [] , [ ( "style" , "color:green;" ) ] ) [ Para [ Str "and" , Space , Str "paragraph" , Space , Str "attributes," , Space , Str "and" , Space , Str "table" , Space , Str "attributes." ] ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "name" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "age" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "sex" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "joan" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "24" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Emph [ Str "(class#id)" , Space , Str "emph" ] ] , Para [ Emph [ Str "(no" , Space , Str "class#id)" , Space , Str "emph" ] ] , Header 1 ( "entities" , [] , [] ) [ Str "Entities" ] , Para [ Str "*" , LineBreak , Str "&" ] , Header 1 ( "raw-html" , [] , [] ) [ Str "Raw" , Space , Str "HTML" ] , Para [ Str "However," , Space , RawInline (Format "html") "" , Space , Str "raw" , Space , Str "HTML" , Space , Str "inlines" , Space , RawInline (Format "html") "" , Space , Str "are" , Space , Str "accepted," , Space , Str "as" , Space , Str "well" , Space , Str "as" , Space , Str ":" ] , RawBlock (Format "html") "
                " , Para [ Str "any" , Space , Strong [ Str "Raw" , Space , Str "HTML" , Space , Str "Block" ] , Space , Str "with" , Space , Str "bold" ] , RawBlock (Format "html") "
                " , Para [ Str "Html" , Space , Str "blocks" , Space , Str "can" ] , RawBlock (Format "html") "
                " , Para [ Str "interrupt" , Space , Str "paragraphs" ] , RawBlock (Format "html") "
                " , Para [ Str "as" , Space , Str "well." ] , Para [ Str "Can" , Space , Str "you" , Space , Str "prove" , Space , Str "that" , Space , Str "2" , Space , Str "<" , Space , Str "3" , Space , Str "?" ] , Header 1 ( "acronyms-and-marks" , [] , [] ) [ Str "Acronyms" , Space , Str "and" , Space , Str "marks" ] , Para [ Str "PBS (Public Broadcasting System)" ] , Para [ Str "Hi\8482" ] , Para [ Str "Hi" , Space , Str "\8482" ] , Para [ Str "\174" , Space , Str "Hi\174" ] , Para [ Str "Hi\169\&2008" , Space , Str "\169" , Space , Str "2008" ] , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "A" , Space , Str "note." , Note [ Para [ Str "The" , Space , Str "note" , LineBreak , Str "is" , Space , Str "here!" ] ] , Space , Str "Another" , Space , Str "note" , Note [ Para [ Str "Other" , Space , Str "note." ] ] , Str "." ] , Header 1 ( "comment-blocks" , [] , [] ) [ Str "Comment" , Space , Str "blocks" ] , Para [ Str "not" , Space , Str "a" , Space , Str "comment." ] ] ================================================ FILE: test/textile-reader.textile ================================================ This is a set of tests for pandoc Textile Reader. Part of it comes from John Gruber's markdown test suite. ----- h1. Headers h2. Level 2 with an "embedded link":http://www.example.com h3. Level 3 with *emphasis* h4. Level 4 h5. Level 5 h6. Level 6 h1. Paragraphs Here's a regular paragraph. Line breaks are preserved in textile, so you can not wrap your very long paragraph with your favourite text editor and have it rendered with no break. Here's one with a bullet. * criminey. There should be a paragraph break between here and here. pandoc converts textile. h1. Block Quotes bq. This is a famous quote from somebody. He had a lot of things to say, so the text is really really long and spans on multiple lines. And a following paragraph. h1. Code Blocks Code:
                    ---- (should be four hyphens)
                
                    sub status {
                        print "working";
                    }
                
                	this code block is indented by one tab
                
                And:
                		this code block is indented by two tabs
                
                    These should not be escaped:  \$ \\ \> \[ \{
                
                bc. Code block with .bc continued @@, @. h1. Notextile A block of text can be protected with notextile : No *bold* and * no bullet and inlines can be protected with ==double *equals (=)* markup==. h1. Lists h2. Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 With line breaks: * asterisk 1 newline * asterisk 2 h2. Ordered Tight: # First # Second # Third h2. Nested * ui 1 ** ui 1.1 ### oi 1.1.1 ### oi 1.1.2 ** ui 1.2 * ui 2 ## oi 2.1 *** ui 2.1.1 *** ui 2.1.2 h2. Issue #1500 * one * two -> and more h2. Issue #1513 List: * one * two h2. Definition List - coffee := Hot and black - tea := Also hot, but a little less black - milk := Nourishing beverage for baby cows. Cold drink that goes great with cookies.=: - beer := fresh and bitter h1. Inline Markup This is _emphasized_, and so __is this__. This is *strong*, and so **is this**. This is +inserted+, and this is -deleted-. Hyphenated-words-are-ok, as well as strange_underscore_notation. A "*strong link*":http://www.foobar.com. _*This is strong and em.*_ So is *_this_* word and __**that one**__. -This is strikeout and *strong*- Superscripts: a[^bc^]d a ^*hello*^ a[^hello there^]. Subscripts: ~here~ H[ ~2~]O, H[ ~23~]O, H[ ~many of them~]O. Dashes : How cool -- automatic dashes. Ellipses : He thought and thought ... and then thought some more. Quotes and apostrophes : "I'd like to thank you" for example. h1. Links h2. Explicit Just a "url":http://www.url.com "Email link":mailto:nobody@nowhere.net "not a link": foo Automatic linking to "$":http://www.example.com. "Example":http://www.example.com/: Example of a link followed by a colon. A link["with brackets":http://www.example.com]and no spaces. A "link":link-target to a named target A "link":missing to a missing target h1. Tables Textile allows tables with and without headers : h2. Without headers | name | age | sex | | joan | 24 | f | | archie | 29 | m | | bella | 45 | f | and some text following ... h2. With headers |_. name |_. age |_. sex | | joan | 24 | f | | archie | 29 | m | | bella | 45 | f | h1. Images Textile inline image syntax, like here !this_is_an_image.png(this is the alt text)! and here !this_is_an_image.png!. h1. Attributes h2[en]{color:red}(foo bar #ident). HTML and CSS attributes are parsed in headers. h2=. Centered h2>. Right h2<>{color:blue}[en]. Justified as well as *(foo)inline attributes* of %{color:red}all kind% p{color:green}. and paragraph attributes, and table attributes. table{foo:bar}. | name | age | sex | | joan | 24 | f | _(class#id) emph_ _(no class#id) emph_ h1. Entities * & h1. Raw HTML However, raw HTML inlines are accepted, as well as :
                any *Raw HTML Block* with bold
                Html blocks can
                interrupt paragraphs
                as well. Can you prove that 2 < 3 ? h1. Acronyms and marks PBS(Public Broadcasting System) Hi(tm) Hi (TM) (r) Hi(r) Hi(c)2008 (C) 2008 h1. Footnotes A note.[1] Another note[2]. fn1. The note is here! fn2^. Other note. h1. Comment blocks ###. my comment is here. not a comment. [link-target]ftp://example.com ================================================ FILE: test/tikiwiki-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "header" , [] , [] ) [ Str "header" ] , Header 2 ( "header-level-two" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "two" ] , Header 3 ( "header-level-3" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "3" ] , Header 4 ( "header-_level_-four" , [] , [] ) [ Str "header" , Space , Str "_level_" , Space , Str "four" ] , Header 5 ( "header-level-5" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "5" ] , Header 6 ( "header-level-6" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "6" ] , Para [ Str "!!!!!!!" , Space , Str "not" , Space , Str "a" , Space , Str "header" ] , Para [ Str "--++" , Space , Str "not" , Space , Str "a" , Space , Str "header" ] , Header 1 ( "emph-and-strong" , [] , [] ) [ Str "emph" , Space , Str "and" , Space , Str "strong" ] , Para [ Emph [ Str "emph" ] , Space , Strong [ Str "strong" ] ] , Para [ Emph [ Strong [ Str "strong" , Space , Str "and" , Space , Str "emph" , Space , Str "1" ] ] ] , Para [ Strong [ Emph [ Str "strong" , Space , Str "and" , Space , Str "emph" , Space , Str "2" ] ] ] , Para [ Strong [ Emph [ Str "emph" , Space , Str "inside" ] , Space , Str "strong" ] ] , Para [ Strong [ Str "strong" , Space , Str "with" , Space , Emph [ Str "emph" ] ] ] , Para [ Emph [ Strong [ Str "strong" , Space , Str "inside" ] , Space , Str "emph" ] ] , Header 1 ( "other-inlines" , [] , [] ) [ Str "other" , Space , Str "inlines" ] , Para [ Underline [ Str "underlined" , Space , Str "text" ] ] , Para [ Strikeout [ Str "strikeout" ] ] , Header 1 ( "horizontal-rule" , [] , [] ) [ Str "horizontal" , Space , Str "rule" ] , Para [ Str "top" ] , HorizontalRule , Para [ Str "bottom" ] , HorizontalRule , Header 1 ( "nop" , [] , [] ) [ Str "nop" ] , Para [ Str "__not emph__" ] , Header 1 ( "entities" , [] , [] ) [ Str "entities" ] , Para [ Str "hi" , Space , Str "&" , Space , Str "low" ] , Para [ Str "hi" , Space , Str "&" , Space , Str "low" ] , Para [ Str "G\246del" ] , Para [ Str "\777\2730" ] , Header 1 ( "linebreaks" , [] , [] ) [ Str "linebreaks" ] , Para [ Str "hi" , LineBreak , Str "there" ] , Para [ Str "hi" , LineBreak , Str "there" ] , Header 1 ( "inline-code" , [] , [] ) [ Str "inline" , Space , Str "code" ] , Para [ Code ( "" , [] , [] ) "*\8594*" , Space , Code ( "" , [] , [] ) "typed" , Space , Code ( "" , [] , [] ) ">>=" ] , Header 1 ( "code-blocks" , [] , [] ) [ Str "code" , Space , Str "blocks" ] , CodeBlock ( "" , [] , [] ) "\ncase xs of\n (_:_) -> reverse xs\n [] -> ['*']\n" , CodeBlock ( "" , [ "haskell" ] , [ ( "colors" , "haskell" ) , ( "ln" , "0" ) ] ) "\ncase xs of\n (_:_) -> reverse xs\n [] -> ['*']\n" , Header 1 ( "external-links" , [] , [] ) [ Str "external" , Space , Str "links" ] , Para [ Link ( "" , [] , [] ) [ Emph [ Str "Google" ] , Space , Str "search" , Space , Str "engine" ] ( "http://google.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "http://pandoc.org" ] ( "http://pandoc.org" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "http://google.com" ] ( "http://google.com" , "" ) , Space , Link ( "" , [] , [] ) [ Str "http://yahoo.com" ] ( "http://yahoo.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "email" , Space , Str "me" ] ( "mailto:info@example.org" , "" ) ] , Para [ Str "http://google.com" ] , Para [ Str "info@example.org" ] , Header 1 ( "lists" , [] , [] ) [ Str "lists" ] , BulletList [ [ Plain [ Str "Start" , Space , Str "each" , Space , Str "line" ] ] , [ Plain [ Str "with" , Space , Str "an" , Space , Str "asterisk" , Space , Str "(*)." ] , BulletList [ [ Plain [ Str "More" , Space , Str "asterisks" , Space , Str "gives" , Space , Str "deeper" ] , BulletList [ [ Plain [ Str "and" , Space , Str "deeper" , Space , Str "levels." ] ] ] ] ] ] , [ Plain [ Str "Line" , Space , Str "breaks" , LineBreak , Str "don't" , Space , Str "break" , Space , Str "levels." ] ] , [ Plain [ Str "Continuations" , Space , Str "are" , Space , Str "also" , Space , Str "possible" ] , BulletList [ [ Plain [ Str "and" , Space , Str "do" , Space , Str "not" , Space , Str "break" , Space , Str "the" , Space , Str "list" , Space , Str "flow" ] ] ] ] , [ Plain [ Str "Level" , Space , Str "one" ] ] ] , Para [ Str "Any" , Space , Str "other" , Space , Str "start" , Space , Str "ends" , Space , Str "the" , Space , Str "list." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Start" , Space , Str "each" , Space , Str "line" ] ] , [ Plain [ Str "with" , Space , Str "a" , Space , Str "number" , Space , Str "(1.)." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "More" , Space , Str "number" , Space , Str "signs" , Space , Str "gives" , Space , Str "deeper" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "and" , Space , Str "deeper" ] ] , [ Plain [ Str "levels." ] ] ] ] ] ] , [ Plain [ Str "Line" , Space , Str "breaks" , LineBreak , Str "don't" , Space , Str "break" , Space , Str "levels." ] ] , [ Plain [ Str "Blank" , Space , Str "lines" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "end" , Space , Str "the" , Space , Str "list" , Space , Str "and" , Space , Str "start" , Space , Str "another." ] ] ] , Para [ Str "Any" , Space , Str "other" , Space , Str "start" , Space , Str "also" , Space , Str "ends" , Space , Str "the" , Space , Str "list." ] , DefinitionList [ ( [ Str "item" , Space , Str "1" ] , [ [ Plain [ Str "definition" , Space , Str "1" ] ] ] ) , ( [ Str "item" , Space , Str "2" ] , [ [ Plain [ Str "definition" , Space , Str "2-1" , Space , Str "definition" , Space , Str "2-2" ] ] ] ) , ( [ Str "item" , Space , Emph [ Str "3" ] ] , [ [ Plain [ Str "definition" , Space , Emph [ Str "3" ] ] ] ] ) ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] , BulletList [ [ Plain [ Str "two" , Space , Str "point" , Space , Str "one" ] ] , [ Plain [ Str "two" , Space , Str "point" , Space , Str "two" ] ] ] ] , [ Plain [ Str "three" ] ] , [ Plain [ Str "four" ] ] , [ Plain [ Str "five" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "five" , Space , Str "sub" , Space , Str "1" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "five" , Space , Str "sub" , Space , Str "1" , Space , Str "sub" , Space , Str "1" ] ] ] ] , [ Plain [ Str "five" , Space , Str "sub" , Space , Str "2" ] ] ] ] ] , Header 1 ( "tables" , [] , [] ) [ Str "tables" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apple" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Pie" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Butter" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apple" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Pie" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "Butter" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apple" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bread" , LineBreak , LineBreak , Str "and" , Space , Str "cheese" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Pie" , LineBreak , LineBreak , Strong [ Str "apple" ] , Space , Str "and" , Space , Emph [ Str "carrot" ] , Space ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "Orange" , Space ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "Apple" , Space ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "more" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "Bread" , Space ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "Pie" , Space ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "more" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "Butter" , Space ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "Ice" , Space , Str "cream" , Space ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Space , Str "and" , Space , Str "more" , Space ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: test/tikiwiki-reader.tikiwiki ================================================ ! header !! header level two !!! header level 3 !!!! header _level_ four !!!!! header level 5 !!!!!! header level 6 !!!!!!! not a header --++ not a header ! emph and strong ''emph'' __strong__ ''__strong and emph 1__'' __''strong and emph 2''__ __''emph inside'' strong__ __strong with ''emph''__ ''__strong inside__ emph'' ! other inlines ===underlined text=== --strikeout-- ! horizontal rule top ---- bottom ---- ! nop ~np~__not emph__~/np~ ! entities hi & low hi & low Gödel ̉પ ! linebreaks hi%%%there hi%%% there ! inline code -+*→*+- -+typed+- -+>>=+- ! code blocks {CODE()} case xs of (_:_) -> reverse xs [] -> ['*'] {CODE} {CODE(colors="haskell" ln=0)} case xs of (_:_) -> reverse xs [] -> ['*'] {CODE} ! external links [http://google.com|''Google'' search engine] [http://pandoc.org] [http://google.com] [http://yahoo.com] [mailto:info@example.org|email me] http://google.com info@example.org ! lists * Start each line * with an asterisk (*). ** More asterisks gives deeper *** and deeper levels. * Line breaks%%%don't break levels. * Continuations + are also possible ** and do not break the list flow * Level one Any other start ends the list. # Start each line # with a number (1.). ## More number signs gives deeper ### and deeper ### levels. # Line breaks%%%don't break levels. # Blank lines # end the list and start another. Any other start also ends the list. ;item 1: definition 1 ;item 2: definition 2-1 + definition 2-2 ;item ''3'': definition ''3'' # one # two ** two point one ** two point two # three # four # five ## five sub 1 ### five sub 1 sub 1 ## five sub 2 ! tables ||Orange|Apple Bread|Pie Butter|Ice cream|| ||Orange|Apple Bread|Pie __Butter__|Ice cream|| ||Orange|Apple Bread%%%%%%and cheese|Pie%%%%%%__apple__ and ''carrot'' || || Orange | Apple | more Bread | Pie | more Butter | Ice cream | and more || ================================================ FILE: test/twiki-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "header" , [] , [] ) [ Str "header" ] , Header 2 ( "header-level-two" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "two" ] , Header 3 ( "header-level-3" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "3" ] , Header 4 ( "header-level-four" , [] , [] ) [ Str "header" , Space , Emph [ Str "level" ] , Space , Str "four" ] , Header 5 ( "header-level-5" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "5" ] , Header 6 ( "header-level-6" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "6" ] , Para [ Str "---+++++++" , Space , Str "not" , Space , Str "a" , Space , Str "header" ] , Para [ Str "--++" , Space , Str "not" , Space , Str "a" , Space , Str "header" ] , Header 1 ( "emph-and-strong" , [] , [] ) [ Str "emph" , Space , Str "and" , Space , Str "strong" ] , Para [ Emph [ Str "emph" ] , Space , Strong [ Str "strong" ] ] , Para [ Emph [ Strong [ Str "strong" , Space , Str "and" , Space , Str "emph" ] ] ] , Para [ Strong [ Emph [ Str "emph" , Space , Str "inside" ] , Space , Str "strong" ] ] , Para [ Strong [ Str "strong" , Space , Str "with" , Space , Emph [ Str "emph" ] ] ] , Para [ Emph [ Strong [ Str "strong" , Space , Str "inside" ] , Space , Str "emph" ] ] , Header 1 ( "horizontal-rule" , [] , [] ) [ Str "horizontal" , Space , Str "rule" ] , Para [ Str "top" ] , HorizontalRule , Para [ Str "bottom" ] , HorizontalRule , Header 1 ( "nop" , [] , [] ) [ Str "nop" ] , Para [ Str "_not" , Space , Str "emph_" ] , Header 1 ( "entities" , [] , [] ) [ Str "entities" ] , Para [ Str "hi" , Space , Str "&" , Space , Str "low" ] , Para [ Str "hi" , Space , Str "&" , Space , Str "low" ] , Para [ Str "G\246del" ] , Para [ Str "\777\2730" ] , Header 1 ( "comments" , [] , [] ) [ Str "comments" ] , Para [ Str "inline" , Space , Str "comment" ] , Para [ Str "between" , Space , Str "blocks" ] , Header 1 ( "linebreaks" , [] , [] ) [ Str "linebreaks" ] , Para [ Str "hi" , LineBreak , Str "there" ] , Para [ Str "hi" , LineBreak , Str "there" ] , Header 1 ( "inline-code" , [] , [] ) [ Str "inline" , Space , Str "code" ] , Para [ Code ( "" , [] , [] ) "*\8594*" , Space , Code ( "" , [] , [] ) "typed" , Space , Code ( "" , [ "haskell" ] , [] ) ">>=" ] , Header 1 ( "code-blocks" , [] , [] ) [ Str "code" , Space , Str "blocks" ] , CodeBlock ( "" , [] , [] ) "case xs of\n (_:_) -> reverse xs\n [] -> ['*']" , CodeBlock ( "" , [ "haskell" ] , [] ) "case xs of\n (_:_) -> reverse xs\n [] -> ['*']" , Header 1 ( "block-quotes" , [] , [] ) [ Str "block" , Space , Str "quotes" ] , Para [ Str "Regular" , Space , Str "paragraph" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." ] , Para [ Str "With" , Space , Str "two" , Space , Str "paragraphs." ] ] , Para [ Str "Nother" , Space , Str "paragraph." ] , Header 1 ( "internal-links" , [] , [] ) [ Str "internal" , Space , Str "links" ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "MySimplePage" ] ( "MySimplePage" , "" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "My23Page23" ] ( "My23Page23" , "" ) ] , Header 1 ( "external-links" , [] , [] ) [ Str "external" , Space , Str "links" ] , Para [ Link ( "" , [] , [] ) [ Emph [ Str "Google" ] , Space , Str "search" , Space , Str "engine" ] ( "http://google.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "http://pandoc.org" ] ( "http://pandoc.org" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "http://google.com" ] ( "http://google.com" , "" ) , Space , Link ( "" , [] , [] ) [ Str "http://yahoo.com" ] ( "http://yahoo.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "email" , Space , Str "me" ] ( "mailto:info@example.org" , "" ) ] , Para [ Str "http://google.com" ] , Para [ Str "http://google.com" ] , Para [ Str "http://google.com" ] , Para [ Str "info@example.org" ] , Para [ Str "info@example.org" ] , Para [ Str "info@example.org" ] , Header 1 ( "lists" , [] , [] ) [ Str "lists" ] , BulletList [ [ Plain [ Str "Start" , Space , Str "each" , Space , Str "line" ] ] , [ Plain [ Str "with" , Space , Str "an" , Space , Str "asterisk" , Space , Str "(*)." ] , BulletList [ [ Plain [ Str "More" , Space , Str "asterisks" , Space , Str "gives" , Space , Str "deeper" ] , BulletList [ [ Plain [ Str "and" , Space , Str "deeper" , Space , Str "levels." ] ] ] ] ] ] , [ Plain [ Str "Line" , Space , Str "breaks" , LineBreak , Str "don't" , Space , Str "break" , Space , Str "levels." ] ] , [ Plain [ Str "Continuations" , Space , Str "are" , Space , Str "also" , Space , Str "possible" ] , BulletList [ [ Plain [ Str "and" , Space , Str "do" , Space , Str "not" , Space , Str "break" , Space , Str "the" , Space , Str "list" , Space , Str "flow" ] ] ] ] , [ Plain [ Str "Level" , Space , Str "one" ] ] ] , Para [ Str "Any" , Space , Str "other" , Space , Str "start" , Space , Str "ends" , Space , Str "the" , Space , Str "list." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Start" , Space , Str "each" , Space , Str "line" ] ] , [ Plain [ Str "with" , Space , Str "a" , Space , Str "number" , Space , Str "(1.)." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "More" , Space , Str "number" , Space , Str "signs" , Space , Str "gives" , Space , Str "deeper" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "and" , Space , Str "deeper" ] ] , [ Plain [ Str "levels." ] ] ] ] ] ] , [ Plain [ Str "Line" , Space , Str "breaks" , LineBreak , Str "don't" , Space , Str "break" , Space , Str "levels." ] ] , [ Plain [ Str "Blank" , Space , Str "lines" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "end" , Space , Str "the" , Space , Str "list" , Space , Str "and" , Space , Str "start" , Space , Str "another." ] ] ] , Para [ Str "Any" , Space , Str "other" , Space , Str "start" , Space , Str "also" , Space , Str "ends" , Space , Str "the" , Space , Str "list." ] , DefinitionList [ ( [ Str "item" , Space , Str "1" ] , [ [ Plain [ Str "definition" , Space , Str "1" ] ] ] ) , ( [ Str "item" , Space , Str "2" ] , [ [ Plain [ Str "definition" , Space , Str "2-1" , Space , Str "definition" , Space , Str "2-2" ] ] ] ) , ( [ Str "item" , Space , Emph [ Str "3" ] ] , [ [ Plain [ Str "definition" , Space , Emph [ Str "3" ] ] ] ] ) ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] , BulletList [ [ Plain [ Str "two" , Space , Str "point" , Space , Str "one" ] ] , [ Plain [ Str "two" , Space , Str "point" , Space , Str "two" ] ] ] ] , [ Plain [ Str "three" ] , DefinitionList [ ( [ Str "three" , Space , Str "item" , Space , Str "one" ] , [ [ Plain [ Str "three" , Space , Str "def" , Space , Str "one" ] ] ] ) ] ] , [ Plain [ Str "four" ] , DefinitionList [ ( [ Str "four" , Space , Str "def" , Space , Str "one" ] , [ [ Plain [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "continuation" ] ] ] ) ] ] , [ Plain [ Str "five" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "five" , Space , Str "sub" , Space , Str "1" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "five" , Space , Str "sub" , Space , Str "1" , Space , Str "sub" , Space , Str "1" ] ] ] ] , [ Plain [ Str "five" , Space , Str "sub" , Space , Str "2" ] ] ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "other" ] , OrderedList ( 1 , UpperRoman , DefaultDelim ) [ [ Plain [ Str "list" ] ] , [ Plain [ Str "styles" ] ] ] ] , [ Plain [ Str "are" ] , OrderedList ( 1 , LowerRoman , DefaultDelim ) [ [ Plain [ Str "also" ] ] , [ Plain [ Str "possible" ] ] ] ] , [ Plain [ Str "all" ] , OrderedList ( 1 , LowerAlpha , DefaultDelim ) [ [ Plain [ Str "the" ] ] , [ Plain [ Str "different" ] ] , [ Plain [ Str "styles" ] ] ] ] , [ Plain [ Str "are" ] , OrderedList ( 1 , UpperAlpha , DefaultDelim ) [ [ Plain [ Str "implemented" ] ] , [ Plain [ Str "and" ] ] , [ Plain [ Str "supported" ] ] ] ] ] , Header 1 ( "tables" , [] , [] ) [ Str "tables" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apple" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Pie" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Butter" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apple" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Pie" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "Butter" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Ice" , Space , Str "cream" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apple" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bread" , LineBreak , LineBreak , Str "and" , Space , Str "cheese" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Pie" , LineBreak , LineBreak , Strong [ Str "apple" ] , Space , Str "and" , Space , Emph [ Str "carrot" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Orange" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apple" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "more" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Bread" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Pie" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "more" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Butter" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Ice" , Space , Str "cream" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "and" , Space , Str "more" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 1 ( "macros" , [] , [] ) [ Str "macros" ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [] ) [] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [] ) [ Str "" ] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [] ) [ Str "content with spaces" ] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [] ) [ Str "content with spaces" ] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [ ( "ARG1" , "test" ) ] ) [ Str "content with spaces" ] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [] ) [ Str "content with spaces ARG1=test" ] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [ ( "ARG1" , "test" ) ] ) [ Str "content with spaces" ] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [ ( "ARG1" , "test" ) , ( "ARG2" , "test2" ) ] ) [ Str "" ] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [ ( "ARG1" , "test" ) , ( "ARG2" , "test2" ) ] ) [ Str "" ] ] , Para [ Span ( "" , [ "twiki-macro" , "TEST" ] , [ ( "ARG1" , "test" ) , ( "ARG2" , "test2" ) ] ) [ Str "multiline\ndoes also work" ] ] ] ================================================ FILE: test/twiki-reader.twiki ================================================ ---+ header ---++ header level two ---+++ header level 3 ---++++ header _level_ four ---+++++ header level 5 ---++++++ header level 6 ---+++++++ not a header --++ not a header ---+ emph and strong _emph_ *strong* __strong and emph__ *emph inside strong* *strong with emph* _strong inside emph_ ---+ horizontal rule top --- bottom --- ---+ nop _not emph_ ---+ entities hi & low hi & low Gödel ̉પ ---+ comments inline comment between blocks ---+ linebreaks hi%BR%there hi%BR% there ---+ inline code *→* =typed= >>= ---+ code blocks case xs of (_:_) -> reverse xs [] -> ['*'] case xs of (_:_) -> reverse xs [] -> ['*'] ---+ block quotes Regular paragraph
                This is a block quote. With two paragraphs.
                Nother paragraph. ---+ internal links MySimplePage My23Page23 ---+ external links [[http://google.com][Google search engine]] http://pandoc.org [[http://google.com]] [[http://yahoo.com]] [[mailto:info@example.org][email me]] !http://google.com http://google.com http://google.com !info@example.org info@example.org info@example.org ---+ lists * Start each line * with an asterisk (*). * More asterisks gives deeper * and deeper levels. * Line breaks%BR%don't break levels. * Continuations are also possible * and do not break the list flow * Level one Any other start ends the list. 1. Start each line 1. with a number (1.). 1. More number signs gives deeper 1. and deeper 1. levels. 1. Line breaks%BR%don't break levels. 1. Blank lines 1. end the list and start another. Any other start also ends the list. $ item 1: definition 1 $ item 2: definition 2-1 definition 2-2 $ item _3_: definition _3_ 1. one 1. two * two point one * two point two 1. three $ three item one: three def one 1. four $ four def one: this is a continuation 1. five 1. five sub 1 1. five sub 1 sub 1 1. five sub 2 1. other I. list I. styles 1. are i. also i. possible 1. all a. the a. different a. styles 1. are A. implemented A. and A. supported ---+ tables |Orange|Apple| |Bread|Pie| |Butter|Ice cream| |*Orange*|*Apple*| |Bread|Pie| |*Butter*|Ice cream| |*Orange*|*Apple*| |Bread%BR%%BR%and cheese|Pie%BR%%BR%*apple* and carrot| | Orange | Apple | more | | Bread | Pie | more | | Butter | Ice cream | and more | ---+ macros %TEST% %TEST{}% %TEST{content with spaces}% %TEST{"content with spaces"}% %TEST{"content with spaces" ARG1="test"}% %TEST{content with spaces ARG1=test}% %TEST{ARG1=test content with spaces}% %TEST{ARG1=test ARG2=test2}% %TEST{ARG1="test" ARG2="test2"}% %TEST{ARG1="test" ARG2="test2" multiline does also work}% ================================================ FILE: test/txt2tags.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "author" ] ] ) , ( "date" , MetaInlines [ Str "date" ] ) , ( "includeconf" , MetaString "rules.conf" ) , ( "title" , MetaInlines [ Str "Txt2tags" , Space , Str "Markup" , Space , Str "Rules" ] ) ] } [ Para [ Str "This" , Space , Str "document" , Space , Str "describes" , Space , Str "all" , Space , Str "the" , Space , Str "details" , Space , Str "about" , Space , Str "each" , Space , Str "txt2tags" , Space , Str "mark." , SoftBreak , Str "The" , Space , Str "target" , Space , Str "audience" , Space , Str "are" , Space , Strong [ Str "experienced" ] , Space , Str "users." , Space , Str "You" , Space , Str "may" , Space , Str "find" , Space , Str "it" , SoftBreak , Str "useful" , Space , Str "if" , Space , Str "you" , Space , Str "want" , Space , Str "to" , Space , Str "master" , Space , Str "the" , Space , Str "marks" , Space , Str "or" , Space , Str "solve" , Space , Str "a" , Space , Str "specific" , Space , Str "problem" , SoftBreak , Str "about" , Space , Str "a" , Space , Str "mark." ] , Para [ Str "If" , Space , Str "you" , Space , Str "are" , Space , Str "new" , Space , Str "to" , Space , Str "txt2tags" , Space , Str "or" , Space , Str "just" , Space , Str "want" , Space , Str "to" , Space , Str "know" , Space , Str "which" , Space , Str "are" , Space , Str "the" , SoftBreak , Str "available" , Space , Str "marks," , Space , Str "please" , Space , Str "read" , Space , Str "the" , Space , Link ( "" , [] , [] ) [ Str "Markup" , Space , Str "Demo" ] ( "MARKUPDEMO" , "" ) , Str "." ] , Para [ Str "Note" , Space , Str "1:" , Space , Str "This" , Space , Str "document" , Space , Str "is" , Space , Str "generated" , Space , Str "directly" , Space , Str "from" , Space , Str "the" , Space , Str "txt2tags" , SoftBreak , Str "test-suite." , Space , Str "All" , Space , Str "the" , Space , Str "rules" , Space , Str "mentioned" , Space , Str "here" , Space , Str "are" , Space , Str "100%" , Space , Str "in" , Space , Str "sync" , Space , Str "with" , Space , Str "the" , SoftBreak , Str "current" , Space , Str "program" , Space , Str "code." ] , Para [ Str "Note" , Space , Str "2:" , Space , Str "A" , Space , Str "good" , Space , Str "practice" , Space , Str "is" , Space , Str "to" , Space , Str "consult" , Space , Link ( "" , [] , [] ) [ Str "the" , Space , Str "sources" ] ( "rules.t2t" , "" ) , Space , Str "when" , SoftBreak , Str "reading," , Space , Str "to" , Space , Str "see" , Space , Str "how" , Space , Str "the" , Space , Str "texts" , Space , Str "were" , Space , Str "made." ] , Para [ Str "Table" , Space , Str "of" , Space , Str "Contents:" ] , HorizontalRule , Header 1 ( "paragraph" , [] , [] ) [ Str "Paragraph" ] , Para [ Str "A" , Space , Str "paragraph" , Space , Str "is" , Space , Str "composed" , Space , Str "by" , Space , Str "one" , Space , Str "or" , Space , Str "more" , Space , Str "lines." , SoftBreak , Str "A" , Space , Str "blank" , Space , Str "line" , Space , Str "(or" , Space , Str "a" , Space , Str "table," , Space , Str "or" , Space , Str "a" , Space , Str "list)" , Space , Str "ends" , Space , Str "the" , SoftBreak , Str "current" , Space , Str "paragraph." ] , Para [ Str "Leading" , Space , Str "and" , Space , Str "trailing" , Space , Str "spaces" , Space , Str "are" , Space , Str "ignored." ] , Para [ Str "A" , Space , Str "comment" , Space , Str "line" , Space , Str "can" , Space , Str "be" , Space , Str "placed" , Space , Str "inside" , Space , Str "a" , Space , Str "paragraph." , SoftBreak , Str "It" , Space , Str "will" , Space , Str "not" , Space , Str "affect" , Space , Str "it." ] , Para [ Str "The" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "file" , Space , Str "(EOF)" , Space , Str "closes" , Space , Str "the" , SoftBreak , Str "currently" , Space , Str "open" , Space , Str "paragraph." ] , Header 1 ( "comment" , [] , [] ) [ Str "Comment" ] , Para [ Str "%" , Space , Str "not" , Space , Str "on" , Space , Str "the" , Space , Str "line" , Space , Str "beginning" , Space , Str "(at" , Space , Str "column" , Space , Str "2)" ] , Para [ Str "some" , Space , Str "text" , Space , Str "%" , Space , Str "half" , Space , Str "line" , Space , Str "comments" , Space , Str "are" , Space , Str "not" , Space , Str "allowed" ] , Header 1 ( "line" , [] , [] ) [ Str "Line" ] , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , HorizontalRule , Para [ Strikeout [ Str "-----" ] , SoftBreak , Strikeout [ Str "-------" , Space , Str "--------" ] ] , Para [ Strikeout [ Str "-------+--------" ] ] , Para [ Str "(" , Space , Strikeout [ Str "----------------" ] , Space , Str ")" ] , Header 1 ( "inline" , [] , [] ) [ Str "Inline" ] , Para [ Str "i)" , Space , Strong [ Str "b" ] , Space , Emph [ Str "i" ] , Space , Underline [ Str "u" ] , Space , Strikeout [ Str "s" ] , Space , Code ( "" , [] , [] ) "m" , Space , Str "r" , Space , RawInline (Format "html") "t" , SoftBreak , Str "i)" , Space , Strong [ Str "bo" ] , Space , Emph [ Str "it" ] , Space , Underline [ Str "un" ] , Space , Strikeout [ Str "st" ] , Space , Code ( "" , [] , [] ) "mo" , Space , Str "ra" , Space , RawInline (Format "html") "tg" , SoftBreak , Str "i)" , Space , Strong [ Str "bold" ] , Space , Emph [ Str "ital" ] , Space , Underline [ Str "undr" ] , Space , Strikeout [ Str "strk" ] , Space , Code ( "" , [] , [] ) "mono" , Space , Str "raw" , Space , RawInline (Format "html") "tggd" , SoftBreak , Str "i)" , Space , Strong [ Str "bo" , Space , Str "ld" ] , Space , Emph [ Str "it" , Space , Str "al" ] , Space , Underline [ Str "un" , Space , Str "dr" ] , Space , Strikeout [ Str "st" , Space , Str "rk" ] , Space , Code ( "" , [] , [] ) "mo no" , Space , Str "r" , Space , Str "aw" , Space , RawInline (Format "html") "tg gd" , SoftBreak , Str "i)" , Space , Strong [ Str "bo" , Space , Str "*" , Space , Str "ld" ] , Space , Emph [ Str "it" , Space , Str "/" , Space , Str "al" ] , Space , Underline [ Str "un" , Space , Str "_" , Space , Str "dr" ] , Space , Strikeout [ Str "st" , Space , Str "-" , Space , Str "rk" ] , Space , Code ( "" , [] , [] ) "mo ` no" , Space , Str "r" , Space , Str "\"" , Space , Str "aw" , Space , RawInline (Format "html") "tg ' gd" , SoftBreak , Str "i)" , Space , Strong [ Str "bo" , Space , Str "**ld" ] , Space , Emph [ Str "it" , Space , Str "//al" ] , Space , Underline [ Str "un" , Space , Str "__dr" ] , Space , Strikeout [ Str "st" , Space , Str "--rk" ] , Space , Code ( "" , [] , [] ) "mo ``no" , Space , Str "r" , Space , Str "\"\"aw" , Space , RawInline (Format "html") "tg ''gd" , SoftBreak , Str "i)" , Space , Strong [ Str "bo" , Space , Str "**" , Space , Str "ld" ] , Space , Emph [ Str "it" , Space , Str "//" , Space , Str "al" ] , Space , Underline [ Str "un" , Space , Str "__" , Space , Str "dr" ] , Space , Strikeout [ Str "st" , Space , Str "--" , Space , Str "rk" ] , Space , Code ( "" , [] , [] ) "mo `` no" , Space , Str "r" , Space , Str "\"\"" , Space , Str "aw" , Space , RawInline (Format "html") "tg '' gd" , SoftBreak , Str "i)" , Space , Strong [ Str "**bold**" ] , Space , Emph [ Str "//ital//" ] , Space , Underline [ Str "__undr__" ] , Space , Strikeout [ Str "--strk--" ] , Space , Code ( "" , [] , [] ) "``mono``" , Space , Str "\"\"raw\"\"" , Space , RawInline (Format "html") "''tggd''" , SoftBreak , Str "i)" , Space , Strong [ Str "*bold*" ] , Space , Emph [ Str "/ital/" ] , Space , Underline [ Str "_undr_" ] , Space , Strikeout [ Str "-strk-" ] , Space , Code ( "" , [] , [] ) "`mono`" , Space , Str "\"raw\"" , Space , RawInline (Format "html") "'tggd'" ] , Para [ Str "i)" , Space , Strong [ Str "*" ] , Space , Emph [ Str "/" ] , Space , Underline [ Str "_" ] , Space , Strikeout [ Str "-" ] , Space , Code ( "" , [] , [] ) "`" , Space , Str "\"" , Space , RawInline (Format "html") "'" , SoftBreak , Str "i)" , Space , Strong [ Str "**" ] , Space , Emph [ Str "//" ] , Space , Underline [ Str "__" ] , Space , Strikeout [ Str "--" ] , Space , Code ( "" , [] , [] ) "``" , Space , Str "\"\"" , Space , RawInline (Format "html") "''" , SoftBreak , Str "i)" , Space , Strong [ Str "***" ] , Space , Emph [ Str "///" ] , Space , Underline [ Str "___" ] , Space , Strikeout [ Str "---" ] , Space , Code ( "" , [] , [] ) "```" , Space , Str "\"\"\"" , Space , RawInline (Format "html") "'''" , SoftBreak , Str "i)" , Space , Strong [ Str "****" ] , Space , Emph [ Str "////" ] , Space , Underline [ Str "____" ] , Space , Strikeout [ Str "----" ] , Space , Code ( "" , [] , [] ) "````" , Space , Str "\"\"\"\"" , Space , RawInline (Format "html") "''''" , SoftBreak , Str "i)" , Space , Strong [ Str "*****" ] , Space , Emph [ Str "/////" ] , Space , Underline [ Str "_____" ] , Space , Strikeout [ Str "-----" ] , Space , Code ( "" , [] , [] ) "`````" , Space , Str "\"\"\"\"\"" , Space , RawInline (Format "html") "'''''" , SoftBreak , Str "i)" , Space , Strong [ Str "******" ] , Space , Emph [ Str "//////" ] , Space , Underline [ Str "______" ] , Space , Strikeout [ Str "------" ] , Space , Code ( "" , [] , [] ) "``````" , Space , Str "\"\"\"\"\"\"" , Space , RawInline (Format "html") "''''''" ] , Para [ Str "i)" , Space , Str "****" , Space , Str "////" , Space , Str "____" , Space , Str "----" , Space , Str "````" , Space , Str "\"\"\"\"" , Space , Str "''''" , SoftBreak , Str "i)" , Space , Str "**" , Space , Str "**" , Space , Str "//" , Space , Str "//" , Space , Str "__" , Space , Str "__" , Space , Str "--" , Space , Str "--" , Space , Str "``" , Space , Str "``" , Space , Str "\"\"" , Space , Str "\"\"" , Space , Str "''" , Space , Str "''" ] , Para [ Str "i)" , Space , Str "**" , Space , Str "bold**" , Space , Str "//" , Space , Str "ital//" , Space , Str "__" , Space , Str "undr__" , Space , Str "--" , Space , Str "strk--" , Space , Str "``" , Space , Str "mono``" , Space , Str "\"\"" , Space , Str "raw\"\"" , Space , Str "''" , Space , Str "tggd''" , SoftBreak , Str "i)" , Space , Str "**bold" , Space , Str "**" , Space , Str "//ital" , Space , Str "//" , Space , Str "__undr" , Space , Str "__" , Space , Str "--strk" , Space , Str "--" , Space , Str "``mono" , Space , Str "``" , Space , Str "\"\"raw" , Space , Str "\"\"" , Space , Str "''tggd" , Space , Str "''" , SoftBreak , Str "i)" , Space , Str "**" , Space , Str "bold" , Space , Str "**" , Space , Str "//" , Space , Str "ital" , Space , Str "//" , Space , Str "__" , Space , Str "undr" , Space , Str "__" , Space , Str "--" , Space , Str "strk" , Space , Str "--" , Space , Str "``" , Space , Str "mono" , Space , Str "``" , Space , Str "\"\"" , Space , Str "raw" , Space , Str "\"\"" , Space , Str "''" , Space , Str "tggd" , Space , Str "''" ] , Header 1 ( "link" , [] , [] ) [ Str "Link" ] , Para [ Link ( "" , [] , [] ) [ Str "mailto:user@domain.com" ] ( "user@domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com" ] ( "user@domain.com" , "" ) , Str "." , SoftBreak , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com" ] ( "user@domain.com" , "" ) , Str "." , Space , Str "any" , Space , Str "text." , SoftBreak , Str "any" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com" ] ( "user@domain.com" , "" ) , Str "." , Space , Str "any" , Space , Str "text." , SoftBreak , Link ( "" , [] , [] ) [ Str "label" ] ( "user@domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com?subject=bla" ] ( "user@domain.com?subject=bla" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com?subject=bla" ] ( "user@domain.com?subject=bla" , "" ) , Str "." , SoftBreak , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com?subject=bla" ] ( "user@domain.com?subject=bla" , "" ) , Str "," , SoftBreak , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com?subject=bla&cc=otheruser@domain.com" ] ( "user@domain.com?subject=bla&cc=otheruser@domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com?subject=bla&cc=otheruser@domain.com" ] ( "user@domain.com?subject=bla&cc=otheruser@domain.com" , "" ) , Str "." , SoftBreak , Link ( "" , [] , [] ) [ Str "mailto:user@domain.com?subject=bla&cc=otheruser@domain.com" ] ( "user@domain.com?subject=bla&cc=otheruser@domain.com" , "" ) , Str "," , SoftBreak , Link ( "" , [] , [] ) [ Str "label" ] ( "user@domain.com?subject=bla&cc=otheruser@domain.com" , "" ) , Str "." , SoftBreak , Link ( "" , [] , [] ) [ Str "label" ] ( "user@domain.com?subject=bla&cc=otheruser@domain.com." , "" ) , Str "." , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com" ] ( "http://www.domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/" ] ( "http://www.domain.com/dir/" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir///" ] ( "http://www.domain.com/dir///" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com." ] ( "http://www.domain.com." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com," ] ( "http://www.domain.com," , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com." ] ( "http://www.domain.com." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com," ] ( "http://www.domain.com," , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/." ] ( "http://www.domain.com/dir/." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Str "any" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "http://www.domain.com." ] ( "http://www.domain.com." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Str "any" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/." ] ( "http://www.domain.com/dir/." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Str "any" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/index.html." ] ( "http://www.domain.com/dir/index.html." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Str "any" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/index.html," ] ( "http://www.domain.com/dir/index.html," , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/#anchor" ] ( "http://www.domain.com/dir/#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/index.html#anchor" ] ( "http://www.domain.com/dir/index.html#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/index.html#anchor." ] ( "http://www.domain.com/dir/index.html#anchor." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/#anchor." ] ( "http://www.domain.com/dir/#anchor." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/index.html#anchor." ] ( "http://www.domain.com/dir/index.html#anchor." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Str "any" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/#anchor." ] ( "http://www.domain.com/dir/#anchor." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Str "any" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "http://www.domain.com/dir/index.html#anchor." ] ( "http://www.domain.com/dir/index.html#anchor." , "" ) , Space , Str "any" , Space , Str "text." , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com?a=a@a.a&b=a+b+c." ] ( "http://domain.com?a=a@a.a&b=a+b+c." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com?a=a@a.a&b=a+b+c," ] ( "http://domain.com?a=a@a.a&b=a+b+c," , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com/bla.cgi?a=a@a.a&b=a+b+c." ] ( "http://domain.com/bla.cgi?a=a@a.a&b=a+b+c." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com/bla.cgi?a=a@a.a&b=a+b+c@." ] ( "http://domain.com/bla.cgi?a=a@a.a&b=a+b+c@." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com?a=a@a.a&b=a+b+c.#anchor" ] ( "http://domain.com?a=a@a.a&b=a+b+c.#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com/bla.cgi?a=a@a.a&b=a+b+c.#anchor" ] ( "http://domain.com/bla.cgi?a=a@a.a&b=a+b+c.#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com/bla.cgi?a=a@a.a&b=a+b+c@.#anchor" ] ( "http://domain.com/bla.cgi?a=a@a.a&b=a+b+c@.#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://user:password@domain.com/bla.html." ] ( "http://user:password@domain.com/bla.html." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://user:password@domain.com/dir/." ] ( "http://user:password@domain.com/dir/." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://user:password@domain.com." ] ( "http://user:password@domain.com." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://user:@domain.com." ] ( "http://user:@domain.com." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://user@domain.com." ] ( "http://user@domain.com." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://user:password@domain.com/bla.cgi?a=a@a.a&b=a+b+c.#anchor" ] ( "http://user:password@domain.com/bla.cgi?a=a@a.a&b=a+b+c.#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://user:password@domain.com/bla.cgi?a=a@a.a&b=a+b+c@#anchor" ] ( "http://user:password@domain.com/bla.cgi?a=a@a.a&b=a+b+c@#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "label" ] ( "www.domain.com" , "" ) , SoftBreak , Str "[" , Space , Str "label" , Space , Link ( "" , [] , [] ) [ Str "www.domain.com" ] ( "www.domain.com" , "" ) , Str "]" , SoftBreak , Link ( "" , [] , [] ) [ Str "label" , Space ] ( "www.domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "anchor" , Space ] ( "http://www.domain.com/dir/index.html#anchor." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "login" , Space ] ( "http://user:password@domain.com/bla.html" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "form" , Space ] ( "http://www.domain.com/bla.cgi?a=a@a.a&b=a+b+c." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "form" , Space , Str "&" , Space , Str "anchor" ] ( "http://www.domain.com/bla.cgi?a=a@a.a&b=a+b+c.#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "login" , Space , Str "&" , Space , Str "form" , Space ] ( "http://user:password@domain.com/bla.cgi?a=a@a.a&b=a+b+c." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "local" , Space , Str "link" , Space , Str "up" , Space ] ( ".." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "local" , Space , Str "link" , Space , Str "file" , Space ] ( "bla.html" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "local" , Space , Str "link" , Space , Str "anchor" , Space ] ( "#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "local" , Space , Str "link" , Space , Str "file/anchor" ] ( "bla.html#anchor" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "local" , Space , Str "link" , Space , Str "file/anchor" ] ( "bla.html#anchor." , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "local" , Space , Str "link" , Space , Str "img" , Space ] ( "abc.gif" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "www.fake.com" ] ( "www.domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm" ] ( "http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_-" ] ( "http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_-" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_-#anchor_" ] ( "http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_-#anchor_" , "" ) , Str "-1%." , SoftBreak , Link ( "" , [] , [] ) [ Str "http://foo._user-9:pass!#$%&*()+word@domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_-#anchor_" ] ( "http://foo._user-9:pass!#$%&*()+word@domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_-#anchor_" , "" ) , Str "-1%." , SoftBreak , Link ( "" , [] , [] ) [ Str "http://L1.com" ] ( "http://L1.com" , "" ) , Space , Str "!" , Space , Link ( "" , [] , [] ) [ Str "mailto:L2@www.com" ] ( "L2@www.com" , "" ) , Space , Str "!" , Space , Link ( "" , [] , [] ) [ Str "L3" ] ( "www.com" , "" ) , Space , Str "!" , Space , Link ( "" , [] , [] ) [ Str "L4" ] ( "w@ww.com" , "" ) , Space , Str "!" , Space , Link ( "" , [] , [] ) [ Str "www.L5.com" ] ( "www.L5.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "www.domain.com" ] ( "www.domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "www2.domain.com" ] ( "www2.domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "ftp.domain.com" ] ( "ftp.domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "WWW.DOMAIN.COM" ] ( "WWW.DOMAIN.COM" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "FTP.DOMAIN.COM" ] ( "FTP.DOMAIN.COM" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "label" ] ( "www.domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "label" ] ( "ftp.domain.com" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "label" ] ( "WWW.DOMAIN.COM" , "" ) , SoftBreak , Link ( "" , [] , [] ) [ Str "label" ] ( "FTP.DOMAIN.COM" , "" ) , SoftBreak , Str "[label" , Space , Link ( "" , [] , [] ) [ Str "www.domain.com" ] ( "www.domain.com" , "" ) , Space , Str "]" , SoftBreak , Str "[label]" , Space , Link ( "" , [] , [] ) [ Str "www.domain.com" ] ( "www.domain.com" , "" ) , Str "]" ] , Header 1 ( "image" , [] , [] ) [ Str "Image" ] , Para [ Image ( "" , [] , [] ) [] ( "img.png" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Image ( "" , [] , [] ) [] ( "img.png" , "" ) ] ( "https://txt2tags.org" , "" ) ] , Para [ Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Space , Str "Image" , Space , Str "at" , Space , Str "the" , Space , Str "line" , Space , Str "beginning." ] , Para [ Str "Image" , Space , Str "in" , Space , Str "the" , Space , Str "middle" , Space , Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Space , Str "of" , Space , Str "the" , Space , Str "line." ] , Para [ Str "Image" , Space , Str "at" , Space , Str "the" , Space , Str "line" , Space , Str "end." , Space , Image ( "" , [] , [] ) [] ( "img.png" , "" ) ] , Para [ Image ( "" , [] , [] ) [] ( "img.png" , "" ) , SoftBreak , Image ( "" , [] , [] ) [] ( "img.png" , "" ) , SoftBreak , Image ( "" , [] , [] ) [] ( "img.png" , "" ) ] , Para [ Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Image ( "" , [] , [] ) [] ( "img.png" , "" ) ] , Para [ Str "Images" , Space , Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Space , Str "mixed" , Space , Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Space , Str "with" , Space , Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Space , Str "text." ] , Para [ Str "Images" , Space , Str "glued" , Space , Str "together:" , Space , Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Image ( "" , [] , [] ) [] ( "img.png" , "" ) , Str "." ] , Para [ Str "[img.png" , Space , Str "]" ] , Para [ Str "[" , Space , Str "img.png]" ] , Para [ Str "[" , Space , Str "img.png" , Space , Str "]" ] , Header 1 ( "numtitle" , [] , [] ) [ Str "Numbered" , Space , Str "Title" ] , Header 1 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "1" ] , Header 2 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "2" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 4 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "4" ] , Header 5 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "5" ] , Header 1 ( "lab_el-1" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "1" ] , Header 2 ( "lab_el-2" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "2" ] , Header 3 ( "lab_el-3" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 4 ( "lab_el-4" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "4" ] , Header 5 ( "lab_el-5" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "5" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "lab_el-9" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Para [ Str "+Not" , Space , Str "Title" ] , Para [ Str "++Not" , Space , Str "Title+" ] , Para [ Str "+++Not" , Space , Str "Title++++" , SoftBreak , Str "++++++Not" , Space , Str "Title" , Space , Str "6++++++" ] , Para [ Str "+++++++Not" , Space , Str "Title" , Space , Str "7+++++++" , SoftBreak , Str "+Not" , Space , Str "Title+" , Space , Str "[label1]" , SoftBreak , Str "+Not" , Space , Str "Title+[" , Space , Str "label" , Space , Str "]" , SoftBreak , Str "+Not" , Space , Str "Title+[la/bel]" ] , Header 1 ( "title" , [] , [] ) [ Str "Title" ] , Header 1 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "1" ] , Header 2 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "2" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 4 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "4" ] , Header 5 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "5" ] , Header 1 ( "lab_el-1" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "1" ] , Header 2 ( "lab_el-2" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "2" ] , Header 3 ( "lab_el-3" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 4 ( "lab_el-4" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "4" ] , Header 5 ( "lab_el-5" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "5" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Header 3 ( "lab_el-9" , [] , [] ) [ Str "Title" , Space , Str "Level" , Space , Str "3" ] , Para [ Str "=Not" , Space , Str "Title" ] , Para [ Str "==Not" , Space , Str "Title=" ] , Para [ Str "===Not" , Space , Str "Title====" , SoftBreak , Str "======Not" , Space , Str "Title" , Space , Str "6======" ] , Para [ Str "=======Not" , Space , Str "Title" , Space , Str "7=======" , SoftBreak , Str "=Not" , Space , Str "Title=" , Space , Str "[label1]" , SoftBreak , Str "=Not" , Space , Str "Title=[" , Space , Str "label" , Space , Str "]" , SoftBreak , Str "=Not" , Space , Str "Title=[la/bel]" ] , Header 1 ( "quote" , [] , [] ) [ Str "Quote" ] , BlockQuote [ Para [ Str "To" , Space , Str "quote" , Space , Str "a" , Space , Str "paragraph," , Space , Str "just" , Space , Str "prefix" , Space , Str "it" , Space , Str "by" , Space , Str "a" , Space , Str "TAB" , SoftBreak , Str "character." , Space , Str "All" , Space , Str "the" , Space , Str "lines" , Space , Str "of" , Space , Str "the" , Space , Str "paragraph" , Space , Str "must" , SoftBreak , Str "begin" , Space , Str "with" , Space , Str "a" , Space , Str "TAB." ] ] , Para [ Str "Any" , Space , Str "non-tabbed" , Space , Str "line" , Space , Str "closes" , Space , Str "the" , Space , Str "quote" , Space , Str "block." ] , BlockQuote [ Para [ Str "The" , Space , Str "number" , Space , Str "of" , Space , Str "leading" , Space , Str "TABs" , Space , Str "identifies" , Space , Str "the" , Space , Str "quote" , SoftBreak , Str "block" , Space , Str "depth." , Space , Str "This" , Space , Str "is" , Space , Str "quote" , Space , Str "level" , Space , Str "1." ] , BlockQuote [ Para [ Str "With" , Space , Str "two" , Space , Str "TABs," , Space , Str "we" , Space , Str "are" , Space , Str "on" , Space , Str "the" , Space , Str "quote" , SoftBreak , Str "level" , Space , Str "2." ] , BlockQuote [ Para [ Str "The" , Space , Str "more" , Space , Str "TABs," , Space , Str "more" , Space , Str "deep" , Space , Str "is" , SoftBreak , Str "the" , Space , Str "quote" , Space , Str "level." ] , BlockQuote [ Para [ Str "There" , Space , Str "isn't" , Space , Str "a" , Space , Str "limit." ] ] ] ] ] , BlockQuote [ BlockQuote [ BlockQuote [ BlockQuote [ Para [ Str "This" , Space , Str "quote" , Space , Str "starts" , Space , Str "at" , SoftBreak , Str "level" , Space , Str "4." ] ] , Para [ Str "Then" , Space , Str "its" , Space , Str "depth" , Space , Str "is" , Space , Str "decreased." ] ] , Para [ Str "Counting" , Space , Str "down," , Space , Str "one" , Space , Str "by" , Space , Str "one." ] ] , Para [ Str "Until" , Space , Str "the" , Space , Str "level" , Space , Str "1." ] ] , BlockQuote [ BlockQuote [ BlockQuote [ Para [ Str "Unlike" , Space , Str "lists," , Space , Str "any" , Space , Str "quote" , Space , Str "block" , Space , Str "is" , SoftBreak , Str "independent," , Space , Str "not" , Space , Str "part" , Space , Str "of" , Space , Str "a" , Space , Str "tree." ] ] ] , Para [ Str "The" , Space , Str "TAB" , Space , Str "count" , Space , Str "don't" , Space , Str "need" , Space , Str "to" , Space , Str "be" , Space , Str "incremental" , SoftBreak , Str "by" , Space , Str "one." ] , BlockQuote [ BlockQuote [ BlockQuote [ Para [ Str "The" , Space , Str "nesting" , Space , Str "don't" , Space , Str "need" , SoftBreak , Str "to" , Space , Str "follow" , Space , Str "any" , Space , Str "rule." ] ] ] , Para [ Str "Quotes" , Space , Str "can" , Space , Str "be" , Space , Str "opened" , Space , Str "and" , Space , Str "closed" , SoftBreak , Str "in" , Space , Str "any" , Space , Str "way." ] , BlockQuote [ BlockQuote [ BlockQuote [ Para [ Str "You" , Space , Str "choose." ] ] ] ] ] ] , BlockQuote [ Para [ Str "Some" , Space , Str "targets" , Space , Str "(as" , Space , Str "sgml)" , Space , Str "don't" , Space , Str "support" , Space , Str "the" , SoftBreak , Str "nesting" , Space , Str "of" , Space , Str "quotes." , Space , Str "There" , Space , Str "is" , Space , Str "only" , Space , Str "one" , Space , Str "quote" , SoftBreak , Str "level." ] , BlockQuote [ Para [ Str "In" , Space , Str "this" , Space , Str "case," , Space , Str "no" , Space , Str "matter" , Space , Str "how" , Space , Str "much" , SoftBreak , Str "TABs" , Space , Str "are" , Space , Str "used" , Space , Str "to" , Space , Str "define" , Space , Str "the" , Space , Str "quote" , SoftBreak , Str "block," , Space , Str "it" , Space , Str "always" , Space , Str "will" , Space , Str "be" , Space , Str "level" , Space , Str "1." ] ] ] , BlockQuote [ Para [ Str "Spaces" , Space , Str "AFTER" , Space , Str "the" , Space , Str "TAB" , Space , Str "character" , Space , Str "are" , Space , Str "allowed." , SoftBreak , Str "But" , Space , Str "be" , Space , Str "careful," , Space , Str "it" , Space , Str "can" , Space , Str "be" , Space , Str "confusing." ] ] , Para [ Str "Spaces" , Space , Str "BEFORE" , Space , Str "the" , Space , Str "TAB" , Space , Str "character" , SoftBreak , Str "invalidate" , Space , Str "the" , Space , Str "mark." , Space , Str "It's" , Space , Str "not" , Space , Str "quote." ] , BlockQuote [ Para [ Str "Paragraph" , Space , Str "breaks" , Space , Str "inside" , Space , Str "a" , Space , Str "quote" , Space , Str "aren't" , SoftBreak , Str "possible." ] , Para [ Str "This" , Space , Str "sample" , Space , Str "are" , Space , Str "two" , Space , Str "separated" , Space , Str "quoted" , SoftBreak , Str "paragraphs," , Space , Str "not" , Space , Str "a" , Space , Str "quote" , Space , Str "block" , Space , Str "with" , SoftBreak , Str "two" , Space , Str "paragraphs" , Space , Str "inside." ] ] , BlockQuote [ Para [ Str "The" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "file" , Space , Str "(EOF)" , Space , Str "closes" , Space , Str "the" , SoftBreak , Str "currently" , Space , Str "open" , Space , Str "quote" , Space , Str "block." ] ] , Header 1 ( "raw" , [] , [] ) [ Str "Raw" ] , Para [ Str "A raw line.\n" ] , Para [ Str " Another raw line, with leading spaces.\n" ] , Para [ Str "A raw area delimited\n by lines with marks.\n" ] , Para [ Str "Trailing spaces and TABs after the area marks\nare allowed, but not encouraged nor documented.\n" ] , Para [ Str "\"\"\"Not" , Space , Str "a" , Space , Str "raw" , Space , Str "line," , Space , Str "need" , Space , Str "one" , Space , Str "space" , Space , Str "after" , Space , Str "mark." ] , Para [ Str "\"\"\"" , SoftBreak , Str "Not" , Space , Str "a" , Space , Str "raw" , Space , Str "area." , SoftBreak , Str "The" , Space , Str "marks" , Space , Str "must" , Space , Str "be" , Space , Str "at" , Space , Str "the" , Space , Str "line" , Space , Str "beginning," , SoftBreak , Str "no" , Space , Str "leading" , Space , Str "spaces." , SoftBreak , Str "\"\"\"" ] , Para [ Str "The end of the file (EOF) closes\nthe currently open raw area.\n" ] , Header 1 ( "verbatim" , [] , [] ) [ Str "Verbatim" ] , CodeBlock ( "" , [] , [] ) "A verbatim line.\n" , CodeBlock ( "" , [] , [] ) " Another verbatim line, with leading spaces.\n" , CodeBlock ( "" , [] , [] ) "A verbatim area delimited\n by lines with marks.\n" , CodeBlock ( "" , [] , [] ) "Trailing spaces and TABs after the area marks\nare allowed, but not encouraged nor documented.\n" , Para [ Str "```Not" , Space , Str "a" , Space , Str "verbatim" , Space , Str "line," , Space , Str "need" , Space , Str "one" , Space , Str "space" , Space , Str "after" , Space , Str "mark." ] , Para [ Str "```" , SoftBreak , Str "Not" , Space , Str "a" , Space , Str "verbatim" , Space , Str "area." , SoftBreak , Str "The" , Space , Str "marks" , Space , Str "must" , Space , Str "be" , Space , Str "at" , Space , Str "the" , Space , Str "line" , Space , Str "beginning," , SoftBreak , Str "no" , Space , Str "leading" , Space , Str "spaces." , SoftBreak , Str "```" ] , CodeBlock ( "" , [] , [] ) "The end of the file (EOF) closes\nthe currently open verbatim area.\n" , Header 1 ( "deflist" , [] , [] ) [ Str "Definition" , Space , Str "List" ] , DefinitionList [ ( [ Str "Definition" , Space , Str "list" ] , [ [ Plain [ Str "A" , Space , Str "list" , Space , Str "with" , Space , Str "terms" ] ] ] ) , ( [ Str "Start" , Space , Str "term" , Space , Str "with" , Space , Str "colon" ] , [ [ Plain [ Str "And" , Space , Str "its" , Space , Str "definition" , Space , Str "follows" ] ] ] ) ] , Header 1 ( "numlist" , [] , [] ) [ Str "Numbered" , Space , Str "List" ] , Para [ Str "See" , Space , Link ( "" , [] , [] ) [ Str "List" ] ( "#list" , "" ) , Str "," , Space , Str "the" , Space , Str "same" , Space , Str "rules" , Space , Str "apply." ] , Header 1 ( "list" , [] , [] ) [ Str "List" ] , BulletList [ [ Plain [ Str "Use" , Space , Str "the" , Space , Str "hyphen" , Space , Str "to" , Space , Str "prefix" , Space , Str "list" , Space , Str "items." ] ] , [ Plain [ Str "There" , Space , Str "must" , Space , Str "be" , Space , Str "one" , Space , Str "space" , Space , Str "after" , Space , Str "the" , Space , Str "hyphen." ] ] , [ Plain [ Str "The" , Space , Str "list" , Space , Str "is" , Space , Str "closed" , Space , Str "by" , Space , Str "two" , Space , Str "consecutive" , Space , Str "blank" , Space , Str "lines." ] ] ] , BulletList [ [ Plain [ Str "The" , Space , Str "list" , Space , Str "can" , Space , Str "be" , Space , Str "indented" , Space , Str "on" , Space , Str "the" , Space , Str "source" , Space , Str "document." ] ] , [ Plain [ Str "You" , Space , Str "can" , Space , Str "use" , Space , Str "any" , Space , Str "number" , Space , Str "of" , Space , Str "spaces." ] ] , [ Plain [ Str "The" , Space , Str "result" , Space , Str "will" , Space , Str "be" , Space , Str "the" , Space , Str "same." ] ] ] , BulletList [ [ Para [ Str "Let" , Space , Str "one" , Space , Str "blank" , Space , Str "line" , Space , Str "between" , Space , Str "the" , Space , Str "list" , Space , Str "items." ] ] , [ Para [ Str "It" , Space , Str "will" , Space , Str "be" , Space , Str "maintained" , Space , Str "on" , Space , Str "the" , Space , Str "conversion." ] ] , [ Para [ Str "Some" , Space , Str "targets" , Space , Str "don't" , Space , Str "support" , Space , Str "this" , Space , Str "behavior." ] ] , [ Para [ Str "This" , Space , Str "one" , Space , Str "was" , Space , Str "separated" , Space , Str "by" , Space , Str "a" , Space , Str "line" , Space , Str "with" , Space , Str "blanks." , SoftBreak , Str "You" , Space , Str "can" , Space , Str "also" , Space , Str "put" , Space , Str "a" , Space , Str "blank" , Space , Str "line" , Space , Str "inside" ] , Para [ Str "the" , Space , Str "item" , Space , Str "contents" , Space , Str "and" , Space , Str "it" , Space , Str "will" , Space , Str "be" , Space , Str "preserved." ] ] ] , Para [ Str "-This" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "list" , Space , Str "(no" , Space , Str "space)" ] , Para [ Str "-" , Space , Str "This" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "list" , Space , Str "(more" , Space , Str "than" , Space , Str "one" , Space , Str "space)" ] , Para [ Str "-" , Space , Str "This" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "list" , Space , Str "(a" , Space , Str "TAB" , Space , Str "instead" , Space , Str "the" , Space , Str "space)" ] , BulletList [ [ BulletList [ [ Plain [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "list" ] ] ] ] , [ OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "list" ] ] ] ] , [ DefinitionList [ ( [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "list" ] , [ [] ] ) ] ] ] , BulletList [ [ Plain [ Str "This" , Space , Str "is" , Space , Str "the" , Space , Str "\"mother\"" , Space , Str "list" , Space , Str "first" , Space , Str "item." ] ] , [ Plain [ Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "second," , Space , Str "but" , Space , Str "inside" , Space , Str "this" , Space , Str "item," ] , BulletList [ [ Plain [ Str "there" , Space , Str "is" , Space , Str "a" , Space , Str "sublist," , Space , Str "with" , Space , Str "its" , Space , Str "own" , Space , Str "items." ] ] , [ Plain [ Str "Note" , Space , Str "that" , Space , Str "the" , Space , Str "items" , Space , Str "of" , Space , Str "the" , Space , Str "same" , Space , Str "sublist" ] ] , [ Plain [ Str "must" , Space , Str "have" , Space , Str "the" , Space , Str "same" , Space , Str "indentation." ] , BulletList [ [ Plain [ Str "And" , Space , Str "this" , Space , Str "can" , Space , Str "go" , Space , Str "on," , Space , Str "opening" , Space , Str "sublists." ] , BulletList [ [ Plain [ Str "Just" , Space , Str "add" , Space , Str "leading" , Space , Str "spaces" , Space , Str "before" , Space , Str "the" ] ] , [ Plain [ Str "hyphen" , Space , Str "and" , Space , Str "sublists" , Space , Str "will" , Space , Str "be" , Space , Str "opened." ] ] , [ Plain [ Str "The" , Space , Str "two" , Space , Str "blank" , Space , Str "lines" , Space , Str "closes" , Space , Str "them" , Space , Str "all." ] ] ] ] ] ] ] ] ] , BulletList [ [ Plain [ Str "When" , Space , Str "nesting" , Space , Str "lists," , Space , Str "the" , Space , Str "additional" , Space , Str "spaces" , Space , Str "are" , Space , Str "free." ] ] , [ Plain [ Str "You" , Space , Str "can" , Space , Str "add" , Space , Str "just" , Space , Str "one," ] , BulletList [ [ Plain [ Str "or" , Space , Str "many." ] , BulletList [ [ Plain [ Str "What" , Space , Str "matters" , Space , Str "is" , Space , Str "to" , Space , Str "put" , Space , Str "more" , Space , Str "than" , Space , Str "the" , Space , Str "previous." ] ] , [ Plain [ Str "But" , Space , Str "remember" , Space , Str "that" , Space , Str "the" , Space , Str "other" , Space , Str "items" , Space , Str "of" , Space , Str "the" , Space , Str "same" , Space , Str "list" ] ] , [ Plain [ Str "must" , Space , Str "use" , Space , Str "the" , Space , Str "same" , Space , Str "indentation." ] ] ] ] ] ] ] , BulletList [ [ Plain [ Str "There" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "depth" , Space , Str "limit," ] , BulletList [ [ Plain [ Str "you" , Space , Str "can" , Space , Str "go" , Space , Str "deeper" , Space , Str "and" , Space , Str "deeper." ] , BulletList [ [ Plain [ Str "But" , Space , Str "some" , Space , Str "targets" , Space , Str "may" , Space , Str "have" , Space , Str "restrictions." ] , BulletList [ [ Plain [ Str "The" , Space , Str "LaTeX" , Space , Str "maximum" , Space , Str "is" , Space , Str "here," , Space , Str "4" , Space , Str "levels." ] ] ] ] ] ] ] ] ] , BulletList [ [ Plain [ Str "Reverse" , Space , Str "nesting" , Space , Str "doesn't" , Space , Str "work." ] ] , [ Plain [ Str "Because" , Space , Str "a" , Space , Str "sublist" , Space , Str "*must*" , Space , Str "have" , Space , Str "a" , Space , Str "mother" , Space , Str "list." ] ] , [ Plain [ Str "It's" , Space , Str "the" , Space , Str "list" , Space , Str "concept," , Space , Str "not" , Space , Str "a" , Space , Str "txt2tags" , Space , Str "limitation." ] ] , [ Plain [ Str "All" , Space , Str "this" , Space , Str "sublists" , Space , Str "will" , Space , Str "be" , Space , Str "bumped" , Space , Str "to" , Space , Str "mother" , Space , Str "lists." ] ] , [ Plain [ Str "At" , Space , Str "level" , Space , Str "1," , Space , Str "like" , Space , Str "this" , Space , Str "one." ] ] ] , BulletList [ [ Plain [ Str "Level" , Space , Str "1" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "2" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "3" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "4" ] ] ] ] , [ Plain [ Str "Level" , Space , Str "3" , Space , Str "--" , Space , Str "(closed" , Space , Str "Level" , Space , Str "4)" ] ] ] ] , [ Plain [ Str "Level" , Space , Str "2" , Space , Str "--" , Space , Str "(closed" , Space , Str "Level" , Space , Str "3)" ] ] ] ] , [ Plain [ Str "Level" , Space , Str "1" , Space , Str "--" , Space , Str "(closed" , Space , Str "Level" , Space , Str "2)" ] ] ] , BulletList [ [ Plain [ Str "Level" , Space , Str "1" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "2" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "3" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "4" ] ] ] ] ] ] ] ] , [ Plain [ Str "Level" , Space , Str "1" , Space , Str "--" , Space , Str "(closed" , Space , Str "Level" , Space , Str "4," , Space , Str "Level" , Space , Str "3" , Space , Str "and" , Space , Str "Level" , Space , Str "2)" ] ] ] , BulletList [ [ Para [ Str "Level" , Space , Str "1" ] , BulletList [ [ Para [ Str "Level" , Space , Str "2" , Space , Str "--" , Space , Str "blank" , Space , Str "BEFORE" , Space , Str "and" , Space , Str "AFTER" , Space , Str "(in)" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "3" ] ] ] ] ] ] ] , BulletList [ [ Plain [ Str "Level" , Space , Str "4" ] ] ] , BulletList [ [ Para [ Str "Level" , Space , Str "3" ] ] , [ Para [ Str "Level" , Space , Str "2" , Space , Str "--" , Space , Str "blank" , Space , Str "BEFORE" , Space , Str "and" , Space , Str "AFTER" , Space , Str "(out)" ] ] , [ Para [ Str "Level" , Space , Str "1" ] , BulletList [ [ Para [ Str "Level" , Space , Str "2" , Space , Str "--" , Space , Str "blank" , Space , Str "BEFORE" , Space , Str "(spaces)" , Space , Str "and" , Space , Str "AFTER" , Space , Str "(TAB)" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "3" ] ] ] ] ] ] ] , BulletList [ [ Plain [ Str "Level" , Space , Str "1" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "2" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "3" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "4" ] ] , [ Plain [ Str "Level" , Space , Str "3.5" , Space , Str "???" ] ] ] ] , [ Plain [ Str "Level" , Space , Str "3" ] ] , [ Plain [ Str "Level" , Space , Str "2.5" , Space , Str "???" ] ] ] ] , [ Plain [ Str "Level" , Space , Str "2" ] ] , [ Plain [ Str "Level" , Space , Str "1.5" , Space , Str "???" ] ] ] ] , [ Plain [ Str "Level" , Space , Str "1" ] ] ] , BulletList [ [ Plain [ Str "This" , Space , Str "list" , Space , Str "is" , Space , Str "closed" , Space , Str "by" , Space , Str "a" , Space , Str "line" , Space , Str "with" , Space , Str "spaces" , Space , Str "and" , Space , Str "other" , Space , Str "with" , Space , Str "TABs" ] ] ] , BulletList [ [ Plain [ Str "This" , Space , Str "list" , Space , Str "is" , Space , Str "NOT" , Space , Str "closed" , Space , Str "by" , Space , Str "two" , Space , Str "comment" , Space , Str "lines" ] ] ] , BulletList [ [ Plain [ Str "This" , Space , Str "list" , Space , Str "is" , Space , Str "closed" , Space , Str "by" , Space , Str "a" , Space , Str "line" , Space , Str "with" , Space , Str "spaces" , Space , Str "and" , Space , Str "TAB," ] ] , [ Plain [ Str "then" , Space , Str "a" , Space , Str "comment" , Space , Str "line," , Space , Str "then" , Space , Str "an" , Space , Str "empty" , Space , Str "line." ] ] ] , BulletList [ [ Plain [ Str "Level" , Space , Str "1" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "2" ] , BulletList [ [ Plain [ Str "Level" , Space , Str "3" ] ] ] , Plain [ Str "-" , SoftBreak , Str "Level" , Space , Str "2" ] ] ] , Plain [ Str "-" , SoftBreak , Str "Level" , Space , Str "1" ] ] ] , Para [ Str "-" ] , BulletList [ [ Plain [ Str "Empty" , Space , Str "item" , Space , Str "with" , Space , Str "trailing" , Space , Str "spaces." ] ] ] , Para [ Str "-" ] , BulletList [ [ Plain [ Str "Empty" , Space , Str "item" , Space , Str "with" , Space , Str "trailing" , Space , Str "TAB." ] ] ] , Para [ Str "-" ] , BulletList [ [ Plain [ Str "If" , Space , Str "the" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "file" , Space , Str "(EOF)" , Space , Str "is" , Space , Str "hit," ] , BulletList [ [ Plain [ Str "all" , Space , Str "the" , Space , Str "currently" , Space , Str "opened" , Space , Str "list" , Space , Str "are" , Space , Str "closed," ] , BulletList [ [ Plain [ Str "just" , Space , Str "like" , Space , Str "when" , Space , Str "using" , Space , Str "the" , Space , Str "two" , Space , Str "blank" , Space , Str "lines." ] ] ] ] ] ] ] , Header 1 ( "table" , [] , [] ) [ Str "Table" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignRight , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "||" , Space , Str "Cell" , Space , Str "1" , Space , Str "|" , Space , Str "Cell" , Space , Str "2" , Space , Str "|" , Space , Str "Cell" , Space , Str "3" , Space , Str "|" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Cell" , Space , Str "3" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Heading" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Heading" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Heading" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "<-" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "--" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "->" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "--" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "--" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "--" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "->" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "--" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "<-" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3+4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1+2+3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2+3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1+2+3+4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "D" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "E" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "F" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Jan" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Fev" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Mar" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Apr" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "May" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "20%" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "40%" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "60%" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "80%" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "100%" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "/" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "/" , Space , Str "/" , Space , Str "/" , Space , Str "/" , Space , Str "/" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "/" , Space , Str "/" , Space , Str "/" , Space , Str "/" , Space , Str "/" , Space , Str "/" , Space , Str "/" , Space , Str "/" , Space , Str "/" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "o" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "o" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "." ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "=" , Space , Str "=" , Space , Str "=" , Space , Str "=" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "01" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "02" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "05" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "07" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "11" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "13" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "16" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "17" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "19" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "20" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "23" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "25" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "26" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "29" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "30" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "32" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "35" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "37" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "39" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "40" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) , ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "9" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "C" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "D" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "E" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "F" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "4" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "6" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "7" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "8" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "9" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "A" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "B" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "C" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "D" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "E" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "F" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignCenter , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Str "|this|is|not|a|table|" ] , Para [ Str "|this|" , Space , Str "is|" , Space , Str "not|" , Space , Str "a|" , Space , Str "table|" ] , Para [ Str "|this" , Space , Str "|is" , Space , Str "|not" , Space , Str "|a" , Space , Str "|table" , Space , Str "|" ] , Para [ Str "|" , Space , Str "this\t|" , Space , Str "is\t|" , Space , Str "not\t|" , Space , Str "a\t|" , Space , Str "table\t|" ] , HorizontalRule , Para [ Str "The" , Space , Str "End." ] ] ================================================ FILE: test/txt2tags.t2t ================================================ Txt2tags Markup Rules author date %!includeconf: rules.conf This document describes all the details about each txt2tags mark. The target audience are **experienced** users. You may find it useful if you want to master the marks or solve a specific problem about a mark. If you are new to txt2tags or just want to know which are the available marks, please read the [Markup Demo MARKUPDEMO]. Note 1: This document is generated directly from the txt2tags test-suite. All the rules mentioned here are 100% in sync with the current program code. Note 2: A good practice is to consult [the sources rules.t2t] when reading, to see how the texts were made. Table of Contents: %%TOC ------------------------------------------------------------- = Paragraph =[paragraph] %INCLUDED(t2t) starts here: ../../../test/marks/paragraph.t2t %%% Syntax: Lines grouped together A paragraph is composed by one or more lines. A blank line (or a table, or a list) ends the current paragraph. %%% Syntax: Leading and trailing spaces are ignored Leading and trailing spaces are ignored. %%% Syntax: A comment don't close a paragraph A comment line can be placed inside a paragraph. % this comment will be ignored It will not affect it. %%% Closing: EOF closes the open paragraph The end of the file (EOF) closes the currently open paragraph. = Comment =[comment] %INCLUDED(t2t) starts here: ../../../test/marks/comment.t2t %%% Syntax: The % character at the line beginning (column 1) %glued with the % mark % separated from the % mark % very distant from the % mark %%%%%%% lots of % marks % a blank comment, used for vertical spacing: % % NOTE: what matters is the first % being at the line beginning, % the rest of the line is just ignored. %%% Syntax: Area (block) %%% You're not seeing this. %%% %%% Syntax: Area (block) with trailing spaces %%% You're not seeing this. %%% %%% Invalid: The % in any other position % not on the line beginning (at column 2) some text % half line comments are not allowed = Line =[line] %INCLUDED(t2t) starts here: ../../../test/marks/line.t2t %%% Syntax: At least 20 chars of - = _ -------------------- ==================== ____________________ %%% Syntax: Any kind of mixing is allowed %% Free mixing is allowed to make the line, %% but the first char is the identifier for %% the difference between separator ( - _ ) %% and strong ( = ) lines. =========----------- -_-_-_-_-_-_-_-_-_-_ =-=-=-=-=-=-=-=-=-=- =------------------= --------====-------- %%% Syntax: Leading and/or trailing spaces are allowed -------------------- -------------------- -------------------- %%% Invalid: Less than 20 chars (but strike matches) --------- %%% Invalid: Strange chars (but strike matches) --------- ---------- ---------+---------- ( -------------------- ) = Inline =[inline] %INCLUDED(t2t) starts here: ../../../test/marks/inline.t2t %%% Syntax: Marks are greedy and must be "glued" with contents %% GLUED: The contents must be glued with the marks, no spaces %% between them. Right after the opening mark there must be a %% non-blank character, as well as right before the closing mark. %% %% GREEDY: If the contents boundary character is the same as %% the mark character, it is considered contents, not mark. %% So ""****bold****"" turns to ""**bold**"" in HTML. i) **b** //i// __u__ --s-- ``m`` ""r"" ''t'' i) **bo** //it// __un__ --st-- ``mo`` ""ra"" ''tg'' i) **bold** //ital// __undr__ --strk-- ``mono`` ""raw"" ''tggd'' i) **bo ld** //it al// __un dr__ --st rk-- ``mo no`` ""r aw"" ''tg gd'' i) **bo * ld** //it / al// __un _ dr__ --st - rk-- ``mo ` no`` ""r " aw"" ''tg ' gd'' i) **bo **ld** //it //al// __un __dr__ --st --rk-- ``mo ``no`` ""r ""aw"" ''tg ''gd'' i) **bo ** ld** //it // al// __un __ dr__ --st -- rk-- ``mo `` no`` ""r "" aw"" ''tg '' gd'' i) ****bold**** ////ital//// ____undr____ ----strk---- ````mono```` """"raw"""" ''''tggd'''' i) ***bold*** ///ital/// ___undr___ ---strk--- ```mono``` """raw""" '''tggd''' %%% Syntax: Repetition is greedy %% When the mark character is repeated many times, %% the contents are expanded to the largest possible. %% That's why they are greedy, the outer marks are %% the ones used. i) ***** ///// _____ ----- ````` """"" ''''' i) ****** ////// ______ ------ `````` """""" '''''' i) ******* /////// _______ ------- ``````` """"""" ''''''' i) ******** //////// ________ -------- ```````` """""""" '''''''' i) ********* ///////// _________ --------- ````````` """"""""" ''''''''' i) ********** ////////// __________ ---------- `````````` """""""""" '''''''''' %%% Invalid: No contents i) **** //// ____ ---- ```` """" '''' i) ** ** // // __ __ -- -- `` `` "" "" '' '' %%% Invalid: Contents not "glued" with marks %% Spaces between the marks and the contents in any side %% invalidate the mark. i) ** bold** // ital// __ undr__ -- strk-- `` mono`` "" raw"" '' tggd'' i) **bold ** //ital // __undr __ --strk -- ``mono `` ""raw "" ''tggd '' i) ** bold ** // ital // __ undr __ -- strk -- `` mono `` "" raw "" '' tggd '' = Link =[link] %INCLUDED(t2t) starts here: ../../../test/marks/link.t2t %%% Syntax: E-mail user@domain.com user@domain.com. user@domain.com. any text. any text: user@domain.com. any text. [label user@domain.com] %%% Syntax: E-mail with form data user@domain.com?subject=bla user@domain.com?subject=bla. user@domain.com?subject=bla, user@domain.com?subject=bla&cc=otheruser@domain.com user@domain.com?subject=bla&cc=otheruser@domain.com. user@domain.com?subject=bla&cc=otheruser@domain.com, [label user@domain.com?subject=bla&cc=otheruser@domain.com]. [label user@domain.com?subject=bla&cc=otheruser@domain.com.]. %%% Syntax: URL http://www.domain.com http://www.domain.com/dir/ http://www.domain.com/dir/// http://www.domain.com. http://www.domain.com, http://www.domain.com. any text. http://www.domain.com, any text. http://www.domain.com/dir/. any text. any text: http://www.domain.com. any text. any text: http://www.domain.com/dir/. any text. any text: http://www.domain.com/dir/index.html. any text. any text: http://www.domain.com/dir/index.html, any text. %%% Syntax: URL with anchor http://www.domain.com/dir/#anchor http://www.domain.com/dir/index.html#anchor http://www.domain.com/dir/index.html#anchor. http://www.domain.com/dir/#anchor. any text. http://www.domain.com/dir/index.html#anchor. any text. any text: http://www.domain.com/dir/#anchor. any text. any text: http://www.domain.com/dir/index.html#anchor. any text. %%% Syntax: URL with form data http://domain.com?a=a@a.a&b=a+b+c. http://domain.com?a=a@a.a&b=a+b+c, http://domain.com/bla.cgi?a=a@a.a&b=a+b+c. http://domain.com/bla.cgi?a=a@a.a&b=a+b+c@. %%% Syntax: URL with form data and anchor http://domain.com?a=a@a.a&b=a+b+c.#anchor http://domain.com/bla.cgi?a=a@a.a&b=a+b+c.#anchor http://domain.com/bla.cgi?a=a@a.a&b=a+b+c@.#anchor %%% Syntax: URL with login data http://user:password@domain.com/bla.html. http://user:password@domain.com/dir/. http://user:password@domain.com. http://user:@domain.com. http://user@domain.com. %%% Syntax: URL with login, form and anchor http://user:password@domain.com/bla.cgi?a=a@a.a&b=a+b+c.#anchor http://user:password@domain.com/bla.cgi?a=a@a.a&b=a+b+c@#anchor %%% Syntax: URL with label [label www.domain.com] %%% Syntax: URL with label (trailing spaces are discarded, leading are maintained) %TODO normalize this behavior [ label www.domain.com] [label www.domain.com] %%% Syntax: URL with label, stressing [anchor http://www.domain.com/dir/index.html#anchor.] [login http://user:password@domain.com/bla.html] [form http://www.domain.com/bla.cgi?a=a@a.a&b=a+b+c.] [form & anchor http://www.domain.com/bla.cgi?a=a@a.a&b=a+b+c.#anchor] [login & form http://user:password@domain.com/bla.cgi?a=a@a.a&b=a+b+c.] %%% Syntax: Link with label for local files [local link up ..] [local link file bla.html] [local link anchor #anchor] [local link file/anchor bla.html#anchor] [local link file/anchor bla.html#anchor.] [local link img abc.gif] %%% Syntax: Another link as a label [www.fake.com www.domain.com] %%% Syntax: URL with funny chars http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_- http://domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_-#anchor_-1%. http://foo._user-9:pass!#$%&*()+word@domain.com:8080/~user/_st-r@a=n$g,e/index%20new.htm?a=/%22&b=+.@*_-#anchor_-1%. %%% Test: Various per line http://L1.com ! L2@www.com ! [L3 www.com] ! [L4 w@ww.com] ! www.L5.com %%% Feature: Guessed link, adding protocol automatically www.domain.com www2.domain.com ftp.domain.com WWW.DOMAIN.COM FTP.DOMAIN.COM [label www.domain.com] [label ftp.domain.com] [label WWW.DOMAIN.COM] [label FTP.DOMAIN.COM] %%% Invalid: Trailing space on link [label www.domain.com ] %%% Invalid: Label with ] char (use postproc) [label] www.domain.com] = Image =[image] %INCLUDED(t2t) starts here: ../../../test/marks/image.t2t %%% Syntax: Image name inside brackets: [img] [img.png] %%% Syntax: Image pointing to a link: [[img] link] [[img.png] https://txt2tags.org] %%% Align: Image position is preserved when inside paragraph [img.png] Image at the line beginning. Image in the middle [img.png] of the line. Image at the line end. [img.png] %%% Align: Image alone with spaces around is aligned [img.png] [img.png] [img.png] %%% Test: Two glued images with no spaces (left & right) [img.png][img.png] %%% Test: Various per line Images [img.png] mixed [img.png] with [img.png] text. Images glued together: [img.png][img.png][img.png]. %%% Invalid: Spaces inside are not allowed [img.png ] [ img.png] [ img.png ] % Ignored as they change every time when run = Numbered Title =[numtitle] %%% Syntax: Balanced equal signs (from 1 to 5) + Title Level 1 + ++ Title Level 2 ++ +++ Title Level 3 +++ ++++ Title Level 4 ++++ +++++ Title Level 5 +++++ %%% Label: Between brackets, alphanumeric [A-Za-z0-9_-] + Title Level 1 +[lab_el-1] ++ Title Level 2 ++[lab_el-2] +++ Title Level 3 +++[lab_el-3] ++++ Title Level 4 ++++[lab_el-4] +++++ Title Level 5 +++++[lab_el-5] %%% Syntax: Spaces around and/or inside are allowed (and ignored) +++Title Level 3+++ +++ Title Level 3 +++ +++ Title Level 3 +++ +++ Title Level 3 +++ +++ Title Level 3 +++ +++ Title Level 3 +++[lab_el-9] %%% Invalid: Unbalanced equal signs +Not Title ++Not Title+ +++Not Title++++ %%% Invalid: Level deeper than 5 ++++++Not Title 6++++++ +++++++Not Title 7+++++++ %%% Invalid: Space between title and label +Not Title+ [label1] %%% Invalid: Space inside label +Not Title+[ label ] %%% Invalid: Strange chars inside label +Not Title+[la/bel] = Title =[title] %INCLUDED(t2t) starts here: ../../../test/marks/title.t2t %%% Syntax: Balanced equal signs (from 1 to 5) = Title Level 1 = == Title Level 2 == === Title Level 3 === ==== Title Level 4 ==== ===== Title Level 5 ===== %%% Label: Between brackets, alphanumeric [A-Za-z0-9_-] = Title Level 1 =[lab_el-1] == Title Level 2 ==[lab_el-2] === Title Level 3 ===[lab_el-3] ==== Title Level 4 ====[lab_el-4] ===== Title Level 5 =====[lab_el-5] %%% Syntax: Spaces around and/or inside are allowed (and ignored) ===Title Level 3=== === Title Level 3 === === Title Level 3 === === Title Level 3 === === Title Level 3 === === Title Level 3 ===[lab_el-9] %%% Invalid: Unbalanced equal signs =Not Title ==Not Title= ===Not Title==== %%% Invalid: Level deeper than 5 ======Not Title 6====== =======Not Title 7======= %%% Invalid: Space between title and label =Not Title= [label1] %%% Invalid: Space inside label =Not Title=[ label ] %%% Invalid: Strange chars inside label =Not Title=[la/bel] = Quote =[quote] %INCLUDED(t2t) starts here: ../../../test/marks/quote.t2t To quote a paragraph, just prefix it by a TAB character. All the lines of the paragraph must begin with a TAB. Any non-tabbed line closes the quote block. %%% Nesting: Creating deeper quotes The number of leading TABs identifies the quote block depth. This is quote level 1. With two TABs, we are on the quote level 2. The more TABs, more deep is the quote level. There isn't a limit. %%% Nesting: Reverse nesting works This quote starts at level 4. Then its depth is decreased. Counting down, one by one. Until the level 1. %%% Nesting: Random count Unlike lists, any quote block is independent, not part of a tree. The TAB count don't need to be incremental by one. The nesting don't need to follow any rule. Quotes can be opened and closed in any way. You choose. %%% Nesting: When not supported Some targets (as sgml) don't support the nesting of quotes. There is only one quote level. In this case, no matter how much TABs are used to define the quote block, it always will be level 1. %%% Syntax: Spaces after TAB Spaces AFTER the TAB character are allowed. But be careful, it can be confusing. %%% Invalid: Spaces before TAB Spaces BEFORE the TAB character invalidate the mark. It's not quote. %%% Invalid: Paragraphs inside Paragraph breaks inside a quote aren't possible. This sample are two separated quoted paragraphs, not a quote block with two paragraphs inside. %%% Closing: EOF closes the open block The end of the file (EOF) closes the currently open quote block. = Raw =[raw] %%% Syntax: A single line """ A raw line. %%% Syntax: A single line with leading spaces """ Another raw line, with leading spaces. %%% Syntax: Area (block) """ A raw area delimited by lines with marks. """ %%% Syntax: Area (block) with trailing spaces """ Trailing spaces and TABs after the area marks are allowed, but not encouraged nor documented. """ %%% Invalid: No space between mark and contents """Not a raw line, need one space after mark. %%% Invalid: Leading spaces on block marks """ Not a raw area. The marks must be at the line beginning, no leading spaces. """ %%% Closing: EOF closes the open block """ The end of the file (EOF) closes the currently open raw area. """ = Verbatim =[verbatim] %INCLUDED(t2t) starts here: ../../../test/marks/verbatim.t2t %%% Syntax: A single line ``` A verbatim line. %%% Syntax: A single line with leading spaces ``` Another verbatim line, with leading spaces. %%% Syntax: Area (block) ``` A verbatim area delimited by lines with marks. ``` %%% Syntax: Area (block) with trailing spaces ``` Trailing spaces and TABs after the area marks are allowed, but not encouraged nor documented. ``` %%% Invalid: No space between mark and contents ```Not a verbatim line, need one space after mark. %%% Invalid: Leading spaces on block marks ``` Not a verbatim area. The marks must be at the line beginning, no leading spaces. ``` %%% Closing: EOF closes the open block ``` The end of the file (EOF) closes the currently open verbatim area. ``` = Definition List =[deflist] : Definition list A list with terms : Start term with colon And its definition follows = Numbered List =[numlist] See [List #list], the same rules apply. = List =[list] %INCLUDED(t2t) starts here: ../../../test/marks/list.t2t %%% Items: Prefixed by hyphen - Use the hyphen to prefix list items. - There must be one space after the hyphen. - The list is closed by two consecutive blank lines. %%% Items: Free leading spacing (indentation) - The list can be indented on the source document. - You can use any number of spaces. - The result will be the same. %%% Items: Vertical spacing between items - Let one blank line between the list items. - It will be maintained on the conversion. - Some targets don't support this behavior. - This one was separated by a line with blanks. You can also put a blank line inside the item contents and it will be preserved. %%% Items: Exactly ONE space after the hyphen -This is not a list (no space) - This is not a list (more than one space) - This is not a list (a TAB instead the space) %%% Items: Catchy cases - - This is a list - + This is a list - : This is a list %%% Nesting: Creating sublists - This is the "mother" list first item. - Here is the second, but inside this item, - there is a sublist, with its own items. - Note that the items of the same sublist - must have the same indentation. - And this can go on, opening sublists. - Just add leading spaces before the - hyphen and sublists will be opened. - The two blank lines closes them all. %%% Nesting: Free leading spacing (indentation) - When nesting lists, the additional spaces are free. - You can add just one, - or many. - What matters is to put more than the previous. - But remember that the other items of the same list - must use the same indentation. %%% Nesting: Maximum depth - There is not a depth limit, - you can go deeper and deeper. - But some targets may have restrictions. - The LaTeX maximum is here, 4 levels. %%% Nesting: Reverse doesn't work - Reverse nesting doesn't work. - Because a sublist *must* have a mother list. - It's the list concept, not a txt2tags limitation. - All this sublists will be bumped to mother lists. - At level 1, like this one. %%% Nesting: Going deeper and back %% When nesting back to an upper level, the previous sublist %% is automatically closed. - Level 1 - Level 2 - Level 3 - Level 4 - Level 3 -- (closed Level 4) - Level 2 -- (closed Level 3) - Level 1 -- (closed Level 2) %% More than one list can be closed when nesting back. - Level 1 - Level 2 - Level 3 - Level 4 - Level 1 -- (closed Level 4, Level 3 and Level 2) %%% Nesting: Vertical spacing between lists - Level 1 - Level 2 -- blank BEFORE and AFTER (in) - Level 3 % comment lines are NOT considered blank lines - Level 4 % comment lines are NOT considered blank lines - Level 3 - Level 2 -- blank BEFORE and AFTER (out) - Level 1 - Level 2 -- blank BEFORE (spaces) and AFTER (TAB) - Level 3 %%% Nesting: Messing up %% Be careful when going back on the nesting, %% it must be on a valid level! If not, it will %% be bumped up to the previous valid level. - Level 1 - Level 2 - Level 3 - Level 4 - Level 3.5 ??? - Level 3 - Level 2.5 ??? - Level 2 - Level 1.5 ??? - Level 1 %%% Closing: Two (not so) empty lines - This list is closed by a line with spaces and other with TABs - This list is NOT closed by two comment lines % comment lines are NOT considered blank lines % comment lines are NOT considered blank lines - This list is closed by a line with spaces and TAB, - then a comment line, then an empty line. % comment lines are NOT considered blank lines %%% Closing: Empty item closes current (sub)list %% The two blank lines closes ALL the lists. %% To close just the current, use an empty item. - Level 1 - Level 2 - Level 3 - Level 2 - Level 1 - %% The empty item can have trailing blanks. - Empty item with trailing spaces. - - Empty item with trailing TAB. - %%% Closing: EOF closes the lists - If the end of the file (EOF) is hit, - all the currently opened list are closed, - just like when using the two blank lines. = Table =[table] %INCLUDED(t2t) starts here: ../../../test/marks/table.t2t %%% Syntax: Lines starting with a pipe | | Cell 1 %%% Syntax: Extra pipes separate cells | Cell 1 | Cell 2 | Cell 3 %%% Syntax: With a trailing pipe, make border | Cell 1 | Cell 2 | Cell 3 | %%% Syntax: Table lines starting with double pipe are heading || Cell 1 | Cell 2 | Cell 3 | %%% Align: Spaces before the leading pipe centralize the table | Cell 1 | Cell 2 | Cell 3 | %%% Align: Spaces inside the cell denote its alignment || Heading | Heading | Heading | % comments don't close an opened table | <- | -- | -> | | -- | -- | -- | | -> | -- | <- | %%% Span: Column span is defined by extra pipes at cell closing || 1 | 2 | 3+4 || | 1 | 2 | 3 | 4 | | 1+2+3 ||| 4 | | 1 | 2+3 || 4 | | 1+2+3+4 |||| %%% Test: Empty cells are placed as expected | 0 | 1 | 2 | | | 4 | 5 | | 7 | | 8 | | A | B | | | D | E | F | %%% Test: Lines with different number of cells | 1 | | 1 | 2 | | 1 | 2 | 3 | | 1 | 2 | 3 | 4 | | 1 | 2 | 3 | 4 | 5 | %%% Test: Empty cells + Span + Messy cell number = Fun! | Jan | | Fev || | Mar ||| | Apr |||| | May ||||| | 20% | 40% | 60% | 80% | 100% | | | | / | | | | | / / / / / ||| | | / / / / / / / / / ||||| | | o | | o | | | | | . | | | | | = = = = ||| | | 01 | 02 | | | 05 | | 07 | | | | | 11 | | 13 | | | 16 | | 17 | | 19 | 20 | | | 23 | | | 25 | 26 | | | 29 | 30 | | 32 | | | | 35 | | 37 | | 39 | 40 | %%% Test: Lots of cells at the same line | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | %%% Test: Empty lines | | | | | | %%% Invalid: There must be at least one space around the pipe |this|is|not|a|table| |this| is| not| a| table| |this |is |not |a |table | %%% Invalid: You must use spaces, not TABs | this | is | not | a | table | ------------------------------------------------------------ The End. ================================================ FILE: test/typst-reader.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 1 ( "" , [] , [] ) [ Str "Fibonacci" , Space , Str "sequence" ] , Para [ Str "The" , Space , Str "Fibonacci" , Space , Str "sequence" , Space , Str "is" , Space , Str "defined" , Space , Str "through" , Space , Str "the" , SoftBreak , Str "recurrence" , Space , Str "relation" , Space , Math InlineMath "F_{n} = F_{n - 1} + F_{n - 2}" , Str "." , SoftBreak , Str "It" , Space , Str "can" , Space , Str "also" , Space , Str "be" , Space , Str "expressed" , Space , Str "in" , Space , Emph [ Str "closed" , Space , Str "form:" ] ] , Para [ Math DisplayMath "F_{n} = \\left\\lfloor {\\frac{1}{\\sqrt{5}}\\phi^{n}} \\right\\rceil,\\quad\\phi = \\frac{1 + \\sqrt{5}}{2}" ] , Para [ Str "The" , Space , Str "first" , Space , Str "8" , Space , Str "numbers" , Space , Str "of" , Space , Str "the" , Space , Str "sequence" , Space , Str "are:" ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "F_{1}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "F_{2}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "F_{3}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "F_{4}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "F_{5}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "F_{6}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "F_{7}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "F_{8}" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "1" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "3" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "5" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "8" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "13" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "21" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Div ( "" , [ "columns-flow" ] , [ ( "count" , "2" ) ] ) [ Para [ Span ( "" , [] , [ ( "align" , "center" ) ] ) [ Link ( "" , [] , [] ) [ SoftBreak , Strong [ Str "Typst" , Space , Str "Math" , Space , Str "for" , Space , Str "Undergrads" ] , SoftBreak ] ( "https://github.com/johanvx/typst-undergradmath" , "" ) ] ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "Typst" , Space , Str "port" , Space , Str "of" , Space , Emph [ Span ( "" , [ "box" ] , [] ) [ Str "L\8202A\8202" , Span ( "" , [ "box" ] , [] ) [ Str "T\8202E\8202X" ] ] , Space , Str "Math" , Space , Str "for" , Space , Str "Undergrads" ] , Space , Str "by" , Space , Str "Jim" , Space , Str "Hefferon." , SoftBreak , Str "The" , Space , Str "original" , Space , Str "version" , Space , Str "is" , Space , Str "available" , Space , Str "at" , Space , Underline [ Link ( "" , [] , [] ) [ Str "https://gitlab.com/jim.hefferon/undergradmath" ] ( "https://gitlab.com/jim.hefferon/undergradmath" , "" ) ] , Str "." ] , Para [ Strong [ Str "Meaning" , Space , Str "of" , Space , Str "annotations\8192\160" ] ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidth 0.3333333333333333 ) , ( AlignLeft , ColWidth 0.6666666666666666 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Str "2023-05-22" , Space , Str "\10060" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "This" , Space , Str "is" , Space , Str "unavailable." , Space , Str "Last" , Space , Str "check" , Space , Str "date" , Space , Str "is" , Space , Str "2023-05-22." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Span ( "unavailable" , [] , [] ) [] ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidth 0.3333333333333333 ) , ( AlignLeft , ColWidth 0.6666666666666666 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Str "\128166" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Get" , Space , Str "this" , Space , Str "in" , Space , Str "a" , Space , Str "tricky" , Space , Str "way." , Space , Str "Need" , Space , Str "a" , Space , Str "simpler" , Space , Str "method." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Span ( "tricky" , [] , [] ) [] ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidth 0.3333333333333333 ) , ( AlignLeft , ColWidth 0.6666666666666666 ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Str "No" , Space , Str "idea" , Space , Str "\128533" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Str "Don\8217t" , Space , Str "know" , Space , Str "how" , Space , Str "to" , Space , Str "get" , Space , Str "this." ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Span ( "noidea" , [] , [] ) [] ] , Para [ Strong [ Str "Rule" , Space , Str "One\8192\160" ] , Str "Any" , Space , Str "mathematics" , Space , Str "at" , Space , Str "all," , Space , Str "even" , Space , Str "a" , Space , Str "single" , Space , Str "character," , Space , Str "gets" , Space , Str "a" , Space , Str "mathematical" , Space , Str "setting." , SoftBreak , Str "Thus," , Space , Str "for" , Space , Str "\8220the" , Space , Str "value" , Space , Str "of" , Space , Math InlineMath "x" , Space , Str "is" , Space , Math InlineMath "7" , Str "\8221" , Space , Str "enter" , Space , Code ( "" , [] , [] ) "the value of $x$ is $7$" , Str "." ] , Para [ Strong [ Str "Template\8192\160" ] , Str "Your" , Space , Str "document" , Space , Str "should" , Space , Str "contain" , Space , Str "at" , Space , Str "least" , Space , Str "this." ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "-- document body here --" ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Strong [ Str "Common" , Space , Str "constructs\8192\160" ] ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "x^{2}" , Str "\8192" , Code ( "" , [] , [] ) "x^2" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\sqrt{2}" , Str "," , Space , Math InlineMath "\\sqrt[n]{3}" , Str "\8192" , Code ( "" , [] , [] ) "sqrt(2)" , Str "," , Space , Code ( "" , [] , [] ) "root(n, 3)" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "x_{i,j}" , Str "\8192" , Code ( "" , [] , [] ) "x_(i, j)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\frac{2}{3}" , Str "," , Space , Math InlineMath "2/3" , Str "\8192" , Code ( "" , [] , [] ) "2 / 3" , Str "," , Space , Code ( "" , [] , [] ) "2 \\/ 3" , Space , Str "or" , Space , Code ( "" , [] , [] ) "2 slash 3" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Strong [ Str "Calligraphic" , Space , Str "letters\8192\160" ] , Str "Use" , Space , Str "as" , Space , Str "in" , Space , Code ( "" , [] , [] ) "$cal(A)$" , Str "." ] , Para [ Math DisplayMath "\\mathcal{ABCDEFGHIJKLMNOPQRSTUVWXYZ}" ] , Para [ Str "Getting" , Space , Str "script" , Space , Str "letters" , Space , Str "is" , Space , Link ( "" , [ "ref" ] , [] ) [ Str "[unavailable]" ] ( "#unavailable" , "" ) , Str "." ] , Para [ Strong [ Str "Greek\8192\160" ] ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\alpha" , Str "\8192" , Code ( "" , [] , [] ) "alpha" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\xi" , Str "," , Space , Math InlineMath "\\Xi" , Str "\8192" , Code ( "" , [] , [] ) "xi" , Str "," , Space , Code ( "" , [] , [] ) "Xi" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\beta" , Str "\8192" , Code ( "" , [] , [] ) "beta" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\959" , Str "\8192" , Code ( "" , [] , [] ) "omicron" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\gamma" , Str "," , Space , Math InlineMath "\\Gamma" , Str "\8192" , Code ( "" , [] , [] ) "gamma" , Str "," , Space , Code ( "" , [] , [] ) "Gamma" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\pi" , Str "," , Space , Math InlineMath "\\Pi" , Str "\8192" , Code ( "" , [] , [] ) "pi" , Str "," , Space , Code ( "" , [] , [] ) "Pi" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\delta" , Str "," , Space , Math InlineMath "\\Delta" , Str "\8192" , Code ( "" , [] , [] ) "delta" , Str "," , Space , Code ( "" , [] , [] ) "Delta" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\varpi" , Str "\8192" , Code ( "" , [] , [] ) "pi.alt" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\epsilon" , Str "\8192" , Code ( "" , [] , [] ) "epsilon.alt" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\rho" , Str "\8192" , Code ( "" , [] , [] ) "rho" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\varepsilon" , Str "\8192" , Code ( "" , [] , [] ) "epsilon" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\varrho" , Str "\8192" , Code ( "" , [] , [] ) "rho.alt" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\zeta" , Str "\8192" , Code ( "" , [] , [] ) "zeta" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\sigma" , Str "," , Space , Math InlineMath "\\Sigma" , Str "\8192" , Code ( "" , [] , [] ) "sigma" , Str "," , Space , Code ( "" , [] , [] ) "Sigma" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\eta" , Str "\8192" , Code ( "" , [] , [] ) "eta" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\varsigma" , Str "\8192" , Code ( "" , [] , [] ) "\\u{03C2}" , Space , Link ( "" , [ "ref" ] , [] ) [ Str "[tricky]" ] ( "#tricky" , "" ) ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\theta" , Str "," , Space , Math InlineMath "\\Theta" , Str "\8192" , Code ( "" , [] , [] ) "theta" , Str "," , Space , Code ( "" , [] , [] ) "Theta" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\tau" , Str "\8192" , Code ( "" , [] , [] ) "tau" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\vartheta" , Str "\8192" , Code ( "" , [] , [] ) "theta.alt" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\upsilon" , Str "," , Space , Math InlineMath "\\Upsilon" , Str "\8192" , Code ( "" , [] , [] ) "upsilon" , Str "," , Space , Code ( "" , [] , [] ) "Upsilon" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\iota" , Str "\8192" , Code ( "" , [] , [] ) "iota" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\phi" , Str "," , Space , Math InlineMath "\\Phi" , Str "\8192" , Code ( "" , [] , [] ) "phi.alt" , Str "," , Space , Code ( "" , [] , [] ) "Phi" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\kappa" , Str "\8192" , Math InlineMath "\922" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\varphi" , Str "\8192" , Code ( "" , [] , [] ) "phi" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\lambda" , Str "," , Space , Math InlineMath "\\Lambda" , Str "\8192" , Code ( "" , [] , [] ) "lambda" , Str "," , Space , Code ( "" , [] , [] ) "Lambda" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\chi" , Str "\8192" , Code ( "" , [] , [] ) "chi" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mu" , Str "\8192" , Code ( "" , [] , [] ) "mu" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\psi" , Str "," , Space , Math InlineMath "\\Psi" , Str "\8192" , Code ( "" , [] , [] ) "psi" , Str "," , Space , Code ( "" , [] , [] ) "Psi" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\nu" , Str "\8192" , Code ( "" , [] , [] ) "nu" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\omega" , Str "," , Space , Math InlineMath "\\Omega" , Str "\8192" , Code ( "" , [] , [] ) "omega" , Str "," , Space , Code ( "" , [] , [] ) "Omega" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Strong [ Str "Sets" , Space , Str "and" , Space , Str "logic\8192\160" ] ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\cup" , Str "\8192" , Code ( "" , [] , [] ) "union" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mathbb{R}" , Str "\8192" , Code ( "" , [] , [] ) "RR" , Str "," , Space , Code ( "" , [] , [] ) "bb(R)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\forall" , Str "\8192" , Code ( "" , [] , [] ) "forall" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\cap" , Str "\8192" , Code ( "" , [] , [] ) "sect" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mathbb{Z}" , Str "\8192" , Code ( "" , [] , [] ) "ZZ" , Str "," , Space , Code ( "" , [] , [] ) "bb(Z)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\exists" , Str "\8192" , Code ( "" , [] , [] ) "exists" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\subset" , Str "\8192" , Code ( "" , [] , [] ) "subset" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mathbb{Q}" , Str "\8192" , Code ( "" , [] , [] ) "QQ" , Str "," , Space , Code ( "" , [] , [] ) "bb(Q)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\neg" , Str "\8192" , Code ( "" , [] , [] ) "not" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\subseteq" , Str "\8192" , Code ( "" , [] , [] ) "subset.eq" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mathbb{N}" , Str "\8192" , Code ( "" , [] , [] ) "NN" , Str "," , Space , Code ( "" , [] , [] ) "bb(N)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\vee" , Str "\8192" , Code ( "" , [] , [] ) "or" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\supset" , Str "\8192" , Code ( "" , [] , [] ) "supset" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mathbb{C}" , Str "\8192" , Code ( "" , [] , [] ) "CC" , Str "," , Space , Code ( "" , [] , [] ) "bb(C)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\land" , Str "\8192" , Code ( "" , [] , [] ) "and" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\supseteq" , Str "\8192" , Code ( "" , [] , [] ) "supset.eq" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\varnothing" , Str "\8192" , Code ( "" , [] , [] ) "diameter" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\vdash" , Str "\8192" , Code ( "" , [] , [] ) "tack.r" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\in" , Str "\8192" , Code ( "" , [] , [] ) "in" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\varnothing" , Str "\8192" , Code ( "" , [] , [] ) "nothing" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\models" , Str "\8192" , Code ( "" , [] , [] ) "models" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\notin" , Str "\8192" , Code ( "" , [] , [] ) "in.not" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\1488" , Str "\8192" , Code ( "" , [] , [] ) "alef" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\smallsetminus" , Str "\8192" , Code ( "" , [] , [] ) "without" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "Negate" , Space , Str "an" , Space , Str "operator," , Space , Str "as" , Space , Str "in" , Space , Math InlineMath "\8836" , Str "," , Space , Str "with" , Space , Code ( "" , [] , [] ) "subset.not" , Str "." , SoftBreak , Str "Get" , Space , Str "the" , Space , Str "set" , Space , Str "complement" , Space , Math InlineMath "A^{\\mathsf{c}}" , Space , Str "with" , Space , Code ( "" , [] , [] ) "A^(sans(c))" , Space , Str "(or" , Space , Math InlineMath "A^{\\complement}" , Space , Str "with" , Space , Code ( "" , [] , [] ) "A^(complement)" , Str "," , Space , Str "or" , Space , Math InlineMath "\\overline{A}" , Space , Str "with" , Space , Code ( "" , [] , [] ) "overline(A)" , Str ")." ] , DefinitionList [ ( [ Str "Remark" ] , [ [ Para [ Str "Using" , Space , Code ( "" , [] , [] ) "diameter" , Space , Str "for" , Space , Code ( "" , [] , [] ) "\\varnothing" , Space , Str "may" , Space , Str "cause" , Space , Str "some" , Space , Str "confusion." , Space , Str "However," , Space , Span ( "" , [ "box" ] , [] ) [ Str "L\8202A\8202" , Span ( "" , [ "box" ] , [] ) [ Str "T\8202E\8202X" ] ] , Space , Str "also" , Space , Str "uses" , Space , Math InlineMath "\\varnothing" , Space , Str "(" , Code ( "" , [] , [] ) "\\u{2300}" , Str ")" , Space , Str "instead" , Space , Str "of" , Space , Math InlineMath "\\varnothing" , Space , Str "(" , Code ( "" , [] , [] ) "\\u{2205}" , Str ")," , Space , Str "see" , Space , Underline [ Link ( "" , [] , [] ) [ Str "newcm" , Space , Math InlineMath "\167" , Str "13.3" ] ( "https://mirrors.sustech.edu.cn/CTAN/fonts/newcomputermodern/doc/newcm-doc.pdf" , "" ) ] , Str "." , SoftBreak , Str "Another" , Space , Str "solution" , Space , Str "is" , Space , Str "to" , Space , Str "use" , Space , Code ( "" , [] , [] ) "text(font: \"Fira Sans\", nothing)" , Str "," , Space , Str "but" , Space , Str "the" , Space , Str "resultant" , Space , Str "glyph" , Space , Math InlineMath "\\varnothing" , Space , Str "is" , Space , Str "subtly" , Space , Str "different" , Space , Str "from" , Space , Str "the" , Space , Str "widely" , Space , Str "used" , Space , Str "one." , SoftBreak , Str "Ultimately," , Space , Str "the" , Space , Str "choice" , Space , Str "is" , Space , Str "always" , Space , Strong [ Str "your" , Space , Str "decision" ] , Str "." ] ] ] ) ] , Para [ Strong [ Str "Decorations\8192\160" ] ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "f'" , Str "\8192" , Code ( "" , [] , [] ) "f'" , Str "," , Space , Code ( "" , [] , [] ) "f prime" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\dot{a}" , Str "\8192" , Code ( "" , [] , [] ) "dot(a)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\widetilde{a}" , Str "\8192" , Code ( "" , [] , [] ) "tilde(a)" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "f''" , Str "\8192" , Code ( "" , [] , [] ) "f prime.double" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\ddot{a}" , Str "\8192" , Code ( "" , [] , [] ) "diaer(a)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\overline{a}" , Str "\8192" , Code ( "" , [] , [] ) "macron(a)" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\Sigma^{\\ast}" , Str "\8192" , Code ( "" , [] , [] ) "Sigma^*" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\hat{a}" , Str "\8192" , Code ( "" , [] , [] ) "hat(a)" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\overset{\\rightarrow}{a}" , Str "\8192" , Code ( "" , [] , [] ) "arrow(a)" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "If" , Space , Str "the" , Space , Str "decorated" , Space , Str "letter" , Space , Str "is" , Space , Math InlineMath "i" , Space , Str "or" , Space , Math InlineMath "j" , Space , Str "then" , Space , Str "some" , Space , Str "decorations" , Space , Str "need" , Space , Code ( "" , [] , [] ) "\\u{1D6A4}" , Space , Link ( "" , [ "ref" ] , [] ) [ Str "[tricky]" ] ( "#tricky" , "" ) , Space , Str "and" , Space , Code ( "" , [] , [] ) "\\u{1D6A5}" , Space , Link ( "" , [ "ref" ] , [] ) [ Str "[tricky]" ] ( "#tricky" , "" ) , Str "," , Space , Str "as" , Space , Str "in" , Space , Math InlineMath "\\overset{\\rightarrow}{\\imath}" , Space , Str "with" , Space , Code ( "" , [] , [] ) "arrow(\\u{1D6A4})" , Str "." , SoftBreak , Str "Some" , Space , Str "authors" , Space , Str "use" , Space , Str "boldface" , Space , Str "for" , Space , Str "vectors:" , Space , Code ( "" , [] , [] ) "bold(x)" , Str "." ] , Para [ Str "Entering" , Space , Code ( "" , [] , [] ) "overline(x + y)" , Space , Str "produces" , Space , Math InlineMath "\\overline{x + y}" , Str "," , Space , Str "and" , Space , Code ( "" , [] , [] ) "hat(x + y)" , Space , Str "gives" , Space , Math InlineMath "\\hat{x + y}" , Str "." , SoftBreak , Str "Comment" , Space , Str "on" , Space , Str "an" , Space , Str "expression" , Space , Str "as" , Space , Str "here" , Space , Str "(there" , Space , Str "is" , Space , Str "also" , Space , Code ( "" , [] , [] ) "overbrace(..)" , Str ")." ] , Para [ Span ( "" , [] , [ ( "align" , "center" ) ] ) [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\underset{|A|}{\\underbrace{x + y}}" , Str "\8192" , Code ( "" , [] , [] ) "underbrace(x + y, |A|)" ] ] ] , Para [ Strong [ Str "Dots\8192\160" ] , Str "Use" , Space , Str "low" , Space , Str "dots" , Space , Str "in" , Space , Str "a" , Space , Str "list" , Space , Math InlineMath "\\left\\{ 0,1,2,\\ldots \\right\\}" , Str "," , Space , Str "entered" , Space , Str "as" , Space , Code ( "" , [] , [] ) "{0, 1, 2, ...}" , Str "." , SoftBreak , Str "Use" , Space , Str "centered" , Space , Str "dots" , Space , Str "in" , Space , Str "a" , Space , Str "sum" , Space , Str "or" , Space , Str "product" , Space , Math InlineMath "1 + \\cdots + 100" , Str "," , Space , Str "entered" , Space , Str "as" , Space , Code ( "" , [] , [] ) "1 + dots.h.c + 100" , Str "." , SoftBreak , Str "You" , Space , Str "can" , Space , Str "also" , Space , Str "get" , Space , Str "vertical" , Space , Str "dots" , Space , Code ( "" , [] , [] ) "dots.v" , Str "," , Space , Str "diagonal" , Space , Str "dots" , Space , Code ( "" , [] , [] ) "dots.down" , Space , Str "and" , Space , Str "anti-diagonal" , Space , Str "dots" , Space , Code ( "" , [] , [] ) "dots.up" , Str "." ] , Para [ Strong [ Str "Roman" , Space , Str "names\8192\160" ] , Str "Just" , Space , Str "type" , Space , Str "them!" ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\sin" , Str "\8192" , Code ( "" , [] , [] ) "sin" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\sinh" , Str "\8192" , Code ( "" , [] , [] ) "sinh" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\arcsin" , Str "\8192" , Code ( "" , [] , [] ) "arcsin" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\cos" , Str "\8192" , Code ( "" , [] , [] ) "cos" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\cosh" , Str "\8192" , Code ( "" , [] , [] ) "cosh" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\arccos" , Str "\8192" , Code ( "" , [] , [] ) "arccos" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\tan" , Str "\8192" , Code ( "" , [] , [] ) "tan" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\tanh" , Str "\8192" , Code ( "" , [] , [] ) "tanh" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\arctan" , Str "\8192" , Code ( "" , [] , [] ) "arctan" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\sec" , Str "\8192" , Code ( "" , [] , [] ) "sec" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\coth" , Str "\8192" , Code ( "" , [] , [] ) "coth" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\min" , Str "\8192" , Code ( "" , [] , [] ) "min" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\csc" , Str "\8192" , Code ( "" , [] , [] ) "csc" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\det" , Str "\8192" , Code ( "" , [] , [] ) "det" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\max" , Str "\8192" , Code ( "" , [] , [] ) "max" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\cot" , Str "\8192" , Code ( "" , [] , [] ) "cot" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\dim" , Str "\8192" , Code ( "" , [] , [] ) "dim" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\inf" , Str "\8192" , Code ( "" , [] , [] ) "inf" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\exp" , Str "\8192" , Code ( "" , [] , [] ) "exp" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\ker" , Str "\8192" , Code ( "" , [] , [] ) "ker" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\sup" , Str "\8192" , Code ( "" , [] , [] ) "sup" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\log" , Str "\8192" , Code ( "" , [] , [] ) "log" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\deg" , Str "\8192" , Code ( "" , [] , [] ) "deg" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\liminf" , Str "\8192" , Code ( "" , [] , [] ) "liminf" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\ln" , Str "\8192" , Code ( "" , [] , [] ) "ln" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\arg" , Str "\8192" , Code ( "" , [] , [] ) "arg" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\limsup" , Str "\8192" , Code ( "" , [] , [] ) "limsup" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\lg" , Str "\8192" , Code ( "" , [] , [] ) "lg" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\gcd" , Str "\8192" , Code ( "" , [] , [] ) "gcd" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\lim" , Str "\8192" , Code ( "" , [] , [] ) "lim" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Strong [ Str "Other" , Space , Str "symbols\8192\160" ] ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "<" , Str "\8192" , Code ( "" , [] , [] ) "<" , Str "," , Space , Code ( "" , [] , [] ) "lt" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\angle" , Str "\8192" , Code ( "" , [] , [] ) "angle" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\cdot" , Str "\8192" , Code ( "" , [] , [] ) "dot" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\leq" , Str "\8192" , Code ( "" , [] , [] ) "<=" , Str "," , Space , Code ( "" , [] , [] ) "lt.eq" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\measuredangle" , Str "\8192" , Code ( "" , [] , [] ) "angle.arc" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\pm" , Str "\8192" , Code ( "" , [] , [] ) "plus.minus" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath ">" , Str "\8192" , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "gt" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\ell" , Str "\8192" , Code ( "" , [] , [] ) "ell" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mp" , Str "\8192" , Code ( "" , [] , [] ) "minus.plus" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\geq" , Str "\8192" , Code ( "" , [] , [] ) ">=" , Str "," , Space , Code ( "" , [] , [] ) "gt.eq" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\parallel" , Str "\8192" , Code ( "" , [] , [] ) "parallel" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\times" , Str "\8192" , Code ( "" , [] , [] ) "times" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\neq" , Str "\8192" , Code ( "" , [] , [] ) "!=" , Str "," , Space , Code ( "" , [] , [] ) "eq.not" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "45{^\\circ}" , Str "\8192" , Code ( "" , [] , [] ) "45 degree" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\div" , Str "\8192" , Code ( "" , [] , [] ) "div" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\ll" , Str "\8192" , Code ( "" , [] , [] ) "<<" , Str "," , Space , Code ( "" , [] , [] ) "lt.double" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\cong" , Str "\8192" , Code ( "" , [] , [] ) "tilde.equiv" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\ast" , Str "\8192" , Code ( "" , [] , [] ) "*" , Str "," , Space , Code ( "" , [] , [] ) "ast" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\gg" , Str "\8192" , Code ( "" , [] , [] ) ">>" , Str "," , Space , Code ( "" , [] , [] ) "gt.double" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\ncong" , Str "\8192" , Code ( "" , [] , [] ) "tilde.equiv.not" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mid" , Str "\8192" , Code ( "" , [] , [] ) "divides" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\approx" , Str "\8192" , Code ( "" , [] , [] ) "approx" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\sim" , Str "\8192" , Code ( "" , [] , [] ) "tilde" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\nmid" , Str "\8192" , Code ( "" , [] , [] ) "divides.not" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\asymp" , Str "\8192" , Code ( "" , [] , [] ) "\\u{224D}" , Space , Link ( "" , [ "ref" ] , [] ) [ Str "[tricky]" ] ( "#tricky" , "" ) ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\simeq" , Str "\8192" , Code ( "" , [] , [] ) "tilde.eq" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "n!" , Str "\8192" , Code ( "" , [] , [] ) "n!" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\equiv" , Str "\8192" , Code ( "" , [] , [] ) "equiv" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\nsim" , Str "\8192" , Code ( "" , [] , [] ) "tilde.not" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\partial" , Str "\8192" , Code ( "" , [] , [] ) "diff" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\prec" , Str "\8192" , Code ( "" , [] , [] ) "prec" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\oplus" , Str "\8192" , Code ( "" , [] , [] ) "plus.circle" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\nabla" , Str "\8192" , Code ( "" , [] , [] ) "nabla" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\preceq" , Str "\8192" , Code ( "" , [] , [] ) "prec.eq" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\ominus" , Str "\8192" , Code ( "" , [] , [] ) "minus.circle" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\295" , Str "\8192" , Code ( "" , [] , [] ) "planck.reduce" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\succ" , Str "\8192" , Code ( "" , [] , [] ) "succ" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\odot" , Str "\8192" , Code ( "" , [] , [] ) "dot.circle" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\circ" , Str "\8192" , Code ( "" , [] , [] ) "circle.stroked.tiny" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\succeq" , Str "\8192" , Code ( "" , [] , [] ) "succ.eq" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\otimes" , Str "\8192" , Code ( "" , [] , [] ) "times.circle" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\star" , Str "\8192" , Code ( "" , [] , [] ) "star" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\propto" , Str "\8192" , Code ( "" , [] , [] ) "prop" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\oslash" , Str "\8192" , Code ( "" , [] , [] ) "\\u{2298}" , Space , Link ( "" , [ "ref" ] , [] ) [ Str "[tricky]" ] ( "#tricky" , "" ) ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\sqrt{}" , Str "\8192" , Code ( "" , [] , [] ) "sqrt(\"\")" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\doteq" , Str "\8192" , Code ( "" , [] , [] ) "\\u{2250}" , Space , Link ( "" , [ "ref" ] , [] ) [ Str "[tricky]" ] ( "#tricky" , "" ) ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\upharpoonright" , Str "\8192" , Code ( "" , [] , [] ) "harpoon.tr" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\10003" , Str "\8192" , Code ( "" , [] , [] ) "checkmark" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "Use" , Space , Code ( "" , [] , [] ) "a divides b" , Space , Str "for" , Space , Str "the" , Space , Str "divides" , Space , Str "relation," , Space , Math InlineMath "a \\mid b" , Str "," , Space , Str "and" , Space , Code ( "" , [] , [] ) "a divides.not b" , Space , Str "for" , Space , Str "the" , Space , Str "negation," , Space , Math InlineMath "a \\nmid b" , Str "." , SoftBreak , Str "Use" , Space , Code ( "" , [] , [] ) "|" , Space , Str "to" , Space , Str "get" , Space , Str "set" , Space , Str "builder" , Space , Str "notation" , Space , Math InlineMath "\\left\\{ a \\in S~|~a\\text{ is odd} \\right\\}" , Space , Str "with" , Space , Code ( "" , [] , [] ) "{a in S | a \"is odd\"}" , Str "." ] , Para [ Strong [ Str "Arrows\8192\160" ] ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\rightarrow" , Str "\8192" , Code ( "" , [] , [] ) "->" , Str "," , Space , Code ( "" , [] , [] ) "arrow.r" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\mapsto" , Str "\8192" , Code ( "" , [] , [] ) "|->" , Str "," , Space , Code ( "" , [] , [] ) "arrow.r.bar" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\nrightarrow" , Str "\8192" , Code ( "" , [] , [] ) "arrow.r.not" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\longmapsto" , Str "\8192" , Code ( "" , [] , [] ) "arrow.r.long.bar" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\longrightarrow" , Str "\8192" , Code ( "" , [] , [] ) "arrow.r.long" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\leftarrow" , Str "\8192" , Code ( "" , [] , [] ) "<-" , Str "," , Space , Code ( "" , [] , [] ) "arrow.l" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\Rightarrow" , Str "\8192" , Code ( "" , [] , [] ) "=>" , Str "," , Space , Code ( "" , [] , [] ) "arrow.r.double" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\longleftrightarrow" , Str "\8192" , Code ( "" , [] , [] ) "<-->" , Str "," , Space , Code ( "" , [] , [] ) "arrow.l.r.long" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\nRightarrow" , Str "\8192" , Code ( "" , [] , [] ) "arrow.r.double.not" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\downarrow" , Str "\8192" , Code ( "" , [] , [] ) "arrow.b" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\Longrightarrow" , Str "\8192" , Code ( "" , [] , [] ) "arrow.r.double.long" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\uparrow" , Str "\8192" , Code ( "" , [] , [] ) "arrow.t" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\rightsquigarrow" , Str "\8192" , Code ( "" , [] , [] ) "arrow.squiggly" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\updownarrow" , Str "\8192" , Code ( "" , [] , [] ) "arrow.t.b" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "The" , Space , Str "right" , Space , Str "arrows" , Space , Str "in" , Space , Str "the" , Space , Str "first" , Space , Str "column" , Space , Str "have" , Space , Str "matching" , Space , Str "left" , Space , Str "arrows," , Space , Str "such" , Space , Str "as" , Space , Code ( "" , [] , [] ) "arrow.l.not" , Str "," , Space , Str "and" , Space , Str "there" , Space , Str "are" , Space , Str "some" , Space , Str "other" , Space , Str "matches" , Space , Str "for" , Space , Str "down" , Space , Str "arrows," , Space , Str "etc." ] , Para [ Strong [ Str "Variable-sized" , Space , Str "operators\8192\160" ] , Str "The" , Space , Str "summation" , Space , Math InlineMath "\\sum_{j = 0}^{3}j^{2}" , Space , Code ( "" , [] , [] ) "sum_(j = 0)^3 j^2" , Space , Str "and" , Space , Str "the" , Space , Str "integral" , Space , Math InlineMath "\\int_{x = 0}^{3}x^{2}dx" , Space , Code ( "" , [] , [] ) "integral_(x = 0)^3 x^2 dif x" , Space , Str "expand" , Space , Str "when" , Space , Str "displayed." ] , Para [ Math DisplayMath "\\sum_{j = 0}^{3}j^{2}\\qquad\\int_{x = 0}^{3}x^{2}dx" ] , Para [ Str "These" , Space , Str "do" , Space , Str "the" , Space , Str "same." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\int" , Str "\8192" , Code ( "" , [] , [] ) "integral" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\iiint" , Str "\8192" , Code ( "" , [] , [] ) "integral.triple" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\bigcup" , Str "\8192" , Code ( "" , [] , [] ) "union.big" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\iint" , Str "\8192" , Code ( "" , [] , [] ) "integral.double" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\oint" , Str "\8192" , Code ( "" , [] , [] ) "integral.cont" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\bigcap" , Str "\8192" , Code ( "" , [] , [] ) "sect.big" ] ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Strong [ Str "Fences\8192\160" ] ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "()" , Str "\8192" , Code ( "" , [] , [] ) "()" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\langle\\rangle" , Str "\8192" , Code ( "" , [] , [] ) "angle.l angle.r" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\left| {} \\right|" , Str "\8192" , Code ( "" , [] , [] ) "abs(\"\")" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\lbrack\\rbrack" , Str "\8192" , Code ( "" , [] , [] ) "[]" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\left\\lfloor {} \\right\\rfloor" , Str "\8192" , Code ( "" , [] , [] ) "floor(\"\")" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\left\\| {} \\right\\|" , Str "\8192" , Code ( "" , [] , [] ) "norm(\"\")" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\left\\{ \\right\\}" , Str "\8192" , Code ( "" , [] , [] ) "{}" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Span ( "" , [ "box" ] , [] ) [ Math InlineMath "\\left\\lceil {} \\right\\rceil" , Str "\8192" , Code ( "" , [] , [] ) "ceil(\"\")" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "Fix" , Space , Str "the" , Space , Str "size" , Space , Str "with" , Space , Str "the" , Space , Code ( "" , [] , [] ) "lr" , Space , Str "function." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\left. \\left\\lbrack \\sum_{k = 0}^{n}e^{k^{2}} \\right\\rbrack \\right." ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "lr([sum_(k = 0)^n e^(k^2)], size: #50%)" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "To" , Space , Str "have" , Space , Str "them" , Space , Str "grow" , Space , Str "with" , Space , Str "the" , Space , Str "enclosed" , Space , Str "formula," , Space , Str "also" , Space , Str "use" , Space , Str "the" , Space , Code ( "" , [] , [] ) "lr" , Space , Str "function." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\left\\langle i,2^{2^{i}} \\right\\rangle" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "lr(angle.l i, 2^(2^i) angle.r)" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "Fences" , Space , Str "scale" , Space , Str "by" , Space , Str "default" , Space , Str "if" , Space , Str "entered" , Space , Str "directly" , Space , Str "as" , Space , Str "codepoints," , Space , Str "and" , Space , Str "don\8217t" , Space , Str "scale" , Space , Str "automatically" , Space , Str "if" , Space , Str "entered" , Space , Str "as" , Space , Str "symbol" , Space , Str "notation." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\left( \\frac{1}{n^{\\alpha}} \\right)" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "(1 / n^(alpha))" ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "(\\frac{1}{n^{\\alpha}})" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "paren.l 1 / n^(alpha) paren.r" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "The" , Space , Code ( "" , [] , [] ) "lr" , Space , Str "function" , Space , Str "also" , Space , Str "allows" , Space , Str "to" , Space , Str "scale" , Space , Str "unmatched" , Space , Str "delimiters" , Space , Str "and" , Space , Str "one-side" , Space , Str "fences." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\left. \\frac{df}{dx} \\right|_{x_{0}}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "lr(frac(dif f, dif x) |)_(x_0)" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Strong [ Str "Arrays," , Space , Str "Matrices\8192\160" ] , Str "Get" , Space , Str "a" , Space , Str "matrix" , Space , Str "with" , Space , Str "the" , Space , Code ( "" , [] , [] ) "mat" , Space , Str "function." , Space , Str "You" , Space , Str "can" , Space , Str "pass" , Space , Str "an" , Space , Str "array" , Space , Str "to" , Space , Str "it." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\begin{pmatrix}\na & b \\\\\nc & d\n\\end{pmatrix}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "$ mat(a, b; c, d) $" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "In" , Space , Str "Typst," , Space , Underline [ Link ( "" , [] , [] ) [ Str "array" ] ( "https://typst.app/docs/reference/typst/array" , "" ) ] , Space , Str "is" , Space , Str "a" , Space , Str "sequence" , Space , Str "of" , Space , Str "values," , SoftBreak , Str "while" , Space , Str "in" , Space , Span ( "" , [ "box" ] , [] ) [ Str "L\8202A\8202" , Span ( "" , [ "box" ] , [] ) [ Str "T\8202E\8202X" ] ] , Str "," , Space , Str "array" , Space , Str "is" , Space , Str "a" , Space , Str "matrix" , Space , Str "without" , Space , Str "fences," , Space , Str "which" , Space , Str "is" , Space , Code ( "" , [] , [] ) "$mat(delim: #none, ..)$" , Space , Str "in" , Space , Str "Typst." ] , Para [ Str "For" , Space , Str "the" , Space , Str "determinant" , Space , Str "use" , Space , Code ( "" , [] , [] ) "|A|" , Str "," , Space , Str "text" , Space , Str "operator" , Space , Math InlineMath "\\det" , Space , Code ( "" , [] , [] ) "det" , Space , Str "or" , Space , Code ( "" , [] , [] ) "mat(delim: \"|\", ..)" , Str "." ] , Para [ Str "Definition" , Space , Str "by" , Space , Str "cases" , Space , Str "can" , Space , Str "be" , Space , Str "easily" , Space , Str "obtained" , Space , Str "with" , Space , Str "the" , Space , Code ( "" , [] , [] ) "cases" , Space , Str "function." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "f_{n} = \\begin{cases}\na & \\text{if }n = 0 \\\\\nr \\cdot f_{n - 1} & \\text{else }\n\\end{cases}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "$ f_n = cases(\n a &\"if\" n = 0,\n r dot f_(n - 1) &\"else\"\n) $" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Strong [ Str "Spacing" , Space , Str "in" , Space , Str "mathematics\8192\160" ] , Str "Improve" , Space , Math InlineMath "\\sqrt{2}x" , Space , Str "to" , Space , Math InlineMath "\\sqrt{2}\\, x" , Space , Str "with" , Space , Str "a" , Space , Str "thin" , Space , Str "space," , Space , Str "as" , Space , Str "in" , Space , Code ( "" , [] , [] ) "sqrt(2) thin x" , Str "." , SoftBreak , Str "Slightly" , Space , Str "wider" , Space , Str "are" , Space , Code ( "" , [] , [] ) "medium" , Space , Str "and" , Space , Code ( "" , [] , [] ) "thick" , Space , Str "(the" , Space , Str "three" , Space , Str "are" , Space , Str "in" , Space , Str "ratio" , Space , Math InlineMath "3:4:5" , Str ")." , SoftBreak , Str "Bigger" , Space , Str "space" , Space , Str "is" , Space , Code ( "" , [] , [] ) "quad" , Space , Str "for" , Space , Math InlineMath "\\rightarrow \\quad \\leftarrow" , Str "," , Space , Str "which" , Space , Str "is" , Space , Str "useful" , Space , Str "between" , Space , Str "parts" , Space , Str "of" , Space , Str "a" , Space , Str "display." , SoftBreak , Str "Get" , Space , Str "arbitrary" , Space , Str "space" , Space , Str "with" , Space , Str "the" , Space , Code ( "" , [] , [] ) "h" , Space , Str "function." , SoftBreak , Str "For" , Space , Str "example," , Space , Str "use" , Space , Code ( "" , [] , [] ) "#h(2em)" , Space , Str "for" , Space , Code ( "" , [] , [] ) "\\qquad" , Space , Str "in" , Space , Span ( "" , [ "box" ] , [] ) [ Str "L\8202A\8202" , Span ( "" , [ "box" ] , [] ) [ Str "T\8202E\8202X" ] ] , Space , Str "and" , Space , Code ( "" , [] , [] ) "#h(-0.1667em)" , Space , Str "for" , Space , Code ( "" , [] , [] ) "\\!" , Str "." ] , Para [ Strong [ Str "Displayed" , Space , Str "equations\8192\160" ] , Str "Display" , Space , Str "equations" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "level" , Space , Str "using" , Space , Code ( "" , [] , [] ) "$ ... $" , Space , Str "with" , Space , Str "at" , Space , Str "least" , Space , Str "one" , Space , Str "space" , Space , Str "separating" , Space , Str "the" , Space , Str "math" , Space , Str "content" , Space , Str "and" , Space , Str "the" , Space , Code ( "" , [] , [] ) "$" , Str "." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "S = k \\cdot \\lg W" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "$ S = k dot lg W $" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "You" , Space , Str "can" , Space , Str "break" , Space , Str "into" , Space , Str "multiple" , Space , Str "lines." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\begin{array}{r}\n\\sin(x) = x - \\frac{x^{3}}{3!} \\\\\n + \\frac{x^{5}}{5!} - \\cdots\n\\end{array}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "$ sin(x) = x - x^3 / 3! \\\n + x^5 / 5! - dots.h.c $" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "Align" , Space , Str "equations" , Space , Str "using" , Space , Code ( "" , [] , [] ) "&" ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignLeft , ColWidthDefault ) , ( AlignLeft , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\begin{aligned}\n\\nabla \\cdot \\mathbf{D} & = \\rho \\\\\n\\nabla \\cdot \\mathbf{B} & = 0\n\\end{aligned}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "$ nabla dot bold(D) &= rho \\\n nabla dot bold(B) &= 0 $" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "(the" , Space , Str "left" , Space , Str "or" , Space , Str "right" , Space , Str "side" , Space , Str "of" , Space , Str "an" , Space , Str "alignment" , Space , Str "can" , Space , Str "be" , Space , Str "empty)." , SoftBreak , Str "Get" , Space , Str "a" , Space , Str "numbered" , Space , Str "version" , Space , Str "by" , Space , Code ( "" , [] , [] ) "#set math.equation(numbering: ..)" , Str "." ] , Para [ Strong [ Str "Calculus" , Space , Str "examples\8192\160" ] , Str "The" , Space , Str "last" , Space , Str "three" , Space , Str "here" , Space , Str "are" , Space , Str "display" , Space , Str "style." ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "f:{\\mathbb{R}} \\rightarrow {\\mathbb{R}}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "f: RR -> RR" ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "9.8\\ \\text{ m/s}^{2}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Code ( "" , [] , [] ) "\"9.8\" \"m/s\"^2" , Space , Link ( "" , [ "ref" ] , [] ) [ Str "[tricky]" ] ( "#tricky" , "" ) ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\lim\\limits_{h \\rightarrow 0}\\frac{f(x + h) - f(x)}{h}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "lim_(h -> 0) (f(x + h) - f(x)) / h" ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\int x^{2}dx = x^{3}/3 + C" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "integral x^2 dif x = x^3 \\/ 3 + C" ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math DisplayMath "\\nabla = \\mathbf{i}\\frac{d}{dx} + \\mathbf{j}\\frac{d}{dy} + \\mathbf{k}\\frac{d}{dz}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z)" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Strong [ Str "Discrete" , Space , Str "mathematics" , Space , Str "examples\8192\160" ] , Str "For" , Space , Str "modulo," , Space , Str "there" , Space , Str "is" , Space , Str "a" , Space , Str "symbol" , Space , Math InlineMath "\\equiv" , Space , Str "from" , Space , Code ( "" , [] , [] ) "equiv" , Space , Str "and" , Space , Str "a" , Space , Str "text" , Space , Str "operator" , Space , Math InlineMath "\\operatorname{mod}" , Space , Str "from" , Space , Code ( "" , [] , [] ) "mod" , Str "." ] , Para [ Str "For" , Space , Str "combinations" , Space , Str "the" , Space , Str "binomial" , Space , Str "symbol" , Space , Math InlineMath "\\binom{n}{k}" , Space , Str "is" , Space , Str "from" , Space , Code ( "" , [] , [] ) "binom(n, k)" , Str "." , SoftBreak , Str "This" , Space , Str "resizes" , Space , Str "to" , Space , Str "be" , Space , Str "bigger" , Space , Str "in" , Space , Str "a" , Space , Str "display." ] , Para [ Str "For" , Space , Str "permutations" , Space , Str "use" , Space , Math InlineMath "n^{\\underline{r}}" , Space , Str "from" , Space , Code ( "" , [] , [] ) "n^(underline(r))" , Space , Str "(some" , Space , Str "authors" , Space , Str "use" , Space , Math InlineMath "P(n,r)" , Str "," , Space , Str "or" , Space , Math InlineMath "{}_{n}P_{r}" , Space , Str "from" , Space , Code ( "" , [] , [] ) "\"\"_n P_r" , Str ")." ] , Para [ Strong [ Str "Statistics" , Space , Str "examples\8192\160" ] ] , Div ( "" , [] , [ ( "align" , "center" ) ] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "\\sigma^{2} = \\sqrt{{\\sum(x_{i} - \\mu)}^{2}/N}" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "sigma^2 = sqrt(sum(x_i - mu)^2 \\/ N)" ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Para [ Math InlineMath "E(X) = \\mu_{X} = \\sum(x_{i} - P\\left( x_{i} \\right))" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "E(X) = mu_X = sum(x_i - P(x_i))" ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Para [ Str "The" , Space , Str "probability" , Space , Str "density" , Space , Str "of" , Space , Str "the" , Space , Str "normal" , Space , Str "distribution" ] , Para [ Math DisplayMath "\\frac{1}{\\sqrt{2\\sigma^{2}\\pi}}e^{- \\frac{(x - \\mu)^{2}}{2\\sigma^{2}}}" ] , Para [ Str "comes" , Space , Str "from" , Space , Str "this." ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) []) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ CodeBlock ( "" , [] , [] ) "1 / sqrt(2 sigma^2 pi)\n e^(- (x - mu)^2 / (2 sigma^2))" ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Para [ Strong [ Str "For" , Space , Str "more\8192\160" ] , Str "See" , Space , Str "also" , Space , Str "the" , Space , Str "Typst" , Space , Str "Documentation" , Space , Str "at" , Space , Underline [ Link ( "" , [] , [] ) [ Str "https://typst.app/docs" ] ( "https://typst.app/docs" , "" ) ] , Str "." ] , HorizontalRule , Para [ Str "johanvx" , Space , Str "(" , Underline [ Link ( "" , [] , [] ) [ Str "https://github.com/johanvx" ] ( "https://github.com/johanvx" , "" ) ] , Str ")" , Space , Str "\8202\8193" , Space , Str "2023-05-22" ] ] ] ================================================ FILE: test/typst-reader.typ ================================================ #set page(width: 10cm, height: auto) #set heading(numbering: "1.") = Fibonacci sequence The Fibonacci sequence is defined through the recurrence relation $F_n = F_(n-1) + F_(n-2)$. It can also be expressed in _closed form:_ $ F_n = round(1 / sqrt(5) phi.alt^n), quad phi.alt = (1 + sqrt(5)) / 2 $ #let count = 8 #let nums = range(1, count + 1) #let fib(n) = ( if n <= 2 { 1 } else { fib(n - 1) + fib(n - 2) } ) The first #count numbers of the sequence are: #align(center, table( columns: count, ..nums.map(n => $F_#n$), ..nums.map(n => str(fib(n))), )) #include "undergradmath.typ" ================================================ FILE: test/undergradmath.typ ================================================ // Licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. // https://creativecommons.org/licenses/by-sa/4.0/ // Meta data #set document(title: "Typst Math for Undergrads", author: "johanvx") // Margin #set page(margin: 0.5in) // Font size #let scriptsize = 7pt #let normalsize = 10pt #let large = 12pt #set text(size: normalsize, lang: "en") // Some horizontal spacing #let kern(length) = h(length, weak: true) #let enspace = kern(0.5em) #let qquad = h(2em) // For table/grid, something like "lhs \enspace rhs" #let cell(lhs, rhs) = box(lhs + enspace + rhs) // Grid for code blocks #set grid(columns: (2em, auto)) // Table for math-code listing #set table(stroke: none, align: horizon + left, inset: 0pt, row-gutter: 0.45em) // LaTeX and TeX logos #let TeX = style(styles => { let e = measure(text(normalsize, "E"), styles) let T = "T" let E = text(normalsize, baseline: e.height / 2, "E") let X = "X" box(T + kern(-0.1667em) + E + kern(-0.125em) + X) }) #let LaTeX = style(styles => { let l = measure(text(10pt, "L"), styles) let a = measure(text(7pt, "A"), styles) let L = "L" let A = text(7pt, baseline: a.height - l.height, "A") box(L + kern(-0.36em) + A + kern(-0.15em) + TeX) }) // Update date #let date = "2023-05-22" // Unavailable (last check date) #show "??": box(text(red, [#date #emoji.crossmark])) // Tricky #show "!!": box(text(blue, emoji.drops)) // No idea #show "?!": box(text(orange, [No idea #emoji.face.unhappy])) // Tricky figure numbering #set figure(numbering: n => { ([??], [!!], [?!]).at(n - 1) }) // No prefix #set ref(supplement: "") // Justified paragraphs #set par(justify: true) // Two-column body #show: rest => columns(2, rest) // headcolor #let headcolor = rgb("004225") // Run-in sections, like LaTeX \paragraph #show heading.where( level: 1 ): it => text( size: normalsize, weight: "bold", fill: headcolor, it.body + h(0.67em) ) // Black raw code // #show raw.where(block: false): it => { it.text } // Title #align(center, link("https://github.com/johanvx/typst-undergradmath")[ #text(large, headcolor)[*Typst Math for Undergrads*] ]) // Put this here to avoid affecting the title #show link: underline This is a Typst port of _#LaTeX Math for Undergrads_ by Jim Hefferon. The original version is available at #link("https://gitlab.com/jim.hefferon/undergradmath"). = Meaning of annotations #figure( table( columns: (1fr, 2fr), [??], [This is unavailable. Last check date is #date.], ) ) #figure( table( columns: (1fr, 2fr), [!!], [Get this in a tricky way. Need a simpler method.], ) ) #figure( table( columns: (1fr, 2fr), [?!], [Don't know how to get this.], ) ) = Rule One Any mathematics at all, even a single character, gets a mathematical setting. Thus, for "the value of $x$ is $7$" enter `the value of $x$ is $7$`. = Template Your document should contain at least this. #grid( "", ``` -- document body here -- ``` ) = Common constructs #align(center, table( columns: 2, column-gutter: 1.5em, cell($x^2$, `x^2`), cell([$sqrt(2)$, $root(n, 3)$], [`sqrt(2)`, `root(n, 3)`]), cell($x_(i, j)$, `x_(i, j)`), cell([$2 / 3$, $2 \/ 3$], [`2 / 3`, `2 \/ 3` or `2 slash 3`]), // Maybe use `slash`? )) = Calligraphic letters Use as in `$cal(A)$`. $ cal(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) $ Getting script letters is @unavailable. = Greek #align(center, table( columns: 2, column-gutter: 1em, cell($alpha$, `alpha`), cell([$xi$, $Xi$], [`xi`, `Xi`]), cell($beta$, `beta`), cell($omicron$, `omicron`), cell([$gamma$, $Gamma$], [`gamma`, `Gamma`]), cell([$pi$, $Pi$], [`pi`, `Pi`]), cell([$delta$, $Delta$], [`delta`, `Delta`]), cell($pi.alt$, `pi.alt`), cell($epsilon.alt$, `epsilon.alt`), cell($rho$, `rho`), cell($epsilon$, `epsilon`), cell($rho.alt$, `rho.alt`), cell($zeta$, `zeta`), cell([$sigma$, $Sigma$], [`sigma`, `Sigma`]), cell($eta$, `eta`), cell($\u{03C2}$, [`\u{03C2}` @tricky]), cell([$theta$, $Theta$], [`theta`, `Theta`]), cell($tau$, `tau`), cell($theta.alt$, `theta.alt`), cell([$upsilon$, $Upsilon$], [`upsilon`, `Upsilon`]), cell($iota$, `iota`), cell([$phi.alt$, $Phi$], [`phi.alt`, `Phi`]), cell($kappa$, $Kappa$), cell($phi$, `phi`), cell([$lambda$, $Lambda$], [`lambda`, `Lambda`]), cell($chi$, `chi`), cell($mu$, `mu`), cell([$psi$, $Psi$], [`psi`, `Psi`]), cell($nu$, `nu`), cell([$omega$, $Omega$], [`omega`, `Omega`]), )) = Sets and logic #align(center, table( columns: 3, column-gutter: 1em, cell($union$, `union`), cell($RR$, [`RR`, `bb(R)`]), cell($forall$, `forall`), cell($sect$, `sect`), cell($bb(Z)$, [`ZZ`, `bb(Z)`]), cell($exists$, `exists`), cell($subset$, `subset`), cell($bb(Q)$, [`QQ`, `bb(Q)`]), cell($not$, `not`), cell($subset.eq$, `subset.eq`), cell($bb(N)$, [`NN`, `bb(N)`]), cell($or$, `or`), cell($supset$, `supset`), cell($bb(C)$, [`CC`, `bb(C)`]), cell($and$, `and`), cell($supset.eq$, `supset.eq`), cell($diameter$, [`diameter`]), cell($tack.r$, `tack.r`), cell($in$, `in`), cell($nothing$, `nothing`), cell($models$, `models`), cell($in.not$, `in.not`), cell($alef$, `alef`), cell($without$, `without`), )) Negate an operator, as in $subset.not$, with `subset.not`. Get the set complement $A^(sans(c))$ with `A^(sans(c))` (or $A^(complement)$ with `A^(complement)`, or $overline(A)$ with `overline(A)`). // https://www.ctan.org/tex-archive/fonts/newcomputermodern // // README // // Version 3.93 // // Provides access to Russian and Greek guillemotleft and guillemotright // using the character variant tables cv3 and cv4 respectively. // // The Math fonts provide the character \varnothing, an alternative to \emptyset, // through Character Variant cv01. The fontsetup package provides the option // 'varnothing' to easily switch to the alternative character. // https://mirrors.sustech.edu.cn/CTAN/fonts/newcomputermodern/doc/newcm-doc.pdf // The NewComputerModern FontFamily §13.3 // The Math fonts provide the character \varnothing (⌀, U+2300), as an alternative to \emptyset (a slashed zero), through Character Variant cv01. // The fontsetup package provides the option ‘varnothing’ to easily switch to the alternative character. / Remark: Using `diameter` for `\varnothing` may cause some confusion. However, #LaTeX also uses $diameter$ (`\u{2300}`) instead of $\u{2205}$ (`\u{2205}`), see #link("https://mirrors.sustech.edu.cn/CTAN/fonts/newcomputermodern/doc/newcm-doc.pdf")[newcm $section$13.3]. Another solution is to use `text(font: "Fira Sans", nothing)`, but the resultant glyph $text(font: "Fira Sans", nothing)$ is subtly different from the widely used one. Ultimately, the choice is always *your decision*. = Decorations #align(center, table( columns: 3, column-gutter: 1em, cell($f'$, [`f'`, `f prime`]), cell($dot(a)$, `dot(a)`), cell($tilde(a)$, `tilde(a)`), cell($f prime.double$, `f prime.double`), cell($diaer(a)$, `diaer(a)`), cell($macron(a)$, `macron(a)`), cell($Sigma^*$, `Sigma^*`), cell($hat(a)$, `hat(a)`), cell($arrow(a)$, `arrow(a)`), )) If the decorated letter is $i$ or $j$ then some decorations need `\u{1D6A4}` @tricky and `\u{1D6A5}` @tricky, as in $arrow(\u{1D6A4})$ with `arrow(\u{1D6A4})`. Some authors use boldface for vectors: `bold(x)`. Entering `overline(x + y)` produces $overline(x + y)$, and `hat(x + y)` gives $hat(x + y)$. Comment on an expression as here (there is also `overbrace(..)`). #align(center, cell( $underbrace(x + y, |A|)$, `underbrace(x + y, |A|)`, )) = Dots Use low dots in a list ${0, 1, 2, ...}$, entered as `{0, 1, 2, ...}`. Use centered dots in a sum or product $1 + dots.h.c + 100$, entered as `1 + dots.h.c + 100`. You can also get vertical dots `dots.v`, diagonal dots `dots.down` and anti-diagonal dots `dots.up`. = Roman names Just type them! #align(center, table( columns: 3, column-gutter: 1.5em, cell($sin$, `sin`), cell($sinh$, `sinh`), cell($arcsin$, `arcsin`), cell($cos$, `cos`), cell($cosh$, `cosh`), cell($arccos$, `arccos`), cell($tan$, `tan`), cell($tanh$, `tanh`), cell($arctan$, `arctan`), cell($sec$, `sec`), cell($coth$, `coth`), cell($min$, `min`), cell($csc$, `csc`), cell($det$, `det`), cell($max$, `max`), cell($cot$, `cot`), cell($dim$, `dim`), cell($inf$, `inf`), cell($exp$, `exp`), cell($ker$, `ker`), cell($sup$, `sup`), cell($log$, `log`), cell($deg$, `deg`), cell($liminf$, `liminf`), cell($ln$, `ln`), cell($arg$, `arg`), cell($limsup$, `limsup`), cell($lg$, `lg`), cell($gcd$, `gcd`), cell($lim$, `lim`), )) = Other symbols #align(center, table( columns: 3, column-gutter: 1.2em, cell($<$, [`<`, `lt`]), cell($angle$, `angle`), cell($dot$, [`dot`]), cell($<=$, [`<=`, `lt.eq`]), cell($angle.arc$, `angle.arc`), cell($plus.minus$, `plus.minus`), cell($>$, [`>`, `gt`]), cell($ell$, `ell`), cell($minus.plus$, `minus.plus`), cell($>=$, [`>=`, `gt.eq`]), cell($parallel$, `parallel`), cell($times$, `times`), cell($!=$, [`!=`, `eq.not`]), cell($45 degree$, `45 degree`), cell($div$, `div`), cell($<<$, [`<<`, `lt.double`]), cell($tilde.equiv$, `tilde.equiv`), cell($*$, [`*`, `ast`]), cell($>>$, [`>>`, `gt.double`]), cell($tilde.equiv.not$, `tilde.equiv.not`), cell($divides$, `divides`), cell($approx$, `approx`), cell($tilde$, `tilde`), cell($divides.not$, `divides.not`), cell($\u{224D}$, [`\u{224D}` @tricky]), cell($tilde.eq$, `tilde.eq`), cell($n!$, `n!`), cell($equiv$, `equiv`), cell($tilde.not$, `tilde.not`), cell($diff$, `diff`), cell($prec$, `prec`), cell($plus.circle$, `plus.circle`), cell($nabla$, `nabla`), cell($prec.eq$, `prec.eq`), cell($minus.circle$, `minus.circle`), cell($planck.reduce$, `planck.reduce`), cell($succ$, `succ`), cell($dot.circle$, `dot.circle`), cell($circle.stroked.tiny$, `circle.stroked.tiny`), cell($succ.eq$, `succ.eq`), cell($times.circle$, `times.circle`), cell($star$, `star`), cell($prop$, `prop`), cell($\u{2298}$, [`\u{2298}` @tricky]), cell($sqrt("")$, `sqrt("")`), cell($\u{2250}$, [`\u{2250}` @tricky]), cell($harpoon.tr$, `harpoon.tr`), cell($checkmark$, `checkmark`), )) Use `a divides b` for the divides relation, $a divides b$, and `a divides.not b` for the negation, $a divides.not b$. Use `|` to get set builder notation ${a in S | a "is odd"}$ with `{a in S | a "is odd"}`. = Arrows #align(center, table( columns: 2, column-gutter: 1.5em, cell($->$, [`->`, `arrow.r`]), cell($|->$, [`|->`, `arrow.r.bar`]), cell($arrow.r.not$, `arrow.r.not`), cell($arrow.r.long.bar$, `arrow.r.long.bar`), cell($arrow.r.long$, `arrow.r.long`), cell($<-$, [`<-`, `arrow.l`]), cell($=>$, [`=>`, `arrow.r.double`]), cell($<-->$, [`<-->`, `arrow.l.r.long`]), cell($arrow.r.double.not$, `arrow.r.double.not`), cell($arrow.b$, `arrow.b`), cell($arrow.r.double.long$, `arrow.r.double.long`), cell($arrow.t$, `arrow.t`), cell($arrow.squiggly$, `arrow.squiggly`), cell($arrow.t.b$, `arrow.t.b`), )) The right arrows in the first column have matching left arrows, such as `arrow.l.not`, and there are some other matches for down arrows, etc. = Variable-sized operators The summation $sum_(j = 0)^3 j^2$ `sum_(j = 0)^3 j^2` and the integral $integral_(x = 0)^3 x^2 dif x$ `integral_(x = 0)^3 x^2 dif x` expand when displayed. $ sum_(j = 0)^3 j^2 qquad integral_(x = 0)^3 x^2 dif x $ These do the same. #align(center, table( columns: 3, cell($integral$, `integral`), cell($integral.triple$, `integral.triple`), cell($union.big$, `union.big`), cell($integral.double$, `integral.double`), cell($integral.cont$, `integral.cont`), cell($sect.big$, `sect.big`), )) = Fences #align(center, table( columns: 3, column-gutter: 1.5em, cell($()$, `()`), cell($angle.l angle.r$, `angle.l angle.r`), cell($abs("")$, `abs("")`), cell($[]$, `[]`), cell($floor("")$, `floor("")`), cell($norm("")$, `norm("")`), cell(${}$, `{}`), cell($ceil("")$, `ceil("")`), )) Fix the size with the `lr` function. #align(center, table( columns: 2, column-gutter: 0.5em, $ lr([sum_(k = 0)^n e^(k^2)], size: #50%) $, ``` lr([sum_(k = 0)^n e^(k^2)], size: #50%) ```, )) To have them grow with the enclosed formula, also use the `lr` function. #align(center, table( columns: 2, column-gutter: 1em, $ lr(angle.l i, 2^(2^i) angle.r) $, ``` lr(angle.l i, 2^(2^i) angle.r) ```, )) Fences scale by default if entered directly as codepoints, and don't scale automatically if entered as symbol notation. #align(center, table( columns: 2, column-gutter: 1em, $ (1 / n^(alpha)) $, ``` (1 / n^(alpha)) ```, $ paren.l 1 / n^(alpha) paren.r $, ``` paren.l 1 / n^(alpha) paren.r ```, )) The `lr` function also allows to scale unmatched delimiters and one-side fences. #align(center, table( columns: 2, column-gutter: 1em, $ lr(frac(dif f, dif x) |)_(x_0) $, ``` lr(frac(dif f, dif x) |)_(x_0) ```, )) = Arrays, Matrices Get a matrix with the `mat` function. You can pass an array to it. #align(center, table( columns: 2, column-gutter: 1em, $ mat(a, b; c, d) $, ``` $ mat(a, b; c, d) $ ``` )) In Typst, #link("https://typst.app/docs/reference/typst/array")[array] is a sequence of values, while in #LaTeX, array is a matrix without fences, which is `$mat(delim: #none, ..)$` in Typst. For the determinant use `|A|`, text operator $det$ `det` or `mat(delim: "|", ..)`. Definition by cases can be easily obtained with the `cases` function. #align(center, table( columns: 2, column-gutter: 1em, $ f_n = cases( a &"if" n = 0, r dot f_(n - 1) &"else" ) $, ``` $ f_n = cases( a &"if" n = 0, r dot f_(n - 1) &"else" ) $ ``` )) = Spacing in mathematics Improve $sqrt(2) x$ to $sqrt(2) thin x$ with a thin space, as in `sqrt(2) thin x`. Slightly wider are `medium` and `thick` (the three are in ratio $3 : 4 : 5$). Bigger space is `quad` for $arrow.r quad arrow.l$, which is useful between parts of a display. Get arbitrary space with the `h` function. For example, use `#h(2em)` for `\qquad` in #LaTeX and `#h(-0.1667em)` for `\!`. = Displayed equations Display equations in a block level using `$ ... $` with at least one space separating the math content and the `$`. #align(center, table( columns: 2, column-gutter: 1em, $ S = k dot lg W $, ``` $ S = k dot lg W $ ```, )) You can break into multiple lines. #align(center, table( columns: 2, column-gutter: 1em, $ sin(x) = x - x^3 / 3! \ + x^5 / 5! - dots.h.c $, ``` $ sin(x) = x - x^3 / 3! \ + x^5 / 5! - dots.h.c $ ```, )) Align equations using `&` #align(center, table( columns: 2, column-gutter: 1em, $ nabla dot bold(D) &= rho \ nabla dot bold(B) &= 0 $, ``` $ nabla dot bold(D) &= rho \ nabla dot bold(B) &= 0 $ ```, )) (the left or right side of an alignment can be empty). Get a numbered version by `#set math.equation(numbering: ..)`. = Calculus examples The last three here are display style. #align(center, table( align: horizon, columns: 2, column-gutter: 1em, block($f: RR -> RR$), ``` f: RR -> RR ```, block($"9.8" "m/s"^2$), block([`"9.8" "m/s"^2` @tricky]), $ lim_(h->0) (f(x+h)-f(x))/h $, ``` lim_(h -> 0) (f(x + h) - f(x)) / h ```, $ integral x^2 dif x = x^3 \/ 3 + C $, ``` integral x^2 dif x = x^3 \/ 3 + C ```, $ nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z) $, ``` nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z) ```, )) = Discrete mathematics examples For modulo, there is a symbol $equiv$ from `equiv` and a text operator $mod$ from `mod`. For combinations the binomial symbol $binom(n, k)$ is from `binom(n, k)`. This resizes to be bigger in a display. For permutations use $n^(underline(r))$ from `n^(underline(r))` (some authors use $P(n, r)$, or $""_n P_r$ from `""_n P_r`). = Statistics examples #align(center, table( align: horizon, columns: 2, block($sigma^2 = sqrt(sum(x_i - mu)^2 \/ N)$), ``` sigma^2 = sqrt(sum(x_i - mu)^2 \/ N) ```, block($E(X) = mu_X = sum(x_i - P(x_i))$), ``` E(X) = mu_X = sum(x_i - P(x_i)) ```, )) The probability density of the normal distribution $ 1 / sqrt(2 sigma^2 pi) e^(- (x - mu)^2 / (2 sigma^2)) $ comes from this. #grid( "", ``` 1 / sqrt(2 sigma^2 pi) e^(- (x - mu)^2 / (2 sigma^2)) ``` ) = For more See also the Typst Documentation at #link("https://typst.app/docs"). #v(1fr) #block( line(length: 100%, stroke: headcolor) + text(headcolor)[johanvx (https://github.com/johanvx) #h(1fr) #date] ) ================================================ FILE: test/vimdoc/definition-lists.markdown ================================================ --- vimdoc-prefix: pandoc abstract: "A short description" filename: "definition-lists.txt" author: Author title: Title --- ## Basic Term 1 : Definition I Term 2 : Definition II Term 3 : Definition III ## Code `leap.opts.keys.next_target` : Jump to the next available target (use the previous search pattern if no input has been given). `:h leap-repeat` `leap.opts.keys.prev_target` : Jump to the previous target (revert `next_target`). `leap.opts.keys.next_group` : Shift to the next group of labeled targets. `leap.opts.keys.prev_group` : Shift to the previous group of labeled targets (revert `next_group`). ## Span [Important concept]{#important-concept} : Definition [I am too long to fit on the same line as a reference, so reference is put above me!]{#i-am-too-long} : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ## Commands In markdown vim commands are represented as inline code starting with colon (ie. `:MyCommand`), but writer strips the backticks `:FnlCompileBuffer`{#:FnlCompileBuffer} : Compiles current active fennel buffer `:FnlCompile[!]`{#:FnlCompile} : Diff compiles all indexed fennel files If bang! is present then forcefully compiles all `source` files `:[range]Fnl {expr}`{#:Fnl} : Evaluates {expr} or range ``` :'<,'>Fnl :Fnl (print "Hello World") :Fnl (values some_var) ``` ================================================ FILE: test/vimdoc/definition-lists.vimdoc ================================================ *definition-lists.txt* A short description Title by Author Type |gO| to see the table of contents. Basic ........................................................... |pandoc-basic| Code ............................................................. |pandoc-code| Span ............................................................. |pandoc-span| Commands ..................................................... |pandoc-commands| ------------------------------------------------------------------------------ Basic *pandoc-basic* Term 1 Definition I Term 2 Definition II Term 3 Definition III ------------------------------------------------------------------------------ Code *pandoc-code* `leap.opts.keys.next_target` Jump to the next available target (use the previous search pattern if no input has been given). |leap-repeat| `leap.opts.keys.prev_target` Jump to the previous target (revert `next_target`). `leap.opts.keys.next_group` Shift to the next group of labeled targets. `leap.opts.keys.prev_group` Shift to the previous group of labeled targets (revert `next_group`). ------------------------------------------------------------------------------ Span *pandoc-span* Important concept *pandoc-important-concept* Definition *pandoc-i-am-too-long* I am too long to fit on the same line as a reference, so reference is put above me! Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ------------------------------------------------------------------------------ Commands *pandoc-commands* In markdown vim commands are represented as inline code starting with colon (ie. `:MyCommand`), but writer strips the backticks :FnlCompileBuffer *:FnlCompileBuffer* Compiles current active fennel buffer :FnlCompile[!] *:FnlCompile* Diff compiles all indexed fennel files If bang! is present then forcefully compiles all `source` files :[range]Fnl {expr} *:Fnl* Evaluates {expr} or range > :'<,'>Fnl :Fnl (print "Hello World") :Fnl (values some_var) < vim:tw=78:sw=4:ts=4:ft=help:norl:et: ================================================ FILE: test/vimdoc/headers-numbered.vimdoc ================================================ Type |gO| to see the table of contents. 1. unremarkable header 1 ............................... |unremarkable-header-1| 1.1 unremarkable header 2 ............................ |unremarkable-header-2| 1.1.1 unremarkable header 3 ...................... |unremarkable-header-3| 1.1.2 unremarkable header 3 .................... |unremarkable-header-3-1| unremarkable header 2 .............................. |unremarkable-header-2-1| unremarkable header 3 ............................ |unremarkable-header-3-2| unremarkable header 3 ............................ |unremarkable-header-3-3| 2. bold italic underline strikethrough ............................... |short11| 2.1 superscript2 subscript3 ........................................ |short21| 2.1.1 smallcaps code inline math display math .................. |short31| 2.1.2 link image ............................................... |short32| 3. Long header 1 long header 1 long header 1 long header 1 long heade... |long1| 3.1 Long header 2 long header 2 long header 2 long header 2 long h... |long21| 3.1.1 Long header 3 long header 3 long header 3 long header 3.... |long31| 3.1.2 Long header 3 long header 3 long header 3 long header 3.... |long32| 3.2 Long header 2 long header 2 long header 2 long header 2 long h... |long22| 3.2.1 Long header 3 long header 3 long header 3 long header 3.... |long33| 3.2.2 Long header 3 long header 3 long header 3 long header 3.... |long34| 4. ... |long-header-1-long-header-1-long-header-1-long-header-1-long-header-1-long-header-1-long-header-1| 4.1 ... |long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2| 4.1.1 ... |long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3| 4.1.2 ... |long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-1| 4.2 ... |long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-1| 4.2.1 ... |long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-2| 4.2.2 ... |long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-3| Headers with a short title and implicit ref. Last 3 headers are unnumbered. ============================================================================== unremarkable header 1 *unremarkable-header-1* ------------------------------------------------------------------------------ unremarkable header 2 *unremarkable-header-2* UNREMARKABLE HEADER 3 *unremarkable-header-3* UNREMARKABLE HEADER 3 *unremarkable-header-3-1* ------------------------------------------------------------------------------ unremarkable header 2 *unremarkable-header-2-1* UNREMARKABLE HEADER 3 *unremarkable-header-3-2* UNREMARKABLE HEADER 3 *unremarkable-header-3-3* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Headers with various inline elements ============================================================================== bold italic underline strikethrough *short11* ------------------------------------------------------------------------------ superscript2 subscript3 *short21* SMALLCAPS `code` `$inline math$` `$display math$` *short31* LINK example.com *short32* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Headers with a long name and explicit short ref ============================================================================== Long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 *long1* ------------------------------------------------------------------------------ Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 *long21* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long31* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long32* ------------------------------------------------------------------------------ Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 *long22* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long33* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long34* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Headers with a long name and implicit ref. Since implicit ref is very lengthy, there is no way to fit it. Therefore title is not rendered and the references exceed the columns (textwidth) limit ============================================================================== Long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 *long-header-1-long-header-1-long-header-1-long-header-1-long-header-1-long-header-1-long-header-1* ------------------------------------------------------------------------------ Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 *long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-1* ------------------------------------------------------------------------------ Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 *long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-1* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-2* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-3* vim:tw=78:sw=4:ts=4:ft=help:norl:et: ================================================ FILE: test/vimdoc/headers.markdown ================================================ Headers with a short title and implicit ref. Last 3 headers are unnumbered. # unremarkable header 1 ## unremarkable header 2 ### unremarkable header 3 ### unremarkable header 3 ## unremarkable header 2 {.unnumbered} ### unremarkable header 3 {.unnumbered} ### unremarkable header 3 {.unnumbered} --- Headers with various inline elements # **bold** *italic* [underline]{.underline} ~strikethrough~ {#short11} ## superscript^2^ subscript~3~ {#short21} ### [smallcaps]{.smallcaps} `code` $inline math$ $$display math$$ {#short31} ### [link](example.com) ![image](https://duckduckgo.com/favicon.ico) {#short32} --- Headers with a long name and explicit short ref # Long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 {#long1} ## Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 {#long21} ### Long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 {#long31} ### Long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 {#long32} ## Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 {#long22} ### Long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 {#long33} ### Long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 {#long34} --- Headers with a long name and implicit ref. Since implicit ref is very lengthy, there is no way to fit it. Therefore title is not rendered and the references exceed the columns (textwidth) limit # Long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 ## Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 ### Long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 ### Long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 ## Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 ### Long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 ### Long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 long header 3 ================================================ FILE: test/vimdoc/headers.vimdoc ================================================ Type |gO| to see the table of contents. unremarkable header 1 .................................. |unremarkable-header-1| unremarkable header 2 ................................ |unremarkable-header-2| unremarkable header 2 .............................. |unremarkable-header-2-1| bold italic underline strikethrough .................................. |short11| superscript2 subscript3 ............................................ |short21| Long header 1 long header 1 long header 1 long header 1 long header 1... |long1| Long header 2 long header 2 long header 2 long header 2 long heade... |long21| Long header 2 long header 2 long header 2 long header 2 long heade... |long22| ... |long-header-1-long-header-1-long-header-1-long-header-1-long-header-1-long-header-1-long-header-1| ... |long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2| ... |long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-1| Headers with a short title and implicit ref. Last 3 headers are unnumbered. ============================================================================== unremarkable header 1 *unremarkable-header-1* ------------------------------------------------------------------------------ unremarkable header 2 *unremarkable-header-2* UNREMARKABLE HEADER 3 *unremarkable-header-3* UNREMARKABLE HEADER 3 *unremarkable-header-3-1* ------------------------------------------------------------------------------ unremarkable header 2 *unremarkable-header-2-1* UNREMARKABLE HEADER 3 *unremarkable-header-3-2* UNREMARKABLE HEADER 3 *unremarkable-header-3-3* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Headers with various inline elements ============================================================================== bold italic underline strikethrough *short11* ------------------------------------------------------------------------------ superscript2 subscript3 *short21* SMALLCAPS `code` `$inline math$` `$display math$` *short31* LINK example.com *short32* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Headers with a long name and explicit short ref ============================================================================== Long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 *long1* ------------------------------------------------------------------------------ Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 *long21* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long31* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long32* ------------------------------------------------------------------------------ Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 *long22* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long33* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long34* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Headers with a long name and implicit ref. Since implicit ref is very lengthy, there is no way to fit it. Therefore title is not rendered and the references exceed the columns (textwidth) limit ============================================================================== Long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 long header 1 *long-header-1-long-header-1-long-header-1-long-header-1-long-header-1-long-header-1-long-header-1* ------------------------------------------------------------------------------ Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 *long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-1* ------------------------------------------------------------------------------ Long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 long header 2 *long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-long-header-2-1* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-2* LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 LONG HEADER 3 *long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-long-header-3-3* vim:tw=78:sw=4:ts=4:ft=help:norl:et: ================================================ FILE: test/vimdoc/vim-online-doc.markdown ================================================ # Online vim documentation While vim documentation can be accessed with `:help`, it may be beneficial to link to the websites inside readme/markdown docs. Two most common websites are: - and - Also it is not uncommon to reference documentation as `:h ` ## Links to vimhelp.org {#vimhelp-links} For introduction to writing help files see Named link: [vimhelp link](https://vimhelp.org/helphelp.txt.html#help-writing) ## Links to neo.vimhelp.org {#neo-vimhelp-links} For introduction to writing help files see Named link: [vimhelp link](https://neo.vimhelp.org/helphelp.txt.html#help-writing) ## Links to neovim.io {#neovim-io-links} For introduction to writing help files see Named link: [neovim.io link](https://neovim.io/doc/user/helphelp.html#help-writing) You can also reference whole files with topic omitted: ## :h-style documentation {#colon-h-docs} For introduction to writing help files see `:h help-writing` Long name for `:help` is also supported: `:help help-writing` This is malformed: `:help help-writing `, but this is not: `:help help-writing` ================================================ FILE: test/vimdoc/vim-online-doc.vimdoc ================================================ Type |gO| to see the table of contents. Online vim documentation ............................ |online-vim-documentation| Links to vimhelp.org ......................................... |vimhelp-links| Links to neo.vimhelp.org ................................. |neo-vimhelp-links| Links to neovim.io ......................................... |neovim-io-links| :h-style documentation ........................................ |colon-h-docs| ============================================================================== Online vim documentation *online-vim-documentation* While vim documentation can be accessed with `:help`, it may be beneficial to link to the websites inside readme/markdown docs. Two most common websites are: - https://vimhelp.org/ and - https://neovim.io/doc/user Also it is not uncommon to reference documentation as || ------------------------------------------------------------------------------ Links to vimhelp.org *vimhelp-links* For introduction to writing help files see |help-writing| Named link: vimhelp link |help-writing| ------------------------------------------------------------------------------ Links to neo.vimhelp.org *neo-vimhelp-links* For introduction to writing help files see |help-writing| Named link: vimhelp link |help-writing| ------------------------------------------------------------------------------ Links to neovim.io *neovim-io-links* For introduction to writing help files see |help-writing| Named link: neovim.io link |help-writing| You can also reference whole files with topic omitted: |vim_diff.txt| ------------------------------------------------------------------------------ :h-style documentation *colon-h-docs* For introduction to writing help files see |help-writing| Long name for `:help` is also supported: |help-writing| This is malformed: |help-writing|, but this is not: |help-writing| vim:tw=78:sw=4:ts=4:ft=help:norl:et: ================================================ FILE: test/vimwiki-reader.native ================================================ Pandoc Meta { unMeta = fromList [ ( "date" , MetaInlines [ Str "2017-05-01" ] ) , ( "title" , MetaInlines [ Str "title" ] ) ] } [ Header 1 ( "implemented" , [] , [] ) [ Emph [ Span ( "implemented" , [] , [] ) [] , Strong [ Str "implemented" ] ] ] , Header 1 ( "header" , [] , [] ) [ Str "header" ] , Header 2 ( "header level two" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "two" ] , Header 3 ( "header level 3" , [] , [] ) [ Str "header" , Space , Code ( "" , [] , [] ) "level" , Space , Str "3" ] , Header 4 ( "header level four" , [] , [] ) [ Str "header" , Space , Strikeout [ Str "level" ] , Space , Str "four" ] , Header 5 ( "header level 5" , [] , [] ) [ Str "header" , Space , Emph [ Span ( "level" , [] , [] ) [] , Strong [ Str "level" ] , Space , Str "5" ] ] , Header 6 ( "header level 6" , [] , [] ) [ Str "header" , Space , Str "level" , Space , Str "6" ] , Para [ Str "=======" , Space , Str "not" , Space , Str "a" , Space , Str "header" , Space , Str "========" ] , Para [ Str "hi==" , Space , Str "not" , Space , Str "a" , Space , Str "header" , Space , Str "==" ] , Para [ Str "===" , Space , Str "not" , Space , Str "a" , Space , Str "header" , Space , Str "==" ] , Para [ Str "===" , Space , Str "not" , Space , Str "a" , Space , Str "header" , Space , Str "===-" ] , Para [ Str "not" , Space , Str "a" , Space , Str "header:" ] , Para [ Str "=n=" ] , Para [ Str "===" , Space , Str "not" , Space , Str "a" , Space , Str "header" , Space , Str "====" ] , Header 2 ( "centred header" , [ "justcenter" ] , [] ) [ Str "centred" , Space , Str "header" ] , Header 2 ( "header with some == in between" , [] , [] ) [ Str "header" , Space , Str "with" , Space , Str "some" , Space , Code ( "" , [] , [] ) "==" , Space , Str "in" , Space , Str "between" ] , Header 2 ( "header with some == in between" , [] , [] ) [ Str "header" , Space , Str "with" , Space , Str "some" , Space , Str "==" , Space , Str "in" , Space , Str "between" ] , Header 2 ( "header with some ==in between" , [] , [] ) [ Str "header" , Space , Str "with" , Space , Str "some" , Space , Str "==in" , Space , Str "between" ] , Header 2 ( "emph strong and strikeout" , [] , [] ) [ Str "emph" , Space , Str "strong" , Space , Str "and" , Space , Str "strikeout" ] , Para [ Emph [ Str "emph" ] , Space , Span ( "strong" , [] , [] ) [] , Strong [ Str "strong" ] ] , Para [ Span ( "strong and emph" , [] , [] ) [] , Strong [ Emph [ Str "strong" , Space , Str "and" , Space , Str "emph" ] ] ] , Para [ Emph [ Span ( "emph and strong" , [] , [] ) [] , Strong [ Str "emph" , Space , Str "and" , Space , Str "strong" ] ] ] , Para [ Span ( "emph inside strong" , [] , [] ) [] , Strong [ Emph [ Str "emph" , Space , Str "inside" ] , Space , Str "strong" ] ] , Para [ Span ( "strong with emph" , [] , [] ) [] , Strong [ Str "strong" , Space , Str "with" , Space , Emph [ Str "emph" ] ] ] , Para [ Emph [ Span ( "strong inside" , [] , [] ) [] , Strong [ Str "strong" , Space , Str "inside" ] , Space , Str "emph" ] ] , Para [ Emph [ Strikeout [ Str "strikeout" ] , Space , Str "inside" , Space , Str "emph" ] ] , Para [ Strikeout [ Str "This" , Space , Str "is" , Space , Emph [ Str "struck" , Space , Str "out" ] , Space , Str "with" , Space , Str "emph" ] ] , Para [ Str "*not" , SoftBreak , Str "strong*" ] , Para [ Str "just" , Space , Str "two" , Space , Str "stars:" , Space , Str "**" ] , Para [ Str "just" , Space , Str "two" , Space , Str "underscores:" , Space , Str "__" ] , Para [ Str "just" , Space , Str "four" , Space , Str "~s:" , Space , Str "~~~~" ] , Para [ Str "_not" , SoftBreak , Str "emph_" ] , Para [ Str "~~not" , SoftBreak , Str "strikeout~~" ] , Header 2 ( "horizontal rule" , [] , [] ) [ Str "horizontal" , Space , Str "rule" ] , Para [ Str "top" ] , HorizontalRule , Para [ Str "middle" ] , HorizontalRule , Para [ Str "not" , Space , Str "a" , Space , Str "rule-----" ] , Para [ Str "not" , Space , Str "a" , Space , Str "rule" , Space , Str "(trailing" , Space , Str "spaces):" , SoftBreak , Str "-----" ] , Para [ Str "not" , Space , Str "a" , Space , Str "rule" , Space , Str "(leading" , Space , Str "spaces):" , SoftBreak , Str "----" ] , Header 2 ( "comments" , [] , [] ) [ Str "comments" ] , Para [ Str "this" , SoftBreak , Str "is" , Space , Str "%%" , Space , Str "not" , Space , Str "secret" ] , Header 2 ( "inline code" , [] , [] ) [ Str "inline" , Space , Str "code" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Code ( "" , [] , [] ) "inline code" , Str "." ] , Para [ Str "Just" , Space , Str "two" , Space , Str "backticks:" , Space , Str "``" ] , Header 2 ( "preformatted text" , [] , [] ) [ Str "preformatted" , Space , Str "text" ] , CodeBlock ( "" , [] , [] ) " Tyger! Tyger! burning bright\n In the forests of the night,\n What immortal hand or eye\n Could frame thy fearful symmetry?\n In what distant deeps or skies\n Burnt the fire of thine eyes?\n On what wings dare he aspire?\n What the hand dare sieze the fire?" , Header 3 ( "preformatted text with attributes" , [] , [] ) [ Str "preformatted" , Space , Str "text" , Space , Str "with" , Space , Str "attributes" ] , CodeBlock ( "" , [] , [ ( "class" , "python" ) , ( "style" , "color:blue" ) ] ) " for i in range(1, 5):\n print(i)" , Header 3 ( "preformatted text with nested syntax" , [] , [] ) [ Str "preformatted" , Space , Str "text" , Space , Str "with" , Space , Str "nested" , Space , Str "syntax" ] , CodeBlock ( "" , [ "sql" ] , [] ) "SELECT * FROM table" , Header 3 ( "empty preformatted text" , [] , [] ) [ Str "empty" , Space , Str "preformatted" , Space , Str "text" ] , CodeBlock ( "" , [] , [] ) "" , Header 2 ( "block quotes" , [] , [] ) [ Str "block" , Space , Str "quotes" ] , BlockQuote [ Plain [ Str "(indentation" , Space , Str "4" , Space , Str "spaces)" , Space , Str "This" , Space , Str "would" , Space , Str "be" , Space , Str "a" , Space , Str "blockquote" , Space , Str "in" , Space , Str "Vimwiki." , Space , Str "It" , Space , Str "is" , Space , Str "not" , Space , Span ( "highlighted" , [] , [] ) [] , Strong [ Str "highlighted" ] , Space , Str "in" , Space , Str "Vim" , Space , Str "but" , SoftBreak , Str "(indentation" , Space , Str "1" , Space , Str "space" , Space , Str "followed" , Space , Str "by" , Space , Str "1" , Space , Str "tab" , Space , Str "of" , Space , Str "width" , Space , Str "4)" , Space , Str "could" , Space , Str "be" , Space , Str "styled" , Space , Str "by" , Space , Str "CSS" , Space , Str "in" , Space , Str "HTML." , Space , Str "Blockquotes" , Space , Str "are" , Space , Str "usually" , Space , Str "used" , Space , Str "to" , Space , Str "quote" , Space , Str "a" , SoftBreak , Str "(indentation" , Space , Str "1" , Space , Str "tab" , Space , Str "of" , Space , Str "width" , Space , Str "4)" , Space , Str "long" , Space , Str "piece" , Space , Str "of" , Space , Str "text" , Space , Str "from" , Space , Str "another" , Space , Str "source." , Space , Strikeout [ Str "blah" , Space , Str "blah" ] , Space , Span ( "-blockquote" , [] , [] ) [ Str "" ] , Span ( "blockquote" , [ "tag" ] , [] ) [ Str "blockquote" ] ] ] , Header 2 ( "external links" , [] , [] ) [ Str "external" , Space , Str "links" ] , Para [ Link ( "" , [] , [] ) [ Emph [ Str "Google" ] , Space , Str "search" , Space , Str "engine" ] ( "http://google.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "http://pandoc.org" ] ( "http://pandoc.org" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "ftp://vim.org" ] ( "ftp://vim.org" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "http://google.com" ] ( "http://google.com" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "email" , Space , Str "me" ] ( "mailto:info@example.org" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "mailto:hello@bye.com" ] ( "mailto:hello@bye.com" , "" ) ] , Header 2 ( "internal links" , [] , [] ) [ Str "internal" , Space , Str "links" ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "This is a link" ] ( "This is a link" , "" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "Description" , Space , Str "of" , Space , Str "the" , Space , Str "link" ] ( "This is a link source" , "" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "projects/Important Project 1" ] ( "projects/Important Project 1" , "" ) , SoftBreak , Link ( "" , [ "wikilink" ] , [] ) [ Str "../index" ] ( "../index" , "" ) , SoftBreak , Link ( "" , [ "wikilink" ] , [] ) [ Str "Other" , Space , Str "files" ] ( "a subdirectory/" , "" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "try" , Space , Str "me" , Space , Str "to" , Space , Str "test" , Space , Str "tag" , Space , Str "anchors" ] ( "#tag-one" , "" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "try" , Space , Str "me" , Space , Str "to" , Space , Str "test" , Space , Str "header" , Space , Str "anchors" ] ( "#block quotes" , "" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "try" , Space , Str "me" , Space , Str "to" , Space , Str "test" , Space , Str "strong" , Space , Str "anchors" ] ( "#strong" , "" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "Tasks" , Space , Str "for" , Space , Str "tomorrow" ] ( "Todo List#Tomorrow" , "" ) ] , Para [ Link ( "" , [ "wikilink" ] , [] ) [ Str "diary:2017-05-01" ] ( "diary/2017-05-01" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Important" , Space , Str "Data" ] ( "file:../assets/data.csv" , "" ) ] , Header 3 ( "links with thumbnails" , [] , [] ) [ Str "links" , Space , Str "with" , Space , Str "thumbnails" ] , Para [ Link ( "" , [] , [] ) [ Image ( "" , [] , [] ) [ Str "" ] ( "./movie.jpg" , "" ) ] ( "http://www.google.com" , "" ) ] , Header 2 ( "images" , [] , [] ) [ Str "images" ] , Para [ Image ( "" , [] , [] ) [ Str "" ] ( "file:./lalune.jpg" , "" ) ] , Para [ Image ( "" , [] , [] ) [ Str "Vimwiki" ] ( "http://vimwiki.googlecode.com/hg/images/vimwiki_logo.png" , "" ) , SoftBreak , Image ( "" , [] , [] ) [ Str "" ] ( "file:./movie.jpg" , "" ) ] , Header 3 ( "image with attributes" , [] , [] ) [ Str "image" , Space , Str "with" , Space , Str "attributes" ] , Para [ Image ( "" , [] , [ ( "style" , "width:150px;height:120px;" ) ] ) [ Emph [ Str "cool" , Space , Str "stuff" ] ] ( "lalune.jpg" , "" ) ] , Para [ Image ( "" , [] , [ ( "style" , "font-color:red" ) ] ) [ Span ( "Non-existing" , [] , [] ) [] , Strong [ Str "Non-existing" ] , Space , Str "image" ] ( "nonexist.jpg" , "" ) ] , Para [ Image ( "" , [] , [ ( "style" , "width:150px;height:120px;" ) ] ) [ Emph [ Str "cool" , Space , Str "stuff" ] ] ( "lalune.jpg" , "" ) ] , Header 2 ( "lists" , [] , [] ) [ Str "lists" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "ordered" , Space , Str "list" , Space , Str "item" , Space , Str "1," , Space , Str "and" , Space , Str "here" , Space , Str "is" , Space , Str "some" , Space , Str "math" , Space , Str "belonging" , Space , Str "to" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] , Para [ Math DisplayMath "a^2 + b^2 = c^2" ] , Plain [ Str "and" , Space , Str "some" , Space , Str "preformatted" , Space , Str "and" , Space , Str "tables" , Space , Str "belonging" , Space , Str "to" , Space , Str "item" , Space , Str "1" , Space , Str "as" , Space , Str "well" ] , CodeBlock ( "" , [] , [] ) "I'm part of item 1." , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "this" , Space , Str "table" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "is" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "also" , Space , Str "a" , Space , Str "part" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "of" , Space , Str "item" , Space , Str "1" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Plain [ Str "and" , Space , Str "some" , Space , Str "more" , Space , Str "text" , Space , Str "belonging" , Space , Str "to" , Space , Str "item" , Space , Str "1." ] ] , [ Plain [ Str "ordered" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] ] , BulletList [ [ Plain [ Str "Bulleted" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Bulleted" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Bulleted" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "the" , Space , Str "#" , Space , Str "become" , Space , Str "numbers" , Space , Str "when" , Space , Str "converted" , Space , Str "to" , Space , Str "HTML" ] ] ] , BulletList [ [ Plain [ Str "Bulleted" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Bulleted" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] ] , BulletList [ [ Plain [ Str "Item" , Space , Str "1" ] ] , [ Plain [ Str "Item" , Space , Str "2" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Sub" , Space , Str "item" , Space , Str "1" , Space , Str "(indentation" , Space , Str "4" , Space , Str "spaces)" , SoftBreak , Str "Sub" , Space , Str "item" , Space , Str "1" , Space , Str "continued" , Space , Str "line." , SoftBreak , Str "Sub" , Space , Str "item" , Space , Str "1" , Space , Str "next" , Space , Str "continued" , Space , Str "line." ] ] , [ Plain [ Str "Sub" , Space , Str "item" , Space , Str "2," , Space , Str "as" , Space , Str "an" , Space , Str "ordered" , Space , Str "list" , Space , Str "item" , Space , Str "even" , Space , Str "though" , Space , Str "the" , Space , Str "identifier" , Space , Str "is" , Space , Code ( "" , [] , [] ) "*" , Space , Str "(indentation" , Space , Str "2" , Space , Str "spaces" , Space , Str "followed" , Space , Str "by" , Space , Str "one" , Space , Str "tab" , Space , Str "of" , Space , Str "width" , Space , Str "4)" ] ] , [ Plain [ Str "etc." , SoftBreak , Str "Continuation" , Space , Str "of" , Space , Str "Item" , Space , Str "2" , SoftBreak , Str "Next" , Space , Str "continuation" , Space , Str "of" , Space , Str "Item" , Space , Str "2" ] ] ] ] ] , Para [ Str "But" , Space , Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "new" , Space , Str "paragraph." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "1" ] , BulletList [ [ Plain [ Code ( "" , [] , [] ) "1.1" ] ] ] ] , [ Plain [ Str "2" ] , BulletList [ [ Plain [ Str "2.1" ] ] ] ] ] , BulletList [ [ Plain [ Str "3" ] ] ] , Header 3 ( "ordered lists with non-# identifiers" , [] , [] ) [ Str "ordered" , Space , Str "lists" , Space , Str "with" , Space , Str "non-#" , Space , Str "identifiers" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "3" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "3" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "3" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "3" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "3" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "3" ] ] ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "item" , Space , Str "3" ] ] ] , BulletList [ [ Plain [ Str "Bulleted" , Space , Str "list" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Bulleted" , Space , Str "list" , Space , Str "item" , Space , Str "2" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "sub" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "more" , Space , Str "..." ] , BulletList [ [ Plain [ Str "and" , Space , Str "more" , Space , Str "..." ] ] , [ Plain [ Str "..." ] ] ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "sub" , Space , Str "item" , Space , Str "3" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "sub" , Space , Str "sub" , Space , Str "item" , Space , Str "1" ] ] , [ Plain [ Str "Numbered" , Space , Str "list" , Space , Str "sub" , Space , Str "sub" , Space , Str "item" , Space , Str "2" ] ] ] ] , [ Plain [ Str "etc." ] ] ] ] , [ Plain [ Str "Bulleted" , Space , Str "list" , Space , Str "item" , Space , Str "3" ] ] ] , Header 2 ( "todo lists" , [] , [] ) [ Str "todo" , Space , Str "lists" ] , BulletList [ [ Plain [ Span ( "" , [ "done0" ] , [] ) [] , Str "task" , Space , Str "1" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Span ( "" , [ "done1" ] , [] ) [] , Str "5" ] ] ] ] , [ Plain [ Span ( "" , [ "done2" ] , [] ) [] , Str "3" ] ] , [ Plain [ Str "[]" , Space , Str "not" , Space , Str "a" , Space , Str "todo" , Space , Str "item" ] ] , [ Plain [ Str "[" , Space , Str "]not" , Space , Str "a" , Space , Str "todo" , Space , Str "item" ] ] , [ Plain [ Str "[r]" , Space , Str "not" , Space , Str "a" , Space , Str "todo" , Space , Str "item" ] ] , [ Plain [ Str "[" , Space , Str "]" , Space , Str "not" , Space , Str "a" , Space , Str "todo" , Space , Str "item" ] ] , [ Plain [ Span ( "" , [ "done2" ] , [] ) [] , Str "a" , Space , Str "tab" , Space , Str "in" , Space , Str "the" , Space , Str "todo" , Space , Str "list" , Space , Str "marker" , Space , Code ( "" , [] , [] ) "[ ]" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Span ( "" , [ "done3" ] , [] ) [] , Str "4" , SoftBreak , Str "5" ] ] , [ Plain [ Span ( "" , [ "done4" ] , [] ) [] ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ] ] , [ Plain [ Span ( "" , [ "done4" ] , [] ) [] , Str "task" , Space , Str "2" ] ] ] , Header 2 ( "math" , [] , [] ) [ Str "math" ] , Para [ Math InlineMath " \\sum_i a_i^2 = 1 " ] , Para [ Math DisplayMath "\\sum_i a_i^2\n=\n1" ] , Para [ Math DisplayMath "\\begin{aligned}\n\\sum_i a_i^2 &= 1 + 1 \\\\\n&= 2.\n\\end{aligned}" ] , Para [ Str "edge" , Space , Str "case" , Space , Str "(the" , Space , Code ( "" , [] , [] ) "c^2 + " , Space , Str "after" , Space , Str "the" , Space , Str "multline" , Space , Str "tag" , Space , Str "is" , Space , Str "in" , Space , Str "the" , Space , Str "equation):" ] , Para [ Math DisplayMath "\\begin{gathered}\nc^2 + \na^2 + b^2\n\\end{gathered}" ] , Para [ Str "edge" , Space , Str "case" , Space , Str "(the" , Space , Str "tag" , Space , Str "is" , Space , Code ( "" , [] , [] ) "hello%bye" , Str ")" ] , Para [ Math DisplayMath "\\begin{hello%bye}\n\\int_a^b f(x) dx\n\\end{hello%bye}" ] , Para [ Str "Just" , Space , Str "two" , Space , Str "dollar" , Space , Str "signs:" , Space , Str "$$" ] , Para [ Str "[not" , Space , Str "math]" , Space , Str "You" , Space , Str "have" , Space , Str "$1" , SoftBreak , Str "and" , Space , Str "I" , Space , Str "have" , Space , Str "$1." ] , Header 2 ( "tags" , [] , [] ) [ Str "tags" ] , Para [ Span ( "-tag-one" , [] , [] ) [ Str "" ] , Span ( "tag-one" , [ "tag" ] , [] ) [ Str "tag-one" ] , Space , Span ( "-tag-two" , [] , [] ) [ Str "" ] , Span ( "tag-two" , [ "tag" ] , [] ) [ Str "tag-two" ] ] , Header 2 ( "tables" , [] , [] ) [ Str "tables" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Year" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Temperature" , Space , Str "(low)" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Temperature" , Space , Str "(high)" ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1900" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-10" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "25" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1910" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-15" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "30" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1920" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-10" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "32" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1930" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Emph [ Str "N/A" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Emph [ Str "N/A" ] ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "1940" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "-2" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "40" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 3 ( "centered headerless tables" , [] , [] ) [ Str "centered" , Space , Str "headerless" , Space , Str "tables" ] , Div ( "" , [ "center" ] , [] ) [ Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "d" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] , Header 2 ( "paragraphs" , [] , [] ) [ Str "paragraphs" ] , Para [ Str "This" , Space , Str "is" , Space , Str "first" , Space , Str "paragraph" , SoftBreak , Str "with" , Space , Str "two" , Space , Str "lines." ] , Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "second" , Space , Str "paragraph" , Space , Str "with" , SoftBreak , Str "two" , Space , Str "lines" , Space , Str "after" , Space , Str "many" , Space , Str "blank" , Space , Str "lines." ] , Header 2 ( "definition list" , [] , [] ) [ Str "definition" , Space , Str "list" ] , DefinitionList [ ( [ Str "Term" , Space , Str "1" ] , [ [ Plain [ Str "Definition" , Space , Str "1" ] ] ] ) , ( [ Str "Term" , Space , Str "2" ] , [ [ Plain [ Str "Definition" , Space , Str "2" ] ] , [ Plain [ Str "Definition" , Space , Str "3" ] ] ] ) , ( [ Str "Term" , Space , Str "::" , Space , Span ( "separated" , [] , [] ) [] , Strong [ Str "separated" ] , Space , Str "by" , Space , Str "::" , Space , Emph [ Str "double" , Space , Str "colons" ] ] , [ [ Plain [ Str "Def1" ] ] , [ Plain [ Str "Def2" ] ] ] ) , ( [ Str "Term" , Space , Str "with" , Space , Str "lots" , Space , Str "of" , Space , Str "trailing" , Space , Str "colons:::::::" ] , [ [ Plain [ Str "Definition" ] ] ] ) , ( [ Str "::" , Space , Str "This" , Space , Str "is" , Space , Str "::" , Space , Str "A" , Space , Str "term" , Space , Str "(rather" , Space , Str "than" , Space , Str "a" , Space , Str "definition)" ] , [ [ Plain [ Str "and" , Space , Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "definition" ] ] ] ) , ( [ Str "Term" , Space , Str "Without" , Space , Str "definitions" ] , [ [] ] ) , ( [ Str "Part" , Space , Str "::" , Space , Str "of" , Space , Str "::" , Space , Str "dt" ] , [ [ Plain [ Str "part" , Space , Str "of" , Space , Str "::dd" ] ] ] ) ] , DefinitionList [ ( [] , [ [ Plain [ Str "Definition" , Space , Str "1" , Space , Str "without" , Space , Str "a" , Space , Str "term" ] ] , [ Plain [ Str "Definition" , Space , Str "2" , Space , Str "without" , Space , Str "a" , Space , Str "term" ] ] ] ) ] , DefinitionList [ ( [ Str "T1" ] , [ [ Plain [ Str "D1" ] ] ] ) ] , Para [ Str "new" , Space , Str "paragraph" ] , DefinitionList [ ( [ Str "T1" ] , [ [ Plain [ Str "D1" ] ] ] ) ] , Para [ Str "Not::Definition" ] , Para [ Str "Not" , Space , Str "::Definition" ] , Para [ Str "::Not" , Space , Str "definition" ] , BlockQuote [ Plain [ Str "::" , Space , Str "blockquote" ] ] , BlockQuote [ Plain [ Str "block" , Space , Str "::" , Space , Str "quote" ] ] , Header 2 ( "metadata placeholders" , [] , [] ) [ Str "metadata" , Space , Str "placeholders" ] , Para [ Str "%this" , Space , Str "is" , Space , Str "not" , Space , Str "a" , Space , Str "placeholder" ] , Para [ Str "placeholders" , SoftBreak , Str "serves" , Space , Str "as" , Space , Str "space" , Space , Str "/" , Space , Str "softbreak" , Space , Str "in" , Space , Str "paragraphs" ] , Header 2 ( "sup, sub" , [] , [] ) [ Str "sup," , Space , Str "sub" ] , Para [ Str "super" , Superscript [ Str "script" ] ] , Para [ Str "sub" , Subscript [ Str "script" ] ] , Header 2 ( "the todo mark" , [] , [] ) [ Str "the" , Space , Str "todo" , Space , Str "mark" ] , Para [ Span ( "" , [ "todo" ] , [] ) [ Str "TODO:" ] ] , Header 1 ( "not implemented yet" , [] , [] ) [ Emph [ Span ( "not implemented yet" , [] , [] ) [] , Strong [ Str "not" , Space , Str "implemented" , Space , Str "yet" ] ] ] , Header 2 ( "tables with spans" , [] , [] ) [ Str "tables" , Space , Str "with" , Space , Str "spans" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "d" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "\\/" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "e" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str ">" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "f" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "\\/" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "\\/" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str ">" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "g" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "h" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str ">" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str ">" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str ">" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "tables with multiple lines of headers" , [] , [] ) [ Str "tables" , Space , Str "with" , Space , Str "multiple" , Space , Str "lines" , Space , Str "of" , Space , Str "headers" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "a" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "b" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "c" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "d" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "---" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "---" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "some other placeholders" , [] , [] ) [ Str "some" , Space , Str "other" , Space , Str "placeholders" ] , Para [ Code ( "" , [] , [] ) "template" , Space , Str "placeholder" , Space , Str "is" , Space , Str "ignored." ] , Para [ Code ( "" , [] , [] ) "nohtml" , Space , Str "placeholder" , Space , Str "is" , Space , Str "ignored." ] ] ================================================ FILE: test/vimwiki-reader.wiki ================================================ = _*implemented*_ = = header = == header level two == === header `level` 3 === ==== header ~~level~~ four ==== ===== header _*level* 5_ ===== ====== header level 6 ====== ======= not a header ======== hi== not a header == === not a header == === not a header ===- not a header: =n= === not a header ==== == centred header == == header with some `==` in between == == header with some == in between == == header with some ==in between == == emph strong and strikeout == _emph_ *strong* *_strong and emph_* _*emph and strong*_ *_emph inside_ strong* *strong with _emph_* _*strong inside* emph_ _~~strikeout~~ inside emph_ ~~This is _struck out_ with emph~~ *not strong* just two stars: ** just two underscores: __ just four ~s: ~~~~ _not %%comment emph_ ~~not %%comment %%comment strikeout~~ == horizontal rule == top ---- middle ------- not a rule----- not a rule (trailing spaces): ----- not a rule (leading spaces): ---- == comments == %% you can't see me. this %% secret is %% not secret == inline code == Here is some `inline code`. Just two backticks: `` == preformatted text == {{{ Tyger! Tyger! burning bright In the forests of the night, What immortal hand or eye Could frame thy fearful symmetry? In what distant deeps or skies Burnt the fire of thine eyes? On what wings dare he aspire? What the hand dare sieze the fire? }}} === preformatted text with attributes === {{{class="python" style="color:blue" for i in range(1, 5): print(i) }}} === preformatted text with nested syntax === {{{sql SELECT * FROM table }}} === empty preformatted text === {{{ }}} == block quotes == (indentation 4 spaces) This would be a blockquote in Vimwiki. It is not *highlighted* in Vim but (indentation 1 space followed by 1 tab of width 4) could be styled by CSS in HTML. Blockquotes are usually used to quote a (indentation 1 tab of width 4) long piece of text from another source. ~~blah blah~~ :blockquote: == external links == [[http://google.com|_Google_ search engine]] http://pandoc.org ftp://vim.org [[http://google.com]] [[mailto:info@example.org|email me]] mailto:hello@bye.com == internal links == [[This is a link]] [[This is a link source|Description of the link]] [[projects/Important Project 1]] [[../index]] [[a subdirectory/|Other files]] [[#tag-one|try me to test tag anchors]] [[#block quotes|try me to test header anchors]] [[#strong|try me to test strong anchors]] [[Todo List#Tomorrow|Tasks for tomorrow]] [[diary:2017-05-01]] [[file:../assets/data.csv|Important Data]] === links with thumbnails === [[http://www.google.com|{{./movie.jpg}}]] == images == {{file:./lalune.jpg}} {{http://vimwiki.googlecode.com/hg/images/vimwiki_logo.png|Vimwiki}} {{local:./movie.jpg}} === image with attributes === {{lalune.jpg|_cool stuff_|style="width:150px;height:120px;"}} {{nonexist.jpg|*Non-existing* image|class="center flow blabla" style="font-color:red"}} {{lalune.jpg|_cool stuff_|style="width:150px;height:120px;"|anything in this segment is ignored}} == lists == # ordered list item 1, and here is some math belonging to list item 1 {{$ a^2 + b^2 = c^2 }}$ and some preformatted and tables belonging to item 1 as well {{{ I'm part of item 1. }}} | this table | is | | also a part | of item 1 | and some more text belonging to item 1. # ordered list item 2 * Bulleted list item 1 * Bulleted list item 2 # Bulleted list item 1 # the # become numbers when converted to HTML - Bulleted list item 1 - Bulleted list item 2 * Item 1 * Item 2 # Sub item 1 (indentation 4 spaces) Sub item 1 continued line. %%comments Sub item 1 next continued line. * Sub item 2, as an ordered list item even though the identifier is `*` (indentation 2 spaces followed by one tab of width 4) * etc. Continuation of Item 2 Next continuation of Item 2 But this is a new paragraph. # 1 * `1.1` * 2 * 2.1 * 3 === ordered lists with non-# identifiers === 1. Numbered list item 1 2. Numbered list item 2 3. Numbered list item 3 4. Numbered list item 1 5. Numbered list item 2 6. Numbered list item 3 1) Numbered list item 1 2) Numbered list item 2 3) Numbered list item 3 a) Numbered list item 1 b) Numbered list item 2 c) Numbered list item 3 A) Numbered list item 1 B) Numbered list item 2 C) Numbered list item 3 i) Numbered list item 1 ii) Numbered list item 2 iii) Numbered list item 3 I) Numbered list item 1 II) Numbered list item 2 III) Numbered list item 3 - Bulleted list item 1 - Bulleted list item 2 a) Numbered list sub item 1 b) more ... * and more ... * ... c) Numbered list sub item 3 1. Numbered list sub sub item 1 2. Numbered list sub sub item 2 d) etc. - Bulleted list item 3 == todo lists == * [ ] task 1 1. [.] 5 * [o] 3 * [] not a todo item * [ ]not a todo item * [r] not a todo item * [ ] not a todo item * [o] a tab in the todo list marker `[ ]` III) [O] 4 5 i) [X] | a | b | * [X] task 2 == math == $ \sum_i a_i^2 = 1 $ {{$ \sum_i a_i^2 = 1 }}$ {{$%align% \sum_i a_i^2 &= 1 + 1 \\ &= 2. }}$ edge case (the `c^2 + ` after the multline tag is in the equation): {{$%multline%c^2 + a^2 + b^2 }}$ edge case (the tag is `hello%bye`) {{$%hello%bye% \int_a^b f(x) dx }}$ Just two dollar signs: $$ [not math] You have $1 and I have $1. == tags == :tag-one:tag-two: == tables == | Year | Temperature (low) | Temperature (high) | |------|-------------------|--------------------| | 1900 | -10 | 25 | | 1910 | -15 | 30 | | 1920 | -10 | 32 | | 1930 | _N/A_ | _N/A_ | | 1940 | -2 | 40 | === centered headerless tables === | a | b | | c | d | == paragraphs == This is first paragraph with two lines. This is a second paragraph with two lines after many blank lines. == definition list == Term 1:: Definition 1 Term 2:: :: Definition 2 :: Definition 3 Term :: *separated* by :: _double colons_ :: Def1 :: Def2 Term with lots of trailing colons::::::::: Definition :: This is :: A term (rather than a definition) :: and this is a definition Term Without definitions :: :: Part :: of :: dt :: part of ::dd :: Definition 1 without a term :: Definition 2 without a term T1 :: D1 new paragraph T1 :: D1 Not::Definition Not ::Definition ::Not definition :: blockquote block :: quote == metadata placeholders == %title title %date 2017-05-01 %title second title is ignored %date second date is ignored %this is not a placeholder placeholders %title another title %date 2017-04-23 serves as space / softbreak in paragraphs == sup, sub == super^script^ sub,,script,, == the todo mark == TODO: = _*not implemented yet*_ = == tables with spans == | a | b | c | d | | \/ | e | > | f | | \/ | \/ | > | g | | h | > | > | > | == tables with multiple lines of headers == | a | b | | c | d | |---|---| == some other placeholders == `template` placeholder is ignored. %template template `nohtml` placeholder is ignored. %nohtml ================================================ FILE: test/writer.asciidoc ================================================ = Pandoc Test Suite John MacFarlane; Anonymous July 17, 2006 :stem: latexmath This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ''''' == Headers === Level 2 with an link:/url[embedded link] ==== Level 3 with _emphasis_ ===== Level 4 ====== Level 5 == Level 1 === Level 2 with _emphasis_ ==== Level 3 with no blank line === Level 2 with no blank line ''''' == Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. ++*++ criminey. There should be a hard line break + here. ''''' == Block Quotes E-mail style: ____ This is a block quote. It is pretty short. ____ ____ -- Code in a block quote: .... sub status { print "working"; } .... A list: [arabic] . item one . item two Nested block quotes: ____ nested ____ ____ nested ____ -- ____ This should not be a block quote: 2 ++>++ 1. And a following paragraph. ''''' == Code Blocks Code: .... ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab .... And: .... this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ .... ''''' == Lists === Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: * Plus 1 * Plus 2 * Plus 3 Pluses loose: * Plus 1 * Plus 2 * Plus 3 Minuses tight: * Minus 1 * Minus 2 * Minus 3 Minuses loose: * Minus 1 * Minus 2 * Minus 3 === Ordered Tight: [arabic] . First . Second . Third and: [arabic] . One . Two . Three Loose using tabs: [arabic] . First . Second . Third and using spaces: [arabic] . One . Two . Three Multiple paragraphs: [arabic] . Item 1, graf one. + Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. . Item 2. . Item 3. === Nested * Tab ** Tab *** Tab Here’s another: [arabic] . First . Second: * Fee * Fie * Foe . Third Same thing but with paragraphs: [arabic] . First . Second: * Fee * Fie * Foe . Third === Tabs and spaces * this is a list item indented with tabs * this is a list item indented with spaces ** this is an example list item indented with tabs ** this is an example list item indented with spaces === Fancy list markers [arabic, start=2] . begins with 2 . and now 3 + with a continuation [lowerroman, start=4] .. sublist with roman numerals, starting with 4 .. more items [upperalpha] ... a subsublist ... a subsublist Nesting: [upperalpha] . Upper Alpha [upperroman] .. Upper Roman. [arabic, start=6] ... Decimal start with 6 [loweralpha, start=3] .... Lower alpha with paren Autonumbering: . Autonumber. . More. .. Nested. Should not be a list item: M.A. 2007 B. Williams ''''' == Definition Lists Tight using spaces: apple:: red fruit orange:: orange fruit banana:: yellow fruit Tight using tabs: apple:: red fruit orange:: orange fruit banana:: yellow fruit Loose: apple:: red fruit orange:: orange fruit banana:: yellow fruit Multiple blocks with italics: _apple_:: red fruit + contains seeds, crisp, pleasant to taste _orange_:: orange fruit + .... { orange code block } .... + ____ orange block quote ____ Multiple definitions, tight: apple:: red fruit + computer orange:: orange fruit + bank Multiple definitions, loose: apple:: red fruit + computer orange:: orange fruit + bank Blank line after term, indented marker, alternate markers: apple:: red fruit + computer orange:: orange fruit + [arabic] . sublist . sublist == HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is _emphasized_ And this is *strong* Here’s a simple block: foo This should be a code block, though: ....
                foo
                .... As should this: ....
                foo
                .... Now, nested: foo This should just be an HTML comment: Multiline: Code block: .... .... Just plain comment, with trailing spaces on the line: Code: ....
                .... Hr’s: ''''' == Inline Markup This is _emphasized_, and so _is this_. This is *strong*, and so *is this*. An _link:/url[emphasized link]_. *_This is strong and em._* So is *_this_* word. *_This is strong and em._* So is *_this_* word. This is code: `++>++`, `$`, `++\++`, `++\++$`, `++<++html++>++`. [line-through]#This is _strikeout_.# Superscripts: a^bc^d a^_hello_^ a^hello there^. Subscripts: H~2~O, H~23~O, H~many of them~O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ''''' == Smart quotes, ellipses, dashes "`Hello,`" said the spider. "`'`Shelob`' is my name.`" '`A`', '`B`', and '`C`' are letters. '`Oak,`' '`elm,`' and '`beech`' are names of trees. So is '`pine.`' '`He said, "`I want to go.`"`' Were you alive in the 70’s? Here is some quoted '``code``' and a "`http://example.com/?foo=1&bar=2[quoted link]`". Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ''''' == LaTeX * * latexmath:[2+2=4] * latexmath:[x \in y] * latexmath:[\alpha \wedge \omega] * latexmath:[223] * latexmath:[p]-Tree * Here’s some display math: + [latexmath] ++++ \frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h} ++++ * Here’s one that has a line break in it: latexmath:[\alpha + \omega \times x^2]. These shouldn’t be math: * To get the famous equation, write `$e = mc^2$`. * $22,000 is a _lot_ of money. So is $34,000. (It worked if "`lot`" is emphasized.) * Shoes ($20) and socks ($5). * Escaped `$`: $73 _this should be emphasized_ 23$. Here’s a LaTeX table: ''''' == Special Characters Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 ++<++ 5. 6 ++>++ 5. Backslash: ++\++ Backtick: ++`++ Asterisk: ++*++ Underscore: ++_++ Left brace: ++{++ Right brace: } Left bracket: ++[++ Right bracket: ++]++ Left paren: ( Right paren: ) Greater-than: ++>++ Hash: ++#++ Period: . Bang: ! Plus: {plus} Minus: - ''''' == Links === Explicit Just a link:/url/[URL]. link:/url/[URL and title]. link:/url/[URL and title]. link:/url/[URL and title]. link:/url/[URL and title] link:/url/[URL and title] link:/url/with_underscore[with++_++underscore] mailto:nobody@nowhere.net[Email link] link:[Empty]. === Reference Foo link:/url/[bar]. With link:/url/[embedded ++[++brackets++]++]. link:/url/[b] by itself should be a link. Indented link:/url[once]. Indented link:/url[twice]. Indented link:/url[thrice]. This should ++[++not++][]++ be a link. .... [not]: /url .... Foo link:/url/[bar]. Foo link:/url/[biz]. === With ampersands Here’s a http://example.com/?foo=1&bar=2[link with an ampersand in the URL]. Here’s a link with an amersand in the link text: http://att.com/[AT&T]. Here’s an link:/script?foo=1&bar=2[inline link]. Here’s an link:/script?foo=1&bar=2[inline link in pointy braces]. === Autolinks With an ampersand: http://example.com/?foo=1&bar=2 * In a list? * http://example.com/ * It should. An e-mail address: nobody@nowhere.net ____ Blockquoted: http://example.com/ ____ Auto-links should not occur here: `++<++http://example.com/++>++` .... or here: .... ''''' == Images From "`Voyage dans la Lune`" by Georges Melies (1902): .lalune image::lalune.jpg[lalune,title="Voyage dans la Lune"] Here is a movie image:movie.jpg[movie] icon. ''''' == Footnotes Here is a footnote reference,footnote:[Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.] and another.[multiblock footnote omitted] This should _not_ be a footnote reference, because it contains a space.++[++^my note++]++ Here is an inline note.footnote:[This is _easier_ to type. Inline notes may contain http://google.com[links] and `++]++` verbatim characters, as well as ++[++bracketed text++]++.] ____ Notes can go in quotes.footnote:[In quote.] ____ [arabic] . And in list items.footnote:[In list.] This paragraph should not be part of the note, as it is not indented. ================================================ FILE: test/writer.asciidoc_legacy ================================================ = Pandoc Test Suite John MacFarlane; Anonymous July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ''''' == Headers === Level 2 with an link:/url[embedded link] ==== Level 3 with _emphasis_ ===== Level 4 ====== Level 5 == Level 1 === Level 2 with _emphasis_ ==== Level 3 with no blank line === Level 2 with no blank line ''''' == Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. ++*++ criminey. There should be a hard line break + here. ''''' == Block Quotes E-mail style: ____ This is a block quote. It is pretty short. ____ ____ -- Code in a block quote: .... sub status { print "working"; } .... A list: [arabic] . item one . item two Nested block quotes: ____ nested ____ ____ nested ____ -- ____ This should not be a block quote: 2 ++>++ 1. And a following paragraph. ''''' == Code Blocks Code: .... ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab .... And: .... this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ .... ''''' == Lists === Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: * Plus 1 * Plus 2 * Plus 3 Pluses loose: * Plus 1 * Plus 2 * Plus 3 Minuses tight: * Minus 1 * Minus 2 * Minus 3 Minuses loose: * Minus 1 * Minus 2 * Minus 3 === Ordered Tight: [arabic] . First . Second . Third and: [arabic] . One . Two . Three Loose using tabs: [arabic] . First . Second . Third and using spaces: [arabic] . One . Two . Three Multiple paragraphs: [arabic] . Item 1, graf one. + Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. . Item 2. . Item 3. === Nested * Tab ** Tab *** Tab Here’s another: [arabic] . First . Second: * Fee * Fie * Foe . Third Same thing but with paragraphs: [arabic] . First . Second: * Fee * Fie * Foe . Third === Tabs and spaces * this is a list item indented with tabs * this is a list item indented with spaces ** this is an example list item indented with tabs ** this is an example list item indented with spaces === Fancy list markers [arabic, start=2] . begins with 2 . and now 3 + with a continuation [lowerroman, start=4] .. sublist with roman numerals, starting with 4 .. more items [upperalpha] ... a subsublist ... a subsublist Nesting: [upperalpha] . Upper Alpha [upperroman] .. Upper Roman. [arabic, start=6] ... Decimal start with 6 [loweralpha, start=3] .... Lower alpha with paren Autonumbering: . Autonumber. . More. .. Nested. Should not be a list item: M.A. 2007 B. Williams ''''' == Definition Lists Tight using spaces: apple:: red fruit orange:: orange fruit banana:: yellow fruit Tight using tabs: apple:: red fruit orange:: orange fruit banana:: yellow fruit Loose: apple:: red fruit orange:: orange fruit banana:: yellow fruit Multiple blocks with italics: _apple_:: red fruit + contains seeds, crisp, pleasant to taste _orange_:: orange fruit + .... { orange code block } .... + ____ orange block quote ____ Multiple definitions, tight: apple:: red fruit + computer orange:: orange fruit + bank Multiple definitions, loose: apple:: red fruit + computer orange:: orange fruit + bank Blank line after term, indented marker, alternate markers: apple:: red fruit + computer orange:: orange fruit + [arabic] . sublist . sublist == HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is _emphasized_ And this is *strong* Here’s a simple block: foo This should be a code block, though: ....
                foo
                .... As should this: ....
                foo
                .... Now, nested: foo This should just be an HTML comment: Multiline: Code block: .... .... Just plain comment, with trailing spaces on the line: Code: ....
                .... Hr’s: ''''' == Inline Markup This is _emphasized_, and so _is this_. This is *strong*, and so *is this*. An _link:/url[emphasized link]_. *_This is strong and em._* So is *_this_* word. *_This is strong and em._* So is *_this_* word. This is code: `>`, `$`, `\`, `\$`, ``. [line-through]#This is _strikeout_.# Superscripts: a^bc^d a^_hello_^ a^hello there^. Subscripts: H~2~O, H~23~O, H~many of them~O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ''''' == Smart quotes, ellipses, dashes ``Hello,'' said the spider. ```Shelob' is my name.'' `A', `B', and `C' are letters. `Oak,' `elm,' and `beech' are names of trees. So is `pine.' `He said, ``I want to go.''' Were you alive in the 70’s? Here is some quoted ``code`' and a ``http://example.com/?foo=1&bar=2[quoted link]''. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ''''' == LaTeX * * latexmath:[$2+2=4$] * latexmath:[$x \in y$] * latexmath:[$\alpha \wedge \omega$] * latexmath:[$223$] * latexmath:[$p$]-Tree * Here’s some display math: + [latexmath] ++++ \[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\] ++++ * Here’s one that has a line break in it: latexmath:[$\alpha + \omega \times x^2$]. These shouldn’t be math: * To get the famous equation, write `$e = mc^2$`. * $22,000 is a _lot_ of money. So is $34,000. (It worked if ``lot'' is emphasized.) * Shoes ($20) and socks ($5). * Escaped `$`: $73 _this should be emphasized_ 23$. Here’s a LaTeX table: ''''' == Special Characters Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 ++<++ 5. 6 ++>++ 5. Backslash: ++\++ Backtick: ++`++ Asterisk: ++*++ Underscore: ++_++ Left brace: ++{++ Right brace: } Left bracket: ++[++ Right bracket: ++]++ Left paren: ( Right paren: ) Greater-than: ++>++ Hash: ++#++ Period: . Bang: ! Plus: {plus} Minus: - ''''' == Links === Explicit Just a link:/url/[URL]. link:/url/[URL and title]. link:/url/[URL and title]. link:/url/[URL and title]. link:/url/[URL and title] link:/url/[URL and title] link:/url/with_underscore[with++_++underscore] mailto:nobody@nowhere.net[Email link] link:[Empty]. === Reference Foo link:/url/[bar]. With link:/url/[embedded ++[++brackets++]++]. link:/url/[b] by itself should be a link. Indented link:/url[once]. Indented link:/url[twice]. Indented link:/url[thrice]. This should ++[++not++][]++ be a link. .... [not]: /url .... Foo link:/url/[bar]. Foo link:/url/[biz]. === With ampersands Here’s a http://example.com/?foo=1&bar=2[link with an ampersand in the URL]. Here’s a link with an amersand in the link text: http://att.com/[AT&T]. Here’s an link:/script?foo=1&bar=2[inline link]. Here’s an link:/script?foo=1&bar=2[inline link in pointy braces]. === Autolinks With an ampersand: http://example.com/?foo=1&bar=2 * In a list? * http://example.com/ * It should. An e-mail address: nobody@nowhere.net ____ Blockquoted: http://example.com/ ____ Auto-links should not occur here: `` .... or here: .... ''''' == Images From ``Voyage dans la Lune'' by Georges Melies (1902): .lalune image::lalune.jpg[lalune,title="Voyage dans la Lune"] Here is a movie image:movie.jpg[movie] icon. ''''' == Footnotes Here is a footnote reference,footnote:[Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.] and another.[multiblock footnote omitted] This should _not_ be a footnote reference, because it contains a space.++[++^my note++]++ Here is an inline note.footnote:[This is _easier_ to type. Inline notes may contain http://google.com[links] and `]` verbatim characters, as well as ++[++bracketed text++]++.] ____ Notes can go in quotes.footnote:[In quote.] ____ [arabic] . And in list items.footnote:[In list.] This paragraph should not be part of the note, as it is not indented. ================================================ FILE: test/writer.bbcode ================================================ This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. * * * [u][b]Headers[/b][/u] [b]Level 2 with an [url=/url]embedded link[/url][/b] [u]Level 3 with [i]emphasis[/i][/u] Level 4 Level 5 [u][b]Level 1[/b][/u] [b]Level 2 with [i]emphasis[/i][/b] [u]Level 3[/u] with no blank line [b]Level 2[/b] with no blank line * * * [u][b]Paragraphs[/b][/u] Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break here. * * * [u][b]Block Quotes[/b][/u] E-mail style: [quote] This is a block quote. It is pretty short. [/quote] [quote] Code in a block quote: [code]sub status { print "working"; } [/code] A list: [list=1] [*]item one [*]item two [/list] Nested block quotes: [quote] nested [/quote] [quote] nested [/quote] [/quote] This should not be a block quote: 2 > 1. And a following paragraph. * * * [u][b]Code Blocks[/b][/u] Code: [code]---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab [/code] And: [code] this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ [/code] * * * [u][b]Lists[/b][/u] [b]Unordered[/b] Asterisks tight: [list] [*]asterisk 1 [*]asterisk 2 [*]asterisk 3 [/list] Asterisks loose: [list] [*]asterisk 1 [*]asterisk 2 [*]asterisk 3 [/list] Pluses tight: [list] [*]Plus 1 [*]Plus 2 [*]Plus 3 [/list] Pluses loose: [list] [*]Plus 1 [*]Plus 2 [*]Plus 3 [/list] Minuses tight: [list] [*]Minus 1 [*]Minus 2 [*]Minus 3 [/list] Minuses loose: [list] [*]Minus 1 [*]Minus 2 [*]Minus 3 [/list] [b]Ordered[/b] Tight: [list=1] [*]First [*]Second [*]Third [/list] and: [list=1] [*]One [*]Two [*]Three [/list] Loose using tabs: [list=1] [*]First [*]Second [*]Third [/list] and using spaces: [list=1] [*]One [*]Two [*]Three [/list] Multiple paragraphs: [list=1] [*]Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. [*]Item 2. [*]Item 3. [/list] [b]Nested[/b] [list] [*]Tab [list] [*]Tab [list] [*]Tab [/list] [/list] [/list] Here's another: [list=1] [*]First [*]Second: [list] [*]Fee [*]Fie [*]Foe [/list] [*]Third [/list] Same thing but with paragraphs: [list=1] [*]First [*]Second: [list] [*]Fee [*]Fie [*]Foe [/list] [*]Third [/list] [b]Tabs and spaces[/b] [list] [*]this is a list item indented with tabs [*]this is a list item indented with spaces [list] [*]this is an example list item indented with tabs [*]this is an example list item indented with spaces [/list] [/list] [b]Fancy list markers[/b] [list=1] [*]begins with 2 [*]and now 3 with a continuation [list=i] [*]sublist with roman numerals, starting with 4 [*]more items [list=A] [*]a subsublist [*]a subsublist [/list] [/list] [/list] Nesting: [list=A] [*]Upper Alpha [list=I] [*]Upper Roman. [list=1] [*]Decimal start with 6 [list=a] [*]Lower alpha with paren [/list] [/list] [/list] [/list] Autonumbering: [list=1] [*]Autonumber. [*]More. [list=1] [*]Nested. [/list] [/list] Should not be a list item: M.A. 2007 B. Williams * * * [u][b]Definition Lists[/b][/u] Tight using spaces: apple [list] [*]red fruit [/list] orange [list] [*]orange fruit [/list] banana [list] [*]yellow fruit [/list] Tight using tabs: apple [list] [*]red fruit [/list] orange [list] [*]orange fruit [/list] banana [list] [*]yellow fruit [/list] Loose: apple [list] [*]red fruit [/list] orange [list] [*]orange fruit [/list] banana [list] [*]yellow fruit [/list] Multiple blocks with italics: [i]apple[/i] [list] [*]red fruit contains seeds, crisp, pleasant to taste [/list] [i]orange[/i] [list] [*]orange fruit [code]{ orange code block } [/code] [quote] orange block quote [/quote] [/list] Multiple definitions, tight: apple [list] [*]red fruit [*]computer [/list] orange [list] [*]orange fruit [*]bank [/list] Multiple definitions, loose: apple [list] [*]red fruit [*]computer [/list] orange [list] [*]orange fruit [*]bank [/list] Blank line after term, indented marker, alternate markers: apple [list] [*]red fruit [*]computer [/list] orange [list] [*]orange fruit [list=1] [*]sublist [*]sublist [/list] [/list] [u][b]HTML Blocks[/b][/u] Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is [i]emphasized[/i] And this is [b]strong[/b] Here's a simple block: foo This should be a code block, though: [code]
                foo
                [/code] As should this: [code]
                foo
                [/code] Now, nested: foo This should just be an HTML comment: Multiline: Code block: [code] [/code] Just plain comment, with trailing spaces on the line: Code: [code]
                [/code] Hr's: * * * [u][b]Inline Markup[/b][/u] This is [i]emphasized[/i], and so [i]is this[/i]. This is [b]strong[/b], and so [b]is this[/b]. An [i][url=/url]emphasized link[/url][/i]. [b][i]This is strong and em.[/i][/b] So is [b][i]this[/i][/b] word. [b][i]This is strong and em.[/i][/b] So is [b][i]this[/i][/b] word. This is code: >, $, \, \$, . [s]This is [i]strikeout[/i].[/s] Superscripts: abcd a[i]hello[/i] ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. * * * [u][b]Smart quotes, ellipses, dashes[/b][/u] "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70's? Here is some quoted 'code' and a "[url=http://example.com/?foo=1&bar=2]quoted link[/url]". Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses...and...and.... * * * [u][b]LaTeX[/b][/u] [list] [*] [*]$2+2=4$ [*]$x \in y$ [*]$\alpha \wedge \omega$ [*]$223$ [*]$p$-Tree [*]Here's some display math: [code=latex]$$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ [/code] [*]Here's one that has a line break in it: $\alpha + \omega \times x^2$. [/list] These shouldn't be math: [list] [*]To get the famous equation, write $e = mc^2$. [*]$22,000 is a [i]lot[/i] of money. So is $34,000. (It worked if "lot" is emphasized.) [*]Shoes ($20) and socks ($5). [*]Escaped $: $73 [i]this should be emphasized[/i] 23$. [/list] Here's a LaTeX table: * * * [u][b]Special Characters[/b][/u] Here is some unicode: [list] [*]I hat: Î [*]o umlaut: ö [*]section: § [*]set membership: ∈ [*]copyright: © [/list] AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - * * * [u][b]Links[/b][/u] [b]Explicit[/b] Just a [url=/url/]URL[/url]. [url=/url/]URL and title[/url]. [url=/url/]URL and title[/url]. [url=/url/]URL and title[/url]. [url=/url/]URL and title[/url] [url=/url/]URL and title[/url] [url=/url/with_underscore]with_underscore[/url] [email=nobody@nowhere.net]Email link[/email] [url]Empty[/url]. [b]Reference[/b] Foo [url=/url/]bar[/url]. With [url=/url/]embedded [brackets][/url]. [url=/url/]b[/url] by itself should be a link. Indented [url=/url]once[/url]. Indented [url=/url]twice[/url]. Indented [url=/url]thrice[/url]. This should [not][] be a link. [code][not]: /url [/code] Foo [url=/url/]bar[/url]. Foo [url=/url/]biz[/url]. [b]With ampersands[/b] Here's a [url=http://example.com/?foo=1&bar=2]link with an ampersand in the URL[/url]. Here's a link with an amersand in the link text: [url=http://att.com/]AT&T[/url]. Here's an [url=/script?foo=1&bar=2]inline link[/url]. Here's an [url=/script?foo=1&bar=2]inline link in pointy braces[/url]. [b]Autolinks[/b] With an ampersand: [url]http://example.com/?foo=1&bar=2[/url] [list] [*]In a list? [*][url]http://example.com/[/url] [*]It should. [/list] An e-mail address: [email]nobody@nowhere.net[/email] [quote] Blockquoted: [url]http://example.com/[/url] [/quote] Auto-links should not occur here: [code]or here: [/code] * * * [u][b]Images[/b][/u] From "Voyage dans la Lune" by Georges Melies (1902): [img alt="lalune" title="Voyage dans la Lune"]lalune.jpg[/img] lalune Here is a movie [img alt="movie"]movie.jpg[/img] icon. * * * [u][b]Footnotes[/b][/u] Here is a footnote reference,(1) and another.(2) This should [i]not[/i] be a footnote reference, because it contains a space.[^my note] Here is an inline note.(3) [quote] Notes can go in quotes.(4) [/quote] [list=1] [*]And in list items.(5) [/list] This paragraph should not be part of the note, as it is not indented. * * * (1) Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. (2) Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). [code] { } [/code] If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. (3) This is [i]easier[/i] to type. Inline notes may contain [url=http://google.com]links[/url] and ] verbatim characters, as well as [bracketed text]. (4) In quote. (5) In list. ================================================ FILE: test/writer.context ================================================ % Enable hyperlinks \setupinteraction [state=start, title={Pandoc Test Suite}, author={John MacFarlane; Anonymous}, style=, color=, contrastcolor=] \setupurl[style=] % make chapter, section bookmarks visible when opening document \placebookmarks[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][chapter, section] \setupinteractionscreen[option={bookmark,title}] \setuppagenumbering[location={footer,middle}] \setupstructure[state=start,method=auto] % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] \definefontfeature[default:tnum][default][tnum=yes, pnum=no] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] \setupbodyfontenvironment[default][em=italic] % use italic as em, not slanted \definefallbackfamily[mainface][rm][CMU Serif][preset=range:greek, force=yes] \definefontfamily[mainface][rm][Latin Modern Roman] \definefontfamily[mainface][mm][Latin Modern Math] \definefontfamily[mainface][ss][Latin Modern Sans] \definefontfamily[mainface][tt][Latin Modern Typewriter][features=none] \setupbodyfont[mainface] \setupwhitespace[medium] \setuphead[chapter] [style=\tfd\setupinterlinespace,header=empty] \setuphead[section] [style=\tfc\setupinterlinespace] \setuphead[subsection] [style=\tfb\setupinterlinespace] \setuphead[subsubsection] [style=\bf] \setuphead[subsubsubsection] [style=\sc] \setuphead[subsubsubsubsection][style=\it] \definesectionlevels [default] [section, subsection, subsubsection, subsubsubsection, subsubsubsubsection] \setuphead[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][number=no] \definedescription [description] [headstyle=bold, style=normal, location=hanging, width=broad, margin=1cm, alternative=hanging] \setupitemize[autointro] % prevent orphan list intro \setupitemize[indentnext=no] \defineitemgroup[enumerate] \setupenumerate[each][fit][itemalign=left,distance=.5em,style={\feature[+][default:tnum]}] \setupfloat[figure][default={here,nonumber}] \setupfloat[table][default={here,nonumber}] \setupxtable[frame=off] \setupxtable[head][topframe=on] \setupxtable[body][] \setupxtable[foot][] \setupxtable[lastrow][bottomframe=on] \starttext \startalignment[middle] {\tfd\setupinterlinespace Pandoc Test Suite} \smallskip {\tfa\setupinterlinespace John MacFarlane\crlf Anonymous} \smallskip {\tfa\setupinterlinespace July 17, 2006} \bigskip \stopalignment This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. \thinrule \startsectionlevel[title={Headers},reference={headers}] \startsectionlevel[title={Level 2 with an \goto{embedded link}[url(/url)]},reference={level-2-with-an-embedded-link}] \startsectionlevel[title={Level 3 with {\em emphasis}},reference={level-3-with-emphasis}] \startsectionlevel[title={Level 4},reference={level-4}] \startsectionlevel[title={Level 5},reference={level-5}] \stopsectionlevel \stopsectionlevel \stopsectionlevel \stopsectionlevel \stopsectionlevel \startsectionlevel[title={Level 1},reference={level-1}] \startsectionlevel[title={Level 2 with {\em emphasis}},reference={level-2-with-emphasis}] \startsectionlevel[title={Level 3},reference={level-3}] with no blank line \stopsectionlevel \stopsectionlevel \startsectionlevel[title={Level 2},reference={level-2}] with no blank line \thinrule \stopsectionlevel \stopsectionlevel \startsectionlevel[title={Paragraphs},reference={paragraphs}] Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break\crlf here. \thinrule \stopsectionlevel \startsectionlevel[title={Block Quotes},reference={block-quotes}] E-mail style: \startblockquote This is a block quote. It is pretty short. \stopblockquote \startblockquote Code in a block quote: \starttyping sub status { print "working"; } \stoptyping A list: \startenumerate[n,packed][stopper=.] \item item one \item item two \stopenumerate Nested block quotes: \startblockquote nested \stopblockquote \startblockquote nested \stopblockquote \stopblockquote This should not be a block quote: 2 > 1. And a following paragraph. \thinrule \stopsectionlevel \startsectionlevel[title={Code Blocks},reference={code-blocks}] Code: \starttyping ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab \stoptyping And: \starttyping this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ \stoptyping \thinrule \stopsectionlevel \startsectionlevel[title={Lists},reference={lists}] \startsectionlevel[title={Unordered},reference={unordered}] Asterisks tight: \startitemize[packed] \item asterisk 1 \item asterisk 2 \item asterisk 3 \stopitemize Asterisks loose: \startitemize \item asterisk 1 \item asterisk 2 \item asterisk 3 \stopitemize Pluses tight: \startitemize[packed] \item Plus 1 \item Plus 2 \item Plus 3 \stopitemize Pluses loose: \startitemize \item Plus 1 \item Plus 2 \item Plus 3 \stopitemize Minuses tight: \startitemize[packed] \item Minus 1 \item Minus 2 \item Minus 3 \stopitemize Minuses loose: \startitemize \item Minus 1 \item Minus 2 \item Minus 3 \stopitemize \stopsectionlevel \startsectionlevel[title={Ordered},reference={ordered}] Tight: \startenumerate[n,packed][stopper=.] \item First \item Second \item Third \stopenumerate and: \startenumerate[n,packed][stopper=.] \item One \item Two \item Three \stopenumerate Loose using tabs: \startenumerate[n][stopper=.] \item First \item Second \item Third \stopenumerate and using spaces: \startenumerate[n][stopper=.] \item One \item Two \item Three \stopenumerate Multiple paragraphs: \startenumerate[n][stopper=.] \item Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. \item Item 2. \item Item 3. \stopenumerate \stopsectionlevel \startsectionlevel[title={Nested},reference={nested}] \startitemize[packed] \item Tab \startitemize[packed] \item Tab \startitemize[packed] \item Tab \stopitemize \stopitemize \stopitemize Here's another: \startenumerate[n,packed][stopper=.] \item First \item Second: \startitemize[packed] \item Fee \item Fie \item Foe \stopitemize \item Third \stopenumerate Same thing but with paragraphs: \startenumerate[n][stopper=.] \item First \item Second: \startitemize[packed] \item Fee \item Fie \item Foe \stopitemize \item Third \stopenumerate \stopsectionlevel \startsectionlevel[title={Tabs and spaces},reference={tabs-and-spaces}] \startitemize \item this is a list item indented with tabs \item this is a list item indented with spaces \startitemize \item this is an example list item indented with tabs \item this is an example list item indented with spaces \stopitemize \stopitemize \stopsectionlevel \startsectionlevel[title={Fancy list markers},reference={fancy-list-markers}] \startenumerate[n][start=2,left=(,stopper=)] \item begins with 2 \item and now 3 with a continuation \startenumerate[r,packed][start=4,stopper=.] \item sublist with roman numerals, starting with 4 \item more items \startenumerate[A,packed][left=(,stopper=)] \item a subsublist \item a subsublist \stopenumerate \stopenumerate \stopenumerate Nesting: \startenumerate[A,packed][stopper=.] \item Upper Alpha \startenumerate[R,packed][stopper=.] \item Upper Roman. \startenumerate[n,packed][start=6,left=(,stopper=)] \item Decimal start with 6 \startenumerate[a,packed][start=3,stopper=)] \item Lower alpha with paren \stopenumerate \stopenumerate \stopenumerate \stopenumerate Autonumbering: \startenumerate[n,packed] \item Autonumber. \item More. \startenumerate[a,packed] \item Nested. \stopenumerate \stopenumerate Should not be a list item: M.A.~2007 B. Williams \thinrule \stopsectionlevel \stopsectionlevel \startsectionlevel[title={Definition Lists},reference={definition-lists}] Tight using spaces: \startdescription{apple} red fruit \stopdescription \startdescription{orange} orange fruit \stopdescription \startdescription{banana} yellow fruit \stopdescription Tight using tabs: \startdescription{apple} red fruit \stopdescription \startdescription{orange} orange fruit \stopdescription \startdescription{banana} yellow fruit \stopdescription Loose: \startdescription{apple} red fruit \stopdescription \startdescription{orange} orange fruit \stopdescription \startdescription{banana} yellow fruit \stopdescription Multiple blocks with italics: \startdescription{{\em apple}} red fruit contains seeds, crisp, pleasant to taste \stopdescription \startdescription{{\em orange}} orange fruit \starttyping { orange code block } \stoptyping \startblockquote orange block quote \stopblockquote \stopdescription Multiple definitions, tight: \startdescription{apple} red fruit computer \stopdescription \startdescription{orange} orange fruit bank \stopdescription Multiple definitions, loose: \startdescription{apple} red fruit computer \stopdescription \startdescription{orange} orange fruit bank \stopdescription Blank line after term, indented marker, alternate markers: \startdescription{apple} red fruit computer \stopdescription \startdescription{orange} orange fruit \startenumerate[n,packed][stopper=.] \item sublist \item sublist \stopenumerate \stopdescription \stopsectionlevel \startsectionlevel[title={HTML Blocks},reference={html-blocks}] Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is {\em emphasized} And this is {\bf strong} Here's a simple block: foo This should be a code block, though: \starttyping
                foo
                \stoptyping As should this: \starttyping
                foo
                \stoptyping Now, nested: foo This should just be an HTML comment: Multiline: Code block: \starttyping \stoptyping Just plain comment, with trailing spaces on the line: Code: \starttyping
                \stoptyping Hr's: \thinrule \stopsectionlevel \startsectionlevel[title={Inline Markup},reference={inline-markup}] This is {\em emphasized}, and so {\em is this}. This is {\bf strong}, and so {\bf is this}. An {\em \goto{emphasized link}[url(/url)]}. {\bf {\em This is strong and em.}} So is {\bf {\em this}} word. {\bf {\em This is strong and em.}} So is {\bf {\em this}} word. This is code: \type{>}, \type{$}, \type{\}, \type{\$}, \type{}. \overstrikes{This is {\em strikeout}.} Superscripts: a\high{bc}d a\high{{\em hello}} a\high{hello~there}. Subscripts: H\low{2}O, H\low{23}O, H\low{many~of~them}O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a\lettertilde{}b c\lettertilde{}d. \thinrule \stopsectionlevel \startsectionlevel[title={Smart quotes, ellipses, dashes},reference={smart-quotes-ellipses-dashes}] \quotation{Hello,} said the spider. \quotation{\quote{Shelob} is my name.} \quote{A}, \quote{B}, and \quote{C} are letters. \quote{Oak,} \quote{elm,} and \quote{beech} are names of trees. So is \quote{pine.} \quote{He said, \quotation{I want to go.}} Were you alive in the 70's? Here is some quoted \quote{\type{code}} and a \quotation{\goto{quoted link}[url(http://example.com/?foo=1&bar=2)]}. Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses\ldots{}and\ldots{}and\ldots{}. \thinrule \stopsectionlevel \startsectionlevel[title={LaTeX},reference={latex}] \startitemize[packed] \item \cite[22-23]{smith.1899} \item $2+2=4$ \item $x \in y$ \item $\alpha \wedge \omega$ \item $223$ \item $p$-Tree \item Here's some display math: \startformula \frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h} \stopformula \item Here's one that has a line break in it: $\alpha + \omega \times x^2$. \stopitemize These shouldn't be math: \startitemize[packed] \item To get the famous equation, write \type{$e = mc^2$}. \item \$22,000 is a {\em lot} of money. So is \$34,000. (It worked if \quotation{lot} is emphasized.) \item Shoes (\$20) and socks (\$5). \item Escaped \type{$}: \$73 {\em this should be emphasized} 23\$. \stopitemize Here's a LaTeX table: \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} \thinrule \stopsectionlevel \startsectionlevel[title={Special Characters},reference={special-characters}] Here is some unicode: \startitemize[packed] \item I hat: Î \item o umlaut: ö \item section: § \item set membership: ∈ \item copyright: © \stopitemize AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \letterbackslash{} Backtick: ` Asterisk: * Underscore: _ Left brace: \{ Right brace: \} Left bracket: {[} Right bracket: {]} Left paren: ( Right paren: ) Greater-than: > Hash: \# Period: . Bang: ! Plus: + Minus: - \thinrule \stopsectionlevel \startsectionlevel[title={Links},reference={links}] \startsectionlevel[title={Explicit},reference={explicit}] Just a \goto{URL}[url(/url/)]. \goto{URL and title}[url(/url/)]. \goto{URL and title}[url(/url/)]. \goto{URL and title}[url(/url/)]. \goto{URL and title}[url(/url/)] \goto{URL and title}[url(/url/)] \goto{with_underscore}[url(/url/with_underscore)] \goto{Email link}[url(mailto:nobody@nowhere.net)] \goto{Empty}[url()]. \stopsectionlevel \startsectionlevel[title={Reference},reference={reference}] Foo \goto{bar}[url(/url/)]. With \goto{embedded {[}brackets{]}}[url(/url/)]. \goto{b}[url(/url/)] by itself should be a link. Indented \goto{once}[url(/url)]. Indented \goto{twice}[url(/url)]. Indented \goto{thrice}[url(/url)]. This should {[}not{]}{[}{]} be a link. \starttyping [not]: /url \stoptyping Foo \goto{bar}[url(/url/)]. Foo \goto{biz}[url(/url/)]. \stopsectionlevel \startsectionlevel[title={With ampersands},reference={with-ampersands}] Here's a \goto{link with an ampersand in the URL}[url(http://example.com/?foo=1&bar=2)]. Here's a link with an amersand in the link text: \goto{AT&T}[url(http://att.com/)]. Here's an \goto{inline link}[url(/script?foo=1&bar=2)]. Here's an \goto{inline link in pointy braces}[url(/script?foo=1&bar=2)]. \stopsectionlevel \startsectionlevel[title={Autolinks},reference={autolinks}] With an ampersand: \useURL[url1][http://example.com/?foo=1&bar=2]\from[url1] \startitemize[packed] \item In a list? \item \useURL[url2][http://example.com/]\from[url2] \item It should. \stopitemize An e-mail address: \goto{nobody@nowhere.net}[url(mailto:nobody@nowhere.net)] \startblockquote Blockquoted: \useURL[url3][http://example.com/]\from[url3] \stopblockquote Auto-links should not occur here: \type{} \starttyping or here: \stoptyping \thinrule \stopsectionlevel \stopsectionlevel \startsectionlevel[title={Images},reference={images}] From \quotation{Voyage dans la Lune} by Georges Melies (1902): \startplacefigure[title={lalune}] {\externalfigure[lalune.jpg]} \stopplacefigure Here is a movie {\externalfigure[movie.jpg]} icon. \thinrule \stopsectionlevel \startsectionlevel[title={Footnotes},reference={footnotes}] Here is a footnote reference,\footnote{Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.} and another.\startbuffer Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). \starttyping { } \stoptyping If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.\stopbuffer\footnote{\getbuffer} This should {\em not} be a footnote reference, because it contains a space.{[}^my note{]} Here is an inline note.\footnote{This is {\em easier} to type. Inline notes may contain \goto{links}[url(http://google.com)] and \type{]} verbatim characters, as well as {[}bracketed text{]}.} \startblockquote Notes can go in quotes.\footnote{In quote.} \stopblockquote \startenumerate[n,packed][stopper=.] \item And in list items.\footnote{In list.} \stopenumerate This paragraph should not be part of the note, as it is not indented. \stopsectionlevel \stoptext ================================================ FILE: test/writer.djot ================================================ # Pandoc Test Suite John MacFarlane Anonymous July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. * * * * {#headers} # Headers {#level-2-with-an-embedded-link} ## Level 2 with an [embedded link](/url) {#level-3-with-emphasis} ### Level 3 with _emphasis_ {#level-4} #### Level 4 {#level-5} ##### Level 5 {#level-1} # Level 1 {#level-2-with-emphasis} ## Level 2 with _emphasis_ {#level-3} ### Level 3 with no blank line {#level-2} ## Level 2 with no blank line * * * * {#paragraphs} # Paragraphs Here's a regular paragraph. 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 a list item. Here's one with a bullet. \* criminey. There should be a hard line break\ here. * * * * {#block-quotes} # Block Quotes E-mail style: > This is a block quote. It is pretty short. > Code in a block quote: > > ``` > sub status { > print "working"; > } > ``` > > A list: > > 1. item one > 2. item two > > Nested block quotes: > > > nested > > > nested This should not be a block quote: 2 \> 1. And a following paragraph. * * * * {#code-blocks} # Code Blocks Code: ``` ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab ``` And: ``` this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ ``` * * * * {#lists} # Lists {#unordered} ## Unordered Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 {#ordered} ## Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. {#nested} ## Nested - Tab - Tab - Tab Here's another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third {#tabs-and-spaces} ## Tabs and spaces - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces {#fancy-list-markers} ## Fancy list markers (2) begins with 2 (3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items (A) a subsublist (B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. (6) Decimal start with 6 c) Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams * * * * {#definition-lists} # Definition Lists Tight using spaces: : apple red fruit : orange orange fruit : banana yellow fruit Tight using tabs: : apple red fruit : orange orange fruit : banana yellow fruit Loose: : apple red fruit : orange orange fruit : banana yellow fruit Multiple blocks with italics: : _apple_ red fruit contains seeds, crisp, pleasant to taste : _orange_ orange fruit ``` { orange code block } ``` > orange block quote Multiple definitions, tight: : apple red fruit computer : orange orange fruit bank Multiple definitions, loose: : apple red fruit computer : orange orange fruit bank Blank line after term, indented marker, alternate markers: : apple red fruit computer : orange orange fruit 1. sublist 2. sublist {#html-blocks} # HTML Blocks Simple block on one line: ::: foo ::: And nested without indentation: :::::: :::: ::: foo ::: :::: ::: bar ::: :::::: Interpreted markdown in a table: This is _emphasized_ And this is *strong* Here's a simple block: ::: foo ::: This should be a code block, though: ```
                foo
                ``` As should this: ```
                foo
                ``` Now, nested: ::::: :::: ::: foo ::: :::: ::::: This should just be an HTML comment: Multiline: Code block: ``` ``` Just plain comment, with trailing spaces on the line: Code: ```
                ``` Hr's: * * * * {#inline-markup} # Inline Markup This is _emphasized_, and so _is this_. This is *strong*, and so *is this*. An _[emphasized link](/url)_. *_This is strong and em._* So is *_this_* word. *_This is strong and em._* So is *_this_* word. This is code: `>`, `$`, `\`, `\$`, ``. {-This is _strikeout_.-} Superscripts: a^bc^d a^_hello_^ a^hello there^. Subscripts: H~2~O, H~23~O, H~many of them~O. These should not be superscripts or subscripts, because of the unescaped spaces: a\^b c\^d, a\~b c\~d. * * * * {#smart-quotes-ellipses-dashes} # Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70's? Here is some quoted '`code`' and a "[quoted link](http://example.com/?foo=1&bar=2)". Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses...and...and.... * * * * {#latex} # LaTeX - - $`2+2=4` - $`x \in y` - $`\alpha \wedge \omega` - $`223` - $`p`-Tree - Here's some display math: $$`\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}` - Here's one that has a line break in it: $`\alpha + \omega \times x^2`. These shouldn't be math: - To get the famous equation, write `$e = mc^2$`. - $22,000 is a _lot_ of money. So is $34,000. (It worked if "lot" is emphasized.) - Shoes ($20) and socks ($5). - Escaped `$`\: $73 _this should be emphasized_ 23$. Here's a LaTeX table: * * * * {#special-characters} # Special Characters Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 \< 5. 6 \> 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: ( Right paren: ) Greater-than: \> Hash: # Period: . Bang: \! Plus: + Minus: - * * * * {#links} # Links {#explicit} ## Explicit Just a [URL](/url/). [URL and title](/url/){title="title"}. [URL and title](/url/){title="title preceded by two spaces"}. [URL and title](/url/){title="title preceded by a tab"}. [URL and title](/url/){title="title with \"quotes\" in it"} [URL and title](/url/){title="title with single quotes"} [with\_underscore](/url/with_underscore) [Email link](mailto:nobody@nowhere.net) [Empty](). {#reference} ## Reference Foo [bar](/url/). With [embedded \[brackets\]](/url/). [b](/url/) by itself should be a link. Indented [once](/url). Indented [twice](/url). Indented [thrice](/url). This should \[not\]\[\] be a link. ``` [not]: /url ``` Foo [bar](/url/){title="Title with \"quotes\" inside"}. Foo [biz](/url/){title="Title with \"quote\" inside"}. {#with-ampersands} ## With ampersands Here's a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2). Here's a link with an amersand in the link text: [AT&T](http://att.com/){title="AT&T"}. Here's an [inline link](/script?foo=1&bar=2). Here's an [inline link in pointy braces](/script?foo=1&bar=2). {#autolinks} ## Autolinks With an ampersand: - In a list? - - It should. An e-mail address: > Blockquoted: Auto-links should not occur here: `` ``` or here: ``` * * * * {#images} # Images From "Voyage dans la Lune" by Georges Melies (1902): :::: ![lalune](lalune.jpg){title="Voyage dans la Lune"} {.caption} ::: lalune ::: :::: Here is a movie ![movie](movie.jpg) icon. * * * * {#footnotes} # Footnotes Here is a footnote reference,[^1] and another.[^2] This should _not_ be a footnote reference, because it contains a space.\[\^my note\] Here is an inline note.[^3] > Notes can go in quotes.[^4] 1. And in list items.[^5] This paragraph should not be part of the note, as it is not indented. [^1]: Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. [^2]: Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). ``` { } ``` If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. [^3]: This is _easier_ to type. Inline notes may contain [links](http://google.com) and `]` verbatim characters, as well as \[bracketed text\]. [^4]: In quote. [^5]: In list. ================================================ FILE: test/writer.docbook4 ================================================
                Pandoc Test Suite John MacFarlane Anonymous July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. Headers Level 2 with an <ulink url="/url">embedded link</ulink> Level 3 with <emphasis>emphasis</emphasis> Level 4 Level 5 Level 1 Level 2 with <emphasis>emphasis</emphasis> Level 3 with no blank line Level 2 with no blank line Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break here. Block Quotes E-mail style:
                This is a block quote. It is pretty short.
                Code in a block quote: sub status { print "working"; } A list: item one item two Nested block quotes:
                nested
                nested
                This should not be a block quote: 2 > 1. And a following paragraph.
                Code Blocks Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ Lists Unordered Asterisks tight: asterisk 1 asterisk 2 asterisk 3 Asterisks loose: asterisk 1 asterisk 2 asterisk 3 Pluses tight: Plus 1 Plus 2 Plus 3 Pluses loose: Plus 1 Plus 2 Plus 3 Minuses tight: Minus 1 Minus 2 Minus 3 Minuses loose: Minus 1 Minus 2 Minus 3 Ordered Tight: First Second Third and: One Two Three Loose using tabs: First Second Third and using spaces: One Two Three Multiple paragraphs: Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. Item 2. Item 3. Nested Tab Tab Tab Here’s another: First Second: Fee Fie Foe Third Same thing but with paragraphs: First Second: Fee Fie Foe Third Tabs and spaces this is a list item indented with tabs this is a list item indented with spaces this is an example list item indented with tabs this is an example list item indented with spaces Fancy list markers begins with 2 and now 3 with a continuation sublist with roman numerals, starting with 4 more items a subsublist a subsublist Nesting: Upper Alpha Upper Roman. Decimal start with 6 Lower alpha with paren Autonumbering: Autonumber. More. Nested. Should not be a list item: M.A. 2007 B. Williams Definition Lists Tight using spaces: apple red fruit orange orange fruit banana yellow fruit Tight using tabs: apple red fruit orange orange fruit banana yellow fruit Loose: apple red fruit orange orange fruit banana yellow fruit Multiple blocks with italics: apple red fruit contains seeds, crisp, pleasant to taste orange orange fruit { orange code block }
                orange block quote
                Multiple definitions, tight: apple red fruit computer orange orange fruit bank Multiple definitions, loose: apple red fruit computer orange orange fruit bank Blank line after term, indented marker, alternate markers: apple red fruit computer orange orange fruit sublist sublist
                HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table:
                This is emphasized And this is strong
                Here’s a simple block: foo This should be a code block, though: <div> foo </div> As should this: <div>foo</div> Now, nested: foo This should just be an HTML comment: Multiline: Code block: <!-- Comment --> Just plain comment, with trailing spaces on the line: Code: <hr /> Hr’s:








                Inline Markup This is emphasized, and so is this. This is strong, and so is this. An emphasized link. This is strong and em. So is this word. This is strong and em. So is this word. This is code: >, $, \, \$, <html>. This is strikeout. Superscripts: abcd ahello ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. Smart quotes, ellipses, dashes Hello, said the spider. Shelob is my name. A, B, and C are letters. Oak, elm, and beech are names of trees. So is pine. He said, I want to go. Were you alive in the 70’s? Here is some quoted code and a quoted link. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. LaTeX 2 + 2 = 4 x ∈ y α ∧ ω 223 p-Tree Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ Here’s one that has a line break in it: α + ω × x2. These shouldn’t be math: To get the famous equation, write $e = mc^2$. $22,000 is a lot of money. So is $34,000. (It worked if lot is emphasized.) Shoes ($20) and socks ($5). Escaped $: $73 this should be emphasized 23$. Here’s a LaTeX table: Special Characters Here is some unicode: I hat: Î o umlaut: ö section: § set membership: ∈ copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - Links Explicit Just a URL. URL and title. URL and title. URL and title. URL and title URL and title with_underscore Email link (nobody@nowhere.net) Empty. Reference Foo bar. With embedded [brackets]. b by itself should be a link. Indented once. Indented twice. Indented thrice. This should [not][] be a link. [not]: /url Foo bar. Foo biz. With ampersands Here’s a link with an ampersand in the URL. Here’s a link with an amersand in the link text: AT&T. Here’s an inline link. Here’s an inline link in pointy braces. Autolinks With an ampersand: http://example.com/?foo=1&bar=2 In a list? http://example.com/ It should. An e-mail address: nobody@nowhere.net
                Blockquoted: http://example.com/
                Auto-links should not occur here: <http://example.com/> or here: <http://example.com/>
                Images From Voyage dans la Lune by Georges Melies (1902):
                lalune lalune
                Here is a movie movie icon.
                Footnotes Here is a footnote reference, Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. and another. Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). { <code> } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].
                Notes can go in quotes. In quote.
                And in list items. In list. This paragraph should not be part of the note, as it is not indented.
                ================================================ FILE: test/writer.docbook5 ================================================
                Pandoc Test Suite John MacFarlane Anonymous July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.
                Headers
                Level 2 with an <link xlink:href="/url">embedded link</link>
                Level 3 with <emphasis>emphasis</emphasis>
                Level 4
                Level 5
                Level 1
                Level 2 with <emphasis>emphasis</emphasis>
                Level 3 with no blank line
                Level 2 with no blank line
                Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break here.
                Block Quotes E-mail style:
                This is a block quote. It is pretty short.
                Code in a block quote: sub status { print "working"; } A list: item one item two Nested block quotes:
                nested
                nested
                This should not be a block quote: 2 > 1. And a following paragraph.
                Code Blocks Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{
                Lists
                Unordered Asterisks tight: asterisk 1 asterisk 2 asterisk 3 Asterisks loose: asterisk 1 asterisk 2 asterisk 3 Pluses tight: Plus 1 Plus 2 Plus 3 Pluses loose: Plus 1 Plus 2 Plus 3 Minuses tight: Minus 1 Minus 2 Minus 3 Minuses loose: Minus 1 Minus 2 Minus 3
                Ordered Tight: First Second Third and: One Two Three Loose using tabs: First Second Third and using spaces: One Two Three Multiple paragraphs: Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. Item 2. Item 3.
                Nested Tab Tab Tab Here’s another: First Second: Fee Fie Foe Third Same thing but with paragraphs: First Second: Fee Fie Foe Third
                Tabs and spaces this is a list item indented with tabs this is a list item indented with spaces this is an example list item indented with tabs this is an example list item indented with spaces
                Fancy list markers begins with 2 and now 3 with a continuation sublist with roman numerals, starting with 4 more items a subsublist a subsublist Nesting: Upper Alpha Upper Roman. Decimal start with 6 Lower alpha with paren Autonumbering: Autonumber. More. Nested. Should not be a list item: M.A. 2007 B. Williams
                Definition Lists Tight using spaces: apple red fruit orange orange fruit banana yellow fruit Tight using tabs: apple red fruit orange orange fruit banana yellow fruit Loose: apple red fruit orange orange fruit banana yellow fruit Multiple blocks with italics: apple red fruit contains seeds, crisp, pleasant to taste orange orange fruit { orange code block }
                orange block quote
                Multiple definitions, tight: apple red fruit computer orange orange fruit bank Multiple definitions, loose: apple red fruit computer orange orange fruit bank Blank line after term, indented marker, alternate markers: apple red fruit computer orange orange fruit sublist sublist
                HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is emphasized And this is strong Here’s a simple block: foo This should be a code block, though: <div> foo </div> As should this: <div>foo</div> Now, nested: foo This should just be an HTML comment: Multiline: Code block: <!-- Comment --> Just plain comment, with trailing spaces on the line: Code: <hr /> Hr’s:
                Inline Markup This is emphasized, and so is this. This is strong, and so is this. An emphasized link. This is strong and em. So is this word. This is strong and em. So is this word. This is code: >, $, \, \$, <html>. This is strikeout. Superscripts: abcd ahello ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.
                Smart quotes, ellipses, dashes Hello, said the spider. Shelob is my name. A, B, and C are letters. Oak, elm, and beech are names of trees. So is pine. He said, I want to go. Were you alive in the 70’s? Here is some quoted code and a quoted link. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and….
                LaTeX 2 + 2 = 4 x ∈ y α ∧ ω 223 p-Tree Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ Here’s one that has a line break in it: α + ω × x2. These shouldn’t be math: To get the famous equation, write $e = mc^2$. $22,000 is a lot of money. So is $34,000. (It worked if lot is emphasized.) Shoes ($20) and socks ($5). Escaped $: $73 this should be emphasized 23$. Here’s a LaTeX table:
                Special Characters Here is some unicode: I hat: Î o umlaut: ö section: § set membership: ∈ copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: -
                Links
                Explicit Just a URL. URL and title. URL and title. URL and title. URL and title URL and title with_underscore Email link (nobody@nowhere.net) Empty.
                Reference Foo bar. With embedded [brackets]. b by itself should be a link. Indented once. Indented twice. Indented thrice. This should [not][] be a link. [not]: /url Foo bar. Foo biz.
                With ampersands Here’s a link with an ampersand in the URL. Here’s a link with an amersand in the link text: AT&T. Here’s an inline link. Here’s an inline link in pointy braces.
                Autolinks With an ampersand: http://example.com/?foo=1&bar=2 In a list? http://example.com/ It should. An e-mail address: nobody@nowhere.net
                Blockquoted: http://example.com/
                Auto-links should not occur here: <http://example.com/> or here: <http://example.com/>
                Images From Voyage dans la Lune by Georges Melies (1902):
                lalune lalune
                Here is a movie movie icon.
                Footnotes Here is a footnote reference, Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. and another. Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). { <code> } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].
                Notes can go in quotes. In quote.
                And in list items. In list. This paragraph should not be part of the note, as it is not indented.
                ================================================ FILE: test/writer.dokuwiki ================================================ This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ---- ====== Headers ====== ===== Level 2 with an embedded link ===== ==== Level 3 with emphasis ==== === Level 4 === == Level 5 == ====== Level 1 ====== ===== Level 2 with emphasis ===== ==== Level 3 ==== with no blank line ===== Level 2 ===== with no blank line ---- ====== Paragraphs ====== Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break\\ here. ---- ====== Block Quotes ====== E-mail style: > This is a block quote. It is pretty short. > Code in a block quote: > sub status { print "working"; } > A list: > - item one > - item two > Nested block quotes: >> nested >> nested This should not be a block quote: 2 > 1. And a following paragraph. ---- ====== Code Blocks ====== Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ ---- ====== Lists ====== ===== Unordered ===== Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: * Plus 1 * Plus 2 * Plus 3 Pluses loose: * Plus 1 * Plus 2 * Plus 3 Minuses tight: * Minus 1 * Minus 2 * Minus 3 Minuses loose: * Minus 1 * Minus 2 * Minus 3 ===== Ordered ===== Tight: - First - Second - Third and: - One - Two - Three Loose using tabs: - First - Second - Third and using spaces: - One - Two - Three Multiple paragraphs: - Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. - Item 2. - Item 3. ===== Nested ===== * Tab * Tab * Tab Here’s another: - First - Second: * Fee * Fie * Foe - Third Same thing but with paragraphs: - First - Second: * Fee * Fie * Foe - Third ===== Tabs and spaces ===== * this is a list item indented with tabs * this is a list item indented with spaces * this is an example list item indented with tabs * this is an example list item indented with spaces ===== Fancy list markers ===== - begins with 2 - and now 3 with a continuation - sublist with roman numerals, starting with 4 - more items - a subsublist - a subsublist Nesting: - Upper Alpha - Upper Roman. - Decimal start with 6 - Lower alpha with paren Autonumbering: - Autonumber. - More. - Nested. Should not be a list item: M.A. 2007 B. Williams ---- ====== Definition Lists ====== Tight using spaces: * **apple** red fruit * **orange** orange fruit * **banana** yellow fruit Tight using tabs: * **apple** red fruit * **orange** orange fruit * **banana** yellow fruit Loose: * **apple** red fruit * **orange** orange fruit * **banana** yellow fruit Multiple blocks with italics: * **//apple//** red fruit contains seeds, crisp, pleasant to taste * **//orange//** orange fruit { orange code block } > orange block quote Multiple definitions, tight: * **apple** red fruit; computer * **orange** orange fruit; bank Multiple definitions, loose: * **apple** red fruit; computer * **orange** orange fruit; bank Blank line after term, indented marker, alternate markers: * **apple** red fruit; computer * **orange** orange fruit - sublist - sublist ====== HTML Blocks ====== Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is //emphasized// And this is **strong** Here’s a simple block: foo This should be a code block, though:
                foo
                As should this:
                foo
                Now, nested: foo This should just be an HTML comment: Multiline: Code block: Just plain comment, with trailing spaces on the line: Code:
                Hr’s: ---- ====== Inline Markup ====== This is //emphasized//, and so //is this//. This is **strong**, and so **is this**. An //[[url|emphasized link]]//. **//This is strong and em.//** So is **//this//** word. **//This is strong and em.//** So is **//this//** word. This is code: ''%%>%%'', ''%%$%%'', ''%%\%%'', ''%%\$%%'', ''%%%%''. This is //strikeout//. Superscripts: abcd a//hello// ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ---- ====== Smart quotes, ellipses, dashes ====== “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘''%%code%%''’ and a “[[http://example.com/?foo=1&bar=2|quoted link]]”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ---- ====== LaTeX ====== * * $2+2=4$ * $x \in y$ * $\alpha \wedge \omega$ * $223$ * $p$-Tree * Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ * Here’s one that has a line break in it: $\alpha + \omega \times x^2$. These shouldn’t be math: * To get the famous equation, write ''%%$e = mc^2$%%''. * $22,000 is a //lot// of money. So is $34,000. (It worked if “lot” is emphasized.) * Shoes ($20) and socks ($5). * Escaped ''%%$%%'': $73 //this should be emphasized// 23$. Here’s a LaTeX table: ---- ====== Special Characters ====== Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - ---- ====== Links ====== ===== Explicit ===== Just a [[url/|URL]]. [[url/|URL and title]]. [[url/|URL and title]]. [[url/|URL and title]]. [[url/|URL and title]] [[url/|URL and title]] [[url/with_underscore|with_underscore]] [[mailto:nobody@nowhere.net|Email link]] [[|Empty]]. ===== Reference ===== Foo [[url/|bar]]. With [[url/|embedded [brackets]]]. [[url/|b]] by itself should be a link. Indented [[url|once]]. Indented [[url|twice]]. Indented [[url|thrice]]. This should [not][] be a link. [not]: /url Foo [[url/|bar]]. Foo [[url/|biz]]. ===== With ampersands ===== Here’s a [[http://example.com/?foo=1&bar=2|link with an ampersand in the URL]]. Here’s a link with an amersand in the link text: [[http://att.com/|AT&T]]. Here’s an [[script?foo=1&bar=2|inline link]]. Here’s an [[script?foo=1&bar=2|inline link in pointy braces]]. ===== Autolinks ===== With an ampersand: http://example.com/?foo=1&bar=2 * In a list? * http://example.com/ * It should. An e-mail address: > Blockquoted: http://example.com/ Auto-links should not occur here: ''%%%%'' or here: ---- ====== Images ====== From “Voyage dans la Lune” by Georges Melies (1902): {{lalune.jpg|Voyage dans la Lune}} lalune Here is a movie {{movie.jpg|movie}} icon. ---- ====== Footnotes ====== Here is a footnote reference,((Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. )) and another.((Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). { } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. )) This should //not// be a footnote reference, because it contains a space.[^my note] Here is an inline note.((This is //easier// to type. Inline notes may contain [[http://google.com|links]] and ''%%]%%'' verbatim characters, as well as [bracketed text]. )) > Notes can go in quotes.((In quote. )) - And in list items.((In list.)) This paragraph should not be part of the note, as it is not indented. ================================================ FILE: test/writer.fb2 ================================================ unrecognised John MacFarlane Anonymous Pandoc Test Suite July 17, 2006 pandoc <p>Pandoc Test Suite</p>

                This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.

                <p>Headers</p>
                <p>Level 1</p>
                <p>Level 2 with <emphasis>emphasis</emphasis> </p>
                <p>Level 3</p>

                with no blank line

                <p>Level 2</p>

                with no blank line

                <p>Paragraphs</p>

                Here’s a regular paragraph.

                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 a list item.

                Here’s one with a bullet. * criminey.

                There should be a hard line break here.

                <p>Block Quotes</p>

                E-mail style:

                This is a block quote. It is pretty short.

                Code in a block quote:

                sub status {

                print "working";

                }

                A list:

                1. item one

                2. item two

                Nested block quotes:

                nested

                nested

                This should not be a block quote: 2 > 1.

                And a following paragraph.

                <p>Code Blocks</p>

                Code:

                ---- (should be four hyphens)

                sub status {

                print "working";

                }

                this code block is indented by one tab

                And:

                this code block is indented by two tabs

                These should not be escaped: \$ \\ \> \[ \{

                <p>Lists</p>
                <p>Unordered</p>

                Asterisks tight:

                • asterisk 1

                • asterisk 2

                • asterisk 3

                Asterisks loose:

                • asterisk 1

                • asterisk 2

                • asterisk 3

                Pluses tight:

                • Plus 1

                • Plus 2

                • Plus 3

                Pluses loose:

                • Plus 1

                • Plus 2

                • Plus 3

                Minuses tight:

                • Minus 1

                • Minus 2

                • Minus 3

                Minuses loose:

                • Minus 1

                • Minus 2

                • Minus 3

                <p>Ordered</p>

                Tight:

                1. First

                2. Second

                3. Third

                and:

                1. One

                2. Two

                3. Three

                Loose using tabs:

                1. First

                2. Second

                3. Third

                and using spaces:

                1. One

                2. Two

                3. Three

                Multiple paragraphs:

                1. Item 1, graf one.

                   Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

                2. Item 2.

                3. Item 3.

                <p>Nested</p>

                • Tab

                • • Tab

                • • • Tab

                Here’s another:

                1. First

                2. Second:

                2. • Fee

                2. • Fie

                2. • Foe

                3. Third

                Same thing but with paragraphs:

                1. First

                2. Second:

                2. • Fee

                2. • Fie

                2. • Foe

                3. Third

                <p>Tabs and spaces</p>

                • this is a list item indented with tabs

                • this is a list item indented with spaces

                • • this is an example list item indented with tabs

                • • this is an example list item indented with spaces

                <p>Fancy list markers</p>

                (2) begins with 2

                (3) and now 3

                    with a continuation

                (3) iv. sublist with roman numerals, starting with 4

                (3) v. more items

                (3) v. (A) a subsublist

                (3) v. (B) a subsublist

                Nesting:

                A. Upper Alpha

                A. I. Upper Roman.

                A. I. (6) Decimal start with 6

                A. I. (6) c) Lower alpha with paren

                Autonumbering:

                1. Autonumber.

                2. More.

                2. 1. Nested.

                Should not be a list item:

                M.A. 2007

                B. Williams

                <p>Definition Lists</p>

                Tight using spaces:

                apple

                    red fruit

                orange

                    orange fruit

                banana

                    yellow fruit

                Tight using tabs:

                apple

                    red fruit

                orange

                    orange fruit

                banana

                    yellow fruit

                Loose:

                apple

                    red fruit

                orange

                    orange fruit

                banana

                    yellow fruit

                Multiple blocks with italics:

                apple

                    red fruit

                    contains seeds, crisp, pleasant to taste

                orange

                    orange fruit

                    { orange code block }

                    orange block quote

                Multiple definitions, tight:

                apple

                    red fruit

                    computer

                orange

                    orange fruit

                    bank

                Multiple definitions, loose:

                apple

                    red fruit

                    computer

                orange

                    orange fruit

                    bank

                Blank line after term, indented marker, alternate markers:

                apple

                    red fruit

                    computer

                orange

                    orange fruit

                1. sublist

                2. sublist

                <p>HTML Blocks</p>

                Simple block on one line:

                foo

                And nested without indentation:

                foo

                bar

                Interpreted markdown in a table:

                This is emphasizedAnd this is strong

                Here’s a simple block:

                foo

                This should be a code block, though:

                <div>

                foo

                </div>

                As should this:

                <div>foo</div>

                Now, nested:

                foo

                This should just be an HTML comment:

                Multiline:

                Code block:

                <!-- Comment -->

                Just plain comment, with trailing spaces on the line:

                Code:

                <hr />

                Hr’s:

                <p>Inline Markup</p>

                This is emphasized, and so is this.

                This is strong, and so is this.

                An emphasized link .

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is strikeout.

                Superscripts: abcd a hello ahello there.

                Subscripts: H2O, H23O, Hmany of themO.

                These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.

                <p>Smart quotes, ellipses, dashes</p>

                “Hello,” said the spider. “‘Shelob’ is my name.”

                ‘A’, ‘B’, and ‘C’ are letters.

                ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

                ‘He said, “I want to go.”’ Were you alive in the 70’s?

                Here is some quoted ‘code’ and a “quoted link”.

                Some dashes: one—two — three—four — five.

                Dashes between numbers: 5–7, 255–66, 1987–1999.

                Ellipses…and…and….

                <p>LaTeX</p>

                • 

                • 2+2=4

                • x \in y

                • \alpha \wedge \omega

                • 223

                • p-Tree

                • Here’s some display math: \frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}

                • Here’s one that has a line break in it: \alpha + \omega \times x^2.

                These shouldn’t be math:

                • To get the famous equation, write $e = mc^2$.

                • $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)

                • Shoes ($20) and socks ($5).

                • Escaped $: $73 this should be emphasized 23$.

                Here’s a LaTeX table:

                <p>Special Characters</p>

                Here is some unicode:

                • I hat: Î

                • o umlaut: ö

                • section: §

                • set membership: ∈

                • copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -

                <p>Images</p>

                From “Voyage dans la Lune” by Georges Melies (1902):

                lalune

                Here is a movie movie icon.

                <p>Footnotes</p>

                Here is a footnote reference,[1] and another.[2] This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.[3]

                Notes can go in quotes.[4]

                1. And in list items.[5]

                This paragraph should not be part of the note, as it is not indented.

                <p>1</p>

                Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

                <p>2</p>

                Here’s the long note. This one contains multiple blocks.

                Subsequent blocks are indented to show that they belong to the footnote (as with list items).

                { <code> }

                If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

                <p>3</p>

                This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

                <p>4</p>

                In quote.

                <p>5</p>

                In list.

                ================================================ FILE: test/writer.haddock ================================================ This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ________________________________________________________________________________ = Headers #headers# == Level 2 with an #level-2-with-an-embedded-link# === Level 3 with /emphasis/ #level-3-with-emphasis# ==== Level 4 #level-4# ===== Level 5 #level-5# = Level 1 #level-1# == Level 2 with /emphasis/ #level-2-with-emphasis# === Level 3 #level-3# with no blank line == Level 2 #level-2# with no blank line ________________________________________________________________________________ = Paragraphs #paragraphs# Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break here. ________________________________________________________________________________ = Block Quotes #block-quotes# E-mail style: This is a block quote. It is pretty short. Code in a block quote: > sub status { > print "working"; > } A list: 1. item one 2. item two Nested block quotes: nested nested This should not be a block quote: 2 > 1. And a following paragraph. ________________________________________________________________________________ = Code Blocks #code-blocks# Code: > ---- (should be four hyphens) > > sub status { > print "working"; > } > > this code block is indented by one tab And: > this code block is indented by two tabs > > These should not be escaped: \$ \\ \> \[ \{ ________________________________________________________________________________ = Lists #lists# == Unordered #unordered# Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 == Ordered #ordered# Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. 2. Item 2. 3. Item 3. == Nested #nested# - Tab - Tab - Tab Here’s another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third == Tabs and spaces #tabs-and-spaces# - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces == Fancy list markers #fancy-list-markers# (2) begins with 2 (3) and now 3 with a continuation 4. sublist with roman numerals, starting with 4 5. more items (1) a subsublist (2) a subsublist Nesting: 1. Upper Alpha 1. Upper Roman. (6) Decimal start with 6 3) Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams ________________________________________________________________________________ = Definition Lists #definition-lists# Tight using spaces: [apple] red fruit [orange] orange fruit [banana] yellow fruit Tight using tabs: [apple] red fruit [orange] orange fruit [banana] yellow fruit Loose: [apple] red fruit [orange] orange fruit [banana] yellow fruit Multiple blocks with italics: [/apple/] red fruit contains seeds, crisp, pleasant to taste [/orange/] orange fruit > { orange code block } orange block quote Multiple definitions, tight: [apple] red fruit computer [orange] orange fruit bank Multiple definitions, loose: [apple] red fruit computer [orange] orange fruit bank Blank line after term, indented marker, alternate markers: [apple] red fruit computer [orange] orange fruit 1. sublist 2. sublist = HTML Blocks #html-blocks# Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is /emphasized/ And this is __strong__ Here’s a simple block: foo This should be a code block, though: >
                > foo >
                As should this: >
                foo
                Now, nested: foo This should just be an HTML comment: Multiline: Code block: > Just plain comment, with trailing spaces on the line: Code: >
                Hr’s: ________________________________________________________________________________ = Inline Markup #inline-markup# This is /emphasized/, and so /is this/. This is __strong__, and so __is this__. An //. __/This is strong and em./__ So is __/this/__ word. __/This is strong and em./__ So is __/this/__ word. This is code: @>@, @$@, @\\@, @\\$@, @\@. ~~This is /strikeout/.~~ Superscripts: abcd a/hello/ ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ________________________________________________________________________________ = Smart quotes, ellipses, dashes #smart-quotes-ellipses-dashes# “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘@code@’ and a “”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ________________________________________________________________________________ = LaTeX #latex# - - \(2+2=4\) - \(x \in y\) - \(\alpha \wedge \omega\) - \(223\) - \(p\)-Tree - Here’s some display math: \[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\] - Here’s one that has a line break in it: \(\alpha + \omega \times x^2\). These shouldn’t be math: - To get the famous equation, write @$e = mc^2$@. - $22,000 is a /lot/ of money. So is $34,000. (It worked if “lot” is emphasized.) - Shoes ($20) and socks ($5). - Escaped @$@: $73 /this should be emphasized/ 23$. Here’s a LaTeX table: ________________________________________________________________________________ = Special Characters #special-characters# Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 \< 5. 6 > 5. Backslash: \\ Backtick: \` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - ________________________________________________________________________________ = Links #links# == Explicit #explicit# Just a . . . . < Empty>. == Reference #reference# Foo . With . by itself should be a link. Indented . Indented . Indented . This should [not][] be a link. > [not]: /url Foo . Foo . == With ampersands #with-ampersands# Here’s a . Here’s a link with an amersand in the link text: . Here’s an . Here’s an . == Autolinks #autolinks# With an ampersand: - In a list? - - It should. An e-mail address: Blockquoted: Auto-links should not occur here: @\@ > or here: ________________________________________________________________________________ = Images #images# From “Voyage dans la Lune” by Georges Melies (1902): <> lalune Here is a movie <> icon. ________________________________________________________________________________ = Footnotes #footnotes# Here is a footnote reference,<#notes [1]> and another.<#notes [2]> This should /not/ be a footnote reference, because it contains a space.[^my note] Here is an inline note.<#notes [3]> Notes can go in quotes.<#notes [4]> 1. And in list items.<#notes [5]> This paragraph should not be part of the note, as it is not indented. #notes# 1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. 2. Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). > { } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. 3. This is /easier/ to type. Inline notes may contain and @]@ verbatim characters, as well as [bracketed text]. 4. In quote. 5. In list. ================================================ FILE: test/writer.html4 ================================================ Pandoc Test Suite

                This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.


                Headers

                Level 3 with emphasis

                Level 4

                Level 5

                Level 1

                Level 2 with emphasis

                Level 3

                with no blank line

                Level 2

                with no blank line


                Paragraphs

                Here’s a regular paragraph.

                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 a list item.

                Here’s one with a bullet. * criminey.

                There should be a hard line break
                here.


                Block Quotes

                E-mail style:

                This is a block quote. It is pretty short.

                Code in a block quote:

                sub status {
                    print "working";
                }

                A list:

                1. item one
                2. item two

                Nested block quotes:

                nested

                nested

                This should not be a block quote: 2 > 1.

                And a following paragraph.


                Code Blocks

                Code:

                ---- (should be four hyphens)
                
                sub status {
                    print "working";
                }
                
                this code block is indented by one tab

                And:

                    this code block is indented by two tabs
                
                These should not be escaped:  \$ \\ \> \[ \{

                Lists

                Unordered

                Asterisks tight:

                • asterisk 1
                • asterisk 2
                • asterisk 3

                Asterisks loose:

                • asterisk 1

                • asterisk 2

                • asterisk 3

                Pluses tight:

                • Plus 1
                • Plus 2
                • Plus 3

                Pluses loose:

                • Plus 1

                • Plus 2

                • Plus 3

                Minuses tight:

                • Minus 1
                • Minus 2
                • Minus 3

                Minuses loose:

                • Minus 1

                • Minus 2

                • Minus 3

                Ordered

                Tight:

                1. First
                2. Second
                3. Third

                and:

                1. One
                2. Two
                3. Three

                Loose using tabs:

                1. First

                2. Second

                3. Third

                and using spaces:

                1. One

                2. Two

                3. Three

                Multiple paragraphs:

                1. Item 1, graf one.

                  Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

                2. Item 2.

                3. Item 3.

                Nested

                • Tab
                  • Tab
                    • Tab

                Here’s another:

                1. First
                2. Second:
                  • Fee
                  • Fie
                  • Foe
                3. Third

                Same thing but with paragraphs:

                1. First

                2. Second:

                  • Fee
                  • Fie
                  • Foe
                3. Third

                Tabs and spaces

                • this is a list item indented with tabs

                • this is a list item indented with spaces

                  • this is an example list item indented with tabs

                  • this is an example list item indented with spaces

                Fancy list markers

                1. begins with 2

                2. and now 3

                  with a continuation

                  1. sublist with roman numerals, starting with 4
                  2. more items
                    1. a subsublist
                    2. a subsublist

                Nesting:

                1. Upper Alpha
                  1. Upper Roman.
                    1. Decimal start with 6
                      1. Lower alpha with paren

                Autonumbering:

                1. Autonumber.
                2. More.
                  1. Nested.

                Should not be a list item:

                M.A. 2007

                B. Williams


                Definition Lists

                Tight using spaces:

                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit

                Tight using tabs:

                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit

                Loose:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Multiple blocks with italics:

                apple

                red fruit

                contains seeds, crisp, pleasant to taste

                orange

                orange fruit

                { orange code block }

                orange block quote

                Multiple definitions, tight:

                apple
                red fruit
                computer
                orange
                orange fruit
                bank

                Multiple definitions, loose:

                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Blank line after term, indented marker, alternate markers:

                apple

                red fruit

                computer

                orange

                orange fruit

                1. sublist
                2. sublist

                HTML Blocks

                Simple block on one line:

                foo

                And nested without indentation:

                foo

                bar

                Interpreted markdown in a table:

                This is emphasized And this is strong

                Here’s a simple block:

                foo

                This should be a code block, though:

                <div>
                    foo
                </div>

                As should this:

                <div>foo</div>

                Now, nested:

                foo

                This should just be an HTML comment:

                Multiline:

                Code block:

                <!-- Comment -->

                Just plain comment, with trailing spaces on the line:

                Code:

                <hr />

                Hr’s:











                Inline Markup

                This is emphasized, and so is this.

                This is strong, and so is this.

                An emphasized link.

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is strikeout.

                Superscripts: abcd ahello ahello there.

                Subscripts: H2O, H23O, Hmany of themO.

                These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.


                Smart quotes, ellipses, dashes

                “Hello,” said the spider. “‘Shelob’ is my name.”

                ‘A’, ‘B’, and ‘C’ are letters.

                ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

                ‘He said, “I want to go.”’ Were you alive in the 70’s?

                Here is some quoted ‘code’ and a “quoted link”.

                Some dashes: one—two — three—four — five.

                Dashes between numbers: 5–7, 255–66, 1987–1999.

                Ellipses…and…and….


                LaTeX

                • 2 + 2 = 4
                • x ∈ y
                • α ∧ ω
                • 223
                • p-Tree
                • Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$
                • Here’s one that has a line break in it: α + ω × x2.

                These shouldn’t be math:

                • To get the famous equation, write $e = mc^2$.
                • $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)
                • Shoes ($20) and socks ($5).
                • Escaped $: $73 this should be emphasized 23$.

                Here’s a LaTeX table:


                Special Characters

                Here is some unicode:

                • I hat: Î
                • o umlaut: ö
                • section: §
                • set membership: ∈
                • copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -


                Links

                Explicit

                Just a URL.

                URL and title.

                URL and title.

                URL and title.

                URL and title

                URL and title

                with_underscore

                Email link

                Empty.

                Reference

                Foo bar.

                With embedded [brackets].

                b by itself should be a link.

                Indented once.

                Indented twice.

                Indented thrice.

                This should [not][] be a link.

                [not]: /url

                Foo bar.

                Foo biz.

                With ampersands

                Here’s a link with an ampersand in the URL.

                Here’s a link with an amersand in the link text: AT&T.

                Here’s an inline link.

                Here’s an inline link in pointy braces.

                With an ampersand: http://example.com/?foo=1&bar=2

                An e-mail address:

                Blockquoted: http://example.com/

                Auto-links should not occur here: <http://example.com/>

                or here: <http://example.com/>

                Images

                From “Voyage dans la Lune” by Georges Melies (1902):

                lalune
                lalune

                Here is a movie movie icon.


                Footnotes

                Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

                Notes can go in quotes.4

                1. And in list items.5

                This paragraph should not be part of the note, as it is not indented.


                1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.↩︎

                2. Here’s the long note. This one contains multiple blocks.

                  Subsequent blocks are indented to show that they belong to the footnote (as with list items).

                    { <code> }

                  If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.↩︎

                3. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].↩︎

                4. In quote.↩︎

                5. In list.↩︎

                ================================================ FILE: test/writer.html5 ================================================ Pandoc Test Suite

                Pandoc Test Suite

                John MacFarlane

                Anonymous

                July 17, 2006

                This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.


                Headers

                Level 3 with emphasis

                Level 4

                Level 5

                Level 1

                Level 2 with emphasis

                Level 3

                with no blank line

                Level 2

                with no blank line


                Paragraphs

                Here’s a regular paragraph.

                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 a list item.

                Here’s one with a bullet. * criminey.

                There should be a hard line break
                here.


                Block Quotes

                E-mail style:

                This is a block quote. It is pretty short.

                Code in a block quote:

                sub status {
                    print "working";
                }

                A list:

                1. item one
                2. item two

                Nested block quotes:

                nested

                nested

                This should not be a block quote: 2 > 1.

                And a following paragraph.


                Code Blocks

                Code:

                ---- (should be four hyphens)
                
                sub status {
                    print "working";
                }
                
                this code block is indented by one tab

                And:

                    this code block is indented by two tabs
                
                These should not be escaped:  \$ \\ \> \[ \{

                Lists

                Unordered

                Asterisks tight:

                • asterisk 1
                • asterisk 2
                • asterisk 3

                Asterisks loose:

                • asterisk 1

                • asterisk 2

                • asterisk 3

                Pluses tight:

                • Plus 1
                • Plus 2
                • Plus 3

                Pluses loose:

                • Plus 1

                • Plus 2

                • Plus 3

                Minuses tight:

                • Minus 1
                • Minus 2
                • Minus 3

                Minuses loose:

                • Minus 1

                • Minus 2

                • Minus 3

                Ordered

                Tight:

                1. First
                2. Second
                3. Third

                and:

                1. One
                2. Two
                3. Three

                Loose using tabs:

                1. First

                2. Second

                3. Third

                and using spaces:

                1. One

                2. Two

                3. Three

                Multiple paragraphs:

                1. Item 1, graf one.

                  Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

                2. Item 2.

                3. Item 3.

                Nested

                • Tab
                  • Tab
                    • Tab

                Here’s another:

                1. First
                2. Second:
                  • Fee
                  • Fie
                  • Foe
                3. Third

                Same thing but with paragraphs:

                1. First

                2. Second:

                  • Fee
                  • Fie
                  • Foe
                3. Third

                Tabs and spaces

                • this is a list item indented with tabs

                • this is a list item indented with spaces

                  • this is an example list item indented with tabs

                  • this is an example list item indented with spaces

                Fancy list markers

                1. begins with 2

                2. and now 3

                  with a continuation

                  1. sublist with roman numerals, starting with 4
                  2. more items
                    1. a subsublist
                    2. a subsublist

                Nesting:

                1. Upper Alpha
                  1. Upper Roman.
                    1. Decimal start with 6
                      1. Lower alpha with paren

                Autonumbering:

                1. Autonumber.
                2. More.
                  1. Nested.

                Should not be a list item:

                M.A. 2007

                B. Williams


                Definition Lists

                Tight using spaces:

                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit

                Tight using tabs:

                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit

                Loose:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Multiple blocks with italics:

                apple

                red fruit

                contains seeds, crisp, pleasant to taste

                orange

                orange fruit

                { orange code block }

                orange block quote

                Multiple definitions, tight:

                apple
                red fruit
                computer
                orange
                orange fruit
                bank

                Multiple definitions, loose:

                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Blank line after term, indented marker, alternate markers:

                apple

                red fruit

                computer

                orange

                orange fruit

                1. sublist
                2. sublist

                HTML Blocks

                Simple block on one line:

                foo

                And nested without indentation:

                foo

                bar

                Interpreted markdown in a table:

                This is emphasized And this is strong

                Here’s a simple block:

                foo

                This should be a code block, though:

                <div>
                    foo
                </div>

                As should this:

                <div>foo</div>

                Now, nested:

                foo

                This should just be an HTML comment:

                Multiline:

                Code block:

                <!-- Comment -->

                Just plain comment, with trailing spaces on the line:

                Code:

                <hr />

                Hr’s:











                Inline Markup

                This is emphasized, and so is this.

                This is strong, and so is this.

                An emphasized link.

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is strikeout.

                Superscripts: abcd ahello ahello there.

                Subscripts: H2O, H23O, Hmany of themO.

                These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.


                Smart quotes, ellipses, dashes

                “Hello,” said the spider. “‘Shelob’ is my name.”

                ‘A’, ‘B’, and ‘C’ are letters.

                ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

                ‘He said, “I want to go.”’ Were you alive in the 70’s?

                Here is some quoted ‘code’ and a “quoted link”.

                Some dashes: one—two — three—four — five.

                Dashes between numbers: 5–7, 255–66, 1987–1999.

                Ellipses…and…and….


                LaTeX

                • 2 + 2 = 4
                • x ∈ y
                • α ∧ ω
                • 223
                • p-Tree
                • Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$
                • Here’s one that has a line break in it: α + ω × x2.

                These shouldn’t be math:

                • To get the famous equation, write $e = mc^2$.
                • $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)
                • Shoes ($20) and socks ($5).
                • Escaped $: $73 this should be emphasized 23$.

                Here’s a LaTeX table:


                Special Characters

                Here is some unicode:

                • I hat: Î
                • o umlaut: ö
                • section: §
                • set membership: ∈
                • copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -


                Links

                Explicit

                Just a URL.

                URL and title.

                URL and title.

                URL and title.

                URL and title

                URL and title

                with_underscore

                Email link

                Empty.

                Reference

                Foo bar.

                With embedded [brackets].

                b by itself should be a link.

                Indented once.

                Indented twice.

                Indented thrice.

                This should [not][] be a link.

                [not]: /url

                Foo bar.

                Foo biz.

                With ampersands

                Here’s a link with an ampersand in the URL.

                Here’s a link with an amersand in the link text: AT&T.

                Here’s an inline link.

                Here’s an inline link in pointy braces.

                With an ampersand: http://example.com/?foo=1&bar=2

                An e-mail address:

                Blockquoted: http://example.com/

                Auto-links should not occur here: <http://example.com/>

                or here: <http://example.com/>

                Images

                From “Voyage dans la Lune” by Georges Melies (1902):

                lalune

                Here is a movie movie icon.


                Footnotes

                Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

                Notes can go in quotes.4

                1. And in list items.5

                This paragraph should not be part of the note, as it is not indented.


                1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.↩︎

                2. Here’s the long note. This one contains multiple blocks.

                  Subsequent blocks are indented to show that they belong to the footnote (as with list items).

                    { <code> }

                  If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.↩︎

                3. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].↩︎

                4. In quote.↩︎

                5. In list.↩︎

                ================================================ FILE: test/writer.icml ================================================ $ID/NormalCharacterStyle $ID/NormalCharacterStyle $ID/NormalCharacterStyle $ID/NormalCharacterStyle Courier New $ID/NormalCharacterStyle $ID/NormalCharacterStyle $ID/NormalCharacterStyle $ID/NormalCharacterStyle $ID/NormalCharacterStyle $ID/NormalCharacterStyle $ID/NormalCharacterStyle $ID/NormalCharacterStyle LeftAlign . 10 $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle Courier New $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle LeftAlign . 10 $ID/NormalParagraphStyle LeftAlign . 30 $ID/NormalParagraphStyle LeftAlign . 20 $ID/NormalParagraphStyle LeftAlign . 20 $ID/NormalParagraphStyle LeftAlign . 20 $ID/NormalParagraphStyle LeftAlign . 10 $ID/NormalParagraphStyle LeftAlign . 10 $ID/NormalParagraphStyle LeftAlign . 10 $ID/NormalParagraphStyle $ID/NormalParagraphStyle Courier New $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle Courier New $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle Courier New $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle LeftAlign . 20 $ID/NormalParagraphStyle LeftAlign . 20 $ID/NormalParagraphStyle a, b, c, d... $ID/NormalParagraphStyle $ID/NormalParagraphStyle A, B, C, D... $ID/NormalParagraphStyle A, B, C, D... $ID/NormalParagraphStyle i, ii, iii, iv... $ID/NormalParagraphStyle $ID/NormalParagraphStyle i, ii, iii, iv... $ID/NormalParagraphStyle I, II, III, IV... $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle $ID/NormalParagraphStyle A, B, C, D... $ID/NormalParagraphStyle $ID/NormalParagraphStyle This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.
                Headers
                Level 2 with an
                Level 3 with emphasis
                Level 4
                Level 5
                Level 1
                Level 2 with emphasis
                Level 3
                with no blank line
                Level 2
                with no blank line
                Paragraphs
                Here’s a regular paragraph.
                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 a list item.
                Here’s one with a bullet. * criminey.
                There should be a hard line break here.
                Block Quotes
                E-mail style:
                This is a block quote. It is pretty short.
                Code in a block quote:
                sub status { print "working"; }
                A list:
                item one
                item two
                Nested block quotes:
                nested
                nested
                This should not be a block quote: 2 > 1.
                And a following paragraph.
                Code Blocks
                Code:
                ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab
                And:
                this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{
                Lists
                Unordered
                Asterisks tight:
                asterisk 1
                asterisk 2
                asterisk 3
                Asterisks loose:
                asterisk 1
                asterisk 2
                asterisk 3
                Pluses tight:
                Plus 1
                Plus 2
                Plus 3
                Pluses loose:
                Plus 1
                Plus 2
                Plus 3
                Minuses tight:
                Minus 1
                Minus 2
                Minus 3
                Minuses loose:
                Minus 1
                Minus 2
                Minus 3
                Ordered
                Tight:
                First
                Second
                Third
                and:
                One
                Two
                Three
                Loose using tabs:
                First
                Second
                Third
                and using spaces:
                One
                Two
                Three
                Multiple paragraphs:
                Item 1, graf one.
                Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.
                Item 2.
                Item 3.
                Nested
                Tab
                Tab
                Tab
                Here’s another:
                First
                Second:
                Fee
                Fie
                Foe
                Third
                Same thing but with paragraphs:
                First
                Second:
                Fee
                Fie
                Foe
                Third
                Tabs and spaces
                this is a list item indented with tabs
                this is a list item indented with spaces
                this is an example list item indented with tabs
                this is an example list item indented with spaces
                Fancy list markers
                begins with 2
                and now 3
                with a continuation
                sublist with roman numerals, starting with 4
                more items
                a subsublist
                a subsublist
                Nesting:
                Upper Alpha
                Upper Roman.
                Decimal start with 6
                Lower alpha with paren
                Autonumbering:
                Autonumber.
                More.
                Nested.
                Should not be a list item:
                M.A. 2007
                B. Williams
                Definition Lists
                Tight using spaces:
                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit
                Tight using tabs:
                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit
                Loose:
                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit
                Multiple blocks with italics:
                apple
                red fruit
                contains seeds, crisp, pleasant to taste
                orange
                orange fruit
                { orange code block }
                orange block quote
                Multiple definitions, tight:
                apple
                red fruit
                computer
                orange
                orange fruit
                bank
                Multiple definitions, loose:
                apple
                red fruit
                computer
                orange
                orange fruit
                bank
                Blank line after term, indented marker, alternate markers:
                apple
                red fruit
                computer
                orange
                orange fruit
                sublist
                sublist
                HTML Blocks
                Simple block on one line:
                foo
                And nested without indentation:
                foo
                bar
                Interpreted markdown in a table:
                This is emphasized
                And this is strong
                Here’s a simple block:
                foo
                This should be a code block, though:
                <div> foo </div>
                As should this:
                <div>foo</div>
                Now, nested:
                foo
                This should just be an HTML comment:
                Multiline:
                Code block:
                <!-- Comment -->
                Just plain comment, with trailing spaces on the line:
                Code:
                <hr />
                Hr’s:
                Inline Markup
                This is emphasized , and so is this .
                This is strong , and so is this .
                An .
                This is strong and em.
                So is this word.
                This is strong and em.
                So is this word.
                This is code: > , $ , \ , \$ , <html> .
                This is strikeout .
                Superscripts: a bc d a hello a hello there .
                Subscripts: H 2 O, H 23 O, H many of them O.
                These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.
                Smart quotes, ellipses, dashes
                “Hello,” said the spider. ‘Shelob’ is my name.”
                ‘A’ , ‘B’ , and ‘C’ are letters.
                ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’
                ‘He said, “I want to go.” Were you alive in the 70’s?
                Here is some quoted code and a .
                Some dashes: one—two — three—four — five.
                Dashes between numbers: 5–7, 255–66, 1987–1999.
                Ellipses…and…and….
                LaTeX

                2 + 2 = 4
                x y
                α ω
                223
                p -Tree
                Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$
                Here’s one that has a line break in it: α + ω × x 2 .
                These shouldn’t be math:
                To get the famous equation, write $e = mc^2$ .
                $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)
                Shoes ($20) and socks ($5).
                Escaped $ : $73 this should be emphasized 23$.
                Here’s a LaTeX table:
                Special Characters
                Here is some unicode:
                I hat: Î
                o umlaut: ö
                section: §
                set membership: ∈
                copyright: ©
                AT&T has an ampersand in their name.
                AT&T is another way to write it.
                This & that.
                4 < 5.
                6 > 5.
                Backslash: \
                Backtick: `
                Asterisk: *
                Underscore: _
                Left brace: {
                Right brace: }
                Left bracket: [
                Right bracket: ]
                Left paren: (
                Right paren: )
                Greater-than: >
                Hash: #
                Period: .
                Bang: !
                Plus: +
                Minus: -
                Links
                Explicit
                Just a .
                .
                .
                .




                .
                Reference
                Foo .
                With .
                by itself should be a link.
                Indented .
                Indented .
                Indented .
                This should [not][] be a link.
                [not]: /url
                Foo .
                Foo .
                With ampersands
                Here’s a .
                Here’s a link with an amersand in the link text: .
                Here’s an .
                Here’s an .
                Autolinks
                With an ampersand:
                In a list?

                It should.
                An e-mail address:
                Blockquoted:
                Auto-links should not occur here: <http://example.com/>
                or here: <http://example.com/>
                Images
                From “Voyage dans la Lune” by Georges Melies (1902):
                $ID/Embedded
                lalune
                Here is a movie $ID/Embedded icon.
                Footnotes
                Here is a footnote reference, Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. and another. Here’s the long note. This one contains multiple blocks.
                Subsequent blocks are indented to show that they belong to the footnote (as with list items).
                { <code> }
                If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.
                This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note. This is easier to type. Inline notes may contain and ] verbatim characters, as well as [bracketed text].

                Notes can go in quotes. In quote.
                And in list items. In list.
                This paragraph should not be part of the note, as it is not indented.
                Black HyperlinkURLDestination/http%3a//google.com Black HyperlinkURLDestination/http%3a//example.com/ Black HyperlinkURLDestination/mailto%3anobody@nowhere.net Black HyperlinkURLDestination/http%3a//example.com/ Black HyperlinkURLDestination/http%3a//example.com/?foo=1&bar=2 Black HyperlinkURLDestination//script?foo=1&bar=2 Black HyperlinkURLDestination//script?foo=1&bar=2 Black HyperlinkURLDestination/http%3a//att.com/ Black HyperlinkURLDestination/http%3a//example.com/?foo=1&bar=2 Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url Black HyperlinkURLDestination//url Black HyperlinkURLDestination//url Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination/ Black HyperlinkURLDestination/mailto%3anobody@nowhere.net Black HyperlinkURLDestination//url/with_underscore Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination//url/ Black HyperlinkURLDestination/http%3a//example.com/?foo=1&bar=2 Black HyperlinkURLDestination//url Black HyperlinkURLDestination//url
                ================================================ FILE: test/writer.jats_archiving ================================================
                Pandoc Test Suite John MacFarlane Anonymous 17 7 2006

                This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.

                Headers Level 2 with an <ext-link ext-link-type="uri" xlink:href="/url">embedded link</ext-link> Level 3 with <italic>emphasis</italic> Level 4 Level 5 Level 1 Level 2 with <italic>emphasis</italic> Level 3

                with no blank line

                Level 2

                with no blank line

                Paragraphs

                Here’s a regular paragraph.

                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 a list item.

                Here’s one with a bullet. * criminey.

                There should be a hard line break here.

                Block Quotes

                E-mail style:

                This is a block quote. It is pretty short.

                Code in a block quote:

                sub status { print "working"; }

                A list:

                item one

                item two

                Nested block quotes:

                nested

                nested

                This should not be a block quote: 2 > 1.

                And a following paragraph.

                Code Blocks

                Code:

                ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab

                And:

                this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{
                Lists Unordered

                Asterisks tight:

                asterisk 1

                asterisk 2

                asterisk 3

                Asterisks loose:

                asterisk 1

                asterisk 2

                asterisk 3

                Pluses tight:

                Plus 1

                Plus 2

                Plus 3

                Pluses loose:

                Plus 1

                Plus 2

                Plus 3

                Minuses tight:

                Minus 1

                Minus 2

                Minus 3

                Minuses loose:

                Minus 1

                Minus 2

                Minus 3

                Ordered

                Tight:

                First

                Second

                Third

                and:

                One

                Two

                Three

                Loose using tabs:

                First

                Second

                Third

                and using spaces:

                One

                Two

                Three

                Multiple paragraphs:

                Item 1, graf one.

                Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

                Item 2.

                Item 3.

                Nested

                Tab

                Tab

                Tab

                Here’s another:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Same thing but with paragraphs:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Tabs and spaces

                this is a list item indented with tabs

                this is a list item indented with spaces

                this is an example list item indented with tabs

                this is an example list item indented with spaces

                Fancy list markers

                begins with 2

                and now 3

                with a continuation

                sublist with roman numerals, starting with 4

                more items

                a subsublist

                a subsublist

                Nesting:

                Upper Alpha

                Upper Roman.

                Decimal start with 6

                Lower alpha with paren

                Autonumbering:

                Autonumber.

                More.

                Nested.

                Should not be a list item:

                M.A. 2007

                B. Williams

                Definition Lists

                Tight using spaces:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Tight using tabs:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Loose:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Multiple blocks with italics:

                apple

                red fruit

                contains seeds, crisp, pleasant to taste

                orange

                orange fruit

                { orange code block }

                orange block quote

                Multiple definitions, tight:

                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Multiple definitions, loose:

                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Blank line after term, indented marker, alternate markers:

                apple

                red fruit

                computer

                orange

                orange fruit

                sublist

                sublist

                HTML Blocks

                Simple block on one line:

                foo

                And nested without indentation:

                foo

                bar

                Interpreted markdown in a table:

                This is emphasized

                And this is strong

                Here’s a simple block:

                foo

                This should be a code block, though:

                <div> foo </div>

                As should this:

                <div>foo</div>

                Now, nested:

                foo

                This should just be an HTML comment:

                Multiline:

                Code block:

                <!-- Comment -->

                Just plain comment, with trailing spaces on the line:

                Code:

                <hr />

                Hr’s:

                Inline Markup

                This is emphasized, and so is this.

                This is strong, and so is this.

                An emphasized link.

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is strikeout.

                Superscripts: abcd ahello ahello there.

                Subscripts: H2O, H23O, Hmany of themO.

                These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.

                Smart quotes, ellipses, dashes

                “Hello,” said the spider. “‘Shelob’ is my name.”

                ‘A’, ‘B’, and ‘C’ are letters.

                ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

                ‘He said, “I want to go.”’ Were you alive in the 70’s?

                Here is some quoted ‘code’ and a “quoted link”.

                Some dashes: one—two — three—four — five.

                Dashes between numbers: 5–7, 255–66, 1987–1999.

                Ellipses…and…and….

                LaTeX

                2+2=4

                xy

                αω

                223

                p-Tree

                Here’s some display math: ddxf(x)=limh0f(x+h)f(x)h

                Here’s one that has a line break in it: α+ω×x2.

                These shouldn’t be math:

                To get the famous equation, write $e = mc^2$.

                $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)

                Shoes ($20) and socks ($5).

                Escaped $: $73 this should be emphasized 23$.

                Here’s a LaTeX table:

                Special Characters

                Here is some unicode:

                I hat: Î

                o umlaut: ö

                section: §

                set membership: ∈

                copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -

                Links Explicit

                Just a URL.

                URL and title.

                URL and title.

                URL and title.

                URL and title

                URL and title

                with_underscore

                Email link

                Empty.

                Reference

                Foo bar.

                With embedded [brackets].

                b by itself should be a link.

                Indented once.

                Indented twice.

                Indented thrice.

                This should [not][] be a link.

                [not]: /url

                Foo bar.

                Foo biz.

                With ampersands

                Here’s a link with an ampersand in the URL.

                Here’s a link with an amersand in the link text: AT&T.

                Here’s an inline link.

                Here’s an inline link in pointy braces.

                Autolinks

                With an ampersand: http://example.com/?foo=1&bar=2

                In a list?

                http://example.com/

                It should.

                An e-mail address: nobody@nowhere.net

                Blockquoted: http://example.com/

                Auto-links should not occur here: <http://example.com/>

                or here: <http://example.com/>
                Images

                From “Voyage dans la Lune” by Georges Melies (1902):

                lalune

                Here is a movie movie icon.

                Footnotes

                Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

                Notes can go in quotes.4

                And in list items.5

                This paragraph should not be part of the note, as it is not indented.

                Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

                Here’s the long note. This one contains multiple blocks.

                Subsequent blocks are indented to show that they belong to the footnote (as with list items).

                { <code> }

                If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

                This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

                In quote.

                In list.

                ================================================ FILE: test/writer.jats_articleauthoring ================================================
                Pandoc Test Suite John MacFarlane Anonymous

                This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.

                Headers Level 2 with an <ext-link ext-link-type="uri" xlink:href="/url">embedded link</ext-link> Level 3 with <italic>emphasis</italic> Level 4 Level 5 Level 1 Level 2 with <italic>emphasis</italic> Level 3

                with no blank line

                Level 2

                with no blank line

                Paragraphs

                Here’s a regular paragraph.

                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 a list item.

                Here’s one with a bullet. * criminey.

                There should be a hard line break here.

                Block Quotes

                E-mail style:

                This is a block quote. It is pretty short.

                Code in a block quote:

                sub status { print "working"; }

                A list:

                item one

                item two

                Nested block quotes:

                nested

                nested

                This should not be a block quote: 2 > 1.

                And a following paragraph.

                Code Blocks

                Code:

                ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab

                And:

                this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{
                Lists Unordered

                Asterisks tight:

                asterisk 1

                asterisk 2

                asterisk 3

                Asterisks loose:

                asterisk 1

                asterisk 2

                asterisk 3

                Pluses tight:

                Plus 1

                Plus 2

                Plus 3

                Pluses loose:

                Plus 1

                Plus 2

                Plus 3

                Minuses tight:

                Minus 1

                Minus 2

                Minus 3

                Minuses loose:

                Minus 1

                Minus 2

                Minus 3

                Ordered

                Tight:

                First

                Second

                Third

                and:

                One

                Two

                Three

                Loose using tabs:

                First

                Second

                Third

                and using spaces:

                One

                Two

                Three

                Multiple paragraphs:

                Item 1, graf one.

                Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

                Item 2.

                Item 3.

                Nested

                Tab

                Tab

                Tab

                Here’s another:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Same thing but with paragraphs:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Tabs and spaces

                this is a list item indented with tabs

                this is a list item indented with spaces

                this is an example list item indented with tabs

                this is an example list item indented with spaces

                Fancy list markers

                begins with 2

                and now 3

                with a continuation

                sublist with roman numerals, starting with 4

                more items

                a subsublist

                a subsublist

                Nesting:

                Upper Alpha

                Upper Roman.

                Decimal start with 6

                Lower alpha with paren

                Autonumbering:

                Autonumber.

                More.

                Nested.

                Should not be a list item:

                M.A. 2007

                B. Williams

                Definition Lists

                Tight using spaces:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Tight using tabs:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Loose:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Multiple blocks with italics:

                apple

                red fruit

                contains seeds, crisp, pleasant to taste

                orange

                orange fruit

                { orange code block }

                orange block quote

                Multiple definitions, tight:

                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Multiple definitions, loose:

                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Blank line after term, indented marker, alternate markers:

                apple

                red fruit

                computer

                orange

                orange fruit

                sublist

                sublist

                HTML Blocks

                Simple block on one line:

                foo

                And nested without indentation:

                foo

                bar

                Interpreted markdown in a table:

                This is emphasized

                And this is strong

                Here’s a simple block:

                foo

                This should be a code block, though:

                <div> foo </div>

                As should this:

                <div>foo</div>

                Now, nested:

                foo

                This should just be an HTML comment:

                Multiline:

                Code block:

                <!-- Comment -->

                Just plain comment, with trailing spaces on the line:

                Code:

                <hr />

                Hr’s:

                Inline Markup

                This is emphasized, and so is this.

                This is strong, and so is this.

                An emphasized link.

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is strikeout.

                Superscripts: abcd ahello ahello there.

                Subscripts: H2O, H23O, Hmany of themO.

                These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.

                Smart quotes, ellipses, dashes

                “Hello,” said the spider. “‘Shelob’ is my name.”

                ‘A’, ‘B’, and ‘C’ are letters.

                ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

                ‘He said, “I want to go.”’ Were you alive in the 70’s?

                Here is some quoted ‘code’ and a “quoted link”.

                Some dashes: one—two — three—four — five.

                Dashes between numbers: 5–7, 255–66, 1987–1999.

                Ellipses…and…and….

                LaTeX

                2+2=4

                xy

                αω

                223

                p-Tree

                Here’s some display math: ddxf(x)=limh0f(x+h)f(x)h

                Here’s one that has a line break in it: α+ω×x2.

                These shouldn’t be math:

                To get the famous equation, write $e = mc^2$.

                $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)

                Shoes ($20) and socks ($5).

                Escaped $: $73 this should be emphasized 23$.

                Here’s a LaTeX table:

                Special Characters

                Here is some unicode:

                I hat: Î

                o umlaut: ö

                section: §

                set membership: ∈

                copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -

                Links Explicit

                Just a URL.

                URL and title.

                URL and title.

                URL and title.

                URL and title

                URL and title

                with_underscore

                Email link

                Empty.

                Reference

                Foo bar.

                With embedded [brackets].

                b by itself should be a link.

                Indented once.

                Indented twice.

                Indented thrice.

                This should [not][] be a link.

                [not]: /url

                Foo bar.

                Foo biz.

                With ampersands

                Here’s a link with an ampersand in the URL.

                Here’s a link with an amersand in the link text: AT&T.

                Here’s an inline link.

                Here’s an inline link in pointy braces.

                Autolinks

                With an ampersand: http://example.com/?foo=1&bar=2

                In a list?

                http://example.com/

                It should.

                An e-mail address: nobody@nowhere.net

                Blockquoted: http://example.com/

                Auto-links should not occur here: <http://example.com/>

                or here: <http://example.com/>
                Images

                From “Voyage dans la Lune” by Georges Melies (1902):

                lalune

                Here is a movie movie icon.

                Footnotes

                Here is a footnote reference,

                Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

                and another.

                Here’s the long note. This one contains multiple blocks.

                Subsequent blocks are indented to show that they belong to the footnote (as with list items).

                { <code> }

                If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

                This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.

                This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

                Notes can go in quotes.

                In quote.

                And in list items.

                In list.

                This paragraph should not be part of the note, as it is not indented.

                ================================================ FILE: test/writer.jats_publishing ================================================
                Pandoc Test Suite John MacFarlane Anonymous 17 7 2006

                This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.

                Headers Level 2 with an <ext-link ext-link-type="uri" xlink:href="/url">embedded link</ext-link> Level 3 with <italic>emphasis</italic> Level 4 Level 5 Level 1 Level 2 with <italic>emphasis</italic> Level 3

                with no blank line

                Level 2

                with no blank line

                Paragraphs

                Here’s a regular paragraph.

                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 a list item.

                Here’s one with a bullet. * criminey.

                There should be a hard line break here.

                Block Quotes

                E-mail style:

                This is a block quote. It is pretty short.

                Code in a block quote:

                sub status { print "working"; }

                A list:

                item one

                item two

                Nested block quotes:

                nested

                nested

                This should not be a block quote: 2 > 1.

                And a following paragraph.

                Code Blocks

                Code:

                ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab

                And:

                this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{
                Lists Unordered

                Asterisks tight:

                asterisk 1

                asterisk 2

                asterisk 3

                Asterisks loose:

                asterisk 1

                asterisk 2

                asterisk 3

                Pluses tight:

                Plus 1

                Plus 2

                Plus 3

                Pluses loose:

                Plus 1

                Plus 2

                Plus 3

                Minuses tight:

                Minus 1

                Minus 2

                Minus 3

                Minuses loose:

                Minus 1

                Minus 2

                Minus 3

                Ordered

                Tight:

                First

                Second

                Third

                and:

                One

                Two

                Three

                Loose using tabs:

                First

                Second

                Third

                and using spaces:

                One

                Two

                Three

                Multiple paragraphs:

                Item 1, graf one.

                Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

                Item 2.

                Item 3.

                Nested

                Tab

                Tab

                Tab

                Here’s another:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Same thing but with paragraphs:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Tabs and spaces

                this is a list item indented with tabs

                this is a list item indented with spaces

                this is an example list item indented with tabs

                this is an example list item indented with spaces

                Fancy list markers

                begins with 2

                and now 3

                with a continuation

                sublist with roman numerals, starting with 4

                more items

                a subsublist

                a subsublist

                Nesting:

                Upper Alpha

                Upper Roman.

                Decimal start with 6

                Lower alpha with paren

                Autonumbering:

                Autonumber.

                More.

                Nested.

                Should not be a list item:

                M.A. 2007

                B. Williams

                Definition Lists

                Tight using spaces:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Tight using tabs:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Loose:

                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Multiple blocks with italics:

                apple

                red fruit

                contains seeds, crisp, pleasant to taste

                orange

                orange fruit

                { orange code block }

                orange block quote

                Multiple definitions, tight:

                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Multiple definitions, loose:

                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Blank line after term, indented marker, alternate markers:

                apple

                red fruit

                computer

                orange

                orange fruit

                sublist

                sublist

                HTML Blocks

                Simple block on one line:

                foo

                And nested without indentation:

                foo

                bar

                Interpreted markdown in a table:

                This is emphasized

                And this is strong

                Here’s a simple block:

                foo

                This should be a code block, though:

                <div> foo </div>

                As should this:

                <div>foo</div>

                Now, nested:

                foo

                This should just be an HTML comment:

                Multiline:

                Code block:

                <!-- Comment -->

                Just plain comment, with trailing spaces on the line:

                Code:

                <hr />

                Hr’s:

                Inline Markup

                This is emphasized, and so is this.

                This is strong, and so is this.

                An emphasized link.

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is strikeout.

                Superscripts: abcd ahello ahello there.

                Subscripts: H2O, H23O, Hmany of themO.

                These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.

                Smart quotes, ellipses, dashes

                “Hello,” said the spider. “‘Shelob’ is my name.”

                ‘A’, ‘B’, and ‘C’ are letters.

                ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

                ‘He said, “I want to go.”’ Were you alive in the 70’s?

                Here is some quoted ‘code’ and a “quoted link”.

                Some dashes: one—two — three—four — five.

                Dashes between numbers: 5–7, 255–66, 1987–1999.

                Ellipses…and…and….

                LaTeX

                2+2=4

                xy

                αω

                223

                p-Tree

                Here’s some display math: ddxf(x)=limh0f(x+h)f(x)h

                Here’s one that has a line break in it: α+ω×x2.

                These shouldn’t be math:

                To get the famous equation, write $e = mc^2$.

                $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)

                Shoes ($20) and socks ($5).

                Escaped $: $73 this should be emphasized 23$.

                Here’s a LaTeX table:

                Special Characters

                Here is some unicode:

                I hat: Î

                o umlaut: ö

                section: §

                set membership: ∈

                copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -

                Links Explicit

                Just a URL.

                URL and title.

                URL and title.

                URL and title.

                URL and title

                URL and title

                with_underscore

                Email link

                Empty.

                Reference

                Foo bar.

                With embedded [brackets].

                b by itself should be a link.

                Indented once.

                Indented twice.

                Indented thrice.

                This should [not][] be a link.

                [not]: /url

                Foo bar.

                Foo biz.

                With ampersands

                Here’s a link with an ampersand in the URL.

                Here’s a link with an amersand in the link text: AT&T.

                Here’s an inline link.

                Here’s an inline link in pointy braces.

                Autolinks

                With an ampersand: http://example.com/?foo=1&bar=2

                In a list?

                http://example.com/

                It should.

                An e-mail address: nobody@nowhere.net

                Blockquoted: http://example.com/

                Auto-links should not occur here: <http://example.com/>

                or here: <http://example.com/>
                Images

                From “Voyage dans la Lune” by Georges Melies (1902):

                lalune

                Here is a movie movie icon.

                Footnotes

                Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

                Notes can go in quotes.4

                And in list items.5

                This paragraph should not be part of the note, as it is not indented.

                Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

                Here’s the long note. This one contains multiple blocks.

                Subsequent blocks are indented to show that they belong to the footnote (as with list items).

                { <code> }

                If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

                This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

                In quote.

                In list.

                ================================================ FILE: test/writer.jira ================================================ This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ---- h1. {anchor:headers}Headers h2. {anchor:level-2-with-an-embedded-link}Level 2 with an [embedded link|/url] h3. {anchor:level-3-with-emphasis}Level 3 with _emphasis_ h4. {anchor:level-4}Level 4 h5. {anchor:level-5}Level 5 h1. {anchor:level-1}Level 1 h2. {anchor:level-2-with-emphasis}Level 2 with _emphasis_ h3. {anchor:level-3}Level 3 with no blank line h2. {anchor:level-2}Level 2 with no blank line ---- h1. {anchor:paragraphs}Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break here. ---- h1. {anchor:block-quotes}Block Quotes E-mail style: bq. This is a block quote. It is pretty short. {quote} Code in a block quote: {noformat} sub status { print "working"; }{noformat} A list: # item one # item two Nested block quotes: bq. nested bq. nested{quote} This should not be a block quote: 2 > 1. And a following paragraph. ---- h1. {anchor:code-blocks}Code Blocks Code: {noformat} ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab{noformat} And: {noformat} this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{{noformat} ---- h1. {anchor:lists}Lists h2. {anchor:unordered}Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: * Plus 1 * Plus 2 * Plus 3 Pluses loose: * Plus 1 * Plus 2 * Plus 3 Minuses tight: * Minus 1 * Minus 2 * Minus 3 Minuses loose: * Minus 1 * Minus 2 * Minus 3 h2. {anchor:ordered}Ordered Tight: # First # Second # Third and: # One # Two # Three Loose using tabs: # First # Second # Third and using spaces: # One # Two # Three Multiple paragraphs: # Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. # Item 2. # Item 3. h2. {anchor:nested}Nested * Tab ** Tab *** Tab Here’s another: # First # Second: #* Fee #* Fie #* Foe # Third Same thing but with paragraphs: # First # Second: #* Fee #* Fie #* Foe # Third h2. {anchor:tabs-and-spaces}Tabs and spaces * this is a list item indented with tabs * this is a list item indented with spaces ** this is an example list item indented with tabs ** this is an example list item indented with spaces h2. {anchor:fancy-list-markers}Fancy list markers # begins with 2 # and now 3 with a continuation ## sublist with roman numerals, starting with 4 ## more items ### a subsublist ### a subsublist Nesting: # Upper Alpha ## Upper Roman. ### Decimal start with 6 #### Lower alpha with paren Autonumbering: # Autonumber. # More. ## Nested. Should not be a list item: M.A. 2007 B. Williams ---- h1. {anchor:definition-lists}Definition Lists Tight using spaces: * *apple* red fruit * *orange* orange fruit * *banana* yellow fruit Tight using tabs: * *apple* red fruit * *orange* orange fruit * *banana* yellow fruit Loose: * *apple* red fruit * *orange* orange fruit * *banana* yellow fruit Multiple blocks with italics: * *_apple_* red fruit contains seeds, crisp, pleasant to taste * *_orange_* orange fruit {noformat} { orange code block }{noformat} bq. orange block quote Multiple definitions, tight: * *apple* red fruit computer * *orange* orange fruit bank Multiple definitions, loose: * *apple* red fruit computer * *orange* orange fruit bank Blank line after term, indented marker, alternate markers: * *apple* red fruit computer * *orange* orange fruit *# sublist *# sublist h1. {anchor:html-blocks}HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is _emphasized_ And this is *strong* Here’s a simple block: foo This should be a code block, though: {noformat}
                foo
                {noformat} As should this: {noformat}
                foo
                {noformat} Now, nested: foo This should just be an HTML comment: Multiline: Code block: {noformat} {noformat} Just plain comment, with trailing spaces on the line: Code: {noformat}
                {noformat} Hr’s: ---- h1. {anchor:inline-markup}Inline Markup This is _emphasized_, and so _is this_. This is *strong*, and so *is this*. An _[emphasized link|/url]_. *_This is strong and em._* So is *_this_* word. *_This is strong and em._* So is *_this_* word. This is code: {{>}}, {{$}}, {{\}}, {{\$}}, {{}}. -This is _strikeout_.- Superscripts: a{^}bc{^}d a{^}_hello_{^} a{^}hello there{^}. Subscripts: H{~}2{~}O, H{~}23{~}O, H{~}many of them{~}O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ---- h1. {anchor:smart-quotes-ellipses-dashes}Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70’s? Here is some quoted '{{code}}' and a "[quoted link|http://example.com/?foo=1&bar=2]". Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ---- h1. {anchor:latex}LaTeX * * 2 + 2 = 4 * _x_ ∈ {_}y{_} * _α_ ∧ {_}ω{_} * 223 * _p_\-Tree * Here’s some display math: $$\frac\{d\}\{dx\}f\(x)=\lim\_\{h\to 0\}\frac\{f(x+h)-f\(x)\}\{h\}$$ * Here’s one that has a line break in it: _α_ + {_}ω{_} × {_}x{_}^2^. These shouldn’t be math: * To get the famous equation, write {{$e = mc^2$}}. * $22,000 is a _lot_ of money. So is $34,000. \(It worked if "lot" is emphasized.) * Shoes \($20) and socks \($5). * Escaped {{$}}: $73 _this should be emphasized_ 23$. Here’s a LaTeX table: ---- h1. {anchor:special-characters}Special Characters Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: \( Right paren: ) Greater-than: > Hash: # Period: . Bang: \! Plus: \+ Minus: \- ---- h1. {anchor:links}Links h2. {anchor:explicit}Explicit Just a [URL|/url/]. [URL and title|/url/]. [URL and title|/url/]. [URL and title|/url/]. [URL and title|/url/] [URL and title|/url/] [with_underscore|/url/with_underscore] [Email link|mailto:nobody@nowhere.net] [Empty|]. h2. {anchor:reference}Reference Foo [bar|/url/]. With [embedded \[brackets\]|/url/]. [b|/url/] by itself should be a link. Indented [once|/url]. Indented [twice|/url]. Indented [thrice|/url]. This should \[not\]\[\] be a link. {noformat} [not]: /url{noformat} Foo [bar|/url/]. Foo [biz|/url/]. h2. {anchor:with-ampersands}With ampersands Here’s a [link with an ampersand in the URL|http://example.com/?foo=1&bar=2]. Here’s a link with an amersand in the link text: [AT&T|http://att.com/]. Here’s an [inline link|/script?foo=1&bar=2]. Here’s an [inline link in pointy braces|/script?foo=1&bar=2]. h2. {anchor:autolinks}Autolinks With an ampersand: [http://example.com/?foo=1&bar=2] * In a list? * [http://example.com/] * It should. An e-mail address: [mailto:nobody@nowhere.net] bq. Blockquoted: [http://example.com/] Auto-links should not occur here: {{}} {noformat} or here: {noformat} ---- h1. {anchor:images}Images From "Voyage dans la Lune" by Georges Melies \(1902): !lalune.jpg|title=Voyage dans la Lune, alt=lalune! Here is a movie !movie.jpg|alt=movie! icon. ---- h1. {anchor:footnotes}Footnotes Here is a footnote reference,[1] and another.[2] This should _not_ be a footnote reference, because it contains a space.\[\^my note\] Here is an inline note.[3] bq. Notes can go in quotes.[4] # And in list items.[5] This paragraph should not be part of the note, as it is not indented. \[1] Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. \[2] Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote \(as with list items). {noformat} { }{noformat} If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. \[3] This is _easier_ to type. Inline notes may contain [links|http://google.com] and {{\]}} verbatim characters, as well as \[bracketed text]. \[4] In quote. \[5] In list. ================================================ FILE: test/writer.latex ================================================ % Options for packages loaded elsewhere \PassOptionsToPackage{unicode}{hyperref} \PassOptionsToPackage{hyphens}{url} \documentclass[ ]{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 \usepackage{fancyvrb} \usepackage{graphicx} \makeatletter \newsavebox\pandoc@box \newcommand*\pandocbounded[1]{% scales image to fit in text height/width \sbox\pandoc@box{#1}% \Gscale@div\@tempa{\textheight}{\dimexpr\ht\pandoc@box+\dp\pandoc@box\relax}% \Gscale@div\@tempb{\linewidth}{\wd\pandoc@box}% \ifdim\@tempb\p@<\@tempa\p@\let\@tempa\@tempb\fi% select the smaller of both \ifdim\@tempa\p@<\p@\scalebox{\@tempa}{\usebox\pandoc@box}% \else\usebox{\pandoc@box}% \fi% } % Set default figure placement to htbp \def\fps@figure{htbp} \makeatother \ifLuaTeX \usepackage{luacolor} \usepackage[soul]{lua-ul} \else \usepackage{soul} \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} \VerbatimFootnotes % allow verbatim text in footnotes \hypersetup{ pdftitle={Pandoc Test Suite}, pdfauthor={John MacFarlane; Anonymous}, hidelinks, pdfcreator={LaTeX via pandoc}} \title{Pandoc Test Suite} \author{John MacFarlane \and Anonymous} \date{July 17, 2006} \begin{document} \maketitle This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Headers}\label{headers} \subsection{\texorpdfstring{Level 2 with an \href{/url}{embedded link}}{Level 2 with an embedded link}}\label{level-2-with-an-embedded-link} \subsubsection{\texorpdfstring{Level 3 with \emph{emphasis}}{Level 3 with emphasis}}\label{level-3-with-emphasis} \paragraph{Level 4}\label{level-4} \subparagraph{Level 5}\label{level-5} \section{Level 1}\label{level-1} \subsection{\texorpdfstring{Level 2 with \emph{emphasis}}{Level 2 with emphasis}}\label{level-2-with-emphasis} \subsubsection{Level 3}\label{level-3} with no blank line \subsection{Level 2}\label{level-2} with no blank line \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Paragraphs}\label{paragraphs} Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break\\ here. \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Block Quotes}\label{block-quotes} E-mail style: \begin{quote} This is a block quote. It is pretty short. \end{quote} \begin{quote} Code in a block quote: \begin{verbatim} sub status { print "working"; } \end{verbatim} A list: \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item item one \item item two \end{enumerate} Nested block quotes: \begin{quote} nested \end{quote} \begin{quote} nested \end{quote} \end{quote} This should not be a block quote: 2 \textgreater{} 1. And a following paragraph. \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Code Blocks}\label{code-blocks} Code: \begin{verbatim} ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab \end{verbatim} And: \begin{verbatim} this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ \end{verbatim} \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Lists}\label{lists} \subsection{Unordered}\label{unordered} Asterisks tight: \begin{itemize} \tightlist \item asterisk 1 \item asterisk 2 \item asterisk 3 \end{itemize} Asterisks loose: \begin{itemize} \item asterisk 1 \item asterisk 2 \item asterisk 3 \end{itemize} Pluses tight: \begin{itemize} \tightlist \item Plus 1 \item Plus 2 \item Plus 3 \end{itemize} Pluses loose: \begin{itemize} \item Plus 1 \item Plus 2 \item Plus 3 \end{itemize} Minuses tight: \begin{itemize} \tightlist \item Minus 1 \item Minus 2 \item Minus 3 \end{itemize} Minuses loose: \begin{itemize} \item Minus 1 \item Minus 2 \item Minus 3 \end{itemize} \subsection{Ordered}\label{ordered} Tight: \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item First \item Second \item Third \end{enumerate} and: \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item One \item Two \item Three \end{enumerate} Loose using tabs: \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \item First \item Second \item Third \end{enumerate} and using spaces: \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \item One \item Two \item Three \end{enumerate} Multiple paragraphs: \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \item Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. \item Item 2. \item Item 3. \end{enumerate} \subsection{Nested}\label{nested} \begin{itemize} \tightlist \item Tab \begin{itemize} \tightlist \item Tab \begin{itemize} \tightlist \item Tab \end{itemize} \end{itemize} \end{itemize} Here's another: \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item First \item Second: \begin{itemize} \tightlist \item Fee \item Fie \item Foe \end{itemize} \item Third \end{enumerate} Same thing but with paragraphs: \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \item First \item Second: \begin{itemize} \tightlist \item Fee \item Fie \item Foe \end{itemize} \item Third \end{enumerate} \subsection{Tabs and spaces}\label{tabs-and-spaces} \begin{itemize} \item this is a list item indented with tabs \item this is a list item indented with spaces \begin{itemize} \item this is an example list item indented with tabs \item this is an example list item indented with spaces \end{itemize} \end{itemize} \subsection{Fancy list markers}\label{fancy-list-markers} \begin{enumerate} \def\labelenumi{(\arabic{enumi})} \setcounter{enumi}{1} \item begins with 2 \item and now 3 with a continuation \begin{enumerate} \def\labelenumii{\roman{enumii}.} \setcounter{enumii}{3} \tightlist \item sublist with roman numerals, starting with 4 \item more items \begin{enumerate} \def\labelenumiii{(\Alph{enumiii})} \tightlist \item a subsublist \item a subsublist \end{enumerate} \end{enumerate} \end{enumerate} Nesting: \begin{enumerate} \def\labelenumi{\Alph{enumi}.} \tightlist \item Upper Alpha \begin{enumerate} \def\labelenumii{\Roman{enumii}.} \tightlist \item Upper Roman. \begin{enumerate} \def\labelenumiii{(\arabic{enumiii})} \setcounter{enumiii}{5} \tightlist \item Decimal start with 6 \begin{enumerate} \def\labelenumiv{\alph{enumiv})} \setcounter{enumiv}{2} \tightlist \item Lower alpha with paren \end{enumerate} \end{enumerate} \end{enumerate} \end{enumerate} Autonumbering: \begin{enumerate} \tightlist \item Autonumber. \item More. \begin{enumerate} \tightlist \item Nested. \end{enumerate} \end{enumerate} Should not be a list item: M.A.~2007 B. Williams \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Definition Lists}\label{definition-lists} Tight using spaces: \begin{description} \tightlist \item[apple] red fruit \item[orange] orange fruit \item[banana] yellow fruit \end{description} Tight using tabs: \begin{description} \tightlist \item[apple] red fruit \item[orange] orange fruit \item[banana] yellow fruit \end{description} Loose: \begin{description} \item[apple] red fruit \item[orange] orange fruit \item[banana] yellow fruit \end{description} Multiple blocks with italics: \begin{description} \item[\emph{apple}] red fruit contains seeds, crisp, pleasant to taste \item[\emph{orange}] orange fruit \begin{verbatim} { orange code block } \end{verbatim} \begin{quote} orange block quote \end{quote} \end{description} Multiple definitions, tight: \begin{description} \tightlist \item[apple] red fruit computer \item[orange] orange fruit bank \end{description} Multiple definitions, loose: \begin{description} \item[apple] red fruit computer \item[orange] orange fruit bank \end{description} Blank line after term, indented marker, alternate markers: \begin{description} \item[apple] red fruit computer \item[orange] orange fruit \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item sublist \item sublist \end{enumerate} \end{description} \section{HTML Blocks}\label{html-blocks} Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is \emph{emphasized} And this is \textbf{strong} Here's a simple block: foo This should be a code block, though: \begin{verbatim}
                foo
                \end{verbatim} As should this: \begin{verbatim}
                foo
                \end{verbatim} Now, nested: foo This should just be an HTML comment: Multiline: Code block: \begin{verbatim} \end{verbatim} Just plain comment, with trailing spaces on the line: Code: \begin{verbatim}
                \end{verbatim} Hr's: \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Inline Markup}\label{inline-markup} This is \emph{emphasized}, and so \emph{is this}. This is \textbf{strong}, and so \textbf{is this}. An \emph{\href{/url}{emphasized link}}. \textbf{\emph{This is strong and em.}} So is \textbf{\emph{this}} word. \textbf{\emph{This is strong and em.}} So is \textbf{\emph{this}} word. This is code: \texttt{\textgreater{}}, \texttt{\$}, \texttt{\textbackslash{}}, \texttt{\textbackslash{}\$}, \texttt{\textless{}html\textgreater{}}. \st{This is \emph{strikeout}.} Superscripts: a\textsuperscript{bc}d a\textsuperscript{\emph{hello}} a\textsuperscript{hello~there}. Subscripts: H\textsubscript{2}O, H\textsubscript{23}O, H\textsubscript{many~of~them}O. These should not be superscripts or subscripts, because of the unescaped spaces: a\^{}b c\^{}d, a\textasciitilde b c\textasciitilde d. \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Smart quotes, ellipses, dashes}\label{smart-quotes-ellipses-dashes} ``Hello,'' said the spider. ``\,`Shelob' is my name.'' `A', `B', and `C' are letters. `Oak,' `elm,' and `beech' are names of trees. So is `pine.' `He said, ``I want to go.''\,' Were you alive in the 70's? Here is some quoted `\texttt{code}' and a ``\href{http://example.com/?foo=1&bar=2}{quoted link}''. Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses\ldots and\ldots and\ldots. \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{LaTeX}\label{latex} \begin{itemize} \tightlist \item \cite[22-23]{smith.1899} \item \(2+2=4\) \item \(x \in y\) \item \(\alpha \wedge \omega\) \item \(223\) \item \(p\)-Tree \item Here's some display math: \[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\] \item Here's one that has a line break in it: \(\alpha + \omega \times x^2\). \end{itemize} These shouldn't be math: \begin{itemize} \tightlist \item To get the famous equation, write \texttt{\$e\ =\ mc\^{}2\$}. \item \$22,000 is a \emph{lot} of money. So is \$34,000. (It worked if ``lot'' is emphasized.) \item Shoes (\$20) and socks (\$5). \item Escaped \texttt{\$}: \$73 \emph{this should be emphasized} 23\$. \end{itemize} Here's a LaTeX table: \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Special Characters}\label{special-characters} Here is some unicode: \begin{itemize} \tightlist \item I hat: Î \item o umlaut: ö \item section: § \item set membership: ∈ \item copyright: © \end{itemize} AT\&T has an ampersand in their name. AT\&T is another way to write it. This \& that. 4 \textless{} 5. 6 \textgreater{} 5. Backslash: \textbackslash{} Backtick: ` Asterisk: * Underscore: \_ Left brace: \{ Right brace: \} Left bracket: {[} Right bracket: {]} Left paren: ( Right paren: ) Greater-than: \textgreater{} Hash: \# Period: . Bang: ! Plus: + Minus: - \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Links}\label{links} \subsection{Explicit}\label{explicit} Just a \href{/url/}{URL}. \href{/url/}{URL and title}. \href{/url/}{URL and title}. \href{/url/}{URL and title}. \href{/url/}{URL and title} \href{/url/}{URL and title} \href{/url/with_underscore}{with\_underscore} \href{mailto:nobody@nowhere.net}{Email link} \href{}{Empty}. \subsection{Reference}\label{reference} Foo \href{/url/}{bar}. With \href{/url/}{embedded {[}brackets{]}}. \href{/url/}{b} by itself should be a link. Indented \href{/url}{once}. Indented \href{/url}{twice}. Indented \href{/url}{thrice}. This should {[}not{]}{[}{]} be a link. \begin{verbatim} [not]: /url \end{verbatim} Foo \href{/url/}{bar}. Foo \href{/url/}{biz}. \subsection{With ampersands}\label{with-ampersands} Here's a \href{http://example.com/?foo=1&bar=2}{link with an ampersand in the URL}. Here's a link with an amersand in the link text: \href{http://att.com/}{AT\&T}. Here's an \href{/script?foo=1&bar=2}{inline link}. Here's an \href{/script?foo=1&bar=2}{inline link in pointy braces}. \subsection{Autolinks}\label{autolinks} With an ampersand: \url{http://example.com/?foo=1&bar=2} \begin{itemize} \tightlist \item In a list? \item \url{http://example.com/} \item It should. \end{itemize} An e-mail address: \href{mailto:nobody@nowhere.net}{\nolinkurl{nobody@nowhere.net}} \begin{quote} Blockquoted: \url{http://example.com/} \end{quote} Auto-links should not occur here: \texttt{\textless{}http://example.com/\textgreater{}} \begin{verbatim} or here: \end{verbatim} \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Images}\label{images} From ``Voyage dans la Lune'' by Georges Melies (1902): \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,alt={lalune}]{lalune.jpg}} \caption{lalune} \end{figure} Here is a movie \pandocbounded{\includegraphics[keepaspectratio,alt={movie}]{movie.jpg}} icon. \begin{center}\rule{0.5\linewidth}{0.5pt}\end{center} \section{Footnotes}\label{footnotes} Here is a footnote reference,\footnote{Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.} and another.\footnote{Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). \begin{Verbatim} { } \end{Verbatim} If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.} This should \emph{not} be a footnote reference, because it contains a space.{[}\^{}my note{]} Here is an inline note.\footnote{This is \emph{easier} to type. Inline notes may contain \href{http://google.com}{links} and \texttt{{]}} verbatim characters, as well as {[}bracketed text{]}.} \begin{quote} Notes can go in quotes.\footnote{In quote.} \end{quote} \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \tightlist \item And in list items.\footnote{In list.} \end{enumerate} This paragraph should not be part of the note, as it is not indented. \end{document} ================================================ FILE: test/writer.man ================================================ .TH "Pandoc Test Suite" "" "July 17, 2006" "" .PP This is a set of tests for pandoc. Most of them are adapted from John Gruber\(cqs markdown test suite. .PP * * * * * .SH Headers .SS Level 2 with an embedded link .SS Level 3 with \f[I]emphasis\f[R] .SS Level 4 .SS Level 5 .SH Level 1 .SS Level 2 with \f[I]emphasis\f[R] .SS Level 3 with no blank line .SS Level 2 with no blank line .PP * * * * * .SH Paragraphs Here\(cqs a regular paragraph. .PP 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 a list item. .PP Here\(cqs one with a bullet. * criminey. .PP There should be a hard line break .PD 0 .P .PD here. .PP * * * * * .SH Block Quotes E\-mail style: .RS .PP This is a block quote. It is pretty short. .RE .RS .PP Code in a block quote: .IP .EX sub status { print \(dqworking\(dq; } .EE .PP A list: .IP "1." 3 item one .IP "2." 3 item two .PP Nested block quotes: .RS .PP nested .RE .RS .PP nested .RE .RE .PP This should not be a block quote: 2 > 1. .PP And a following paragraph. .PP * * * * * .SH Code Blocks Code: .IP .EX \-\-\-\- (should be four hyphens) sub status { print \(dqworking\(dq; } this code block is indented by one tab .EE .PP And: .IP .EX this code block is indented by two tabs These should not be escaped: \(rs$ \(rs\(rs \(rs> \(rs[ \(rs{ .EE .PP * * * * * .SH Lists .SS Unordered Asterisks tight: .IP \(bu 2 asterisk 1 .IP \(bu 2 asterisk 2 .IP \(bu 2 asterisk 3 .PP Asterisks loose: .IP \(bu 2 asterisk 1 .IP \(bu 2 asterisk 2 .IP \(bu 2 asterisk 3 .PP Pluses tight: .IP \(bu 2 Plus 1 .IP \(bu 2 Plus 2 .IP \(bu 2 Plus 3 .PP Pluses loose: .IP \(bu 2 Plus 1 .IP \(bu 2 Plus 2 .IP \(bu 2 Plus 3 .PP Minuses tight: .IP \(bu 2 Minus 1 .IP \(bu 2 Minus 2 .IP \(bu 2 Minus 3 .PP Minuses loose: .IP \(bu 2 Minus 1 .IP \(bu 2 Minus 2 .IP \(bu 2 Minus 3 .SS Ordered Tight: .IP "1." 3 First .IP "2." 3 Second .IP "3." 3 Third .PP and: .IP "1." 3 One .IP "2." 3 Two .IP "3." 3 Three .PP Loose using tabs: .IP "1." 3 First .IP "2." 3 Second .IP "3." 3 Third .PP and using spaces: .IP "1." 3 One .IP "2." 3 Two .IP "3." 3 Three .PP Multiple paragraphs: .IP "1." 3 Item 1, graf one. .RS 4 .PP Item 1. graf two. The quick brown fox jumped over the lazy dog\(cqs back. .RE .IP "2." 3 Item 2. .IP "3." 3 Item 3. .SS Nested .IP \(bu 2 Tab .RS 2 .IP \(bu 2 Tab .RS 2 .IP \(bu 2 Tab .RE .RE .PP Here\(cqs another: .IP "1." 3 First .IP "2." 3 Second: .RS 4 .IP \(bu 2 Fee .IP \(bu 2 Fie .IP \(bu 2 Foe .RE .IP "3." 3 Third .PP Same thing but with paragraphs: .IP "1." 3 First .IP "2." 3 Second: .RS 4 .IP \(bu 2 Fee .IP \(bu 2 Fie .IP \(bu 2 Foe .RE .IP "3." 3 Third .SS Tabs and spaces .IP \(bu 2 this is a list item indented with tabs .IP \(bu 2 this is a list item indented with spaces .RS 2 .IP \(bu 2 this is an example list item indented with tabs .IP \(bu 2 this is an example list item indented with spaces .RE .SS Fancy list markers .IP "(2)" 4 begins with 2 .IP "(3)" 4 and now 3 .RS 4 .PP with a continuation .IP "iv." 4 sublist with roman numerals, starting with 4 .IP " v." 4 more items .RS 4 .IP "(A)" 4 a subsublist .IP "(B)" 4 a subsublist .RE .RE .PP Nesting: .IP "A." 3 Upper Alpha .RS 4 .IP "I." 3 Upper Roman. .RS 4 .IP "(6)" 4 Decimal start with 6 .RS 4 .IP "c)" 3 Lower alpha with paren .RE .RE .RE .PP Autonumbering: .IP "1." 3 Autonumber. .IP "2." 3 More. .RS 4 .IP "1." 3 Nested. .RE .PP Should not be a list item: .PP M.A.\ 2007 .PP B. Williams .PP * * * * * .SH Definition Lists Tight using spaces: .TP apple red fruit .TP orange orange fruit .TP banana yellow fruit .PP Tight using tabs: .TP apple red fruit .TP orange orange fruit .TP banana yellow fruit .PP Loose: .TP apple red fruit .TP orange orange fruit .TP banana yellow fruit .PP Multiple blocks with italics: .TP \f[I]apple\f[R] red fruit .RS .PP contains seeds, crisp, pleasant to taste .RE .TP \f[I]orange\f[R] orange fruit .RS .IP .EX { orange code block } .EE .RS .PP orange block quote .RE .RE .PP Multiple definitions, tight: .TP apple red fruit computer .TP orange orange fruit bank .PP Multiple definitions, loose: .TP apple red fruit computer .TP orange orange fruit bank .PP Blank line after term, indented marker, alternate markers: .TP apple red fruit computer .TP orange orange fruit .RS .IP "1." 3 sublist .IP "2." 3 sublist .RE .SH HTML Blocks Simple block on one line: foo .PP And nested without indentation: .PP foo bar .PP Interpreted markdown in a table: This is \f[I]emphasized\f[R] And this is \f[B]strong\f[R] .PP Here\(cqs a simple block: .PP foo .PP This should be a code block, though: .IP .EX
                foo
                .EE .PP As should this: .IP .EX
                foo
                .EE .PP Now, nested: foo .PP This should just be an HTML comment: .PP Multiline: .PP Code block: .IP .EX .EE .PP Just plain comment, with trailing spaces on the line: .PP Code: .IP .EX
                .EE .PP Hr\(cqs: .PP * * * * * .SH Inline Markup This is \f[I]emphasized\f[R], and so \f[I]is this\f[R]. .PP This is \f[B]strong\f[R], and so \f[B]is this\f[R]. .PP An \f[I]emphasized link\f[R]. .PP \f[B]\f[BI]This is strong and em.\f[B]\f[R] .PP So is \f[B]\f[BI]this\f[B]\f[R] word. .PP \f[B]\f[BI]This is strong and em.\f[B]\f[R] .PP So is \f[B]\f[BI]this\f[B]\f[R] word. .PP This is code: \f[CR]>\f[R], \f[CR]$\f[R], \f[CR]\(rs\f[R], \f[CR]\(rs$\f[R], \f[CR]\f[R]. .PP [STRIKEOUT:This is \f[I]strikeout\f[R].] .PP Superscripts: a^bc^d a^\f[I]hello\f[R]^ a^hello\ there^. .PP Subscripts: H~2~O, H~23~O, H~many\ of\ them~O. .PP These should not be superscripts or subscripts, because of the unescaped spaces: a\(hab c\(had, a\(tib c\(tid. .PP * * * * * .SH Smart quotes, ellipses, dashes \(lqHello,\(rq said the spider. \(lq`Shelob' is my name.\(rq .PP `A', `B', and `C' are letters. .PP `Oak,' `elm,' and `beech' are names of trees. So is `pine.' .PP `He said, \(lqI want to go.\(rq' Were you alive in the 70\(cqs? .PP Here is some quoted `\f[CR]code\f[R]' and a \(lq\c .UR http://example.com/?foo=1&bar=2 quoted link .UE \c \(rq. .PP Some dashes: one\(emtwo \(em three\(emfour \(em five. .PP Dashes between numbers: 5\(en7, 255\(en66, 1987\(en1999. .PP Ellipses\&...and\&...and\&.... .PP * * * * * .SH LaTeX .IP \(bu 2 .IP \(bu 2 2 + 2 = 4 .IP \(bu 2 \f[I]x\f[R] ∈ \f[I]y\f[R] .IP \(bu 2 \f[I]α\f[R] ∧ \f[I]ω\f[R] .IP \(bu 2 223 .IP \(bu 2 \f[I]p\f[R]\-Tree .IP \(bu 2 Here\(cqs some display math: .RS $$\(rsfrac{d}{dx}f(x)=\(rslim_{h\(rsto 0}\(rsfrac{f(x+h)\-f(x)}{h}$$ .RE .IP \(bu 2 Here\(cqs one that has a line break in it: \f[I]α\f[R] + \f[I]ω\f[R] × \f[I]x\f[R]^2^. .PP These shouldn\(cqt be math: .IP \(bu 2 To get the famous equation, write \f[CR]$e = mc\(ha2$\f[R]. .IP \(bu 2 $22,000 is a \f[I]lot\f[R] of money. So is $34,000. (It worked if \(lqlot\(rq is emphasized.) .IP \(bu 2 Shoes ($20) and socks ($5). .IP \(bu 2 Escaped \f[CR]$\f[R]: $73 \f[I]this should be emphasized\f[R] 23$. .PP Here\(cqs a LaTeX table: .PP * * * * * .SH Special Characters Here is some unicode: .IP \(bu 2 I hat: Î .IP \(bu 2 o umlaut: ö .IP \(bu 2 section: § .IP \(bu 2 set membership: ∈ .IP \(bu 2 copyright: © .PP AT&T has an ampersand in their name. .PP AT&T is another way to write it. .PP This & that. .PP 4 < 5. .PP 6 > 5. .PP Backslash: \(rs .PP Backtick: \(ga .PP Asterisk: * .PP Underscore: _ .PP Left brace: { .PP Right brace: } .PP Left bracket: [ .PP Right bracket: ] .PP Left paren: ( .PP Right paren: ) .PP Greater\-than: > .PP Hash: # .PP Period: . .PP Bang: ! .PP Plus: + .PP Minus: \- .PP * * * * * .SH Links .SS Explicit Just a URL. .PP URL and title. .PP URL and title. .PP URL and title. .PP URL and title .PP URL and title .PP with_underscore .PP \c .MT nobody@nowhere.net Email link .ME \c .PP Empty. .SS Reference Foo bar. .PP With embedded [brackets]. .PP b by itself should be a link. .PP Indented once. .PP Indented twice. .PP Indented thrice. .PP This should [not][] be a link. .IP .EX [not]: /url .EE .PP Foo bar. .PP Foo biz. .SS With ampersands Here\(cqs a \c .UR http://example.com/?foo=1&bar=2 link with an ampersand in the URL .UE \c \&. .PP Here\(cqs a link with an amersand in the link text: \c .UR http://att.com/ AT&T .UE \c \&. .PP Here\(cqs an inline link. .PP Here\(cqs an inline link in pointy braces. .SS Autolinks With an ampersand: \c .UR http://example.com/?foo=1&bar=2 .UE \c .IP \(bu 2 In a list? .IP \(bu 2 \c .UR http://example.com/ .UE \c .IP \(bu 2 It should. .PP An e\-mail address: \c .MT nobody@nowhere.net .ME \c .RS .PP Blockquoted: \c .UR http://example.com/ .UE \c .RE .PP Auto\-links should not occur here: \f[CR]\f[R] .IP .EX or here: .EE .PP * * * * * .SH Images From \(lqVoyage dans la Lune\(rq by Georges Melies (1902): [IMAGE: lalune] lalune .PP Here is a movie [IMAGE: movie] icon. .PP * * * * * .SH Footnotes Here is a footnote reference,[1] and another.[2] This should \f[I]not\f[R] be a footnote reference, because it contains a space.[\(hamy note] Here is an inline note.[3] .RS .PP Notes can go in quotes.[4] .RE .IP "1." 3 And in list items.[5] .PP This paragraph should not be part of the note, as it is not indented. .SH NOTES .SS [1] .PP Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. .SS [2] .PP Here\(cqs the long note. This one contains multiple blocks. .PP Subsequent blocks are indented to show that they belong to the footnote (as with list items). .IP .EX { } .EE .PP If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. .SS [3] .PP This is \f[I]easier\f[R] to type. Inline notes may contain \c .UR http://google.com links .UE \c \ and \f[CR]]\f[R] verbatim characters, as well as [bracketed text]. .SS [4] .PP In quote. .SS [5] .PP In list. .SH AUTHORS John MacFarlane; Anonymous. ================================================ FILE: test/writer.markdown ================================================ --- author: - John MacFarlane - Anonymous date: July 17, 2006 title: Pandoc Test Suite --- This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. -------------------------------------------------------------------------------- # Headers ## Level 2 with an [embedded link](/url) ### Level 3 with *emphasis* #### Level 4 ##### Level 5 # Level 1 ## Level 2 with *emphasis* ### Level 3 with no blank line ## Level 2 with no blank line -------------------------------------------------------------------------------- # Paragraphs Here's a regular paragraph. 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 a list item. Here's one with a bullet. \* criminey. There should be a hard line break\ here. -------------------------------------------------------------------------------- # Block Quotes E-mail style: > This is a block quote. It is pretty short. > Code in a block quote: > > sub status { > print "working"; > } > > A list: > > 1. item one > 2. item two > > Nested block quotes: > > > nested > > > nested This should not be a block quote: 2 \> 1. And a following paragraph. -------------------------------------------------------------------------------- # Code Blocks Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ -------------------------------------------------------------------------------- # Lists ## Unordered Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 ## Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. ## Nested - Tab - Tab - Tab Here's another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third ## Tabs and spaces - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces ## Fancy list markers (2) begins with 2 (3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items (A) a subsublist (B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. (6) Decimal start with 6 c) Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams -------------------------------------------------------------------------------- # Definition Lists Tight using spaces: apple : red fruit orange : orange fruit banana : yellow fruit Tight using tabs: apple : red fruit orange : orange fruit banana : yellow fruit Loose: apple : red fruit orange : orange fruit banana : yellow fruit Multiple blocks with italics: *apple* : red fruit contains seeds, crisp, pleasant to taste *orange* : orange fruit { orange code block } > orange block quote Multiple definitions, tight: apple : red fruit : computer orange : orange fruit : bank Multiple definitions, loose: apple : red fruit : computer orange : orange fruit : bank Blank line after term, indented marker, alternate markers: apple : red fruit : computer orange : orange fruit 1. sublist 2. sublist # HTML Blocks Simple block on one line: ::: {} foo ::: And nested without indentation: :::::: {} :::: {} ::: {} foo ::: :::: ::: {} bar ::: :::::: Interpreted markdown in a table:
                This is *emphasized* And this is **strong**
                Here's a simple block: ::: {} foo ::: This should be a code block, though:
                foo
                As should this:
                foo
                Now, nested: ::::: {} :::: {} ::: {} foo ::: :::: ::::: This should just be an HTML comment: Multiline: Code block: Just plain comment, with trailing spaces on the line: Code:
                Hr's:








                -------------------------------------------------------------------------------- # Inline Markup This is *emphasized*, and so *is this*. This is **strong**, and so **is this**. An *[emphasized link](/url)*. ***This is strong and em.*** So is ***this*** word. ***This is strong and em.*** So is ***this*** word. This is code: `>`, `$`, `\`, `\$`, ``. ~~This is *strikeout*.~~ Superscripts: a^bc^d a^*hello*^ a^hello there^. Subscripts: H~2~O, H~23~O, H~many of them~O. These should not be superscripts or subscripts, because of the unescaped spaces: a\^b c\^d, a\~b c\~d. -------------------------------------------------------------------------------- # Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70's? Here is some quoted '`code`' and a "[quoted link](http://example.com/?foo=1&bar=2)". Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses...and...and.... -------------------------------------------------------------------------------- # LaTeX - `\cite[22-23]{smith.1899}`{=tex} - $2+2=4$ - $x \in y$ - $\alpha \wedge \omega$ - $223$ - $p$-Tree - Here's some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ - Here's one that has a line break in it: $\alpha + \omega \times x^2$. These shouldn't be math: - To get the famous equation, write `$e = mc^2$`. - \$22,000 is a *lot* of money. So is \$34,000. (It worked if "lot" is emphasized.) - Shoes (\$20) and socks (\$5). - Escaped `$`: \$73 *this should be emphasized* 23\$. Here's a LaTeX table: \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} -------------------------------------------------------------------------------- # Special Characters Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 \< 5. 6 \> 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: { Right brace: } Left bracket: \[ Right bracket: \] Left paren: ( Right paren: ) Greater-than: \> Hash: \# Period: . Bang: ! Plus: + Minus: - -------------------------------------------------------------------------------- # Links ## Explicit Just a [URL](/url/). [URL and title](/url/ "title"). [URL and title](/url/ "title preceded by two spaces"). [URL and title](/url/ "title preceded by a tab"). [URL and title](/url/ "title with "quotes" in it") [URL and title](/url/ "title with single quotes") [with_underscore](/url/with_underscore) [Email link](mailto:nobody@nowhere.net) [Empty](). ## Reference Foo [bar](/url/). With [embedded \[brackets\]](/url/). [b](/url/) by itself should be a link. Indented [once](/url). Indented [twice](/url). Indented [thrice](/url). This should \[not\]\[\] be a link. [not]: /url Foo [bar](/url/ "Title with "quotes" inside"). Foo [biz](/url/ "Title with "quote" inside"). ## With ampersands Here's a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2). Here's a link with an amersand in the link text: [AT&T](http://att.com/ "AT&T"). Here's an [inline link](/script?foo=1&bar=2). Here's an [inline link in pointy braces](/script?foo=1&bar=2). ## Autolinks With an ampersand: - In a list? - - It should. An e-mail address: > Blockquoted: Auto-links should not occur here: `` or here: -------------------------------------------------------------------------------- # Images From "Voyage dans la Lune" by Georges Melies (1902): ![lalune](lalune.jpg "Voyage dans la Lune") Here is a movie ![movie](movie.jpg) icon. -------------------------------------------------------------------------------- # Footnotes Here is a footnote reference,[^1] and another.[^2] This should *not* be a footnote reference, because it contains a space.\[\^my note\] Here is an inline note.[^3] > Notes can go in quotes.[^4] 1. And in list items.[^5] This paragraph should not be part of the note, as it is not indented. [^1]: Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. [^2]: Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). { } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. [^3]: This is *easier* to type. Inline notes may contain [links](http://google.com) and `]` verbatim characters, as well as \[bracketed text\]. [^4]: In quote. [^5]: In list. ================================================ FILE: test/writer.markua ================================================ This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. * * * {id: headers} # Headers {id: level-2-with-an-embedded-link} ## Level 2 with an [embedded link](/url) {id: level-3-with-emphasis} ### Level 3 with *emphasis* {id: level-4} #### Level 4 {id: level-5} ##### Level 5 {id: level-1} # Level 1 {id: level-2-with-emphasis} ## Level 2 with *emphasis* {id: level-3} ### Level 3 with no blank line {id: level-2} ## Level 2 with no blank line * * * {id: paragraphs} # Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break here. * * * {id: block-quotes} # Block Quotes E-mail style: > This is a block quote. It is pretty short. > Code in a block quote: > > ``` > sub status { > print "working"; > } > ``` > > A list: > > 1. item one > 2. item two > > Nested block quotes: > > > nested > > > nested This should not be a block quote: 2 > 1. And a following paragraph. * * * {id: code-blocks} # Code Blocks Code: ``` ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab ``` And: ``` this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ ``` * * * {id: lists} # Lists {id: unordered} ## Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: * Plus 1 * Plus 2 * Plus 3 Pluses loose: * Plus 1 * Plus 2 * Plus 3 Minuses tight: * Minus 1 * Minus 2 * Minus 3 Minuses loose: * Minus 1 * Minus 2 * Minus 3 {id: ordered} ## Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. 2. Item 2. 3. Item 3. {id: nested} ## Nested * Tab * Tab * Tab Here’s another: 1. First 2. Second: * Fee * Fie * Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: * Fee * Fie * Foe 3. Third {id: tabs-and-spaces} ## Tabs and spaces * this is a list item indented with tabs * this is a list item indented with spaces * this is an example list item indented with tabs * this is an example list item indented with spaces {id: fancy-list-markers} ## Fancy list markers 2) begins with 2 3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items A) a subsublist B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. 6) Decimal start with 6 c) Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams * * * {id: definition-lists} # Definition Lists Tight using spaces: apple : red fruit orange : orange fruit banana : yellow fruit Tight using tabs: apple : red fruit orange : orange fruit banana : yellow fruit Loose: apple : red fruit orange : orange fruit banana : yellow fruit Multiple blocks with italics: *apple* : red fruit contains seeds, crisp, pleasant to taste *orange* : orange fruit ``` { orange code block } ``` > orange block quote Multiple definitions, tight: apple : red fruit : computer orange : orange fruit : bank Multiple definitions, loose: apple : red fruit : computer orange : orange fruit : bank Blank line after term, indented marker, alternate markers: apple : red fruit : computer orange : orange fruit 1. sublist 2. sublist {id: html-blocks} # HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is *emphasized* And this is **strong** Here’s a simple block: foo This should be a code block, though: ```
                foo
                ``` As should this: ```
                foo
                ``` Now, nested: foo This should just be an HTML comment: Multiline: Code block: ``` ``` Just plain comment, with trailing spaces on the line: Code: ```
                ``` Hr’s: * * * {id: inline-markup} # Inline Markup This is *emphasized*, and so *is this*. This is **strong**, and so **is this**. An *[emphasized link](/url)*. ***This is strong and em.*** So is ***this*** word. ***This is strong and em.*** So is ***this*** word. This is code: `>`, `$`, `\`, `\$`, ``. ~~This is *strikeout*.~~ Superscripts: a^bc^d a^*hello*^ a^hello there^. Subscripts: H~2~O, H~23~O, H~many of them~O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. * * * {id: smart-quotes-ellipses-dashes} # Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70’s? Here is some quoted '`code`' and a "[quoted link](http://example.com/?foo=1&bar=2)". Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. * * * {id: latex} # LaTeX * * `2+2=4`$ * `x \in y`$ * `\alpha \wedge \omega`$ * `223`$ * `p`$-Tree * Here’s some display math: {format: latex} ``` \frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h} ``` * Here’s one that has a line break in it: `\alpha + \omega \times x^2`$. These shouldn’t be math: * To get the famous equation, write `$e = mc^2$`. * $22,000 is a *lot* of money. So is $34,000. (It worked if "lot" is emphasized.) * Shoes ($20) and socks ($5). * Escaped `$`: $73 *this should be emphasized* 23$. Here’s a LaTeX table: * * * {id: special-characters} # Special Characters Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - * * * {id: links} # Links {id: explicit} ## Explicit Just a [URL](/url/). [URL and title](/url/){title: "title"}. [URL and title](/url/){title: "title preceded by two spaces"}. [URL and title](/url/){title: "title preceded by a tab"}. [URL and title](/url/){title: "title with "quotes" in it"} [URL and title](/url/){title: "title with single quotes"} [with_underscore](/url/with_underscore) [Email link](mailto:nobody@nowhere.net) [Empty](). {id: reference} ## Reference Foo [bar](/url/). With [embedded [brackets]](/url/). [b](/url/) by itself should be a link. Indented [once](/url). Indented [twice](/url). Indented [thrice](/url). This should [not][] be a link. ``` [not]: /url ``` Foo [bar](/url/){title: "Title with "quotes" inside"}. Foo [biz](/url/){title: "Title with "quote" inside"}. {id: with-ampersands} ## With ampersands Here’s a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2). Here’s a link with an amersand in the link text: [AT&T](http://att.com/){title: "AT&T"}. Here’s an [inline link](/script?foo=1&bar=2). Here’s an [inline link in pointy braces](/script?foo=1&bar=2). {id: autolinks} ## Autolinks With an ampersand: [http:~/~/example.com/?foo=1&bar=2](http://example.com/?foo=1&bar=2){class: uri} * In a list? * [http:~/~/example.com/](http://example.com/){class: uri} * It should. An e-mail address: [nobody@nowhere.net](mailto:nobody@nowhere.net){class: email} > Blockquoted: [http:~/~/example.com/](http://example.com/){class: uri} Auto-links should not occur here: `` ``` or here: ``` * * * {id: images} # Images From "Voyage dans la Lune" by Georges Melies (1902): {alt: "lalune", title: "Voyage dans la Lune"} ![](lalune.jpg) lalune Here is a movie {alt: "movie"} ![](movie.jpg) icon. * * * {id: footnotes} # Footnotes Here is a footnote reference,[^1] and another.[^2] This should *not* be a footnote reference, because it contains a space.[^my note] Here is an inline note.[^3] > Notes can go in quotes.[^4] 1. And in list items.[^5] This paragraph should not be part of the note, as it is not indented. [^1]: Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. [^2]: Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). ``` { } ``` If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. [^3]: This is *easier* to type. Inline notes may contain [links](http://google.com) and `]` verbatim characters, as well as [bracketed text]. [^4]: In quote. [^5]: In list. ================================================ FILE: test/writer.mediawiki ================================================ This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ----- = Headers = == Level 2 with an [[url|embedded link]] == === Level 3 with ''emphasis'' === ==== Level 4 ==== ===== Level 5 ===== = Level 1 = == Level 2 with ''emphasis'' == === Level 3 === with no blank line == Level 2 == with no blank line ----- = Paragraphs = Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break
                here. ----- = Block Quotes = E-mail style:
                This is a block quote. It is pretty short.
                Code in a block quote:
                sub status {
                    print "working";
                }
                A list: # item one # item two Nested block quotes:
                nested
                nested
                This should not be a block quote: 2 > 1. And a following paragraph. ----- = Code Blocks = Code:
                ---- (should be four hyphens)
                
                sub status {
                    print "working";
                }
                
                this code block is indented by one tab
                And:
                    this code block is indented by two tabs
                
                These should not be escaped:  \$ \\ \> \[ \{
                ----- = Lists = == Unordered == Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: * Plus 1 * Plus 2 * Plus 3 Pluses loose: * Plus 1 * Plus 2 * Plus 3 Minuses tight: * Minus 1 * Minus 2 * Minus 3 Minuses loose: * Minus 1 * Minus 2 * Minus 3 == Ordered == Tight: # First # Second # Third and: # One # Two # Three Loose using tabs: # First # Second # Third and using spaces: # One # Two # Three Multiple paragraphs:
                1. Item 1, graf one.

                  Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

                2. Item 2.

                3. Item 3.

                == Nested == * Tab ** Tab *** Tab Here’s another: # First # Second: #* Fee #* Fie #* Foe # Third Same thing but with paragraphs: # First # Second: #* Fee #* Fie #* Foe # Third == Tabs and spaces == * this is a list item indented with tabs * this is a list item indented with spaces ** this is an example list item indented with tabs ** this is an example list item indented with spaces == Fancy list markers ==
                1. begins with 2

                2. and now 3

                  with a continuation

                  1. sublist with roman numerals, starting with 4
                  2. more items
                    1. a subsublist
                    2. a subsublist
                Nesting:
                1. Upper Alpha
                  1. Upper Roman.
                    1. Decimal start with 6
                      1. Lower alpha with paren
                Autonumbering: # Autonumber. # More. ## Nested. Should not be a list item: M.A. 2007 B. Williams ----- = Definition Lists = Tight using spaces: ; apple : red fruit ; orange : orange fruit ; banana : yellow fruit Tight using tabs: ; apple : red fruit ; orange : orange fruit ; banana : yellow fruit Loose: ; apple : red fruit ; orange : orange fruit ; banana : yellow fruit Multiple blocks with italics:
                ''apple''

                red fruit

                contains seeds, crisp, pleasant to taste

                ''orange''

                orange fruit

                { orange code block }

                orange block quote

                Multiple definitions, tight: ; apple : red fruit : computer ; orange : orange fruit : bank Multiple definitions, loose: ; apple : red fruit : computer ; orange : orange fruit : bank Blank line after term, indented marker, alternate markers: ; apple : red fruit : computer ; orange : orange fruit ;# sublist ;# sublist = HTML Blocks = Simple block on one line:
                foo
                And nested without indentation:
                foo
                bar
                Interpreted markdown in a table:
                This is ''emphasized'' And this is '''strong'''
                Here’s a simple block:
                foo
                This should be a code block, though:
                <div>
                    foo
                </div>
                As should this:
                <div>foo</div>
                Now, nested:
                foo
                This should just be an HTML comment: Multiline: Code block:
                <!-- Comment -->
                Just plain comment, with trailing spaces on the line: Code:
                <hr />
                Hr’s:








                ----- = Inline Markup = This is ''emphasized'', and so ''is this''. This is '''strong''', and so '''is this'''. An ''[[url|emphasized link]]''. '''''This is strong and em.''''' So is '''''this''''' word. '''''This is strong and em.''''' So is '''''this''''' word. This is code: >, $, \, \$, <html>. This is ''strikeout''. Superscripts: abcd a''hello'' ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ----- = Smart quotes, ellipses, dashes = “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘code’ and a “[http://example.com/?foo=1&bar=2 quoted link]”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ----- = LaTeX = * * 2+2=4 * x \in y * \alpha \wedge \omega * 223 * p-Tree * Here’s some display math: \frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h} * Here’s one that has a line break in it: \alpha + \omega \times x^2. These shouldn’t be math: * To get the famous equation, write $e = mc^2$. * $22,000 is a ''lot'' of money. So is $34,000. (It worked if “lot” is emphasized.) * Shoes ($20) and socks ($5). * Escaped $: $73 ''this should be emphasized'' 23$. Here’s a LaTeX table: ----- = Special Characters = Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - ----- = Links = == Explicit == Just a [[url/|URL]]. [[url/|URL and title]]. [[url/|URL and title]]. [[url/|URL and title]]. [[url/|URL and title]] [[url/|URL and title]] [[url/with_underscore|with_underscore]] [mailto:nobody@nowhere.net Email link] [[|Empty]]. == Reference == Foo [[url/|bar]]. With [[url/|embedded [brackets]]]. [[url/|b]] by itself should be a link. Indented [[url|once]]. Indented [[url|twice]]. Indented [[url|thrice]]. This should [not][] be a link.
                [not]: /url
                Foo [[url/|bar]]. Foo [[url/|biz]]. == With ampersands == Here’s a [http://example.com/?foo=1&bar=2 link with an ampersand in the URL]. Here’s a link with an amersand in the link text: [http://att.com/ AT&T]. Here’s an [[script?foo=1&bar=2|inline link]]. Here’s an [[script?foo=1&bar=2|inline link in pointy braces]]. == Autolinks == With an ampersand: http://example.com/?foo=1&bar=2 * In a list? * http://example.com/ * It should. An e-mail address: [mailto:nobody@nowhere.net nobody@nowhere.net]
                Blockquoted: http://example.com/
                Auto-links should not occur here: <http://example.com/>
                or here: <http://example.com/>
                ----- = Images = From “Voyage dans la Lune” by Georges Melies (1902):
                [[File:lalune.jpg|lalune]]
                Here is a movie [[File:movie.jpg|movie]] icon. ----- = Footnotes = Here is a footnote reference,Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. and another.Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items).
                  { <code> }
                If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.
                This should ''not'' be a footnote reference, because it contains a space.[^my note] Here is an inline note.This is ''easier'' to type. Inline notes may contain [http://google.com links] and ] verbatim characters, as well as [bracketed text].
                Notes can go in quotes.In quote.
                # And in list items.In list. This paragraph should not be part of the note, as it is not indented. ================================================ FILE: test/writer.ms ================================================ .\" **** Custom macro definitions ********************************* .\" * Super/subscript .\" (https://lists.gnu.org/archive/html/groff/2012-07/msg00046.html) .ds { \v'-0.3m'\\s[\\n[.s]*9u/12u] .ds } \s0\v'0.3m' .ds < \v'0.3m'\s[\\n[.s]*9u/12u] .ds > \s0\v'-0.3m' .\" * Horizontal line .de HLINE .LP .ce \l'20' .. .\" **** Settings ************************************************* .\" text width .nr LL 5.5i .\" left margin .nr PO 1.25i .\" top margin .nr HM 1.25i .\" bottom margin .nr FM 1.25i .\" header/footer width .nr LT \n[LL] .\" point size .nr PS 10p .\" line height .nr VS 12p .\" font family: A, BM, H, HN, N, P, T, ZCM .fam T .\" paragraph indent .nr PI 0m .\" interparagraph space .nr PD 0.4v .\" footnote width .nr FL \n[LL] .\" footnote point size .nr FPS (\n[PS] - 2000) .\" color used for strikeout .defcolor strikecolor rgb 0.7 0.7 0.7 .\" point size difference between heading levels .nr PSINCR 1p .\" heading level above which point size no longer changes .nr GROWPS 2 .\" comment these out if you want a dot after section numbers: .als SN SN-NO-DOT .als SN-STYLE SN-NO-DOT .\" page numbers in footer, centered .ds CH .ds CF % .hy .EQ delim @@ .EN .\" color for links (rgb) .ds PDFHREF.COLOUR 0.35 0.00 0.60 .\" border for links (default none) .ds PDFHREF.BORDER 0 0 0 .\" pdf outline fold level .nr PDFOUTLINE.FOLDLEVEL 3 .\" start out in outline view .pdfview /PageMode /UseOutlines .\" *************************************************************** .\" PDF metadata .pdfinfo /Title "Pandoc Test Suite" .pdfinfo /Author "John MacFarlane; Anonymous" .TL Pandoc Test Suite .AU John MacFarlane .AU Anonymous .AU .sp 0.5 .ft R July 17, 2006 .\" 1 column (use .2C for two column) .1C .LP This is a set of tests for pandoc. Most of them are adapted from John Gruber\(cqs markdown test suite. .HLINE .SH 1 Headers .pdfhref O 1 "Headers" .pdfhref M "headers" .SH 2 Level 2 with an \c .pdfhref W -D "/url" -A "\c" \ -- "embedded link" \& .pdfhref O 2 "Level 2 with an embedded link" .pdfhref M "level-2-with-an-embedded-link" .SH 3 Level 3 with \f[BI]emphasis\f[B] .pdfhref O 3 "Level 3 with emphasis" .pdfhref M "level-3-with-emphasis" .SH 4 Level 4 .pdfhref O 4 "Level 4" .pdfhref M "level-4" .SH 5 Level 5 .pdfhref O 5 "Level 5" .pdfhref M "level-5" .SH 1 Level 1 .pdfhref O 1 "Level 1" .pdfhref M "level-1" .SH 2 Level 2 with \f[BI]emphasis\f[B] .pdfhref O 2 "Level 2 with emphasis" .pdfhref M "level-2-with-emphasis" .SH 3 Level 3 .pdfhref O 3 "Level 3" .pdfhref M "level-3" .LP with no blank line .SH 2 Level 2 .pdfhref O 2 "Level 2" .pdfhref M "level-2" .LP with no blank line .HLINE .SH 1 Paragraphs .pdfhref O 1 "Paragraphs" .pdfhref M "paragraphs" .LP Here\(cqs a regular paragraph. .PP 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 a list item. .PP Here\(cqs one with a bullet. * criminey. .PP There should be a hard line break .br here. .HLINE .SH 1 Block Quotes .pdfhref O 1 "Block Quotes" .pdfhref M "block-quotes" .LP E-mail style: .QS .LP This is a block quote. It is pretty short. .QE .QS .LP Code in a block quote: .IP .nf \f[C] sub status { print \(dqworking\(dq; } \f[] .fi .LP A list: .IP " 1." 4 item one .IP " 2." 4 item two .LP Nested block quotes: .QS .LP nested .QE .QS .LP nested .QE .QE .LP This should not be a block quote: 2 > 1. .PP And a following paragraph. .HLINE .SH 1 Code Blocks .pdfhref O 1 "Code Blocks" .pdfhref M "code-blocks" .LP Code: .IP .nf \f[C] ---- (should be four hyphens) sub status { print \(dqworking\(dq; } this code block is indented by one tab \f[] .fi .LP And: .IP .nf \f[C] this code block is indented by two tabs These should not be escaped: \(rs$ \(rs\(rs \(rs> \(rs[ \(rs{ \f[] .fi .HLINE .SH 1 Lists .pdfhref O 1 "Lists" .pdfhref M "lists" .SH 2 Unordered .pdfhref O 2 "Unordered" .pdfhref M "unordered" .LP Asterisks tight: .IP \(bu 3 asterisk 1 .IP \(bu 3 asterisk 2 .IP \(bu 3 asterisk 3 .LP Asterisks loose: .IP \(bu 3 asterisk 1 .IP \(bu 3 asterisk 2 .IP \(bu 3 asterisk 3 .LP Pluses tight: .IP \(bu 3 Plus 1 .IP \(bu 3 Plus 2 .IP \(bu 3 Plus 3 .LP Pluses loose: .IP \(bu 3 Plus 1 .IP \(bu 3 Plus 2 .IP \(bu 3 Plus 3 .LP Minuses tight: .IP \(bu 3 Minus 1 .IP \(bu 3 Minus 2 .IP \(bu 3 Minus 3 .LP Minuses loose: .IP \(bu 3 Minus 1 .IP \(bu 3 Minus 2 .IP \(bu 3 Minus 3 .SH 2 Ordered .pdfhref O 2 "Ordered" .pdfhref M "ordered" .LP Tight: .IP " 1." 4 First .IP " 2." 4 Second .IP " 3." 4 Third .LP and: .IP " 1." 4 One .IP " 2." 4 Two .IP " 3." 4 Three .LP Loose using tabs: .IP " 1." 4 First .IP " 2." 4 Second .IP " 3." 4 Third .LP and using spaces: .IP " 1." 4 One .IP " 2." 4 Two .IP " 3." 4 Three .LP Multiple paragraphs: .IP " 1." 4 Item 1, graf one. .RS 4 .PP Item 1. graf two. The quick brown fox jumped over the lazy dog\(cqs back. .RE .IP " 2." 4 Item 2. .IP " 3." 4 Item 3. .SH 2 Nested .pdfhref O 2 "Nested" .pdfhref M "nested" .IP \(bu 3 Tab .RS 3 .IP \(bu 3 Tab .RS 3 .IP \(bu 3 Tab .RE .RE .LP Here\(cqs another: .IP " 1." 4 First .IP " 2." 4 Second: .RS 4 .IP \(bu 3 Fee .IP \(bu 3 Fie .IP \(bu 3 Foe .RE .IP " 3." 4 Third .LP Same thing but with paragraphs: .IP " 1." 4 First .IP " 2." 4 Second: .RS 4 .IP \(bu 3 Fee .IP \(bu 3 Fie .IP \(bu 3 Foe .RE .IP " 3." 4 Third .SH 2 Tabs and spaces .pdfhref O 2 "Tabs and spaces" .pdfhref M "tabs-and-spaces" .IP \(bu 3 this is a list item indented with tabs .IP \(bu 3 this is a list item indented with spaces .RS 3 .IP \(bu 3 this is an example list item indented with tabs .IP \(bu 3 this is an example list item indented with spaces .RE .SH 2 Fancy list markers .pdfhref O 2 "Fancy list markers" .pdfhref M "fancy-list-markers" .IP " (2)" 5 begins with 2 .IP " (3)" 5 and now 3 .RS 5 .LP with a continuation .IP " iv." 5 sublist with roman numerals, starting with 4 .IP " v." 5 more items .RS 5 .IP " (A)" 5 a subsublist .IP " (B)" 5 a subsublist .RE .RE .LP Nesting: .IP " A." 4 Upper Alpha .RS 4 .IP " I." 4 Upper Roman. .RS 4 .IP " (6)" 5 Decimal start with 6 .RS 5 .IP " c)" 4 Lower alpha with paren .RE .RE .RE .LP Autonumbering: .IP " 1." 4 Autonumber. .IP " 2." 4 More. .RS 4 .IP " 1." 4 Nested. .RE .LP Should not be a list item: .PP M.A.\ 2007 .PP B. Williams .HLINE .SH 1 Definition Lists .pdfhref O 1 "Definition Lists" .pdfhref M "definition-lists" .LP Tight using spaces: .IP "\f[B]apple\f[R]" 3 red fruit .RS 3 .RE .IP "\f[B]orange\f[R]" 3 orange fruit .RS 3 .RE .IP "\f[B]banana\f[R]" 3 yellow fruit .RS 3 .RE .LP Tight using tabs: .IP "\f[B]apple\f[R]" 3 red fruit .RS 3 .RE .IP "\f[B]orange\f[R]" 3 orange fruit .RS 3 .RE .IP "\f[B]banana\f[R]" 3 yellow fruit .RS 3 .RE .LP Loose: .IP "\f[B]apple\f[R]" 3 red fruit .RS 3 .RE .IP "\f[B]orange\f[R]" 3 orange fruit .RS 3 .RE .IP "\f[B]banana\f[R]" 3 yellow fruit .RS 3 .RE .LP Multiple blocks with italics: .IP "\f[B]\f[BI]apple\f[B]\f[R]" 3 red fruit .RS 3 .PP contains seeds, crisp, pleasant to taste .RE .IP "\f[B]\f[BI]orange\f[B]\f[R]" 3 orange fruit .RS 3 .IP .nf \f[C] { orange code block } \f[] .fi .QS .LP orange block quote .QE .RE .LP Multiple definitions, tight: .IP "\f[B]apple\f[R]" 3 red fruit .RS 3 .RE computer .RS 3 .RE .IP "\f[B]orange\f[R]" 3 orange fruit .RS 3 .RE bank .RS 3 .RE .LP Multiple definitions, loose: .IP "\f[B]apple\f[R]" 3 red fruit .RS 3 .RE computer .RS 3 .RE .IP "\f[B]orange\f[R]" 3 orange fruit .RS 3 .RE bank .RS 3 .RE .LP Blank line after term, indented marker, alternate markers: .IP "\f[B]apple\f[R]" 3 red fruit .RS 3 .RE computer .RS 3 .RE .IP "\f[B]orange\f[R]" 3 orange fruit .RS 3 .IP " 1." 4 sublist .IP " 2." 4 sublist .RE .SH 1 HTML Blocks .pdfhref O 1 "HTML Blocks" .pdfhref M "html-blocks" .LP Simple block on one line: foo .LP And nested without indentation: .LP foo bar .LP Interpreted markdown in a table: This is \f[I]emphasized\f[R] And this is \f[B]strong\f[R] .PP Here\(cqs a simple block: .LP foo .LP This should be a code block, though: .IP .nf \f[C]
                foo
                \f[] .fi .LP As should this: .IP .nf \f[C]
                foo
                \f[] .fi .LP Now, nested: foo .LP This should just be an HTML comment: .PP Multiline: .PP Code block: .IP .nf \f[C] \f[] .fi .LP Just plain comment, with trailing spaces on the line: .PP Code: .IP .nf \f[C]
                \f[] .fi .LP Hr\(cqs: .HLINE .SH 1 Inline Markup .pdfhref O 1 "Inline Markup" .pdfhref M "inline-markup" .LP This is \f[I]emphasized\f[R], and so \f[I]is this\f[R]. .PP This is \f[B]strong\f[R], and so \f[B]is this\f[R]. .PP An \f[I]\c .pdfhref W -D "/url" -A "\c" \ -- "emphasized link" \&\f[R]. .PP \f[B]\f[BI]This is strong and em.\f[B]\f[R] .PP So is \f[B]\f[BI]this\f[B]\f[R] word. .PP \f[B]\f[BI]This is strong and em.\f[B]\f[R] .PP So is \f[B]\f[BI]this\f[B]\f[R] word. .PP This is code: \f[CR]>\f[R], \f[CR]$\f[R], \f[CR]\(rs\f[R], \f[CR]\(rs$\f[R], \f[CR]\f[R]. .PP \m[strikecolor]This is \f[I]strikeout\f[R].\m[] .PP Superscripts: a\*{bc\*}d a\*{\f[I]hello\f[R]\*} a\*{hello\ there\*}. .PP Subscripts: H\*<2\*>O, H\*<23\*>O, H\*O. .PP These should not be superscripts or subscripts, because of the unescaped spaces: a\(hab c\(had, a\(tib c\(tid. .HLINE .SH 1 Smart quotes, ellipses, dashes .pdfhref O 1 "Smart quotes, ellipses, dashes" .pdfhref M "smart-quotes-ellipses-dashes" .LP \(lqHello,\(rq said the spider. \(lq`Shelob' is my name.\(rq .PP `A', `B', and `C' are letters. .PP `Oak,' `elm,' and `beech' are names of trees. So is `pine.' .PP `He said, \(lqI want to go.\(rq' Were you alive in the 70\(cqs? .PP Here is some quoted `\f[CR]code\f[R]' and a \(lq\c .pdfhref W -D "http://example.com/?foo=1&bar=2" -A "\c" \ -- "quoted link" \&\(rq. .PP Some dashes: one\(emtwo \(em three\(emfour \(em five. .PP Dashes between numbers: 5\(en7, 255\(en66, 1987\(en1999. .PP Ellipses\&...and\&...and\&.... .HLINE .SH 1 LaTeX .pdfhref O 1 "LaTeX" .pdfhref M "latex" .IP \(bu 3 .IP \(bu 3 @2 + 2 = 4@ .IP \(bu 3 @x \[u2208] y@ .IP \(bu 3 @alpha \[u2227] omega@ .IP \(bu 3 @223@ .IP \(bu 3 @p@-Tree .IP \(bu 3 Here\(cqs some display math: .EQ d over {d x} f ( x ) = lim from {h -> 0} {f ( x + h ) - f ( x )} over h .EN .IP \(bu 3 Here\(cqs one that has a line break in it: @alpha + omega times x sup 2@. .LP These shouldn\(cqt be math: .IP \(bu 3 To get the famous equation, write \f[CR]$e = mc\(ha2$\f[R]. .IP \(bu 3 $22,000 is a \f[I]lot\f[R] of money. So is $34,000. (It worked if \(lqlot\(rq is emphasized.) .IP \(bu 3 Shoes ($20) and socks ($5). .IP \(bu 3 Escaped \f[CR]$\f[R]: $73 \f[I]this should be emphasized\f[R] 23$. .LP Here\(cqs a LaTeX table: .HLINE .SH 1 Special Characters .pdfhref O 1 "Special Characters" .pdfhref M "special-characters" .LP Here is some unicode: .IP \(bu 3 I hat: Î .IP \(bu 3 o umlaut: ö .IP \(bu 3 section: § .IP \(bu 3 set membership: ∈ .IP \(bu 3 copyright: © .LP AT&T has an ampersand in their name. .PP AT&T is another way to write it. .PP This & that. .PP 4 < 5. .PP 6 > 5. .PP Backslash: \(rs .PP Backtick: \(ga .PP Asterisk: * .PP Underscore: _ .PP Left brace: { .PP Right brace: } .PP Left bracket: [ .PP Right bracket: ] .PP Left paren: ( .PP Right paren: ) .PP Greater-than: > .PP Hash: # .PP Period: . .PP Bang: ! .PP Plus: + .PP Minus: - .HLINE .SH 1 Links .pdfhref O 1 "Links" .pdfhref M "links" .SH 2 Explicit .pdfhref O 2 "Explicit" .pdfhref M "explicit" .LP Just a \c .pdfhref W -D "/url/" -A "\c" \ -- "URL" \&. .PP \c .pdfhref W -D "/url/" -A "\c" \ -- "URL and title" \&. .PP \c .pdfhref W -D "/url/" -A "\c" \ -- "URL and title" \&. .PP \c .pdfhref W -D "/url/" -A "\c" \ -- "URL and title" \&. .PP \c .pdfhref W -D "/url/" -A "\c" \ -- "URL and title" \& .PP \c .pdfhref W -D "/url/" -A "\c" \ -- "URL and title" \& .PP \c .pdfhref W -D "/url/with_underscore" -A "\c" \ -- "with_underscore" \& .PP \c .pdfhref W -D "mailto:nobody%40nowhere.net" -A "\c" \ -- "Email link" \& .PP \c .pdfhref W -D "" -A "\c" \ -- "Empty" \&. .SH 2 Reference .pdfhref O 2 "Reference" .pdfhref M "reference" .LP Foo \c .pdfhref W -D "/url/" -A "\c" \ -- "bar" \&. .PP With \c .pdfhref W -D "/url/" -A "\c" \ -- "embedded [brackets]" \&. .PP \c .pdfhref W -D "/url/" -A "\c" \ -- "b" \& by itself should be a link. .PP Indented \c .pdfhref W -D "/url" -A "\c" \ -- "once" \&. .PP Indented \c .pdfhref W -D "/url" -A "\c" \ -- "twice" \&. .PP Indented \c .pdfhref W -D "/url" -A "\c" \ -- "thrice" \&. .PP This should [not][] be a link. .IP .nf \f[C] [not]: /url \f[] .fi .LP Foo \c .pdfhref W -D "/url/" -A "\c" \ -- "bar" \&. .PP Foo \c .pdfhref W -D "/url/" -A "\c" \ -- "biz" \&. .SH 2 With ampersands .pdfhref O 2 "With ampersands" .pdfhref M "with-ampersands" .LP Here\(cqs a \c .pdfhref W -D "http://example.com/?foo=1&bar=2" -A "\c" \ -- "link with an ampersand in the URL" \&. .PP Here\(cqs a link with an amersand in the link text: \c .pdfhref W -D "http://att.com/" -A "\c" \ -- "AT&T" \&. .PP Here\(cqs an \c .pdfhref W -D "/script?foo=1&bar=2" -A "\c" \ -- "inline link" \&. .PP Here\(cqs an \c .pdfhref W -D "/script?foo=1&bar=2" -A "\c" \ -- "inline link in pointy braces" \&. .SH 2 Autolinks .pdfhref O 2 "Autolinks" .pdfhref M "autolinks" .LP With an ampersand: \c .pdfhref W -D "http://example.com/?foo=1&bar=2" -A "\c" \ -- "http://example.com/?foo=1&bar=2" \& .IP \(bu 3 In a list? .IP \(bu 3 \c .pdfhref W -D "http://example.com/" -A "\c" \ -- "http://example.com/" \& .IP \(bu 3 It should. .LP An e-mail address: \c .pdfhref W -D "mailto:nobody%40nowhere.net" -A "\c" \ -- "nobody\(atnowhere.net" \& .QS .LP Blockquoted: \c .pdfhref W -D "http://example.com/" -A "\c" \ -- "http://example.com/" \& .QE .LP Auto-links should not occur here: \f[CR]\f[R] .IP .nf \f[C] or here: \f[] .fi .HLINE .SH 1 Images .pdfhref O 1 "Images" .pdfhref M "images" .LP From \(lqVoyage dans la Lune\(rq by Georges Melies (1902): \" .IMAGE "lalune.jpg" .ce 1 lalune .sp 1 .LP Here is a movie [IMAGE: movie] \" "movie.jpg" icon. .HLINE .SH 1 Footnotes .pdfhref O 1 "Footnotes" .pdfhref M "footnotes" .LP Here is a footnote reference,\** .FS Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. .FE and another.\** .FS Here\(cqs the long note. This one contains multiple blocks. .PP Subsequent blocks are indented to show that they belong to the footnote (as with list items). .IP .nf \f[C] { } \f[] .fi .LP If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. .FE This should \f[I]not\f[R] be a footnote reference, because it contains a space.[\(hamy note] Here is an inline note.\** .FS This is \f[I]easier\f[R] to type. Inline notes may contain \c .pdfhref W -D "http://google.com" -A "\c" \ -- "links" \& and \f[CR]]\f[R] verbatim characters, as well as [bracketed text]. .FE .QS .LP Notes can go in quotes.\** .FS In quote. .FE .QE .IP " 1." 4 And in list items.\** .FS In list. .FE .LP This paragraph should not be part of the note, as it is not indented. .pdfsync ================================================ FILE: test/writer.muse ================================================ #author John MacFarlane; Anonymous #title Pandoc Test Suite #date July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ---- * Headers ** Level 2 with an [[/url][embedded link]] *** Level 3 with *emphasis* **** Level 4 ***** Level 5 * Level 1 ** Level 2 with *emphasis* *** Level 3 with no blank line ** Level 2 with no blank line ---- * Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break
                here. ---- * Block Quotes E-mail style: This is a block quote. It is pretty short. Code in a block quote: sub status { print "working"; } A list: 1. item one 2. item two Nested block quotes: nested nested This should not be a block quote: 2 > 1. And a following paragraph. ---- * Code Blocks Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ ---- * Lists ** Unordered Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 ** Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. 2. Item 2. 3. Item 3. ** Nested - Tab - Tab - Tab Here’s another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third ** Tabs and spaces - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces ** Fancy list markers 2. begins with 2 3. and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items A. a subsublist B. a subsublist Nesting: A. Upper Alpha I. Upper Roman. 6. Decimal start with 6 c. Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams ---- * Definition Lists Tight using spaces: apple :: red fruit orange :: orange fruit banana :: yellow fruit Tight using tabs: apple :: red fruit orange :: orange fruit banana :: yellow fruit Loose: apple :: red fruit orange :: orange fruit banana :: yellow fruit Multiple blocks with italics: *apple* :: red fruit contains seeds, crisp, pleasant to taste *orange* :: orange fruit { orange code block } orange block quote Multiple definitions, tight: apple :: red fruit :: computer orange :: orange fruit :: bank Multiple definitions, loose: apple :: red fruit :: computer orange :: orange fruit :: bank Blank line after term, indented marker, alternate markers: apple :: red fruit :: computer orange :: orange fruit 1. sublist 2. sublist * HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table:
                This is *emphasized* And this is **strong**
                Here’s a simple block: foo This should be a code block, though:
                foo
                As should this:
                foo
                Now, nested: foo This should just be an HTML comment: Multiline: Code block: Just plain comment, with trailing spaces on the line: Code:
                Hr’s:








                ---- * Inline Markup This is *emphasized*, and so *is this*. This is **strong**, and so **is this**. An *[[/url][emphasized link]]*. ***This is strong and em.*** So is ***this*** word. ***This is strong and em.*** So is ***this*** word. This is code: =>=, =$=, =\=, =\$=, ==. This is *strikeout*. Superscripts: abcd a*hello* ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ---- * Smart quotes, ellipses, dashes “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘=code=’ and a “[[http://example.com/?foo=1&bar=2][quoted link]]”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ---- * LaTeX - \cite[22-23]{smith.1899} - 2 + 2 = 4 - *x* ∈ *y* - *α* ∧ *ω* - 223 - *p*-Tree - Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ - Here’s one that has a line break in it: *α* + *ω* × *x*2. These shouldn’t be math: - To get the famous equation, write $e = mc^2$. - $22,000 is a *lot* of money. So is $34,000. (It worked if “lot” is emphasized.) - Shoes ($20) and socks ($5). - Escaped =$=: $73 *this should be emphasized* 23$. Here’s a LaTeX table: \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} ---- * Special Characters Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - ---- * Links ** Explicit Just a [[/url/][URL]]. [[/url/][URL and title]]. [[/url/][URL and title]]. [[/url/][URL and title]]. [[/url/][URL and title]] [[/url/][URL and title]] [[/url/with_underscore][with_underscore]] [[mailto:nobody@nowhere.net][Email link]] [[][Empty]]. ** Reference Foo [[/url/][bar]]. With [[/url/][embedded [brackets]]]. [[/url/][b]] by itself should be a link. Indented [[/url][once]]. Indented [[/url][twice]]. Indented [[/url][thrice]]. This should [not][] be a link. [not]: /url Foo [[/url/][bar]]. Foo [[/url/][biz]]. ** With ampersands Here’s a [[http://example.com/?foo=1&bar=2][link with an ampersand in the URL]]. Here’s a link with an amersand in the link text: [[http://att.com/][AT&T]]. Here’s an [[/script?foo=1&bar=2][inline link]]. Here’s an [[/script?foo=1&bar=2][inline link in pointy braces]]. ** Autolinks With an ampersand: [[http://example.com/?foo=1&bar=2]] - In a list? - [[http://example.com/]] - It should. An e-mail address: [[mailto:nobody@nowhere.net][nobody@nowhere.net]] Blockquoted: [[http://example.com/]] Auto-links should not occur here: == or here: ---- * Images From “Voyage dans la Lune” by Georges Melies (1902): [[lalune.jpg][Voyage dans la Lune]] lalune Here is a movie [[movie.jpg][movie]] icon. ---- * Footnotes Here is a footnote reference,[1] and another.[2] This should *not* be a footnote reference, because it contains a space.[^my note] Here is an inline note.[3] Notes can go in quotes.[4] 1. And in list items.[5] This paragraph should not be part of the note, as it is not indented. [1] Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. [2] Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). { } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. [3] This is *easier* to type. Inline notes may contain [[http://google.com][links]] and =]= verbatim characters, as well as [bracketed text]. [4] In quote. [5] In list. ================================================ FILE: test/writer.native ================================================ Pandoc Meta { unMeta = fromList [ ( "author" , MetaList [ MetaInlines [ Str "John" , Space , Str "MacFarlane" ] , MetaInlines [ Str "Anonymous" ] ] ) , ( "date" , MetaInlines [ Str "July" , Space , Str "17," , Space , Str "2006" ] ) , ( "title" , MetaInlines [ Str "Pandoc" , Space , Str "Test" , Space , Str "Suite" ] ) ] } [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "set" , Space , Str "of" , Space , Str "tests" , Space , Str "for" , Space , Str "pandoc." , Space , Str "Most" , Space , Str "of" , Space , Str "them" , Space , Str "are" , Space , Str "adapted" , Space , Str "from" , SoftBreak , Str "John" , Space , Str "Gruber\8217s" , Space , Str "markdown" , Space , Str "test" , Space , Str "suite." ] , HorizontalRule , Header 1 ( "headers" , [] , [] ) [ Str "Headers" ] , Header 2 ( "level-2-with-an-embedded-link" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "link" ] ( "/url" , "" ) ] , Header 3 ( "level-3-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "3" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 4 ( "level-4" , [] , [] ) [ Str "Level" , Space , Str "4" ] , Header 5 ( "level-5" , [] , [] ) [ Str "Level" , Space , Str "5" ] , Header 1 ( "level-1" , [] , [] ) [ Str "Level" , Space , Str "1" ] , Header 2 ( "level-2-with-emphasis" , [] , [] ) [ Str "Level" , Space , Str "2" , Space , Str "with" , Space , Emph [ Str "emphasis" ] ] , Header 3 ( "level-3" , [] , [] ) [ Str "Level" , Space , Str "3" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , Header 2 ( "level-2" , [] , [] ) [ Str "Level" , Space , Str "2" ] , Para [ Str "with" , Space , Str "no" , Space , Str "blank" , Space , Str "line" ] , HorizontalRule , Header 1 ( "paragraphs" , [] , [] ) [ Str "Paragraphs" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "regular" , Space , Str "paragraph." ] , Para [ Str "In" , Space , Str "Markdown" , Space , Str "1.0.0" , Space , Str "and" , Space , Str "earlier." , Space , Str "Version" , SoftBreak , Str "8." , Space , Str "This" , Space , Str "line" , Space , Str "turns" , Space , Str "into" , Space , Str "a" , Space , Str "list" , Space , Str "item." , SoftBreak , Str "Because" , Space , Str "a" , Space , Str "hard-wrapped" , Space , Str "line" , Space , Str "in" , Space , Str "the" , SoftBreak , Str "middle" , Space , Str "of" , Space , Str "a" , Space , Str "paragraph" , Space , Str "looked" , Space , Str "like" , Space , Str "a" , SoftBreak , Str "list" , Space , Str "item." ] , Para [ Str "Here\8217s" , Space , Str "one" , Space , Str "with" , Space , Str "a" , Space , Str "bullet." , SoftBreak , Str "*" , Space , Str "criminey." ] , Para [ Str "There" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "hard" , Space , Str "line" , Space , Str "break" , LineBreak , Str "here." ] , HorizontalRule , Header 1 ( "block-quotes" , [] , [] ) [ Str "Block" , Space , Str "Quotes" ] , Para [ Str "E-mail" , Space , Str "style:" ] , BlockQuote [ Para [ Str "This" , Space , Str "is" , Space , Str "a" , Space , Str "block" , Space , Str "quote." , SoftBreak , Str "It" , Space , Str "is" , Space , Str "pretty" , Space , Str "short." ] ] , BlockQuote [ Para [ Str "Code" , Space , Str "in" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" ] , CodeBlock ( "" , [] , [] ) "sub status {\n print \"working\";\n}" , Para [ Str "A" , Space , Str "list:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "item" , Space , Str "one" ] ] , [ Plain [ Str "item" , Space , Str "two" ] ] ] , Para [ Str "Nested" , Space , Str "block" , Space , Str "quotes:" ] , BlockQuote [ Para [ Str "nested" ] ] , BlockQuote [ Para [ Str "nested" ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "block" , Space , Str "quote:" , Space , Str "2" , SoftBreak , Str ">" , Space , Str "1." ] , Para [ Str "And" , Space , Str "a" , Space , Str "following" , Space , Str "paragraph." ] , HorizontalRule , Header 1 ( "code-blocks" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ] , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "---- (should be four hyphens)\n\nsub status {\n print \"working\";\n}\n\nthis code block is indented by one tab" , Para [ Str "And:" ] , CodeBlock ( "" , [] , [] ) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" , HorizontalRule , Header 1 ( "lists" , [] , [] ) [ Str "Lists" ] , Header 2 ( "unordered" , [] , [] ) [ Str "Unordered" ] , Para [ Str "Asterisks" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "asterisk" , Space , Str "1" ] ] , [ Plain [ Str "asterisk" , Space , Str "2" ] ] , [ Plain [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Asterisks" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "asterisk" , Space , Str "1" ] ] , [ Para [ Str "asterisk" , Space , Str "2" ] ] , [ Para [ Str "asterisk" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Plus" , Space , Str "1" ] ] , [ Plain [ Str "Plus" , Space , Str "2" ] ] , [ Plain [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Pluses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Plus" , Space , Str "1" ] ] , [ Para [ Str "Plus" , Space , Str "2" ] ] , [ Para [ Str "Plus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "tight:" ] , BulletList [ [ Plain [ Str "Minus" , Space , Str "1" ] ] , [ Plain [ Str "Minus" , Space , Str "2" ] ] , [ Plain [ Str "Minus" , Space , Str "3" ] ] ] , Para [ Str "Minuses" , Space , Str "loose:" ] , BulletList [ [ Para [ Str "Minus" , Space , Str "1" ] ] , [ Para [ Str "Minus" , Space , Str "2" ] ] , [ Para [ Str "Minus" , Space , Str "3" ] ] ] , Header 2 ( "ordered" , [] , [] ) [ Str "Ordered" ] , Para [ Str "Tight:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second" ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "and:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "One" ] ] , [ Plain [ Str "Two" ] ] , [ Plain [ Str "Three" ] ] ] , Para [ Str "Loose" , Space , Str "using" , Space , Str "tabs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second" ] ] , [ Para [ Str "Third" ] ] ] , Para [ Str "and" , Space , Str "using" , Space , Str "spaces:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "One" ] ] , [ Para [ Str "Two" ] ] , [ Para [ Str "Three" ] ] ] , Para [ Str "Multiple" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "Item" , Space , Str "1," , Space , Str "graf" , Space , Str "one." ] , Para [ Str "Item" , Space , Str "1." , Space , Str "graf" , Space , Str "two." , Space , Str "The" , Space , Str "quick" , Space , Str "brown" , Space , Str "fox" , Space , Str "jumped" , Space , Str "over" , Space , Str "the" , Space , Str "lazy" , Space , Str "dog\8217s" , SoftBreak , Str "back." ] ] , [ Para [ Str "Item" , Space , Str "2." ] ] , [ Para [ Str "Item" , Space , Str "3." ] ] ] , Header 2 ( "nested" , [] , [] ) [ Str "Nested" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] , BulletList [ [ Plain [ Str "Tab" ] ] ] ] ] ] ] , Para [ Str "Here\8217s" , Space , Str "another:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "First" ] ] , [ Plain [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Plain [ Str "Third" ] ] ] , Para [ Str "Same" , Space , Str "thing" , Space , Str "but" , Space , Str "with" , Space , Str "paragraphs:" ] , OrderedList ( 1 , Decimal , Period ) [ [ Para [ Str "First" ] ] , [ Para [ Str "Second:" ] , BulletList [ [ Plain [ Str "Fee" ] ] , [ Plain [ Str "Fie" ] ] , [ Plain [ Str "Foe" ] ] ] ] , [ Para [ Str "Third" ] ] ] , Header 2 ( "tabs-and-spaces" , [] , [] ) [ Str "Tabs" , Space , Str "and" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "a" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "spaces" ] , BulletList [ [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "tabs" ] ] , [ Para [ Str "this" , Space , Str "is" , Space , Str "an" , Space , Str "example" , Space , Str "list" , Space , Str "item" , SoftBreak , Str "indented" , Space , Str "with" , Space , Str "spaces" ] ] ] ] ] , Header 2 ( "fancy-list-markers" , [] , [] ) [ Str "Fancy" , Space , Str "list" , Space , Str "markers" ] , OrderedList ( 2 , Decimal , TwoParens ) [ [ Para [ Str "begins" , Space , Str "with" , Space , Str "2" ] ] , [ Para [ Str "and" , Space , Str "now" , Space , Str "3" ] , Para [ Str "with" , Space , Str "a" , Space , Str "continuation" ] , OrderedList ( 4 , LowerRoman , Period ) [ [ Plain [ Str "sublist" , Space , Str "with" , Space , Str "roman" , Space , Str "numerals," , SoftBreak , Str "starting" , Space , Str "with" , Space , Str "4" ] ] , [ Plain [ Str "more" , Space , Str "items" ] , OrderedList ( 1 , UpperAlpha , TwoParens ) [ [ Plain [ Str "a" , Space , Str "subsublist" ] ] , [ Plain [ Str "a" , Space , Str "subsublist" ] ] ] ] ] ] ] , Para [ Str "Nesting:" ] , OrderedList ( 1 , UpperAlpha , Period ) [ [ Plain [ Str "Upper" , Space , Str "Alpha" ] , OrderedList ( 1 , UpperRoman , Period ) [ [ Plain [ Str "Upper" , Space , Str "Roman." ] , OrderedList ( 6 , Decimal , TwoParens ) [ [ Plain [ Str "Decimal" , Space , Str "start" , Space , Str "with" , Space , Str "6" ] , OrderedList ( 3 , LowerAlpha , OneParen ) [ [ Plain [ Str "Lower" , Space , Str "alpha" , Space , Str "with" , Space , Str "paren" ] ] ] ] ] ] ] ] ] , Para [ Str "Autonumbering:" ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Autonumber." ] ] , [ Plain [ Str "More." ] , OrderedList ( 1 , DefaultStyle , DefaultDelim ) [ [ Plain [ Str "Nested." ] ] ] ] ] , Para [ Str "Should" , Space , Str "not" , Space , Str "be" , Space , Str "a" , Space , Str "list" , Space , Str "item:" ] , Para [ Str "M.A.\160\&2007" ] , Para [ Str "B." , Space , Str "Williams" ] , HorizontalRule , Header 1 ( "definition-lists" , [] , [] ) [ Str "Definition" , Space , Str "Lists" ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "spaces:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Plain [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Tight" , Space , Str "using" , Space , Str "tabs:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Plain [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] ] ) , ( [ Str "banana" ] , [ [ Para [ Str "yellow" , Space , Str "fruit" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "blocks" , Space , Str "with" , Space , Str "italics:" ] , DefinitionList [ ( [ Emph [ Str "apple" ] ] , [ [ Para [ Str "red" , Space , Str "fruit" ] , Para [ Str "contains" , Space , Str "seeds," , SoftBreak , Str "crisp," , Space , Str "pleasant" , Space , Str "to" , Space , Str "taste" ] ] ] ) , ( [ Emph [ Str "orange" ] ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , CodeBlock ( "" , [] , [] ) "{ orange code block }" , BlockQuote [ Para [ Str "orange" , Space , Str "block" , Space , Str "quote" ] ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "tight:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Plain [ Str "red" , Space , Str "fruit" ] ] , [ Plain [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Plain [ Str "orange" , Space , Str "fruit" ] ] , [ Plain [ Str "bank" ] ] ] ) ] , Para [ Str "Multiple" , Space , Str "definitions," , Space , Str "loose:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] , [ Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] ] , [ Para [ Str "bank" ] ] ] ) ] , Para [ Str "Blank" , Space , Str "line" , Space , Str "after" , Space , Str "term," , Space , Str "indented" , Space , Str "marker," , Space , Str "alternate" , Space , Str "markers:" ] , DefinitionList [ ( [ Str "apple" ] , [ [ Para [ Str "red" , Space , Str "fruit" ] ] , [ Para [ Str "computer" ] ] ] ) , ( [ Str "orange" ] , [ [ Para [ Str "orange" , Space , Str "fruit" ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "sublist" ] ] , [ Plain [ Str "sublist" ] ] ] ] ] ) ] , Header 1 ( "html-blocks" , [] , [] ) [ Str "HTML" , Space , Str "Blocks" ] , Para [ Str "Simple" , Space , Str "block" , Space , Str "on" , Space , Str "one" , Space , Str "line:" ] , Div ( "" , [] , [] ) [ Plain [ Str "foo" ] ] , Para [ Str "And" , Space , Str "nested" , Space , Str "without" , Space , Str "indentation:" ] , Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] ] , Div ( "" , [] , [] ) [ Plain [ Str "bar" ] ] ] , Para [ Str "Interpreted" , Space , Str "markdown" , Space , Str "in" , Space , Str "a" , Space , Str "table:" ] , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "" , RawBlock (Format "html") "
                " , Plain [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] ] , RawBlock (Format "html") "" , Plain [ Str "And" , Space , Str "this" , Space , Str "is" , Space , Strong [ Str "strong" ] ] , RawBlock (Format "html") "
                " , RawBlock (Format "html") "" , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "simple" , Space , Str "block:" ] , Div ( "" , [] , [] ) [ Para [ Str "foo" ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "code" , Space , Str "block," , Space , Str "though:" ] , CodeBlock ( "" , [] , [] ) "
                \n foo\n
                " , Para [ Str "As" , Space , Str "should" , Space , Str "this:" ] , CodeBlock ( "" , [] , [] ) "
                foo
                " , Para [ Str "Now," , Space , Str "nested:" ] , Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Div ( "" , [] , [] ) [ Plain [ Str "foo" ] ] ] ] , Para [ Str "This" , Space , Str "should" , Space , Str "just" , Space , Str "be" , Space , Str "an" , Space , Str "HTML" , Space , Str "comment:" ] , RawBlock (Format "html") "" , Para [ Str "Multiline:" ] , RawBlock (Format "html") "" , RawBlock (Format "html") "" , Para [ Str "Code" , Space , Str "block:" ] , CodeBlock ( "" , [] , [] ) "" , Para [ Str "Just" , Space , Str "plain" , Space , Str "comment," , Space , Str "with" , Space , Str "trailing" , Space , Str "spaces" , Space , Str "on" , Space , Str "the" , Space , Str "line:" ] , RawBlock (Format "html") "" , Para [ Str "Code:" ] , CodeBlock ( "" , [] , [] ) "
                " , Para [ Str "Hr\8217s:" ] , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , RawBlock (Format "html") "
                " , HorizontalRule , Header 1 ( "inline-markup" , [] , [] ) [ Str "Inline" , Space , Str "Markup" ] , Para [ Str "This" , Space , Str "is" , Space , Emph [ Str "emphasized" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Emph [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "This" , Space , Str "is" , Space , Strong [ Str "strong" ] , Str "," , Space , Str "and" , Space , Str "so" , Space , Strong [ Str "is" , Space , Str "this" ] , Str "." ] , Para [ Str "An" , Space , Emph [ Link ( "" , [] , [] ) [ Str "emphasized" , Space , Str "link" ] ( "/url" , "" ) ] , Str "." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Strong [ Emph [ Str "This" , Space , Str "is" , Space , Str "strong" , Space , Str "and" , Space , Str "em." ] ] ] , Para [ Str "So" , Space , Str "is" , Space , Strong [ Emph [ Str "this" ] ] , Space , Str "word." ] , Para [ Str "This" , Space , Str "is" , Space , Str "code:" , Space , Code ( "" , [] , [] ) ">" , Str "," , Space , Code ( "" , [] , [] ) "$" , Str "," , Space , Code ( "" , [] , [] ) "\\" , Str "," , Space , Code ( "" , [] , [] ) "\\$" , Str "," , Space , Code ( "" , [] , [] ) "" , Str "." ] , Para [ Strikeout [ Str "This" , Space , Str "is" , Space , Emph [ Str "strikeout" ] , Str "." ] ] , Para [ Str "Superscripts:" , Space , Str "a" , Superscript [ Str "bc" ] , Str "d" , Space , Str "a" , Superscript [ Emph [ Str "hello" ] ] , Space , Str "a" , Superscript [ Str "hello\160there" ] , Str "." ] , Para [ Str "Subscripts:" , Space , Str "H" , Subscript [ Str "2" ] , Str "O," , Space , Str "H" , Subscript [ Str "23" ] , Str "O," , Space , Str "H" , Subscript [ Str "many\160of\160them" ] , Str "O." ] , Para [ Str "These" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "superscripts" , Space , Str "or" , Space , Str "subscripts," , SoftBreak , Str "because" , Space , Str "of" , Space , Str "the" , Space , Str "unescaped" , Space , Str "spaces:" , Space , Str "a^b" , Space , Str "c^d," , Space , Str "a~b" , Space , Str "c~d." ] , HorizontalRule , Header 1 ( "smart-quotes-ellipses-dashes" , [] , [] ) [ Str "Smart" , Space , Str "quotes," , Space , Str "ellipses," , Space , Str "dashes" ] , Para [ Quoted DoubleQuote [ Str "Hello," ] , Space , Str "said" , Space , Str "the" , Space , Str "spider." , Space , Quoted DoubleQuote [ Quoted SingleQuote [ Str "Shelob" ] , Space , Str "is" , Space , Str "my" , Space , Str "name." ] ] , Para [ Quoted SingleQuote [ Str "A" ] , Str "," , Space , Quoted SingleQuote [ Str "B" ] , Str "," , Space , Str "and" , Space , Quoted SingleQuote [ Str "C" ] , Space , Str "are" , Space , Str "letters." ] , Para [ Quoted SingleQuote [ Str "Oak," ] , Space , Quoted SingleQuote [ Str "elm," ] , Space , Str "and" , Space , Quoted SingleQuote [ Str "beech" ] , Space , Str "are" , Space , Str "names" , Space , Str "of" , Space , Str "trees." , SoftBreak , Str "So" , Space , Str "is" , Space , Quoted SingleQuote [ Str "pine." ] ] , Para [ Quoted SingleQuote [ Str "He" , Space , Str "said," , Space , Quoted DoubleQuote [ Str "I" , Space , Str "want" , Space , Str "to" , Space , Str "go." ] ] , Space , Str "Were" , Space , Str "you" , Space , Str "alive" , Space , Str "in" , Space , Str "the" , SoftBreak , Str "70\8217s?" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "quoted" , Space , Quoted SingleQuote [ Code ( "" , [] , [] ) "code" ] , Space , Str "and" , Space , Str "a" , Space , Quoted DoubleQuote [ Link ( "" , [] , [] ) [ Str "quoted" , Space , Str "link" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , Str "." ] , Para [ Str "Some" , Space , Str "dashes:" , Space , Str "one\8212two" , Space , Str "\8212" , Space , Str "three\8212four" , Space , Str "\8212" , Space , Str "five." ] , Para [ Str "Dashes" , Space , Str "between" , Space , Str "numbers:" , Space , Str "5\8211\&7," , Space , Str "255\8211\&66," , Space , Str "1987\8211\&1999." ] , Para [ Str "Ellipses\8230and\8230and\8230." ] , HorizontalRule , Header 1 ( "latex" , [] , [] ) [ Str "LaTeX" ] , BulletList [ [ Plain [ RawInline (Format "tex") "\\cite[22-23]{smith.1899}" ] ] , [ Plain [ Math InlineMath "2+2=4" ] ] , [ Plain [ Math InlineMath "x \\in y" ] ] , [ Plain [ Math InlineMath "\\alpha \\wedge \\omega" ] ] , [ Plain [ Math InlineMath "223" ] ] , [ Plain [ Math InlineMath "p" , Str "-Tree" ] ] , [ Plain [ Str "Here\8217s" , Space , Str "some" , Space , Str "display" , Space , Str "math:" , SoftBreak , Math DisplayMath "\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)-f(x)}{h}" ] ] , [ Plain [ Str "Here\8217s" , Space , Str "one" , Space , Str "that" , Space , Str "has" , Space , Str "a" , Space , Str "line" , Space , Str "break" , Space , Str "in" , Space , Str "it:" , Space , Math InlineMath "\\alpha + \\omega \\times x^2" , Str "." ] ] ] , Para [ Str "These" , Space , Str "shouldn\8217t" , Space , Str "be" , Space , Str "math:" ] , BulletList [ [ Plain [ Str "To" , Space , Str "get" , Space , Str "the" , Space , Str "famous" , Space , Str "equation," , Space , Str "write" , Space , Code ( "" , [] , [] ) "$e = mc^2$" , Str "." ] ] , [ Plain [ Str "$22,000" , Space , Str "is" , Space , Str "a" , Space , Emph [ Str "lot" ] , Space , Str "of" , Space , Str "money." , Space , Str "So" , Space , Str "is" , Space , Str "$34,000." , SoftBreak , Str "(It" , Space , Str "worked" , Space , Str "if" , Space , Quoted DoubleQuote [ Str "lot" ] , Space , Str "is" , Space , Str "emphasized.)" ] ] , [ Plain [ Str "Shoes" , Space , Str "($20)" , Space , Str "and" , Space , Str "socks" , Space , Str "($5)." ] ] , [ Plain [ Str "Escaped" , Space , Code ( "" , [] , [] ) "$" , Str ":" , Space , Str "$73" , Space , Emph [ Str "this" , Space , Str "should" , Space , Str "be" , Space , Str "emphasized" ] , Space , Str "23$." ] ] ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "LaTeX" , Space , Str "table:" ] , RawBlock (Format "tex") "\\begin{tabular}{|l|l|}\\hline\nAnimal & Number \\\\ \\hline\nDog & 2 \\\\\nCat & 1 \\\\ \\hline\n\\end{tabular}" , HorizontalRule , Header 1 ( "special-characters" , [] , [] ) [ Str "Special" , Space , Str "Characters" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "some" , Space , Str "unicode:" ] , BulletList [ [ Plain [ Str "I" , Space , Str "hat:" , Space , Str "\206" ] ] , [ Plain [ Str "o" , Space , Str "umlaut:" , Space , Str "\246" ] ] , [ Plain [ Str "section:" , Space , Str "\167" ] ] , [ Plain [ Str "set" , Space , Str "membership:" , Space , Str "\8712" ] ] , [ Plain [ Str "copyright:" , Space , Str "\169" ] ] ] , Para [ Str "AT&T" , Space , Str "has" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "their" , Space , Str "name." ] , Para [ Str "AT&T" , Space , Str "is" , Space , Str "another" , Space , Str "way" , Space , Str "to" , Space , Str "write" , Space , Str "it." ] , Para [ Str "This" , Space , Str "&" , Space , Str "that." ] , Para [ Str "4" , Space , Str "<" , Space , Str "5." ] , Para [ Str "6" , Space , Str ">" , Space , Str "5." ] , Para [ Str "Backslash:" , Space , Str "\\" ] , Para [ Str "Backtick:" , Space , Str "`" ] , Para [ Str "Asterisk:" , Space , Str "*" ] , Para [ Str "Underscore:" , Space , Str "_" ] , Para [ Str "Left" , Space , Str "brace:" , Space , Str "{" ] , Para [ Str "Right" , Space , Str "brace:" , Space , Str "}" ] , Para [ Str "Left" , Space , Str "bracket:" , Space , Str "[" ] , Para [ Str "Right" , Space , Str "bracket:" , Space , Str "]" ] , Para [ Str "Left" , Space , Str "paren:" , Space , Str "(" ] , Para [ Str "Right" , Space , Str "paren:" , Space , Str ")" ] , Para [ Str "Greater-than:" , Space , Str ">" ] , Para [ Str "Hash:" , Space , Str "#" ] , Para [ Str "Period:" , Space , Str "." ] , Para [ Str "Bang:" , Space , Str "!" ] , Para [ Str "Plus:" , Space , Str "+" ] , Para [ Str "Minus:" , Space , Str "-" ] , HorizontalRule , Header 1 ( "links" , [] , [] ) [ Str "Links" ] , Header 2 ( "explicit" , [] , [] ) [ Str "Explicit" ] , Para [ Str "Just" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "URL" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by two spaces" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title preceded by a tab" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title with \"quotes\" in it" ) ] , Para [ Link ( "" , [] , [] ) [ Str "URL" , Space , Str "and" , Space , Str "title" ] ( "/url/" , "title with single quotes" ) ] , Para [ Link ( "" , [] , [] ) [ Str "with_underscore" ] ( "/url/with_underscore" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Email" , Space , Str "link" ] ( "mailto:nobody@nowhere.net" , "" ) ] , Para [ Link ( "" , [] , [] ) [ Str "Empty" ] ( "" , "" ) , Str "." ] , Header 2 ( "reference" , [] , [] ) [ Str "Reference" ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "" ) , Str "." ] , Para [ Str "With" , Space , Link ( "" , [] , [] ) [ Str "embedded" , Space , Str "[brackets]" ] ( "/url/" , "" ) , Str "." ] , Para [ Link ( "" , [] , [] ) [ Str "b" ] ( "/url/" , "" ) , Space , Str "by" , Space , Str "itself" , Space , Str "should" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "once" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "twice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "Indented" , Space , Link ( "" , [] , [] ) [ Str "thrice" ] ( "/url" , "" ) , Str "." ] , Para [ Str "This" , Space , Str "should" , Space , Str "[not][]" , Space , Str "be" , Space , Str "a" , Space , Str "link." ] , CodeBlock ( "" , [] , [] ) "[not]: /url" , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "bar" ] ( "/url/" , "Title with \"quotes\" inside" ) , Str "." ] , Para [ Str "Foo" , Space , Link ( "" , [] , [] ) [ Str "biz" ] ( "/url/" , "Title with \"quote\" inside" ) , Str "." ] , Header 2 ( "with-ampersands" , [] , [] ) [ Str "With" , Space , Str "ampersands" ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Link ( "" , [] , [] ) [ Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "ampersand" , Space , Str "in" , Space , Str "the" , Space , Str "URL" ] ( "http://example.com/?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "a" , Space , Str "link" , Space , Str "with" , Space , Str "an" , Space , Str "amersand" , Space , Str "in" , Space , Str "the" , Space , Str "link" , Space , Str "text:" , Space , Link ( "" , [] , [] ) [ Str "AT&T" ] ( "http://att.com/" , "AT&T" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Para [ Str "Here\8217s" , Space , Str "an" , Space , Link ( "" , [] , [] ) [ Str "inline" , Space , Str "link" , Space , Str "in" , Space , Str "pointy" , Space , Str "braces" ] ( "/script?foo=1&bar=2" , "" ) , Str "." ] , Header 2 ( "autolinks" , [] , [] ) [ Str "Autolinks" ] , Para [ Str "With" , Space , Str "an" , Space , Str "ampersand:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/?foo=1&bar=2" ] ( "http://example.com/?foo=1&bar=2" , "" ) ] , BulletList [ [ Plain [ Str "In" , Space , Str "a" , Space , Str "list?" ] ] , [ Plain [ Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , [ Plain [ Str "It" , Space , Str "should." ] ] ] , Para [ Str "An" , Space , Str "e-mail" , Space , Str "address:" , Space , Link ( "" , [ "email" ] , [] ) [ Str "nobody@nowhere.net" ] ( "mailto:nobody@nowhere.net" , "" ) ] , BlockQuote [ Para [ Str "Blockquoted:" , Space , Link ( "" , [ "uri" ] , [] ) [ Str "http://example.com/" ] ( "http://example.com/" , "" ) ] ] , Para [ Str "Auto-links" , Space , Str "should" , Space , Str "not" , Space , Str "occur" , Space , Str "here:" , Space , Code ( "" , [] , [] ) "" ] , CodeBlock ( "" , [] , [] ) "or here: " , HorizontalRule , Header 1 ( "images" , [] , [] ) [ Str "Images" ] , Para [ Str "From" , Space , Quoted DoubleQuote [ Str "Voyage" , Space , Str "dans" , Space , Str "la" , Space , Str "Lune" ] , Space , Str "by" , Space , Str "Georges" , Space , Str "Melies" , Space , Str "(1902):" ] , Figure ( "" , [] , [] ) (Caption Nothing [ Plain [ Str "lalune" ] ]) [ Plain [ Image ( "" , [] , [] ) [ Str "lalune" ] ( "lalune.jpg" , "Voyage dans la Lune" ) ] ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "movie" , Space , Image ( "" , [] , [] ) [ Str "movie" ] ( "movie.jpg" , "" ) , Space , Str "icon." ] , HorizontalRule , Header 1 ( "footnotes" , [] , [] ) [ Str "Footnotes" ] , Para [ Str "Here" , Space , Str "is" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Note [ Para [ Str "Here" , Space , Str "is" , Space , Str "the" , Space , Str "footnote." , Space , Str "It" , Space , Str "can" , Space , Str "go" , Space , Str "anywhere" , Space , Str "after" , Space , Str "the" , Space , Str "footnote" , SoftBreak , Str "reference." , Space , Str "It" , Space , Str "need" , Space , Str "not" , Space , Str "be" , Space , Str "placed" , Space , Str "at" , Space , Str "the" , Space , Str "end" , Space , Str "of" , Space , Str "the" , Space , Str "document." ] ] , Space , Str "and" , Space , Str "another." , Note [ Para [ Str "Here\8217s" , Space , Str "the" , Space , Str "long" , Space , Str "note." , Space , Str "This" , Space , Str "one" , Space , Str "contains" , Space , Str "multiple" , SoftBreak , Str "blocks." ] , Para [ Str "Subsequent" , Space , Str "blocks" , Space , Str "are" , Space , Str "indented" , Space , Str "to" , Space , Str "show" , Space , Str "that" , Space , Str "they" , Space , Str "belong" , Space , Str "to" , Space , Str "the" , SoftBreak , Str "footnote" , Space , Str "(as" , Space , Str "with" , Space , Str "list" , Space , Str "items)." ] , CodeBlock ( "" , [] , [] ) " { }" , Para [ Str "If" , Space , Str "you" , Space , Str "want," , Space , Str "you" , Space , Str "can" , Space , Str "indent" , Space , Str "every" , Space , Str "line," , Space , Str "but" , Space , Str "you" , Space , Str "can" , Space , Str "also" , Space , Str "be" , SoftBreak , Str "lazy" , Space , Str "and" , Space , Str "just" , Space , Str "indent" , Space , Str "the" , Space , Str "first" , Space , Str "line" , Space , Str "of" , Space , Str "each" , Space , Str "block." ] ] , SoftBreak , Str "This" , Space , Str "should" , Space , Emph [ Str "not" ] , Space , Str "be" , Space , Str "a" , Space , Str "footnote" , Space , Str "reference," , Space , Str "because" , Space , Str "it" , SoftBreak , Str "contains" , Space , Str "a" , Space , Str "space.[^my" , Space , Str "note]" , Space , Str "Here" , Space , Str "is" , Space , Str "an" , Space , Str "inline" , Space , Str "note." , Note [ Para [ Str "This" , SoftBreak , Str "is" , Space , Emph [ Str "easier" ] , Space , Str "to" , Space , Str "type." , Space , Str "Inline" , Space , Str "notes" , Space , Str "may" , Space , Str "contain" , SoftBreak , Link ( "" , [] , [] ) [ Str "links" ] ( "http://google.com" , "" ) , Space , Str "and" , Space , Code ( "" , [] , [] ) "]" , Space , Str "verbatim" , Space , Str "characters," , SoftBreak , Str "as" , Space , Str "well" , Space , Str "as" , Space , Str "[bracketed" , Space , Str "text]." ] ] ] , BlockQuote [ Para [ Str "Notes" , Space , Str "can" , Space , Str "go" , Space , Str "in" , Space , Str "quotes." , Note [ Para [ Str "In" , Space , Str "quote." ] ] ] ] , OrderedList ( 1 , Decimal , Period ) [ [ Plain [ Str "And" , Space , Str "in" , Space , Str "list" , Space , Str "items." , Note [ Para [ Str "In" , Space , Str "list." ] ] ] ] ] , Para [ Str "This" , Space , Str "paragraph" , Space , Str "should" , Space , Str "not" , Space , Str "be" , Space , Str "part" , Space , Str "of" , Space , Str "the" , Space , Str "note," , Space , Str "as" , Space , Str "it" , Space , Str "is" , Space , Str "not" , Space , Str "indented." ] ] ================================================ FILE: test/writer.opendocument ================================================ Pandoc Test Suite John MacFarlane Anonymous July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. Headers Level 2 with an embedded link Level 3 with emphasis Level 4 Level 5 Level 1 Level 2 with emphasis Level 3 with no blank line Level 2 with no blank line Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line breakhere. Block Quotes E-mail style: This is a block quote. It is pretty short. Code in a block quote: sub status { print "working"; } A list: item one item two Nested block quotes: nested nested This should not be a block quote: 2 > 1. And a following paragraph. Code Blocks Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ Lists Unordered Asterisks tight: asterisk 1 asterisk 2 asterisk 3 Asterisks loose: asterisk 1 asterisk 2 asterisk 3 Pluses tight: Plus 1 Plus 2 Plus 3 Pluses loose: Plus 1 Plus 2 Plus 3 Minuses tight: Minus 1 Minus 2 Minus 3 Minuses loose: Minus 1 Minus 2 Minus 3 Ordered Tight: First Second Third and: One Two Three Loose using tabs: First Second Third and using spaces: One Two Three Multiple paragraphs: Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. Item 2. Item 3. Nested Tab Tab Tab Here’s another: First Second: Fee Fie Foe Third Same thing but with paragraphs: First Second: Fee Fie Foe Third Tabs and spaces this is a list item indented with tabs this is a list item indented with spaces this is an example list item indented with tabs this is an example list item indented with spaces Fancy list markers begins with 2 and now 3 with a continuation sublist with roman numerals, starting with 4 more items a subsublist a subsublist Nesting: Upper Alpha Upper Roman. Decimal start with 6 Lower alpha with paren Autonumbering: Autonumber. More. Nested. Should not be a list item: M.A. 2007 B. Williams Definition Lists Tight using spaces: apple red fruit orange orange fruit banana yellow fruit Tight using tabs: apple red fruit orange orange fruit banana yellow fruit Loose: apple red fruit orange orange fruit banana yellow fruit Multiple blocks with italics: apple red fruitcontains seeds, crisp, pleasant to taste orange orange fruit{ orange code block }orange block quote Multiple definitions, tight: apple red fruit computer orange orange fruit bank Multiple definitions, loose: apple red fruit computer orange orange fruit bank Blank line after term, indented marker, alternate markers: apple red fruit computer orange orange fruit sublist sublist HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is emphasized And this is strong Here’s a simple block: foo This should be a code block, though: <div> foo </div> As should this: <div>foo</div> Now, nested: foo This should just be an HTML comment: Multiline: Code block: <!-- Comment --> Just plain comment, with trailing spaces on the line: Code: <hr /> Hr’s: Inline Markup This is emphasized, and so is this. This is strong, and so is this. An emphasized link. This is strong and em. So is this word. This is strong and em. So is this word. This is code: >, $, \, \$, <html>. This is strikeout. Superscripts: abcd ahello ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. Smart quotes, ellipses, dashes “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘code’ and a “quoted link”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. LaTeX 2 + 2 = 4 x ∈ y α ∧ ω 223 p-Tree Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ Here’s one that has a line break in it: α + ω × x2. These shouldn’t be math: To get the famous equation, write $e = mc^2$. $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.) Shoes ($20) and socks ($5). Escaped $: $73 this should be emphasized 23$. Here’s a LaTeX table: Special Characters Here is some unicode: I hat: Î o umlaut: ö section: § set membership: ∈ copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - Links Explicit Just a URL. URL and title. URL and title. URL and title. URL and title URL and title with_underscore Email link Empty. Reference Foo bar. With embedded [brackets]. b by itself should be a link. Indented once. Indented twice. Indented thrice. This should [not][] be a link. [not]: /url Foo bar. Foo biz. With ampersands Here’s a link with an ampersand in the URL. Here’s a link with an amersand in the link text: AT&T. Here’s an inline link. Here’s an inline link in pointy braces. Autolinks With an ampersand: http://example.com/?foo=1&bar=2 In a list? http://example.com/ It should. An e-mail address: nobody@nowhere.net Blockquoted: http://example.com/ Auto-links should not occur here: <http://example.com/> or here: <http://example.com/> Images From “Voyage dans la Lune” by Georges Melies (1902): lalune Here is a movie icon. Footnotes Here is a footnote reference,1Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. and another.2Here’s the long note. This one contains multiple blocks.Subsequent blocks are indented to show that they belong to the footnote (as with list items).{ <code> }If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text]. Notes can go in quotes.4In quote. And in list items.5In list. This paragraph should not be part of the note, as it is not indented. ================================================ FILE: test/writer.opml ================================================ Pandoc Test Suite Mon, 17 Jul 2006 00:00:00 UTC John MacFarlane; Anonymous ================================================ FILE: test/writer.org ================================================ #+title: Pandoc Test Suite #+author: John MacFarlane; Anonymous #+date: July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. -------------- * Headers :PROPERTIES: :CUSTOM_ID: headers :END: ** Level 2 with an [[/url][embedded link]] :PROPERTIES: :CUSTOM_ID: level-2-with-an-embedded-link :END: *** Level 3 with /emphasis/ :PROPERTIES: :CUSTOM_ID: level-3-with-emphasis :END: **** Level 4 :PROPERTIES: :CUSTOM_ID: level-4 :END: ***** Level 5 :PROPERTIES: :CUSTOM_ID: level-5 :END: * Level 1 :PROPERTIES: :CUSTOM_ID: level-1 :END: ** Level 2 with /emphasis/ :PROPERTIES: :CUSTOM_ID: level-2-with-emphasis :END: *** Level 3 :PROPERTIES: :CUSTOM_ID: level-3 :END: with no blank line ** Level 2 :PROPERTIES: :CUSTOM_ID: level-2 :END: with no blank line -------------- * Paragraphs :PROPERTIES: :CUSTOM_ID: paragraphs :END: Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break\\ here. -------------- * Block Quotes :PROPERTIES: :CUSTOM_ID: block-quotes :END: E-mail style: #+begin_quote This is a block quote. It is pretty short. #+end_quote #+begin_quote Code in a block quote: #+begin_example sub status { print "working"; } #+end_example A list: 1. item one 2. item two Nested block quotes: #+begin_quote nested #+end_quote #+begin_quote nested #+end_quote #+end_quote This should not be a block quote: 2 > 1. And a following paragraph. -------------- * Code Blocks :PROPERTIES: :CUSTOM_ID: code-blocks :END: Code: #+begin_example ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab #+end_example And: #+begin_example this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ #+end_example -------------- * Lists :PROPERTIES: :CUSTOM_ID: lists :END: ** Unordered :PROPERTIES: :CUSTOM_ID: unordered :END: Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 ** Ordered :PROPERTIES: :CUSTOM_ID: ordered :END: Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. ** Nested :PROPERTIES: :CUSTOM_ID: nested :END: - Tab - Tab - Tab Here's another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third ** Tabs and spaces :PROPERTIES: :CUSTOM_ID: tabs-and-spaces :END: - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces ** Fancy list markers :PROPERTIES: :CUSTOM_ID: fancy-list-markers :END: 2) [@2] begins with 2 3) and now 3 with a continuation 4. [@4] sublist with roman numerals, starting with 4 5. more items 1) a subsublist 2) a subsublist Nesting: 1. Upper Alpha 1. Upper Roman. 6) [@6] Decimal start with 6 3) [@3] Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams -------------- * Definition Lists :PROPERTIES: :CUSTOM_ID: definition-lists :END: Tight using spaces: - apple :: red fruit - orange :: orange fruit - banana :: yellow fruit Tight using tabs: - apple :: red fruit - orange :: orange fruit - banana :: yellow fruit Loose: - apple :: red fruit - orange :: orange fruit - banana :: yellow fruit Multiple blocks with italics: - /apple/ :: red fruit contains seeds, crisp, pleasant to taste - /orange/ :: orange fruit #+begin_example { orange code block } #+end_example #+begin_quote orange block quote #+end_quote Multiple definitions, tight: - apple :: red fruit computer - orange :: orange fruit bank Multiple definitions, loose: - apple :: red fruit computer - orange :: orange fruit bank Blank line after term, indented marker, alternate markers: - apple :: red fruit computer - orange :: orange fruit 1. sublist 2. sublist * HTML Blocks :PROPERTIES: :CUSTOM_ID: html-blocks :END: Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: #+begin_html #+end_html #+begin_html #+end_html #+begin_html #+end_html #+begin_html #+end_html #+begin_html #+end_html #+begin_html
                #+end_html This is /emphasized/ #+begin_html #+end_html And this is *strong* #+begin_html
                #+end_html #+begin_html #+end_html Here's a simple block: foo This should be a code block, though: #+begin_example
                foo
                #+end_example As should this: #+begin_example
                foo
                #+end_example Now, nested: foo This should just be an HTML comment: #+begin_html #+end_html Multiline: #+begin_html #+end_html #+begin_html #+end_html Code block: #+begin_example #+end_example Just plain comment, with trailing spaces on the line: #+begin_html #+end_html Code: #+begin_example
                #+end_example Hr's: #+begin_html
                #+end_html #+begin_html
                #+end_html #+begin_html
                #+end_html #+begin_html
                #+end_html #+begin_html
                #+end_html #+begin_html
                #+end_html #+begin_html
                #+end_html #+begin_html
                #+end_html #+begin_html
                #+end_html -------------- * Inline Markup :PROPERTIES: :CUSTOM_ID: inline-markup :END: This is /emphasized/, and so /is this/. This is *strong*, and so *is this*. An /[[/url][emphasized link]]/. */This is strong and em./* So is */this/* word. */This is strong and em./* So is */this/* word. This is code: =>=, =$=, =\=, =\$=, ==. +This is /strikeout/.+ Superscripts: a^{bc}d a^{/hello/} a^{hello there}. Subscripts: H_{2}O, H_{23}O, H_{many of them}O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. -------------- * Smart quotes, ellipses, dashes :PROPERTIES: :CUSTOM_ID: smart-quotes-ellipses-dashes :END: “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70's? Here is some quoted ‘=code=’ and a “[[http://example.com/?foo=1&bar=2][quoted link]]”. Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses...and...and.... -------------- * LaTeX :PROPERTIES: :CUSTOM_ID: latex :END: - \cite[22-23]{smith.1899} - \(2+2=4\) - \(x \in y\) - \(\alpha \wedge \omega\) - \(223\) - \(p\)-Tree - Here's some display math: \[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\] - Here's one that has a line break in it: \(\alpha + \omega \times x^2\). These shouldn't be math: - To get the famous equation, write =$e = mc^2$=. - $22,000 is a /lot/ of money. So is $34,000. (It worked if “lot” is emphasized.) - Shoes ($20) and socks ($5). - Escaped =$=: $73 /this should be emphasized/ 23$. Here's a LaTeX table: \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} -------------- * Special Characters :PROPERTIES: :CUSTOM_ID: special-characters :END: Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - -------------- * Links :PROPERTIES: :CUSTOM_ID: links :END: ** Explicit :PROPERTIES: :CUSTOM_ID: explicit :END: Just a [[/url/][URL]]. [[/url/][URL and title]]. [[/url/][URL and title]]. [[/url/][URL and title]]. [[/url/][URL and title]] [[/url/][URL and title]] [[/url/with_underscore][with_underscore]] [[mailto:nobody@nowhere.net][Email link]] [[][Empty]]. ** Reference :PROPERTIES: :CUSTOM_ID: reference :END: Foo [[/url/][bar]]. With [[/url/][embedded [brackets]]]. [[/url/][b]] by itself should be a link. Indented [[/url][once]]. Indented [[/url][twice]]. Indented [[/url][thrice]]. This should [not][] be a link. #+begin_example [not]: /url #+end_example Foo [[/url/][bar]]. Foo [[/url/][biz]]. ** With ampersands :PROPERTIES: :CUSTOM_ID: with-ampersands :END: Here's a [[http://example.com/?foo=1&bar=2][link with an ampersand in the URL]]. Here's a link with an amersand in the link text: [[http://att.com/][AT&T]]. Here's an [[/script?foo=1&bar=2][inline link]]. Here's an [[/script?foo=1&bar=2][inline link in pointy braces]]. ** Autolinks :PROPERTIES: :CUSTOM_ID: autolinks :END: With an ampersand: [[http://example.com/?foo=1&bar=2]] - In a list? - [[http://example.com/]] - It should. An e-mail address: [[mailto:nobody@nowhere.net][nobody@nowhere.net]] #+begin_quote Blockquoted: [[http://example.com/]] #+end_quote Auto-links should not occur here: == #+begin_example or here: #+end_example -------------- * Images :PROPERTIES: :CUSTOM_ID: images :END: From “Voyage dans la Lune” by Georges Melies (1902): #+caption: lalune [[file:lalune.jpg]] Here is a movie [[file:movie.jpg]] icon. -------------- * Footnotes :PROPERTIES: :CUSTOM_ID: footnotes :END: Here is a footnote reference,[fn:1] and another.[fn:2] This should /not/ be a footnote reference, because it contains a space.[^my note] Here is an inline note.[fn:3] #+begin_quote Notes can go in quotes.[fn:4] #+end_quote 1. And in list items.[fn:5] This paragraph should not be part of the note, as it is not indented. [fn:1] Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. [fn:2] Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). #+begin_example { } #+end_example If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. [fn:3] This is /easier/ to type. Inline notes may contain [[http://google.com][links]] and =]= verbatim characters, as well as [bracketed text]. [fn:4] In quote. [fn:5] In list. ================================================ FILE: test/writer.plain ================================================ Pandoc Test Suite John MacFarlane; Anonymous July 17, 2006 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. -------------------------------------------------------------------------------- Headers Level 2 with an embedded link Level 3 with emphasis Level 4 Level 5 Level 1 Level 2 with emphasis Level 3 with no blank line Level 2 with no blank line -------------------------------------------------------------------------------- Paragraphs Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break here. -------------------------------------------------------------------------------- Block Quotes E-mail style: This is a block quote. It is pretty short. Code in a block quote: sub status { print "working"; } A list: 1. item one 2. item two Nested block quotes: nested nested This should not be a block quote: 2 > 1. And a following paragraph. -------------------------------------------------------------------------------- Code Blocks Code: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ -------------------------------------------------------------------------------- Lists Unordered Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. 2. Item 2. 3. Item 3. Nested - Tab - Tab - Tab Here’s another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third Tabs and spaces - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces Fancy list markers (2) begins with 2 (3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items (A) a subsublist (B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. (6) Decimal start with 6 c) Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams -------------------------------------------------------------------------------- Definition Lists Tight using spaces: apple red fruit orange orange fruit banana yellow fruit Tight using tabs: apple red fruit orange orange fruit banana yellow fruit Loose: apple red fruit orange orange fruit banana yellow fruit Multiple blocks with italics: apple red fruit contains seeds, crisp, pleasant to taste orange orange fruit { orange code block } orange block quote Multiple definitions, tight: apple red fruit computer orange orange fruit bank Multiple definitions, loose: apple red fruit computer orange orange fruit bank Blank line after term, indented marker, alternate markers: apple red fruit computer orange orange fruit 1. sublist 2. sublist HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is emphasized And this is strong Here’s a simple block: foo This should be a code block, though:
                foo
                As should this:
                foo
                Now, nested: foo This should just be an HTML comment: Multiline: Code block: Just plain comment, with trailing spaces on the line: Code:
                Hr’s: -------------------------------------------------------------------------------- Inline Markup This is emphasized, and so is this. This is strong, and so is this. An emphasized link. This is strong and em. So is this word. This is strong and em. So is this word. This is code: >, $, \, \$, . ~~This is strikeout.~~ Superscripts: a^(bc)d a^(hello) a^(hello there). Subscripts: H₂O, H₂₃O, H_(many of them)O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. -------------------------------------------------------------------------------- Smart quotes, ellipses, dashes “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘code’ and a “quoted link”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. -------------------------------------------------------------------------------- LaTeX - - 2 + 2 = 4 - x ∈ y - α ∧ ω - 223 - p-Tree - Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ - Here’s one that has a line break in it: α + ω × x². These shouldn’t be math: - To get the famous equation, write $e = mc^2$. - $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.) - Shoes ($20) and socks ($5). - Escaped $: $73 this should be emphasized 23$. Here’s a LaTeX table: -------------------------------------------------------------------------------- Special Characters Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - -------------------------------------------------------------------------------- Links Explicit Just a URL. URL and title. URL and title. URL and title. URL and title URL and title with_underscore Email link Empty. Reference Foo bar. With embedded [brackets]. b by itself should be a link. Indented once. Indented twice. Indented thrice. This should [not][] be a link. [not]: /url Foo bar. Foo biz. With ampersands Here’s a link with an ampersand in the URL. Here’s a link with an amersand in the link text: AT&T. Here’s an inline link. Here’s an inline link in pointy braces. Autolinks With an ampersand: http://example.com/?foo=1&bar=2 - In a list? - http://example.com/ - It should. An e-mail address: nobody@nowhere.net Blockquoted: http://example.com/ Auto-links should not occur here: or here: -------------------------------------------------------------------------------- Images From “Voyage dans la Lune” by Georges Melies (1902): [lalune] Here is a movie [movie] icon. -------------------------------------------------------------------------------- Footnotes Here is a footnote reference,[1] and another.[2] This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.[3] Notes can go in quotes.[4] 1. And in list items.[5] This paragraph should not be part of the note, as it is not indented. [1] Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. [2] Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). { } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. [3] This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text]. [4] In quote. [5] In list. ================================================ FILE: test/writer.rst ================================================ ================= Pandoc Test Suite ================= :Author: John MacFarlane :Author: Anonymous :Date: July 17, 2006 .. role:: raw-latex(raw) :format: latex .. This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. -------------- Headers ======= Level 2 with an `embedded link `__ ---------------------------------------- Level 3 with *emphasis* ~~~~~~~~~~~~~~~~~~~~~~~ Level 4 ^^^^^^^ Level 5 ''''''' Level 1 ======= Level 2 with *emphasis* ----------------------- Level 3 ~~~~~~~ with no blank line Level 2 ------- with no blank line -------------- Paragraphs ========== Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. \* criminey. | There should be a hard line break | here. -------------- Block Quotes ============ E-mail style: This is a block quote. It is pretty short. .. Code in a block quote: :: sub status { print "working"; } A list: 1. item one 2. item two Nested block quotes: nested .. nested This should not be a block quote: 2 > 1. And a following paragraph. -------------- Code Blocks =========== Code: :: ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab And: :: this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ -------------- Lists ===== Unordered --------- Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 Ordered ------- Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. 2. Item 2. 3. Item 3. Nested ------ - Tab - Tab - Tab Here’s another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third Tabs and spaces --------------- - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces Fancy list markers ------------------ (2) begins with 2 (3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items (A) a subsublist (B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. (6) Decimal start with 6 c) Lower alpha with paren Autonumbering: #. Autonumber. #. More. #. Nested. Should not be a list item: M.A. 2007 B. Williams -------------- Definition Lists ================ Tight using spaces: apple red fruit orange orange fruit banana yellow fruit Tight using tabs: apple red fruit orange orange fruit banana yellow fruit Loose: apple red fruit orange orange fruit banana yellow fruit Multiple blocks with italics: *apple* red fruit contains seeds, crisp, pleasant to taste *orange* orange fruit :: { orange code block } .. orange block quote Multiple definitions, tight: apple red fruit computer orange orange fruit bank Multiple definitions, loose: apple red fruit computer orange orange fruit bank Blank line after term, indented marker, alternate markers: apple red fruit computer orange orange fruit 1. sublist 2. sublist HTML Blocks =========== Simple block on one line: .. container:: foo And nested without indentation: .. container:: .. container:: .. container:: foo .. container:: bar Interpreted markdown in a table: .. raw:: html .. raw:: html .. raw:: html .. raw:: html .. raw:: html .. raw:: html
                This is *emphasized* .. raw:: html And this is **strong** .. raw:: html
                .. raw:: html Here’s a simple block: .. container:: foo This should be a code block, though: ::
                foo
                As should this: ::
                foo
                Now, nested: .. container:: .. container:: .. container:: foo This should just be an HTML comment: .. raw:: html Multiline: .. raw:: html .. raw:: html Code block: :: Just plain comment, with trailing spaces on the line: .. raw:: html Code: ::
                Hr’s: .. raw:: html
                .. raw:: html
                .. raw:: html
                .. raw:: html
                .. raw:: html
                .. raw:: html
                .. raw:: html
                .. raw:: html
                .. raw:: html
                -------------- Inline Markup ============= This is *emphasized*, and so *is this*. This is **strong**, and so **is this**. An `emphasized link `__. **This is strong and em.** So is **this** word. **This is strong and em.** So is **this** word. This is code: ``>``, ``$``, ``\``, ``\$``, ````. [STRIKEOUT:This is strikeout.] Superscripts: a\ :sup:`bc`\ d a\ :sup:`hello` a\ :sup:`hello there`. Subscripts: H\ :sub:`2`\ O, H\ :sub:`23`\ O, H\ :sub:`many of them`\ O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. -------------- Smart quotes, ellipses, dashes ============================== “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘``code``’ and a “`quoted link `__”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. -------------- LaTeX ===== - :raw-latex:`\cite[22-23]{smith.1899}` - :math:`2+2=4` - :math:`x \in y` - :math:`\alpha \wedge \omega` - :math:`223` - :math:`p`-Tree - Here’s some display math: .. math:: \frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h} - Here’s one that has a line break in it: :math:`\alpha + \omega \times x^2`. These shouldn’t be math: - To get the famous equation, write ``$e = mc^2$``. - $22,000 is a *lot* of money. So is $34,000. (It worked if “lot” is emphasized.) - Shoes ($20) and socks ($5). - Escaped ``$``: $73 *this should be emphasized* 23$. Here’s a LaTeX table: .. raw:: latex \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} -------------- Special Characters ================== Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - -------------- Links ===== Explicit -------- Just a `URL `__. `URL and title `__. `URL and title `__. `URL and title `__. `URL and title `__ `URL and title `__ `with_underscore `__ `Email link `__ `Empty <>`__. Reference --------- Foo `bar `__. With `embedded [brackets] `__. `b `__ by itself should be a link. Indented `once `__. Indented `twice `__. Indented `thrice `__. This should [not][] be a link. :: [not]: /url Foo `bar `__. Foo `biz `__. With ampersands --------------- Here’s a `link with an ampersand in the URL `__. Here’s a link with an amersand in the link text: `AT&T `__. Here’s an `inline link `__. Here’s an `inline link in pointy braces `__. Autolinks --------- With an ampersand: http://example.com/?foo=1&bar=2 - In a list? - http://example.com/ - It should. An e-mail address: nobody@nowhere.net Blockquoted: http://example.com/ Auto-links should not occur here: ```` :: or here: -------------- Images ====== From “Voyage dans la Lune” by Georges Melies (1902): .. figure:: lalune.jpg :alt: lalune lalune Here is a movie |movie| icon. -------------- Footnotes ========= Here is a footnote reference, [1]_ and another. [2]_ This should *not* be a footnote reference, because it contains a space.[^my note] Here is an inline note. [3]_ Notes can go in quotes. [4]_ 1. And in list items. [5]_ This paragraph should not be part of the note, as it is not indented. .. [1] Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. .. [2] Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). :: { } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. .. [3] This is *easier* to type. Inline notes may contain `links `__ and ``]`` verbatim characters, as well as [bracketed text]. .. [4] In quote. .. [5] In list. .. |movie| image:: movie.jpg ================================================ FILE: test/writer.rtf ================================================ {\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 \fmodern Courier;}} {\colortbl;\red255\green0\blue0;\red0\green0\blue255;} \widowctrl\hyphauto {\pard \qc \f0 \sa180 \li0 \fi0 \b \fs36 Pandoc Test Suite\par} {\pard \qc \f0 \sa180 \li0 \fi0 John MacFarlane\par} {\pard \qc \f0 \sa180 \li0 \fi0 Anonymous\par} {\pard \qc \f0 \sa180 \li0 \fi0 July 17, 2006\par} {\pard \ql \f0 \sa180 \li0 \fi0 \par} {\pard \ql \f0 \sa180 \li0 \fi0 This is a set of tests for pandoc. Most of them are adapted from John Gruber\u8217's markdown test suite.\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Headers\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Level 2 with an {\field{\*\fldinst{HYPERLINK "/url"}}{\fldrslt{\ul embedded link }}} \par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel2 \b \fs28 Level 3 with {\i emphasis}\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel3 \b \fs24 Level 4\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel4 \b \fs20 Level 5\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Level 1\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Level 2 with {\i emphasis}\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel2 \b \fs28 Level 3\par} {\pard \ql \f0 \sa180 \li0 \fi0 with no blank line\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Level 2\par} {\pard \ql \f0 \sa180 \li0 \fi0 with no blank line\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Paragraphs\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's a regular paragraph.\par} {\pard \ql \f0 \sa180 \li0 \fi0 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 a list item.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's one with a bullet. * criminey.\par} {\pard \ql \f0 \sa180 \li0 \fi0 There should be a hard line break\line here.\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Block Quotes\par} {\pard \ql \f0 \sa180 \li0 \fi0 E-mail style:\par} {\pard \ql \f0 \sa180 \li720 \fi0 This is a block quote. It is pretty short.\par} {\pard \ql \f0 \sa180 \li720 \fi0 Code in a block quote:\par} {\pard \ql \f0 \sa180 \li720 \fi0 \f1 sub status \{\line print "working";\line \}\par} {\pard \ql \f0 \sa180 \li720 \fi0 A list:\par} {\pard \ql \f0 \sa0 \li1080 \fi-360 1.\tx360\tab item one\par} {\pard \ql \f0 \sa0 \li1080 \fi-360 2.\tx360\tab item two\sa180\par} {\pard \ql \f0 \sa180 \li720 \fi0 Nested block quotes:\par} {\pard \ql \f0 \sa180 \li1440 \fi0 nested\par} {\pard \ql \f0 \sa180 \li1440 \fi0 nested\par} {\pard \ql \f0 \sa180 \li0 \fi0 This should not be a block quote: 2 > 1.\par} {\pard \ql \f0 \sa180 \li0 \fi0 And a following paragraph.\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Code Blocks\par} {\pard \ql \f0 \sa180 \li0 \fi0 Code:\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1 ---- (should be four hyphens)\line \line sub status \{\line print "working";\line \}\line \line this code block is indented by one tab\par} {\pard \ql \f0 \sa180 \li0 \fi0 And:\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1 this code block is indented by two tabs\line \line These should not be escaped: \\$ \\\\ \\> \\[ \\\{\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Lists\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Unordered\par} {\pard \ql \f0 \sa180 \li0 \fi0 Asterisks tight:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab asterisk 1\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab asterisk 2\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab asterisk 3\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Asterisks loose:\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab asterisk 1\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab asterisk 2\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab asterisk 3\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Pluses tight:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Plus 1\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Plus 2\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Plus 3\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Pluses loose:\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab Plus 1\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab Plus 2\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab Plus 3\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Minuses tight:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Minus 1\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Minus 2\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Minus 3\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Minuses loose:\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab Minus 1\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab Minus 2\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab Minus 3\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Ordered\par} {\pard \ql \f0 \sa180 \li0 \fi0 Tight:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 1.\tx360\tab First\par} {\pard \ql \f0 \sa0 \li360 \fi-360 2.\tx360\tab Second\par} {\pard \ql \f0 \sa0 \li360 \fi-360 3.\tx360\tab Third\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 and:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 1.\tx360\tab One\par} {\pard \ql \f0 \sa0 \li360 \fi-360 2.\tx360\tab Two\par} {\pard \ql \f0 \sa0 \li360 \fi-360 3.\tx360\tab Three\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Loose using tabs:\par} {\pard \ql \f0 \sa180 \li360 \fi-360 1.\tx360\tab First\par} {\pard \ql \f0 \sa180 \li360 \fi-360 2.\tx360\tab Second\par} {\pard \ql \f0 \sa180 \li360 \fi-360 3.\tx360\tab Third\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 and using spaces:\par} {\pard \ql \f0 \sa180 \li360 \fi-360 1.\tx360\tab One\par} {\pard \ql \f0 \sa180 \li360 \fi-360 2.\tx360\tab Two\par} {\pard \ql \f0 \sa180 \li360 \fi-360 3.\tx360\tab Three\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiple paragraphs:\par} {\pard \ql \f0 \sa180 \li360 \fi-360 1.\tx360\tab Item 1, graf one.\par} {\pard \ql \f0 \sa180 \li360 \fi0 Item 1. graf two. The quick brown fox jumped over the lazy dog\u8217's back.\par} {\pard \ql \f0 \sa180 \li360 \fi-360 2.\tx360\tab Item 2.\par} {\pard \ql \f0 \sa180 \li360 \fi-360 3.\tx360\tab Item 3.\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Nested\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Tab\par} {\pard \ql \f0 \sa0 \li720 \fi-360 \endash \tx360\tab Tab\par} {\pard \ql \f0 \sa0 \li1080 \fi-360 \bullet \tx360\tab Tab\sa180\sa180\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's another:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 1.\tx360\tab First\par} {\pard \ql \f0 \sa0 \li360 \fi-360 2.\tx360\tab Second:\par} {\pard \ql \f0 \sa0 \li720 \fi-360 \endash \tx360\tab Fee\par} {\pard \ql \f0 \sa0 \li720 \fi-360 \endash \tx360\tab Fie\par} {\pard \ql \f0 \sa0 \li720 \fi-360 \endash \tx360\tab Foe\sa180\par} {\pard \ql \f0 \sa0 \li360 \fi-360 3.\tx360\tab Third\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Same thing but with paragraphs:\par} {\pard \ql \f0 \sa180 \li360 \fi-360 1.\tx360\tab First\par} {\pard \ql \f0 \sa180 \li360 \fi-360 2.\tx360\tab Second:\par} {\pard \ql \f0 \sa0 \li720 \fi-360 \endash \tx360\tab Fee\par} {\pard \ql \f0 \sa0 \li720 \fi-360 \endash \tx360\tab Fie\par} {\pard \ql \f0 \sa0 \li720 \fi-360 \endash \tx360\tab Foe\sa180\par} {\pard \ql \f0 \sa180 \li360 \fi-360 3.\tx360\tab Third\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Tabs and spaces\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab this is a list item indented with tabs\par} {\pard \ql \f0 \sa180 \li360 \fi-360 \bullet \tx360\tab this is a list item indented with spaces\par} {\pard \ql \f0 \sa180 \li720 \fi-360 \endash \tx360\tab this is an example list item indented with tabs\par} {\pard \ql \f0 \sa180 \li720 \fi-360 \endash \tx360\tab this is an example list item indented with spaces\sa180\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Fancy list markers\par} {\pard \ql \f0 \sa180 \li360 \fi-360 (2)\tx360\tab begins with 2\par} {\pard \ql \f0 \sa180 \li360 \fi-360 (3)\tx360\tab and now 3\par} {\pard \ql \f0 \sa180 \li360 \fi0 with a continuation\par} {\pard \ql \f0 \sa0 \li720 \fi-360 iv.\tx360\tab sublist with roman numerals, starting with 4\par} {\pard \ql \f0 \sa0 \li720 \fi-360 v.\tx360\tab more items\par} {\pard \ql \f0 \sa0 \li1080 \fi-360 (A)\tx360\tab a subsublist\par} {\pard \ql \f0 \sa0 \li1080 \fi-360 (B)\tx360\tab a subsublist\sa180\sa180\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Nesting:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 A.\tx360\tab Upper Alpha\par} {\pard \ql \f0 \sa0 \li720 \fi-360 I.\tx360\tab Upper Roman.\par} {\pard \ql \f0 \sa0 \li1080 \fi-360 (6)\tx360\tab Decimal start with 6\par} {\pard \ql \f0 \sa0 \li1440 \fi-360 c)\tx360\tab Lower alpha with paren\sa180\sa180\sa180\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Autonumbering:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 1.\tx360\tab Autonumber.\par} {\pard \ql \f0 \sa0 \li360 \fi-360 2.\tx360\tab More.\par} {\pard \ql \f0 \sa0 \li720 \fi-360 a.\tx360\tab Nested.\sa180\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Should not be a list item:\par} {\pard \ql \f0 \sa180 \li0 \fi0 M.A.\u160 ?2007\par} {\pard \ql \f0 \sa180 \li0 \fi0 B. Williams\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Definition Lists\par} {\pard \ql \f0 \sa180 \li0 \fi0 Tight using spaces:\par} {\pard \ql \f0 \sa0 \li0 \fi0 apple\par} {\pard \ql \f0 \sa0 \li360 \fi0 red fruit\par} {\pard \ql \f0 \sa0 \li0 \fi0 orange\par} {\pard \ql \f0 \sa0 \li360 \fi0 orange fruit\par} {\pard \ql \f0 \sa0 \li0 \fi0 banana\par} {\pard \ql \f0 \sa0 \li360 \fi0 yellow fruit\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Tight using tabs:\par} {\pard \ql \f0 \sa0 \li0 \fi0 apple\par} {\pard \ql \f0 \sa0 \li360 \fi0 red fruit\par} {\pard \ql \f0 \sa0 \li0 \fi0 orange\par} {\pard \ql \f0 \sa0 \li360 \fi0 orange fruit\par} {\pard \ql \f0 \sa0 \li0 \fi0 banana\par} {\pard \ql \f0 \sa0 \li360 \fi0 yellow fruit\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Loose:\par} {\pard \ql \f0 \sa0 \li0 \fi0 apple\par} {\pard \ql \f0 \sa180 \li360 \fi0 red fruit\par} {\pard \ql \f0 \sa0 \li0 \fi0 orange\par} {\pard \ql \f0 \sa180 \li360 \fi0 orange fruit\par} {\pard \ql \f0 \sa0 \li0 \fi0 banana\par} {\pard \ql \f0 \sa180 \li360 \fi0 yellow fruit\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiple blocks with italics:\par} {\pard \ql \f0 \sa0 \li0 \fi0 {\i apple}\par} {\pard \ql \f0 \sa180 \li360 \fi0 red fruit\par} {\pard \ql \f0 \sa180 \li360 \fi0 contains seeds, crisp, pleasant to taste\par} {\pard \ql \f0 \sa0 \li0 \fi0 {\i orange}\par} {\pard \ql \f0 \sa180 \li360 \fi0 orange fruit\par} {\pard \ql \f0 \sa180 \li360 \fi0 \f1 \{ orange code block \}\par} {\pard \ql \f0 \sa180 \li1080 \fi0 orange block quote\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiple definitions, tight:\par} {\pard \ql \f0 \sa0 \li0 \fi0 apple\par} {\pard \ql \f0 \sa0 \li360 \fi0 red fruit\par} {\pard \ql \f0 \sa0 \li360 \fi0 computer\par} {\pard \ql \f0 \sa0 \li0 \fi0 orange\par} {\pard \ql \f0 \sa0 \li360 \fi0 orange fruit\par} {\pard \ql \f0 \sa0 \li360 \fi0 bank\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiple definitions, loose:\par} {\pard \ql \f0 \sa0 \li0 \fi0 apple\par} {\pard \ql \f0 \sa180 \li360 \fi0 red fruit\par} {\pard \ql \f0 \sa180 \li360 \fi0 computer\par} {\pard \ql \f0 \sa0 \li0 \fi0 orange\par} {\pard \ql \f0 \sa180 \li360 \fi0 orange fruit\par} {\pard \ql \f0 \sa180 \li360 \fi0 bank\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Blank line after term, indented marker, alternate markers:\par} {\pard \ql \f0 \sa0 \li0 \fi0 apple\par} {\pard \ql \f0 \sa180 \li360 \fi0 red fruit\par} {\pard \ql \f0 \sa180 \li360 \fi0 computer\par} {\pard \ql \f0 \sa0 \li0 \fi0 orange\par} {\pard \ql \f0 \sa180 \li360 \fi0 orange fruit\par} {\pard \ql \f0 \sa0 \li720 \fi-360 1.\tx360\tab sublist\par} {\pard \ql \f0 \sa0 \li720 \fi-360 2.\tx360\tab sublist\sa180\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 HTML Blocks\par} {\pard \ql \f0 \sa180 \li0 \fi0 Simple block on one line:\par} {\pard \ql \f0 \sa0 \li0 \fi0 foo\par} {\pard \ql \f0 \sa180 \li0 \fi0 And nested without indentation:\par} {\pard \ql \f0 \sa180 \li0 \fi0 foo\par} {\pard \ql \f0 \sa0 \li0 \fi0 bar\par} {\pard \ql \f0 \sa180 \li0 \fi0 Interpreted markdown in a table:\par} {\pard \ql \f0 \sa0 \li0 \fi0 This is {\i emphasized}\par} {\pard \ql \f0 \sa0 \li0 \fi0 And this is {\b strong}\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's a simple block:\par} {\pard \ql \f0 \sa180 \li0 \fi0 foo\par} {\pard \ql \f0 \sa180 \li0 \fi0 This should be a code block, though:\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1
                \line foo\line
                \par} {\pard \ql \f0 \sa180 \li0 \fi0 As should this:\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1
                foo
                \par} {\pard \ql \f0 \sa180 \li0 \fi0 Now, nested:\par} {\pard \ql \f0 \sa0 \li0 \fi0 foo\par} {\pard \ql \f0 \sa180 \li0 \fi0 This should just be an HTML comment:\par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiline:\par} {\pard \ql \f0 \sa180 \li0 \fi0 Code block:\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1 \par} {\pard \ql \f0 \sa180 \li0 \fi0 Just plain comment, with trailing spaces on the line:\par} {\pard \ql \f0 \sa180 \li0 \fi0 Code:\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1
                \par} {\pard \ql \f0 \sa180 \li0 \fi0 Hr\u8217's:\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Inline Markup\par} {\pard \ql \f0 \sa180 \li0 \fi0 This is {\i emphasized}, and so {\i is this}.\par} {\pard \ql \f0 \sa180 \li0 \fi0 This is {\b strong}, and so {\b is this}.\par} {\pard \ql \f0 \sa180 \li0 \fi0 An {\i {\field{\*\fldinst{HYPERLINK "/url"}}{\fldrslt{\ul emphasized link }}} }.\par} {\pard \ql \f0 \sa180 \li0 \fi0 {\b {\i This is strong and em.}}\par} {\pard \ql \f0 \sa180 \li0 \fi0 So is {\b {\i this}} word.\par} {\pard \ql \f0 \sa180 \li0 \fi0 {\b {\i This is strong and em.}}\par} {\pard \ql \f0 \sa180 \li0 \fi0 So is {\b {\i this}} word.\par} {\pard \ql \f0 \sa180 \li0 \fi0 This is code: {\f1 >}, {\f1 $}, {\f1 \\}, {\f1 \\$}, {\f1 }.\par} {\pard \ql \f0 \sa180 \li0 \fi0 {\strike This is {\i strikeout}.}\par} {\pard \ql \f0 \sa180 \li0 \fi0 Superscripts: a{\super bc}d a{\super {\i hello}} a{\super hello\u160 ?there}.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Subscripts: H{\sub 2}O, H{\sub 23}O, H{\sub many\u160 ?of\u160 ?them}O.\par} {\pard \ql \f0 \sa180 \li0 \fi0 These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Smart quotes, ellipses, dashes\par} {\pard \ql \f0 \sa180 \li0 \fi0 \u8220"Hello,\u8221" said the spider. \u8220"\u8216'Shelob\u8217' is my name.\u8221"\par} {\pard \ql \f0 \sa180 \li0 \fi0 \u8216'A\u8217', \u8216'B\u8217', and \u8216'C\u8217' are letters.\par} {\pard \ql \f0 \sa180 \li0 \fi0 \u8216'Oak,\u8217' \u8216'elm,\u8217' and \u8216'beech\u8217' are names of trees. So is \u8216'pine.\u8217'\par} {\pard \ql \f0 \sa180 \li0 \fi0 \u8216'He said, \u8220"I want to go.\u8221"\u8217' Were you alive in the 70\u8217's?\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here is some quoted \u8216'{\f1 code}\u8217' and a \u8220"{\field{\*\fldinst{HYPERLINK "http://example.com/?foo=1&bar=2"}}{\fldrslt{\ul quoted link }}} \u8221".\par} {\pard \ql \f0 \sa180 \li0 \fi0 Some dashes: one\u8212-two \u8212- three\u8212-four \u8212- five.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Dashes between numbers: 5\u8211-7, 255\u8211-66, 1987\u8211-1999.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Ellipses\u8230 ?and\u8230 ?and\u8230 ?.\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 LaTeX\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab \par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab 2\u8197 ?+\u8197 ?2\u8196 ?=\u8196 ?4\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab {\i x}\u8196 ?\u8712 ?\u8196 ?{\i y}\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab {\i \u945 ?}\u8197 ?\u8743 ?\u8197 ?{\i \u969 ?}\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab 223\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab {\i p}-Tree\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Here\u8217's some display math: $$\\frac\{d\}\{dx\}f(x)=\\lim_\{h\\to 0\}\\frac\{f(x+h)-f(x)\}\{h\}$$\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Here\u8217's one that has a line break in it: {\i \u945 ?}\u8197 ?+\u8197 ?{\i \u969 ?}\u8197 ?\u215 ?\u8197 ?{\i x}{\super 2}.\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 These shouldn\u8217't be math:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab To get the famous equation, write {\f1 $e = mc^2$}.\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab $22,000 is a {\i lot} of money. So is $34,000. (It worked if \u8220"lot\u8221" is emphasized.)\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Shoes ($20) and socks ($5).\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab Escaped {\f1 $}: $73 {\i this should be emphasized} 23$.\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's a LaTeX table:\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Special Characters\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here is some unicode:\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab I hat: \u206 ?\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab o umlaut: \u246 ?\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab section: \u167 ?\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab set membership: \u8712 ?\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab copyright: \u169 ?\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 AT&T has an ampersand in their name.\par} {\pard \ql \f0 \sa180 \li0 \fi0 AT&T is another way to write it.\par} {\pard \ql \f0 \sa180 \li0 \fi0 This & that.\par} {\pard \ql \f0 \sa180 \li0 \fi0 4 < 5.\par} {\pard \ql \f0 \sa180 \li0 \fi0 6 > 5.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Backslash: \\\par} {\pard \ql \f0 \sa180 \li0 \fi0 Backtick: `\par} {\pard \ql \f0 \sa180 \li0 \fi0 Asterisk: *\par} {\pard \ql \f0 \sa180 \li0 \fi0 Underscore: _\par} {\pard \ql \f0 \sa180 \li0 \fi0 Left brace: \{\par} {\pard \ql \f0 \sa180 \li0 \fi0 Right brace: \}\par} {\pard \ql \f0 \sa180 \li0 \fi0 Left bracket: [\par} {\pard \ql \f0 \sa180 \li0 \fi0 Right bracket: ]\par} {\pard \ql \f0 \sa180 \li0 \fi0 Left paren: (\par} {\pard \ql \f0 \sa180 \li0 \fi0 Right paren: )\par} {\pard \ql \f0 \sa180 \li0 \fi0 Greater-than: >\par} {\pard \ql \f0 \sa180 \li0 \fi0 Hash: #\par} {\pard \ql \f0 \sa180 \li0 \fi0 Period: .\par} {\pard \ql \f0 \sa180 \li0 \fi0 Bang: !\par} {\pard \ql \f0 \sa180 \li0 \fi0 Plus: +\par} {\pard \ql \f0 \sa180 \li0 \fi0 Minus: -\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Links\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Explicit\par} {\pard \ql \f0 \sa180 \li0 \fi0 Just a {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul URL }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul URL and title }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul URL and title }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul URL and title }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul URL and title }}} \par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul URL and title }}} \par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "/url/with_underscore"}}{\fldrslt{\ul with_underscore }}} \par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "mailto:nobody@nowhere.net"}}{\fldrslt{\ul Email link }}} \par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK ""}}{\fldrslt{\ul Empty }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Reference\par} {\pard \ql \f0 \sa180 \li0 \fi0 Foo {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul bar }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 With {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul embedded [brackets] }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul b }}} by itself should be a link.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Indented {\field{\*\fldinst{HYPERLINK "/url"}}{\fldrslt{\ul once }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 Indented {\field{\*\fldinst{HYPERLINK "/url"}}{\fldrslt{\ul twice }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 Indented {\field{\*\fldinst{HYPERLINK "/url"}}{\fldrslt{\ul thrice }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 This should [not][] be a link.\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1 [not]: /url\par} {\pard \ql \f0 \sa180 \li0 \fi0 Foo {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul bar }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 Foo {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul biz }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 With ampersands\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's a {\field{\*\fldinst{HYPERLINK "http://example.com/?foo=1&bar=2"}}{\fldrslt{\ul link with an ampersand in the URL }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's a link with an amersand in the link text: {\field{\*\fldinst{HYPERLINK "http://att.com/"}}{\fldrslt{\ul AT&T }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's an {\field{\*\fldinst{HYPERLINK "/script?foo=1&bar=2"}}{\fldrslt{\ul inline link }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's an {\field{\*\fldinst{HYPERLINK "/script?foo=1&bar=2"}}{\fldrslt{\ul inline link in pointy braces }}} .\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel1 \b \fs32 Autolinks\par} {\pard \ql \f0 \sa180 \li0 \fi0 With an ampersand: {\field{\*\fldinst{HYPERLINK "http://example.com/?foo=1&bar=2"}}{\fldrslt{\ul http://example.com/?foo=1&bar=2 }}} \par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab In a list?\par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab {\field{\*\fldinst{HYPERLINK "http://example.com/"}}{\fldrslt{\ul http://example.com/ }}} \par} {\pard \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab It should.\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 An e-mail address: {\field{\*\fldinst{HYPERLINK "mailto:nobody@nowhere.net"}}{\fldrslt{\ul nobody@nowhere.net }}} \par} {\pard \ql \f0 \sa180 \li720 \fi0 Blockquoted: {\field{\*\fldinst{HYPERLINK "http://example.com/"}}{\fldrslt{\ul http://example.com/ }}} \par} {\pard \ql \f0 \sa180 \li0 \fi0 Auto-links should not occur here: {\f1 }\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1 or here: \par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Images\par} {\pard \ql \f0 \sa180 \li0 \fi0 From \u8220"Voyage dans la Lune\u8221" by Georges Melies (1902):\par} {\pard \ql \f0 \sa0 \li0 \fi0 {\pict\jpegblip\picw250\pich250\picwgoal3000\pichgoal3000 ffd8ffe000104a46494600010101007800780000ffdb00430006040506050406060506070706080a100a0a09090a140e0f0c1017141818171416161a1d251f1a1b231c1616202c20232627292a29191f2d302d283025282928ffdb0043010707070a080a130a0a13281a161a2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828ffc000110800fa00fa03011100021101031101ffc4001c0000000701010000000000000000000000010203040506070008ffc4003e100002010303020404040502050500030001020300041105122106311322415107617181143291a1234252b1c115f016336272d1082443e1f1265382ffc40017010101010100000000000000000000000000010204ffc4001b11010101010003010000000000000000000001110212213141ffda000c03010002110311003f00dadd18a10a704f6a95ccc57e37750782b0d8d9ea0cd32e7c5446e07e9f4ad723119a7b89e61e348f260719278aad613cbb640002938c76a182b264fc87bd13009c0c019c76e3d68a072e1cf6f4cd502d330c28269a61bb39c923923d4fad44c08dccb95cfd28b8280769ee08a263891e1808739e4f1d8d149392172cc714050dbb9fde8960ed8c60b79b1ed44103b05c331dbdb1dc5026ac1946d20ff8140aa631c773ec738a0346a003bf93e9cf02801895e7b9a01886796c923bd0090a06393c76a0142003ce3d86680d8dd9392303f5341ccc1b3cf7a2c812c37e4923d381429757013209fa511c18146c9247a0f6a007900c0c671c6280854e086c673eb45c27c038fd68aedff2fda836ef881f136f25d5e6b7d1262964aa02b03f98fbf153131935edcc97576f35c33349212cc4f39f7ab26186dfce5b200f73451f7600dcb8cf27e7400c0b291914046c9e0718fde81371b8e7273f4ef4007691919240e714097f31f376e7b5008caee27807b0f5a02c8e1b3c6d27d33cd0201d839523144d1a149ae084b78da47638211771fd050d582c3a0faab5119b6d12f8ab1c06788a0fd4e2ac356fd1fe08754ddccaba849696309e598c9e2103fed1dcfdeadc44fea5ff00a7f956366d375e492403ca935bedcf1eea4ff6ac68a55efc1beb3b552574f8e7009ff933a927ec715bc82b3a8f4d6bba5ca1352d22fe061cf9a0383f71dea5119cc6c0baed3eaac0f1fad40897dcc3d81f7a052366c1007df3400f21edefc5008c28f30c9c5170ee4fc37830086395250a7c66770c18e78da31c0c63de8609b41f7c515c5172a30c3b76344a3e377cb2339cf7a242aea89808cce368272b8c1a2e107c672a49f5c1a181c9c7ae7da8a2119e7b1f5068099ffa68258a132062d8f9e0f34059502b61bf2824e681bb297ced2a71efda80f19c47b9c77fdbe74057c13e539cf3c1a0eeeb9c73f33405ddb4f18249c1e28062b79ae242902024465b9214614649e7bd0362a7249eddc513456e5720f38f5a1a98e96e95d6baa6ebc2d1ad1e65521649bb469f563534d6d7d31f04347d2a2fc5f535db6a0e83718906c887cbbe5a9a8bef44dce9f731ca9a2e89169d6d6f2184b1455dc07b11dcfeb4d16f119c649a681285b03d3e5500f87820ff006a0e098191de80ac9b8904647b55d11da9681a56a31f87a869f6970b8ffe4883629a289aefc16e92d441682da5b098f21ed9f033f353914d19b751fc08d66cc16d12fe2bf45ec92ff0df1fdbfb559ec667aff4eeb1a04db359d3ae6d40eccebe53f46ec7f5ab82263da7dcf3eb5174e5181076918c7de869503232491ee4515c1803824f03b51287f30e0e7d803449494832c157278a2e8c71fcb9f9d144639236824d01e142efb1768cfb9c7ef40512a818de78ff00a682518e7cc30ab9e00a02cce9953247bd41e467191ed9a04205ee99da4e4e3d283a524b0427b5026c18a8e082067db8341ce0e39ed409b6502907391edda80f2dfdc496f0c124ac6184b144cf0a4e338fd2894f7a7342d4ba9b568f4fd261f12571966270a8bfd47d8511bae85f02b47b7fc34bac5d5c5dc88a0c90ab6c8d9bedce3ef4d1ad691a6dae976a96d616d15b409f9638d70054a1dbc68ea51d4329f4619a8022b78e04548515117b05000a035c5c4702a995c26e3819f534047bcb68a458cce866719540724d02e41c0f7a012a40f6141cbc8e7bd01719e38e6838af1c0a04ca8206d3cd037bdb082fad9e0bd8a39e0718649141047d0d5d18f759fc0cd3af164b8e9999acae4e4f81236e898f7c0f55fed574615aee83a96817ef67abda3db4ebdb7f66f983d88a061bb8db9e3d45165076db83c1f950a53780d8247c80a2398f182724f1c7ad080c608cfa7a51a0062adc7afbd0130173bb9c5070f071cb37e82826106e8f615e01c9e680d6b35bc534be3c1e3831b2aa962bb188f2b71df1de819ae4b1048c7f57bd01a58268e332642ae0704f7cf6207af6a01b99e17b7b6416e227407c494139909ed9f4c0a04a4e501e0f1eb40d263e53dce7fa682c9d0bd13abf58dd6db18bc2b157c4975270ab8ef8f563f21447a73a03a1f4de8eb031582b497328066b97fcd21ff038edfde88b7e32703bd64188e7e6283864b73400cd804b67ca3268317eacea6d56ff005233592f8b6303f953fa4af7c2fa93417fe98b763e0ea171297bab98558068f695ce0e08fdbd281e5c6a57ba5e9d14d716ef7774f2ec112601da4f27ec2827ada74bab559a20e148fcae36b0f91140283729c77a0e0a7777a01dac68395719e39a029607cb901b19c501480ab9279f4a086ea8d0b48d76c0586b7143224a76c61ce1831fe93e86b43cd1f12fe19ea7d2533dcdbeebcd20b612651e68f9ece3fcf6fa5067cbcf20ff009a051724905411e94032799060723da8406d6c67e7ea68d0e1770fe5cfb5026c37039ef9a02eca098c91e6e01e71c500b1d8a49c12786cf6a06a4e256008e3d050119958007920e2801154e32fb4120927b014017eb1c523ac5209a356215c291b87be28957ef853f0d66eaa99352d515e1d190f947669ce7b0ffa7e74a8f4be996569a5d9c56b6704705b46bb5238d42851f2ac875712bc70b3c30f892019540704d01ad2669a0491936330c95ce7140b2e4939a03638c7e8680ae485e33bbe540d60d3ada162c90a02c7270a39340a4f28b68da4645007a8f6a069a746f73235ddcefc391e12b2e1916824948742c99382473c73404791c617695279dc0640f9502c578c9efeb4095cb4cb0830ba21cf999c6401f4f5a05061d430c8079a02e03b3004311c7d281b5e3cd676c65489ee594e4aafe6c7ae07a9a0a075bbea3a8ea96f047d3935ebc404f04ad29411e08e011d98fed416fd212ee5b05b4d5ad6300c615807f1171eaa49eff5ad418c7c55f8466dd66d57a521f20cbcd66a7247a9283dbe5418a63862479877cf1f6c5008e400bdf1ce684016fe53dfbd1a73794600e08ce3d6800377c8c7d6800a9cf75fd4503d91492460f7ee3d28247a7b459f5fd592d22711c206f9e563858a31f99cfd050583518ba75247b1d134f9aed21396d4669769931dc01c003f7a329c4d17458ac5b55d36c12e040a3f1da75c1cb04ede2447f7f6a94567ad7a66db4fbbb29ba7d65b8b4bd8ccd09c8231eaa07b8ab04a7c2cf87b3f53ea8d77abc72c1a5dabe2452bb5a561fc83e5ee7e541e988218ed2dd22b7855228d76a46a00000ec00a510bd4dd511f4fe84da95cc31f880022da4902b1e7d3e99ac86fd03d631f565b4ee6d4dbbc649009cab2e48c83f514165b8b94b6895c44f279c280839e78ce28178ae6de46748a789e453865570483ec6812d42e85a421fc37918b00a883924d024c6e99b7a2a966c0009e17dc9f9fed40f81c77e28139218e4ff9815b9cf23340a01c907b9140201038ed402fcafd28386464e4fd33c50272bc60032609cf00fbd024f722dc66f24822ddf972f8feff00e280f69b24844919cac9ce7de83a447f30ded823007b50459d6ecacb528349b979127651b1dc795f1f3f7a0990148054823dc5015d491c0a0c3be337c2ff00c489b5ee9c87172016b9b541c49ff5a8f7f71eb560c1fc43e0a47e1aa94277310431f91fa551c1727f29ed409b641c86e31839a3454805739c1f6a026f1fd740f64665fc8c31cfde82db79bb40e9e8f49b62eb7d7e8b717ec832c91ff247fa1dc7df2281bcc9369d671493c422b7911654c1215f92bb8827bf068624ba635392df5eb4b9924558ee5bc19b71cee43c6dc7cf34c657be8db0b0b9d0f51d2afe668934dbf9628ddb8c2b8c0073f3a80da37546a7d25174fd95dc125c69f7313ee5655dfc313bd483cf07b1f6a68d5b48d5ec758b612e9d7293211c8fe653f35ee2a084eb9e8bb1eafb3582fe496278f3e1c919fcb9f97ad03ee8dd017a6741b6d3229dae161057c5750a48249ec3eb4139238568f6a9e7b103b5037934cb3793c610a2cd9277a8da73f5140ee38f6280c4b11c65b934023006d50050030e4647de80c846de3b500fcf9fbd0197273ed4007b91400e580c8e45074a82400e72682b36fd2162b7f25ddc09af2766ceeb872db79cf00f6a0b3229550140e07007a50092db860673de818df473c862686dad6470d9cce3b7b63e740fa1de6252ebb5bd81cd00bee2d800d003a6464004763ce683ce9f1cbe1f1d3a67ea1d1a30b68edffba814708c7f9c63d0fafceaca31e6dc71e1f07daa82608c83819f7a1a11ce149238fd68d0a579ec682cfd27a7c3a86bd10bc38b3b756b8b93c1fe1a8c91f7381f7a034f752ea5aa5c5eb292f732128037619c018f6ec282e5a2cb047abda74d5ce9b6da80f136de4a496219b3e48c92000323ea73467519d3da5bb757dbda410ac90c77c23058f99007ee7ec31416882ee47d23acb5185caf8bab4691b1efe57fff0038a9457ee75a82f6e7429350466b482f2742c0f74241c80c38c64541a9cfa1e89ac0177d33ab3d8de28f2b5bca429f91140e2c7a9b5ae9fb85b6ea9b46b9b3c796fe040768f76ec0fafb1f9505df4ebdb3d4edd6e74db98ee216fe68ce47d280648f75e2485a44da385ddc13f4a025ddbdbea16a633286566ce55f9c8f6c502ad750db2c514f30dec428247e6340bbf04100b73402afb943ed2b9e30683836defe9403bc1e06734020e06280cafe8683a375941d841c77c1a0151b467b50159f00e4127e5402872371040c5046eb5aadbe9b1c02e2f6dad25b89047099c677b7b0140fe3f1010afc803f3018e68160c3041ee2823f5dba92d34db89a1d9e2843b03b6d05bd013560c1748d57aa2797c6b35bab78e6959dc47231580ff336dcfb03c1a58364b5bbb7d7fa7b7427f1f673830c8664285bd1815238fad20f2c7c41e979ba43aa2e2c1cb1b663bede438f3a13c7dc76fb5515e9065b851f7a02950002a09c51a1b83cf14176e90d3645e9ad7752752aac23b3439c066665c827e944d29a6410aea725c4567135bd840d3c88a723728c29c9efe6c50d3ee9545b0bf8ef2e6e3c2fc2c6f72f2920e5f19039ee4938a9a875d03278377acf52ddf867f036ef71923932bfe51fbd3475cdc369df0db4fb389d4ea37970fa9cc0b00511795ce7d4f181eb4cd2451755fc45ac16d637381b14ca36b641dfce723e4053170d6cb52bbb362f6d3cb19241f2b9029862f09f143549ba7e7d2eef6caf2797c66ee17fdfd69862d1a069da7dfdac579d17adcda5ea9e1a992376c4723e39e3b024fd7e94c458ac3e25dee8d31d3bae74e7b79002bf8b8549471db38f5f4ed4c165e943a06a328d4ba605b4b22a1523c420c64fbaf38a82d36f0ce7cf7463790729b53017e940e0b0ceceed8ce0500e1b70daa08f5c9ed41d271cd0132476e7d7ff00aa069797d2411168ed9a41fcc858211f73c5075acb25ca6fb82aa31e58a36c81f561dcd033d42169e158ac64b98151b3981c2966cf639f4a064c7a8ac55e4865b7d493701e1c8e52403ea3cbfda827e390ca3f2c914aa81991bd281cdacc2747215c60e0ee5c67e940cb51b0b2bcbd824bfb08ee1a252d1caea1821cfa67b1fa504982b2283ce08f518a08abb82f6dd0369a5662081e14ce40c7ae1b04fda82275cb0bfd4f4536f7114589a5412461f3e4ce4e0e060f63f6ab2893d3b4b5b5b78e22ed22aae3cc3cc7e64fad3449a22a461500007602a0cd7e3b74c26b5d2ad79147baf34eccca40e4a7f30ff3f6aba3cd0543267eb5427b86f1f4c76ef45d0eca1ad5ef224d13e1cf4fd9b22192fa67bc955f8c8c617fba9fb510d7a6ed3fd43a735e5b54964be658c048fb6cdd9e7eb8a186bac97d174e6d22e23437b7ac26b95e77c68bf950fa7279e2b22dba45b59e97a669fa4ea36aeff89cea9a90451fc355ff0096ad9f4ce3f41570675d4fa8c77da8de5cde5be26bc653171ca47dc1f6c9fed5562b97f70276808da7c24f0c1c63804e33fa8a2928c0e0383c8f7ed41d92a41393f4a2548595c2c37493db4cd04e8a08f139566edfef3445b6e7aeaf65d2df48ea2b11776ae02a93e564c772adef409f4ee8ba9a21d73a36fa579ad9f325afe599171ed9c30a960d5ba0fe2843abb47a6f510fc26a4c36890f9558fcc6783506a1147b510024e30339ce680d2c6ae9861eb9a009178a0205443b989e39cd01d8075e3047ce80563057ca381ed4011c4531e503d85013c91b804a21279c903341131cda8c3abdc8650f6d20c4321232adec3dc504bab2c113c9293bb1963df3408dd4b75e1efb2856463dbc43b4631fad047e9177ad4fe32ea16b1db4b8fe1aa92571f5f5a0916bc8e0895af5c46c17cd8c9ff7da80f6d736f7f6915c59cab35bc837238ed8a072a31c1ef4062870718a06f7702dc5b3c522ee4752ae0fa8230683c75d6ba3b74ef535fe984929149e4278ca9e47edfdab42058003763ed409f88ffd6dfad06b1f12ae612fa0c76e0b471e9916d23f973eb4158d3efeff004a984da5debc1295d8e4018dbf3145d583a2ad96f356bbd7f5d90dc59587f1e79a6392f28fcaa3ee47159444ea3aa5ddfc7acf50dcdc344d7a4dbc317f52641200f6000fdeb41b5ef51d8eab672ffa9e971c97c11638268e431a46000012a3b9a351567db823b11f3ef40948e428048207a8340ab48ae83cb83ee0f3428a982719edf3e68c9cc97d3fe15ad8c9be138f2bf38e7b8f6ef40f7a5f55bdd3f56b46d3649127f1405f08e7249c76f5fa50689d48ba5f545cdcbdb462cba9206411b2b055bb07d4fb1c73528d4fa8f52d62cf47b6b8d2e65fc458c49f8a818795c151939f977a823ba0fe253750eb7fe937b04293f9f6c90be41dbdc7ff006283473c1efc1a06f69776d73bbf0f2aca32572bc80470450284a46dfca19f819f5a04e799614def26c0bdce09cfd85045dc75769d12dc3c3e2491db0dd3c85195235f7c91cfd066ae0cdba9be31f4ec61a386c1ef9d4ee473e45cfb1cf34c101d3ff1ac9d481d46c628ed24751881880833f988e7b0fa5328dfed2f2def2ce2b9b79925b791772ca87208f7a60182ee2b95cc0c48f53823fbd40ac658b30f4f4a086d4ee1d75bb6b78f4e965596366fc5211b23238008fde81f43692da5bc30d97831a0397dc09e3d714087506bf61a2c4cd77324726d2caaec141f9fd2ae0c435bf8c57173ad7876f7a2daca10489121244cdf319ce3dbf5a834fe81f881a6f57bcb6ba7c53c72c11873e28cee1db391dbef4199ff00ea4348116a5a66a8a8a04aad04847a90723f6ad7d18c312ddc02a3815423ba0f63fa541687bd9efe1b533b3c9e0a78473e899e318a09bd0ba6eef543e3b2bd8e9b10064bd9e4da001dc81401aeeb29a984d0ba7d5e1d06d4e6594f06523bc8e7f5c0ac8af752dfc17d7090d9218ec6d9447129ee71fcc4fb9cd6842ab10dc0014f3e5a2c1704b671dfdc734525226dc939e283a362c7f940344a380393df144733e2276e38f5efcd01b4bbbfc3dda4a9298a44395902e4a9c70682660d4265d62de40b1bdc1545054f95c8fe627df141af7c3af8808f3dd68dd5d2a45765884b8908d8c3b6c27b7a77a945d7a5fa474bd2ba8e4d5748b28624955d5d8b13b79ee9e983d8d40a753758c7a46b96f6114725dc92279a2810b3a64f94900763cfafa503fd3b59b79ed84da34713c0cd890f0a158fa1f981de826e1b548959fc4dc5cee24b6467e59ed4101ff19e9f676baa5d6a72c50adb4ad1ac790ccc076200f7ad41e7df881d79a87576a5f87b0f161d381db1c2a36e7e6d8ff3416bf87ff082c6f208ef7a82f22b9761bd6d619785f6dc477fa53705ab57f83bd297ceb1e9caf67708db9c4526723e849e3e94f212dd25d117fd29a8c09a76b534fa39cf8b6b71ced38e36fb73417f52e64548e34007e673e9f21ef590a1c918c90718dc281a69b68f67118d9da5058b798f6fa7fe280daadd1b2d36eae70710c4d263df0a4d583cc7a668fd4bf11b5837d7c93dcd9a3146959822a0e781f4cfa55161e9dd7fa67a4f55b9e9aea3e9f81fc09ca0ba118998fcdb2338c7b528d39f4cd2ba76e2d357d292df4eb391809963420ce1b1b576fa1e7359119f1eb4e17dd033ca172d6b2a4df303383fdeb5c8f2eef3bce4e0e335684cb0c9f354160d36f64b0baf16072b91b5f03391f43c51aab23a5debe91c4fad4d73689e6fc3a290573ff4f03e59f4a3280d67581ce916567f84b58ce0a1fccec3d58fa9a084de08c90464e4d1a8e419059b201f4a05630a176918efc50176293872c17bf14042aa0125b03db14046c60b60123fde6827fa0c68edd5365ff11346ba6292ee64194240c807e59a32b7fc51bfe8bd5ed5db424860beb62b89218422ce09c11c01dbbd0660ae110bf1bf2154838dbebfefeb41a8f4cdac7f117458f4d9ecd2df53b4cf81a822808c47255c0f7c8e7fb54a2ec2cfabba3b4b4b8d3af12eedad40926b0f070a13f9b633649f7a82eba6ea4357d321d4ecad512daf20df26e016507fa4fbfaf3e98f9d067dd2bd2faac9aa4d72d72d1e9510ca46a7631c7a320e18f1dfd7bd059afb7da816d23de4ba5de211346a1e4785f190548e4648c63b64e6b43ce9d5baafe3b539c5b452dbda46c638a167cb281c73ee4ff9340e3a3fa5f5aea4ba58f4bb57dbfcf2b02a8bf7f7a0de3a5fa0b50d2a2d92eb3e048c0a97c867dbedcf6a944e5cf4f6b76d1b3d8ea42795066266c87c81c65b9cfaf15048e83af3cd64abac08edaf01546c38dae4e0657ee6826e5b892de3702292e2545ddb55700fd0fbfca81c4b3bc718716eef9eeaa402280d14ab3c0b2c65c06fe571823ed40df56b217fa6dd5ab9216689a33f2c8c558307e83b8d77a37aaa7d22f2512c28768800c9954671b3d33ebef568d0ef7a7749eb0b5bbbb162d657b32b46d2e1564c8ed9c5644d1b0b9bbd261d2a440af6cb0e2e5b1e7dbc1238e0f7a0375b696daa7496a3a4dac8a92cf078685b271db04d391e40d5ec4586a1716de2a49e0c8c85d3b120f715ba1899173ff305413070abd89cfe9f5a2d3ee9c8639fa874eb6b804c52dc46b20c9f302c3bd11e84d47e1af4d5dc6521d3e3b662c19a58721ff5a9a321f89bd27a374b456d158dccd34d333332c9b4b2afbe47a7cb1f7aa33d2bc0d8c0f1c8f6a2c14b6d501b39cf63450897380c319e3de8065031c038f7ed40d8faf1ce41e4d004876a8dc3cc7e743025c956c818028c904579e7f0e15695c9c0541924f6c00283d0bf07f42d6b48820b8d62d20b2b58d656404959e52f83c8ff00fcfafbd4a35bb06f12391a48dd55cee2b2f3818ed8f6a8158a159890f02242079147623e631c502b0db436d1ecb7458d4738038fb0a087d6eeb508f48bb7d32d95750752b6c26c905b3ddb6f61eb574794f5cb6b9d0fa9678b512b25d24bbe52b8c1638278fbd582c57ff12afaed45b5bc0d0d8a8c08a2731ee3eec5793f40450466a1d59af446293c186cd53ca0c36eab93dc649e49fbd048e89f1675ed35e301e293919dcbf9867b37cbe94a35fe94ea4d33aba6824306dc48015750d86c6e247b0cf63591a40b8dc23fc30f14138dcac3000f9d03687547f12e8dd5af816b13148dddbcd29039c0f6f6f7a0eb5d62caf5636825db70c9bc4328f0dc0271c8a090627d3073ce681acf6505ccf14d35bc2f2c2731bb28254fb8a075144a83ca806792400334049ee6281e2496408656d880ff0031f61fa50446bd76058ea1b9e21025a3bb48afe71df9c7b71de9c8f196a0de23ca7b827d4f7add117e0cbfd4b5059392369663ff004e71c51aa97e8f555eadd258f2bf8a889cff00dc28cbd0bf123a926e96d163bdb74490bca2321c678209ff001591e71eafd7a7d7ef45cde2c20aae144638033fb9ad2e1b5e69d058da431ccf21d4a5c3b4631b62523807feaf5c7a50222f2d648c25f5aeec8c2cd19dae3d3e87e944d3eb7e90d425b49ef2292de38224f1505c3f8724a9eeaa7bd0d57a60406059436306868a7803839c7ad1a158039c13f4a33a716767f8cb9b6b55e1ae2458813e9938cd07a9f42d0b4de99b4b7d1f41b58ff19b03c93ba06607fa8b1f5f619a5b8266d74a65d42da6ba90cce996dcdc8c9fff006a5a2c2635083b05ef83eb5028076341db4b1ed9f7a04651fc41db18ed419d75b744dbea335fcb0db0335f2057901c05da73c8f9d5d18a75174a3f4c47335e35da4ce418a489374254f707d463d33565d1529b569a489a17944b06ec8057d71dcd037b4b6b8bfba31584124b27e62a8a4f1ea68357f83da7ea5a76bfe0912453ccabb49194653cb60f6ce3dfda983d196cd108c2401711f9768e306b2297d73fc6d02773a8b591922693c5004bb9d72542fa2f6efde8314ff867aeeec27500b77bb5670e36ca19b1dff2e7f2fd2837ce8bd5dd348b78b552219022870d9c46e792a4f6c608a0b846c8e03232b29ec41cd0199f1410fd4b24b1e8f712c0a5e4452d851e6c639dbf3238a0afa42ba77475e4ba8470896681da45180b18da76af3c9029c8f26dc1df2b9c606e273e86b743331924f27f4a82c12280e59b008e79f6a2d4d74188ff00e30d203a82ad7519c1ff00b860d11ba7c5e86c9fa3afae6f4091e043e021270b21e01c7dcd6479ab4dd3aeb56be4b7b184cf2b301b57d07bfd2b4bad0fe25e9f6960ba7c7a55ac50cd750335ccaade7723b83b8f6e38a2207a5ba4e7ea3d93780cb616ca53781f99fbff009a0b675a5be9765d43a75a6ad3b25adb4185429b831c70303dfdfd2831eb8954ca48f3827819c71ed406b2b1b9bf9a5fc1c4ce2253238047957dc9345d122b792eee522811a495ce1157b93ed444ff0049f476b1aaf51c761345269d25be269259570c833c6077249e062a68f53e8ef0da69509d4ae225b92a04af232ab16f98cf1f4a5a266d4dbca8af13a329ecca723f51502d14f0cb9f05d1c8ee01c91f6a0393b4edfe63c8a031608859b38f97340d84d04e5846eae50f9829ce3eb400fb24466041f5e3d2823f56d22db57b192d6e61468a41c823ff0035651916bbf04ada7badda5cad6d1b72c09dc33f2a6875d25f0865d06fe2bc6d4c4d3282026cca8cfafcfd29a34cd234a10c768f711a78f1bb392a3001208e3ec69a26a58d640c832091c90706a084ea1d3eeafdadf4f86da3166c0b4b397c18f046140f5ce4d04f4702436e91c28a9122e028ed8a0a9f5a5b6a09a1bc5a135bc72cce048b3c5bc15c638f9d59043fc2db997481aa69dae49e1cb6bb643239211939e467818f97bd305965ebce9a10bc8da9dbaa2679dd9ce3d8530572cfac87566ab05ae9f1490692b9696e1f833738555f96793504df5f25945d2576b7ec16dc46792381c7b7ad5e60f234980e42f6c9c56a82ec3eff00bd4124e49700f1c646e3cd169ce9575f83d52cee324347323f6e3861ff008a23d47d4ba6a75074fdcd8ef317e2e2ff0098bdd4706a60c3f4ae8cd5f44eb8fc3e97248a638cbc73bf90483d463b373e9574685abf42a6b5649fea72bbdeac4a8d3b018cfae31f7a6895b6b29b41d30d8e9f6bbed9213e1b7a994e724fcbb5064bd7835a9752d3af75d8116354778f660788cbd9483dbb0a0cd20b2b8d43528ad2088bdcccf854039cff00e2827a0d34e9da1de896f2182492efc0976f998aa827d3d334113a74aa9a9298628e74570478bc0c7cf1da83724bb8246d3e6416b0384da61b5501c9f5c3704f152c037561a95f5fce61d3ad6db4ab950f34b331dd9f4191db8fdcd406d67a675ab4b6d325d1af248272a43430315ddec7038f6ad4b3f448bf47eb71cb69abddebf21d5e26896203846c30c8603b9c6452d9835901405660376319ac84bf13180779d8bb82827d4fb0a06f777367a75acd7170f1430a9f331200cfceae061a0cf6dac692d7365266191db0578f5edf3a6075a6c9278b3433188a467860d96fbd40fe540471409aa0c1140750001ed8ed4058e15133c983960077edf6a06faade5c5b7822d2d926766cb967da2341dd8f0727d85033d27597d62e2ee3163756915bbf8799d71e2f19dcbf2a092b88dd9a311950a0e5b70ce47fe6ac18af53f5b69da9752dd69da9ca906876e24465d9e69881c6ff005c679c0aa2bfd267a347512c93dbb5d42d90d3c800b68c9ce0ec3cfa528db74ad034db5905ee8be1ac728dc153984f3f980f4fb56453be2de8f647a6aff53796596f0aed46798ec033ce149c0fb0ad71479c9bb9dc3bf3c55a0b95f65a825150b481a407b93c0fda8a29c06671dc93803bd131eafd0af6dffe18d2ee25982a4b04603b7a9c631fad03bbe586381bc5945b96385718c827db3eb5288eb8ba934eb15fc3c535f05427796dcccdec7150637d5bd55d5362b7975aa4d0d919018adec8637807bb60723000e4f7cd58203538aefa8aeb478a7b9beba924547b8774cf8608036a80704639cf1f9855d1a7f4c68b67a03de5e5d59dad8d988c62e1c0f107a1e7fdf7a082ea6d07a6e3e99375a7cb6b3db093c727701e2360f7f53dfb50653d4130d42ee18f48b3f0232a15218936963ebf5fbd06dbf0cfa74855b9d46e04b730c6144691e12307d33ea7de8348ba6b5478a279a004f98c479247b81f5a9438805ac0be2e02e73c9ef8fbd40c2346d43578eefc40da7da1dc8b8eefea4fcb9fef419beabf12a4bcebcb2d2fa7ee0dc58492084b30236bb6467dce383f6a0b87556a67a7f4d95a0bbb5468816f0a69c78b2e072572719ce78357079dfab3af2e7a92c963b88d94abbbf91cedc93edf418aa2c5f0dbe2a6a9a0c90d8de34773a6a8da1186d6403fa48fec682d9adeb7a9b4c9d572dacc9a6c9700456e5ca910e000ecbd8e580352fb1ae7476bd6dd49a325e5a9f3025245fe961dea097523cc0919a032af039a006936c81423104649c703ef40dcce64bc3035a87b6f0c378f9fe7cf2b8fdf340a4b6e25962915d94a67807839f7a043586922b5636ec44c061063f31f6ab079d7fe19b7eb2d4efeef55d5d74f992e9a0fc3a441dcb13927b838c9aa2d4bf042c618e178efee6e18104870172318edf5c1e6945bbe1a748ea7d2315edbea3a99bcb190030c401010e4e783ee0fa56455be3d6b90c1a6268d69e17f1486900ee98ec29ccc183119419e7e55ba11f089f523ef5058363a8059fb8e31e9421b491056c01819e28d3d0ff07eea3d53a1e3b6b8db235aca63c139c0eea68ca47a9fa6ef757b83ff00bf68ad428c2f248c7a8f9fcea518df516adac74e7544f63a2ea172f1800291e6cee19ec78cd5826344e85b6bad25ba8fade5b99dae0ee11efc71e858f7e7d054a2d7d39a75ae8f7b6b00b8917f1516624b78429da327cec493db1db1d8540cf4aea28f58d6aeae2f6dd64b498082d880488d149fcea7d4939ab04175e6850a8d32de3b78e380c8de32c4db4e18e430fef543cf86fd0d691b4fabdf6648b3b2db69ce7dd87be68342d2f4fb9b5b891af1a28a2dc05bc5036d001e0eef7352884b961a2ea9aa7555c885e08d45b5bc52b61b686c120fb939fb541276bd4b61d53624d942e2f6200bc32290633e99f4233416dd3ad3f0d611c0c77b632e71dc9ef41156dd27a45addcb3dbd9c513b1dd941821bdc7b558333b9f873757fd69a85e3f813e9c7723b5d93265d872473c11544a68ff08fa75e290b42ec4e4124e70738fa5048e89f0f745d2aeadd2decedda6525c975121183c77f7a945c754d0e0d563682ed43425369403bff00bcd58308ba7d5fe13f5a05889974a9d8b46aede4914f707d88ff001528ddf4fd7edb54d1a1d56c312dab2e64dbc9418e78f5c541296d70b716d1cd6f8789977230ecc280cb7519b816f212b205de4e0843ce300f6cfcb39a0545c42cee88e0b458ddec33ee680eac92266360debc7b5056fad2d354b9b189745744be121daf27e550548ce3d4d58324d07a725e98ea2b7ff5381f5169a7579d021fe13904ee43ddfbe49038ab46e76cf05cc714f6d309232a4a98ce54fd6b2196bb7f2e9b631b2c427b891b6851db3eff002007341e5bf887aa2ea3d4f77378be381e42fdb711ed5a1554395caf1cd07617dcd04fc85402101da791421b49920331381c60d1a69bf02f56f03a925b12c162b98c955f775e47df19a32d99b518268e4491668704a79d4aeec7b7bd4a30feb7e9144d76e265697c3e2693631674273803e556087d76f35fd49859e9925ccf611141106f2e182e3241f727f5a94681a268f79ad1b1b8b9dd66b1c2b1ce9bb06361c1e7bf3fe6a096b0d3ba57488e485b52b40909c386901607d47bf7a0ae758eb69ac4d15be81a748f0a9c35cc90b2eff4c03c1c638a0b77405c5d5d45f87be5fe359a88b81b401dc1c7d38fb50586fed18ea3015790091591e447c1518c8c7a0a0a5754d8c9d49a8d9f4ee9dba0b3b5224b9692327728f627e7c6682f9a7e996b6b3a8b6b748a348820c7720761412c064e3041ce282b5d4bd73d3bd3f33daea97ac2e540dd0a292dc8cd043c1f15ba2a7923b65bb910371b9a12141f9d02edf13ba2ade56857551e5e77244c54fd0e280746ebfe99bbbd655d56dcdc9ce08465565f4ee3bfca82d53eb3a6dac3e25c5f5ba646402e33fa77a0aff5b74ad8757e9ca972844aa37c520fcca7d3f5ab067bd369a8f467544d626c98e9f7118f0200e4465c903049c8c9e6ad1b24334b6fa6249716cab20037c309dd83db03b5640dddac3764a4f02cb1103863919fa7a1a06d6f600c37162911b7b252b87dc773f1927393f4a079f868edae1ae6328a8b1ed38e30050226e85d5dc1f879011b3c47c2f604719f9d01eff4f4b83e3c6b18bb452b1cac9b8a83de80b16e8208a3b7b58e1407cc061427cc0ff1560ce7a8f592d69a97504cad2dac01a2b53900c3e9e51ea58f727d0551e73bfb86b99a49a46dcf21c96340dc13804f20f6f9501c0e3b8a0963316fcc4607007f57bd084d64dec01200ce483468ff0040d525d1755b4d4206ff0095207c11dc67ff0019a18f56584f6daad9dade4211e39104a8ded9152b235cd8c530613229c8c1c8ef50472e81690ee00322b0c100f0debdbb7ca8111ace856371358cba85aa5cc407891ccf83f2ef4048b4ad2e59bf116769672c72f99dd1437239078e2824a4d3e1b94559234110e781839fa0a0561b38ad532a12319c86c6307e740a9732a3a00cac870cd8e0faf0681be8b6db965bb909df3c85806eeabced5a09523647e6e0fef419a6adf116daf7ac34be9dd1da686e7f1ca2e243b76320ce57df9ff1560cc3e3f470ff00c78255b842b35bc6c4af9b6e323d3e95467b6365f8dbcf062beb68c119595d8aaff6e282422e9899b4f6bb5d46cda2562a76316c1078f4a0859e1b9b762c0bf94f0e84feb4125a57505c58ea70dd5d0174a986d92b1c13f6f5a0de7a5be366877260b5d42dee2d2423124a487507ebdf15289dd3fac7a7bab6feded74d61733473acdb5a162142ff00313d81ed505fa58fc6d809380c1b9f5c502e0e05040ea367a85dea454de2ff00a610375b04c16c7a16ef8340b5f6930dce9375636acf6a278f04c5dd4f1dbf4a084e91d06f3a52dee62bbd4a2b882494ced3c8a448063b63b638fde803ab7ae749b2b3096d792c93c8c109b55dcd18ce3710473ffdd043dc758dc5869044565aa5dda4c3c2b7bc78c1f14e3963db03e7c0ab066bf1327d41ba76ca6bc48ecad24c456f6b0c87cf8e4bbfa138c0fa9aa3297c83dd4e28395811cf2680a5b93c7ed413cd1ff0f3c797b8031406645236b0508406f30e68ba49c051b97d0e0e7d28ad57e19f575c5be8f269515dac772877c11bc464dea7ba8c739ce78f9d3193bb8ebfea2bcbd6b5b6b8b58bb7f13c2f0ce31cf0deb4c0f2dbad6d743d2ee99efae752d6c02a86e0054524f6383c7ff94c0b6af274c75149a46a5a82c46f1e1479fc3190bd8156fbe7f4a960bf74e9d253f1167a3ac09e0856610e3041ec7f6c540f67b892cee7f8d18368232ef2af2508c7047cf340ead5bf130accc9b1186541e723d09ffc5033bbb1b82d74d6b37f1244daa1b38073df3f4a08eeb1d3b50d4fa6a5b4d32f12cef1902ee73e523d476fde8306eb0d3fabfa4ba92c278af67bb7281606472f90bdd58558253a6afb40ea1d62283a8b461a66a6f931cf6a7c2466f7cfb939e7b5515fbed3747b5eb8f06eb78d35080a2ec9719c76f98049a0b4ebdd37d1bad869acbf0b03c5c16b29444adf50ded41995f68564a263a66b31cd02be152505493f51c1a088bab69ad1bc179e320f07c37c8a0692b46c4995f0381db39a0b2fc34e971d57d4705bc8db6c50ef9dd97b81fcbf7381528f5ae97a1d8e996d1db69b0c7648855b10a81b80f43c739c54134147b9a036063279fb5046e957726a0f2cfe04915b06db1788305ffeac7a0a00d72f8d9c491c06337533050ac7185cf2df6a06da2da4293488f34973328c34aea428c9ec3eded40ee7d2ad249448f6b133820ee2833df3fde819ea96897461b05b87815f2ee919e5d47704fa039ab079b3e326b70eafd4ef6d6650d8e9ebf868b69c8247723efebf2aa280eb9193ebedc5002a124e015340018fb8fde82cf32b1603d0678cd02406dce3008e47ce81b499c331383c9c9f5a2e9c69377369f7d6f796a4acf148acb83fb511e91b3b3d0bab745b7d45acedd8e3732b71b1fd73f7a5a19eafd09a56a0a96b1c705b49c48510761ce4fcfbd4d0d752826b2b583476fc3daacb295b79d768de8a32b1e71f989e49f6a7d14dd1af5ba275a45b8b093c054492ea769092373765c1da4679fbd306e36ba9595fe9f0dcdbcc92c33e1579cf27d0d409ea178f68521b6b76926ee8a7853f7ff140bc768d78f6f73748d1cd103b543f0091cf6efc502f7319dac194371d8b6326829f0c501d7ae6fa568d5d4942c806d4db81839f53c8cd59456fae6e628749b5bab8b1865b08e4726588ec11e7f2e49071c93da9a31dd4341d675e9a5bad374f9858162d0b4ac70e18f0573df35a0c759f87bd53a404f174f965dfff00f479b1ef570576e34bd4b4cc0bdb3b9b7258a00e846e3f2a94376475cee4914af7ca9150685f09ba61efb52fc7dd69bf8eb7c158d1d0b47bb38f37efde968d6eeb4eb5e91d6ad25b660aee59974db6881690918e31ce39279e062a5a34bb57b88adedd1e379679065d80036679e6a07e8391bce7e940c659ef5afe1286de3b16f2b8903094b7b2fa7ce81eb380c4260ed193f2a0cd6fa5d4a4ea0fc7bdadcce923158a3039db83c038e3ef41a0e96b2ad8a35e009291b8a939d9f227e43bd590436bbd6fa269202c974b7123602c76e779624e00e29833bf897d493e896525dbcb2a6b5a9c3e1456b91b6d60cf989c7f31f7a60c02490961ebcf3eb541308e39e067b507007b96007a67fb50178f97eb4165ce18165c2927b773fad023202d9c8dbb7818ff003408300b87f2f1c107d0d0c15a4f2e339c90467f6a18bdfc2cea8ff4bd561b4b9ba686d6e64552c4f954e7d7e46a60f4688d240af1b2bc6cbf5047ca960617ba658de1b792f2d94a5ab33461b18524633fa1a81b5c8b5168967369d23c0e0a24622dc981d81c76aba29df0bb48d660d52fceb88d069f04aeb69130037127f37b9c0ed9a8350781240bbc06da72323b1f7a0eb8816e633192ebc8c9472a78fa50349b4c83c068e24f0ddbcc1c13b837be4d055b57e98b996d4db5b4a893dc1e6620b6ccf2c467efc504ce97a38d3f4d874bf09af6d46e2f25cb82724e791db15650a43f878b51fc34f3da8f132b6d6a98c80a39ff007e99aba249631b58b9047239ec3e55368aeeb7d2da6f52c0eb7f16e87f2c6e836ba90724ab7a67b55d115ac7c3e8aed2182def4c56a14096368959a423d77e3229a27b41e9d8343b01069c8a8dc9660aa3713df3c64d4a1c695d3d6b67a8cba94b9b8d4e61869e4e4a8c636a7f4afcaa09a485519caae19b966f7a0435196582c656b74df3e308beec7b50629375775b691ac5bdb6b16d6378779f019b00a31cf391c9c2f1daae0b35ef52f5374de88d77aa45a6cd25ddc0108694ee2188c28007603d6a0d16c92430a4b2ed3230dc401855cfa0a0a07c45d7b5db9d462e9ee960b14b2ee134ef8c850012147ec78ab0670b643a2efae753d72686e6e2da211db42176079c8e768f65e39f7aa332d6f58bbd635096f6fe6692695b24f603d801e82823cb900ee00e68006460051dfd680ed9f0c905b713d8d006d5f5419fa505a18f94e40501b1b81a04186d62402c87be3d6810b81290aea8467201231cd1749dbc437f9f1e201db3de8ba07dc0175c027f28a335b17c26f888d6b6d0e8fabf892a29c453b1e547f49f7a946d6424f08236491c833ee0835073294888894120700f0280813c40a6711bc8843e00fca7d3ef40e41dc081f9a811681c6f7565329185623b7e9de812b3bcf11ff0b74563bd50494cf120071b97e5fda80d7577046c9019d5669dbc340324eec67fb734103d5da96bf67a7cf0e916f6e2765f25ddc4c11107ab1c8c647cce2816d3ec7f0f6564cae6e67da375e6d52cc4af2e4fb13ed4145f899d47b3499f4bb6d46cda49b69b92921565c3648e3dd40c81cd5c0ae89f13ec246d3e379ed2d6da180b5d34849da1780b128e49271c9f4a60ba685d5fa36bc42d95c324ec7090cc9b1d87b81ed50588958977cacaaa3b9341c655f12348d1dcb8dc1946540f99a019e2134454eeda7bed3839cd052fe2136b536b5d3563a1ca53c49da4b9507198940ce7e5c9fbe281c6b7a974cf4ee4ea7242d76dc784a3c595b3e9b464e3f6ad0ac745ccbd4fadcbabea42da56959a382ce7460d6b1a93c01f97272093ebf6a82f1ad6af0e9da5cb3cecf6902216919f82aa3818c7a9f4c530649a9f5269ba7429d472239bc991a1b0d3c3152880f0f23039e7bf3de90635ab6a377aa5ebdcdecef2c9239e59f3827e5ed54302195fcc38cd01245395c1f5f4a05b606538e483c501b6939e3b5077860f3914165754c33e549000dbe87de813b820c27fa7baafb8a04a69c98e281e42618c795338033df1f3a06c03e4e029247007ad010093c35674da71f977640340081a190658e41c820f141b0fc33f8926c218b4ed609366a02249bb2d19ce3ea4528dbece68eead926b7916689c643a9e0d643387521fea2f693c2d6efc786ee46d9bfed3eff2a04669edf5295ff057a60bd865309246d3bbbedc1efef41d63a8dec9ab4fa7dd4510fc3c69234ca186e2d9c003b7a67bd034bad0a5bb96e25d575267889cc1b54446d9bd0a37bfbfbd02925945a72cba8dddc48b3f87b1e58f23c623f292bdb7fa7cf38a0a5750f54ebba56a9a67fc516b6d6fd3970ea9234677c8dc7f38f6e4640ce282d1ac4b16bfa72c1d2bad430cd1ba822061865f5007d3daac19cf547c189357d561bbd3eee683c62cd786e9b73337b8c7bd512bd39f06adedec2de0d5ae639da372e6485363107f97767b505cee755d03a5e58ac228659af123184b7b733322e38c91dbf5a943ab3d52e64b49ee755d2e64950e238e35f10c884e17cbe87dc540e6d7509a4f110e9f7566a471712850abf6ce463e6280bacea36fd33a4497f773cf32c698dcc4beee33938edc7ad05534aea683ae61d64c4d2a69b6b88d16d5ca5c303f3e386cf61db140e27b5e99e96b64bdd562b6b30aa36c6c37cac7dc9eec6b42c1a66a962fa70beb6b516d0cbe76322f86c78f6c66831bf8b5d5d2dfa35a5dce60b1933b6ce3c788769f2b331ec1b8fd2831b79649984b239773c1dc68129725c1c640e73400ec781d8fb1a03c4a85d0c8582640257bd01e51fc42236263c9c67be280429c6037eb405dbf5fd682c2a0bb976cf07d3fc5009279ef8ec4e68193a9902b28e7dc71fb501d95b098e0f6dd9e0d0049131019b047b8390281bb292484e47c8500c4f242c3076bf704704739a0be7c3febfbae9a5daf23cd017c7e19fb107bb67d0f6fd6837cd2757d1fab34f4f05e37dde630b1c3a91edf4f7159103d6eb7da65fdbdf59dadb5e2460ac876ed9e1c8c060f9efe9c8a0a75cfc42d5742d2ee5a7e9dbb494b6d6b9bc930cce4f940e3cd81ed4160e98f88315e689fff0022b57664199cc5196f0c7a164ef8f98cd0589baffa68590985eb15c0db1985839f6c2919340b69d03f5285bdd6f4bf021424db4329cb153fccc3d09c76a090d3340d2f479a7bab3b38e2924e5e451cfd280d7dafe936f6c5e4bd89813b02a36589ce318a0358dc35cc4e17f9bf234cdbb78fa0c607a7340e6cec20b1596610c6934b8323226379edda81da8dcb9ec40a042f50b5bb21645473b58b11dbd7bfca8304f8add5d67a9ea8ba5d8ea72c1a4d8a952f10f2c920e368cf71e99fad5833dd0754d6ed66bdb2e9a91a5babb2a310465a57c1ddc1038e7bd5171d3748b8d46ee1ff005298dc6a764c27d4eeaee7fe1c01795881c9e7804fe940dbe287c4b6d75a1b1d1c986d62277c91bf131f4c0f6fad0663737135d3b497124924871f98e7803007d2811dc5b83903dc0a0333f03be7de8122df2c9a05a11e5f51f7a05ce7071804fca8033e5da0734020b0183bb23e5416269577ed0e5323078ceeefdff6a03dbcd62914c2f629a47c622689800879e4fbd046f9d8a2a03e31385c0e73da8b83ca590947dcaead8208e73da8849b3e19ce704f7c5015586d006431f5cf61f3a04ee586ff002481c8fe6191408f0011eb8e30682774dea9bbb5784492ca6385832e1ca95e3d2834be9df8c312e2db5eb4375080337000f1303d18763591a469baef4d755989ec6f6dae5a23e20b599406dd8e080ddbed41272f4dd8caf1c86d163910f9595882a3d718a035edd695d3b6a926b57d0ac65b10b4e06eedd863bf141077fd7af2782bd3fa26a1a8891d57c630948c0279393dcd04cf5875258f4de9f0cb7b7b6d66d2b81ba752d85f5214724fed41036bd79d2fe319d7a8f4b9c81e58de2f04827b9ce09a07a3acecf54b790685ace8697606009e52c377b7f2e682b097d7d36acf0f55df5edbdc0977412468cb6ce3be10af3c63b9c8a0b0751f505be9690dd5d752436f62a3fe4c6448f3b7b0c64e38f615734651f107e3045ac42b67a7693018633b965bc1bc838ee173807bf7cd33065baaeb1a95f2c11dfcd2bc51fe48880aa3e8a062a8b059f595df4ae9a74de9e9ec499d43c97b1427c6e47e525bb63e4282ad3ea3712893c599dbc4259c1627713c927de81043950df97db1406da8411c92063be280230839627078e79a0390a71b5bb5003a8c1232338e4507025573e9fbd02a0e41dc0e680c3691cf714053bb34160895dc16f291d98d02322aa39f3e14707ffaa06f202a5bb0c7201f4a343aca51090497efdfbfce89840c8db8e46573923ff14410b00490d9f5c1a0425dd8ce4673e873cd0265e4dbe7041ed814009b8cbb8e5863bd07163b7716e0607b6698060b96b7b9478dc8643918247ee39a60b7e97f133a8b4e0c63d4ee597380923970a3ef4c0e13e25dfcfab457ba95bdbddb212016501867b9cfbfda982f907c74b282da34874a9048aa479c83838edc62982b7ac7c42d235cd67f15ac5b40c366418a2cb0c1c81e6f5e31db14c160d035de8cd4a3f18ea96562cc37359dfe9cac887fef039fd6b39446f56ea5d13a9dca4579a922496b1975b8d22dfc3566cf9557230703be715ac1431d4d2275325d7fae6b4f6f182a93ef1e32a9f41938f6a60afeb1a95c5fea53dccf772cf2c8c489240031f627e7565c0d67bbf160487c1801073bc0c31f91f953420f2bc8c7c52cc540032738a809905c0f4fa501940208e73df34028e703938a0577ae013f4a001300db4f03b71407461ce0502dca8c9c7dcf6a003fafcbdcd0070bc1e0fd734070db467b8a04cb9c9e68274b93bcb1daa40200ed4099765249ec476cf340849b8b264823bf34689ee009cb671df1409bc8e8b8e770e3ec68984d9c953cf97be2860b248caaebc107dc67f7a184c31232db88fd451031ce50e76039f5c8045026efb8f93279e0fd680b239504f0483d88ef409ee25bb90c79c0e050151c6e21b39ce783eb40adbcc2cefa17bdb61322387781c950e3dbdf9c8a066f28790b22e013db3dbef54726081e63c6460d34191b1bb0720f634060e7600412c781502409c8e3d7b500ed71dbef8ed40243f181c0a02e5d4e0fad006f644e4819e3b501f7e3049e7b501bc43b4e391da80558003392d40a23305e320d02e64cf998e1bf5a032b0c927b8e68049c8c8e71c6280377043118f4e680bbff00de0504ddbb3128371c1c64668024e59f3cd02521254e4fad1a2107e48fe6a6809ddb9e78a029e1463d05027ffc744a6c3857c7b1a205ff00e637fbf4a02b12b1794e39f4a02024e7249ed409924720906800005173fd7404989698ee39e4f7a04cf723d07a501fff0097ed406ffe36a018ff00281e99a037f4d0731f3bfd28007e53400ff99a810248c0cf140bc60123233cff008a0557f9beb4056eff007a07109243e79a03778b27bfbd02b128c27039a054001b818a06609de793da815006070283ffd9}\par} {\pard \ql \f0 \sa0 \li0 \fi0 lalune\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here is a movie {\pict\jpegblip\picw20\pich22\picwgoal400\pichgoal440 ffd8ffe000104a46494600010101004800480000fffe0050546869732061727420697320696e20746865207075626c696320646f6d61696e2e204b6576696e204875676865732c206b6576696e68406569742e636f6d2c2053657074656d6265722031393935ffdb00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffdb00430101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffc00011080016001403012200021101031101ffc4001a000100020301000000000000000000000000080905060a07ffc400231000010501000300010500000000000000060304050708020001090a11153976b7ffc400160101010100000000000000000000000000060800ffc400261101000102050109000000000000000000010200030405061121b33134365154717475b4ffda000c03010002110311003f00a90cf388f366a62aa720ed6ae07f96901f3831d973452b8cf36fe3570fc908e46d466433e5dd954f2e96992d9e498c7753faa44916e016ca91cc7d88b38fe60a5b97737defcbcc539c98d336a57f4fc2ca9a486bf07ab575ad9a3af4df221d8215e36df86c4504ff0024574551b3d687ee0575757b3ad64e311ee62bd94158d37e24198c43973099f1fc0c41614d950246513a081abf76cfe7061f6863281e6352fd1670949c148dd6dfb0d25f5b3689b1d5c965b0eacbf4e0932ad28e22ab9ae945633f4744bd3c8cee0a7fdf085b9000f449c5f7afa30b83e0b6fd7b0c8429c9467ff9715347c891e25fa24a205861aa715e6a09bd0488237dc2723414d9891381524e8ca7c0894664f835653631ab55ee7e3de433e4ff001b30949124e4c10c8b6ad0a479b3f9c937b2cf5bc0095ad600a0a41a0e9faee174a1c605e161c6c7a313539650b0113190f1a8368e60d5b24f30ff008ea7f0bf867fa6595feeb6978f1fe0f9c26177f4d63a51a9235184750e7d18811339cd000000c75f000e00380380ae390c350def826ed42ad051fa6f501c50f9b699c3b69cbeb76476d202bf3ac985b6e0e968be66572893e6a744540bd9722e5c87956848629bc2559306bd113e8653d3b6aff651dfad7a3ac8b02958cba02a93ccf525757039bae6cff090e1d90688e8aa233ee86a4c4a3e0586d6b2340522e47dcb7d0046d8a5acb05a123ee25d2b230b2ada6e2e2f9ede3c05202520ec2487b0d56562529d8b3393bca76adca4ec1bca508abb001babc007915d84fe3dd14e207e3c62f8379da2a3b861fb6629d28dba53b6ea388ebfed866bf6dfb553455e91ed547ae92e9445253a4fdf3efb4f8ebdfbe7d3c78f1ee0bb9e13e358e942a4ed49e22cff00eeb35fdd7ebfffd9} icon.\par} {\pard \qc \f0 \sa180 \li0 \fi0 \emdash\emdash\emdash\emdash\emdash\par} {\pard \ql \f0 \sa180 \li0 \fi0 \outlinelevel0 \b \fs36 Footnotes\par} {\pard \ql \f0 \sa180 \li0 \fi0 Here is a footnote reference,{\super\chftn}{\*\footnote\chftn\~\plain\pard {\pard \ql \f0 \sa180 \li0 \fi0 Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.\par} } and another.{\super\chftn}{\*\footnote\chftn\~\plain\pard {\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's the long note. This one contains multiple blocks.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Subsequent blocks are indented to show that they belong to the footnote (as with list items).\par} {\pard \ql \f0 \sa180 \li0 \fi0 \f1 \{ \}\par} {\pard \ql \f0 \sa180 \li0 \fi0 If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.\par} } This should {\i not} be a footnote reference, because it contains a space.[^my note] Here is an inline note.{\super\chftn}{\*\footnote\chftn\~\plain\pard {\pard \ql \f0 \sa180 \li0 \fi0 This is {\i easier} to type. Inline notes may contain {\field{\*\fldinst{HYPERLINK "http://google.com"}}{\fldrslt{\ul links }}} and {\f1 ]} verbatim characters, as well as [bracketed text].\par} }\par} {\pard \ql \f0 \sa180 \li720 \fi0 Notes can go in quotes.{\super\chftn}{\*\footnote\chftn\~\plain\pard {\pard \ql \f0 \sa180 \li0 \fi0 In quote.\par} }\par} {\pard \ql \f0 \sa0 \li360 \fi-360 1.\tx360\tab And in list items.{\super\chftn}{\*\footnote\chftn\~\plain\pard {\pard \ql \f0 \sa180 \li0 \fi0 In list.\par} }\sa180\par} {\pard \ql \f0 \sa180 \li0 \fi0 This paragraph should not be part of the note, as it is not indented.\par} } ================================================ FILE: test/writer.tei ================================================ Pandoc Test Suite John MacFarlane Anonymous July 17, 2006

                Produced by pandoc.

                This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.

                Headers
                Level 2 with an embedded link
                Level 3 with emphasis
                Level 4
                Level 5

                Level 1
                Level 2 with emphasis
                Level 3

                with no blank line

                Level 2

                with no blank line

                Paragraphs

                Here’s a regular paragraph.

                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 a list item.

                Here’s one with a bullet. * criminey.

                There should be a hard line breakhere.

                Block Quotes

                E-mail style:

                This is a block quote. It is pretty short.

                Code in a block quote:

                sub status { print "working"; }

                A list:

                item one

                item two

                Nested block quotes:

                nested

                nested

                This should not be a block quote: 2 > 1.

                And a following paragraph.

                Code Blocks

                Code:

                ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab

                And:

                this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{
                Lists
                Unordered

                Asterisks tight:

                asterisk 1

                asterisk 2

                asterisk 3

                Asterisks loose:

                asterisk 1

                asterisk 2

                asterisk 3

                Pluses tight:

                Plus 1

                Plus 2

                Plus 3

                Pluses loose:

                Plus 1

                Plus 2

                Plus 3

                Minuses tight:

                Minus 1

                Minus 2

                Minus 3

                Minuses loose:

                Minus 1

                Minus 2

                Minus 3

                Ordered

                Tight:

                First

                Second

                Third

                and:

                One

                Two

                Three

                Loose using tabs:

                First

                Second

                Third

                and using spaces:

                One

                Two

                Three

                Multiple paragraphs:

                Item 1, graf one.

                Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

                Item 2.

                Item 3.

                Nested

                Tab

                Tab

                Tab

                Here’s another:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Same thing but with paragraphs:

                First

                Second:

                Fee

                Fie

                Foe

                Third

                Tabs and spaces

                this is a list item indented with tabs

                this is a list item indented with spaces

                this is an example list item indented with tabs

                this is an example list item indented with spaces

                Fancy list markers

                begins with 2

                and now 3

                with a continuation

                sublist with roman numerals, starting with 4

                more items

                a subsublist

                a subsublist

                Nesting:

                Upper Alpha

                Upper Roman.

                Decimal start with 6

                Lower alpha with paren

                Autonumbering:

                Autonumber.

                More.

                Nested.

                Should not be a list item:

                M.A. 2007

                B. Williams

                Definition Lists

                Tight using spaces:

                red fruit

                orange fruit

                yellow fruit

                Tight using tabs:

                red fruit

                orange fruit

                yellow fruit

                Loose:

                red fruit

                orange fruit

                yellow fruit

                Multiple blocks with italics:

                red fruit

                contains seeds, crisp, pleasant to taste

                orange fruit

                { orange code block }

                orange block quote

                Multiple definitions, tight:

                red fruit

                computer

                orange fruit

                bank

                Multiple definitions, loose:

                red fruit

                computer

                orange fruit

                bank

                Blank line after term, indented marker, alternate markers:

                red fruit

                computer

                orange fruit

                sublist

                sublist

                HTML Blocks

                Simple block on one line:

                foo

                And nested without indentation:

                foo

                bar

                Interpreted markdown in a table:

                This is emphasized

                And this is strong

                Here’s a simple block:

                foo

                This should be a code block, though:

                <div> foo </div>

                As should this:

                <div>foo</div>

                Now, nested:

                foo

                This should just be an HTML comment:

                Multiline:

                Code block:

                <!-- Comment -->

                Just plain comment, with trailing spaces on the line:

                Code:

                <hr />

                Hr’s:

                Inline Markup

                This is emphasized, and so is this.

                This is strong, and so is this.

                An emphasized link.

                This is strong and em.

                So is this word.

                This is strong and em.

                So is this word.

                This is code: >, $, \, \$, <html>.

                This is strikeout.

                Superscripts: abcd ahello ahello there.

                Subscripts: H2O, H23O, Hmany of themO.

                These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.

                Smart quotes, ellipses, dashes

                Hello, said the spider. Shelob is my name.

                A, B, and C are letters.

                Oak, elm, and beech are names of trees. So is pine.

                He said, I want to go. Were you alive in the 70’s?

                Here is some quoted code and a quoted link.

                Some dashes: one—two — three—four — five.

                Dashes between numbers: 5–7, 255–66, 1987–1999.

                Ellipses…and…and….

                LaTeX

                2+2=4

                x \in y

                \alpha \wedge \omega

                223

                p-Tree

                Here’s some display math:

                \frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}

                Here’s one that has a line break in it: \alpha + \omega \times x^2.

                These shouldn’t be math:

                To get the famous equation, write $e = mc^2$.

                $22,000 is a lot of money. So is $34,000. (It worked if lot is emphasized.)

                Shoes ($20) and socks ($5).

                Escaped $: $73 this should be emphasized 23$.

                Here’s a LaTeX table:

                Special Characters

                Here is some unicode:

                I hat: Î

                o umlaut: ö

                section: §

                set membership: ∈

                copyright: ©

                AT&T has an ampersand in their name.

                AT&T is another way to write it.

                This & that.

                4 < 5.

                6 > 5.

                Backslash: \

                Backtick: `

                Asterisk: *

                Underscore: _

                Left brace: {

                Right brace: }

                Left bracket: [

                Right bracket: ]

                Left paren: (

                Right paren: )

                Greater-than: >

                Hash: #

                Period: .

                Bang: !

                Plus: +

                Minus: -

                Links
                Explicit

                Just a URL.

                URL and title.

                URL and title.

                URL and title.

                URL and title

                URL and title

                with_underscore

                Email link (nobody@nowhere.net)

                Empty.

                Reference

                Foo bar.

                With embedded [brackets].

                b by itself should be a link.

                Indented once.

                Indented twice.

                Indented thrice.

                This should [not][] be a link.

                [not]: /url

                Foo bar.

                Foo biz.

                With ampersands

                Here’s a link with an ampersand in the URL.

                Here’s a link with an amersand in the link text: AT&T.

                Here’s an inline link.

                Here’s an inline link in pointy braces.

                Autolinks

                With an ampersand: http://example.com/?foo=1&bar=2

                In a list?

                http://example.com/

                It should.

                An e-mail address: nobody@nowhere.net

                Blockquoted: http://example.com/

                Auto-links should not occur here: <http://example.com/>

                or here: <http://example.com/>
                Images

                From Voyage dans la Lune by Georges Melies (1902):

                lalune Voyage dans la Lune

                lalune

                Here is a movie

                movie
                icon.

                Footnotes

                Here is a footnote reference,

                Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

                and another.

                Here’s the long note. This one contains multiple blocks.

                Subsequent blocks are indented to show that they belong to the footnote (as with list items).

                { <code> }

                If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

                This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.

                This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

                Notes can go in quotes.

                In quote.

                And in list items.

                In list.

                This paragraph should not be part of the note, as it is not indented.

                ================================================ FILE: test/writer.texinfo ================================================ \input texinfo @c -*-texinfo-*- @settitle Pandoc Test Suite @documentencoding UTF-8 @macro textstrikeout{text} ~~\text\~~ @end macro @ifnottex @paragraphindent 0 @end ifnottex @titlepage @title Pandoc Test Suite @author John MacFarlane @author Anonymous July 17, 2006 @end titlepage @node Top @top Pandoc Test Suite This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @menu * Headers:: * Level 1:: * Paragraphs:: * Block Quotes:: * Code Blocks:: * Lists:: * Definition Lists:: * HTML Blocks:: * Inline Markup:: * Smart quotes ellipses dashes:: * LaTeX:: * Special Characters:: * Links:: * Images:: * Footnotes:: @end menu @node Headers @chapter Headers @menu * Level 2 with an @uref{/url,embedded link}:: @end menu @node Level 2 with an @uref{/url,embedded link} @section Level 2 with an @uref{/url,embedded link} @menu * Level 3 with @emph{emphasis}:: @end menu @node Level 3 with @emph{emphasis} @subsection Level 3 with @emph{emphasis} @menu * Level 4:: @end menu @node Level 4 @subsubsection Level 4 Level 5 @node Level 1 @chapter Level 1 @menu * Level 2 with @emph{emphasis}:: * Level 2:: @end menu @node Level 2 with @emph{emphasis} @section Level 2 with @emph{emphasis} @menu * Level 3:: @end menu @node Level 3 @subsection Level 3 with no blank line @node Level 2 @section Level 2 with no blank line @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Paragraphs @chapter Paragraphs Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break@* here. @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Block Quotes @chapter Block Quotes E-mail style: @quotation This is a block quote. It is pretty short. @end quotation @quotation Code in a block quote: @verbatim sub status { print "working"; } @end verbatim A list: @enumerate @item item one @item item two @end enumerate Nested block quotes: @quotation nested @end quotation @quotation nested @end quotation @end quotation This should not be a block quote: 2 > 1. And a following paragraph. @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Code Blocks @chapter Code Blocks Code: @verbatim ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab @end verbatim And: @verbatim this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ @end verbatim @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Lists @chapter Lists @menu * Unordered:: * Ordered:: * Nested:: * Tabs and spaces:: * Fancy list markers:: @end menu @node Unordered @section Unordered Asterisks tight: @itemize @item asterisk 1 @item asterisk 2 @item asterisk 3 @end itemize Asterisks loose: @itemize @item asterisk 1 @item asterisk 2 @item asterisk 3 @end itemize Pluses tight: @itemize @item Plus 1 @item Plus 2 @item Plus 3 @end itemize Pluses loose: @itemize @item Plus 1 @item Plus 2 @item Plus 3 @end itemize Minuses tight: @itemize @item Minus 1 @item Minus 2 @item Minus 3 @end itemize Minuses loose: @itemize @item Minus 1 @item Minus 2 @item Minus 3 @end itemize @node Ordered @section Ordered Tight: @enumerate @item First @item Second @item Third @end enumerate and: @enumerate @item One @item Two @item Three @end enumerate Loose using tabs: @enumerate @item First @item Second @item Third @end enumerate and using spaces: @enumerate @item One @item Two @item Three @end enumerate Multiple paragraphs: @enumerate @item Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. @item Item 2. @item Item 3. @end enumerate @node Nested @section Nested @itemize @item Tab @itemize @item Tab @itemize @item Tab @end itemize @end itemize @end itemize Here's another: @enumerate @item First @item Second: @itemize @item Fee @item Fie @item Foe @end itemize @item Third @end enumerate Same thing but with paragraphs: @enumerate @item First @item Second: @itemize @item Fee @item Fie @item Foe @end itemize @item Third @end enumerate @node Tabs and spaces @section Tabs and spaces @itemize @item this is a list item indented with tabs @item this is a list item indented with spaces @itemize @item this is an example list item indented with tabs @item this is an example list item indented with spaces @end itemize @end itemize @node Fancy list markers @section Fancy list markers @enumerate 2 @item begins with 2 @item and now 3 with a continuation @enumerate 4 @item sublist with roman numerals, starting with 4 @item more items @enumerate A @item a subsublist @item a subsublist @end enumerate @end enumerate @end enumerate Nesting: @enumerate A @item Upper Alpha @enumerate @item Upper Roman. @enumerate 6 @item Decimal start with 6 @enumerate c @item Lower alpha with paren @end enumerate @end enumerate @end enumerate @end enumerate Autonumbering: @enumerate @item Autonumber. @item More. @enumerate @item Nested. @end enumerate @end enumerate Should not be a list item: M.A.@ 2007 B. Williams @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Definition Lists @chapter Definition Lists Tight using spaces: @table @asis @item apple red fruit @item orange orange fruit @item banana yellow fruit @end table Tight using tabs: @table @asis @item apple red fruit @item orange orange fruit @item banana yellow fruit @end table Loose: @table @asis @item apple red fruit @item orange orange fruit @item banana yellow fruit @end table Multiple blocks with italics: @table @asis @item @emph{apple} red fruit contains seeds, crisp, pleasant to taste @item @emph{orange} orange fruit @verbatim { orange code block } @end verbatim @quotation orange block quote @end quotation @end table Multiple definitions, tight: @table @asis @item apple red fruit computer @item orange orange fruit bank @end table Multiple definitions, loose: @table @asis @item apple red fruit computer @item orange orange fruit bank @end table Blank line after term, indented marker, alternate markers: @table @asis @item apple red fruit computer @item orange orange fruit @enumerate @item sublist @item sublist @end enumerate @end table @node HTML Blocks @chapter HTML Blocks Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is @emph{emphasized} And this is @strong{strong} Here's a simple block: foo This should be a code block, though: @verbatim
                foo
                @end verbatim As should this: @verbatim
                foo
                @end verbatim Now, nested: foo This should just be an HTML comment: Multiline: Code block: @verbatim @end verbatim Just plain comment, with trailing spaces on the line: Code: @verbatim
                @end verbatim Hr's: @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Inline Markup @chapter Inline Markup This is @emph{emphasized}, and so @emph{is this}. This is @strong{strong}, and so @strong{is this}. An @emph{@uref{/url,emphasized link}}. @strong{@emph{This is strong and em.}} So is @strong{@emph{this}} word. @strong{@emph{This is strong and em.}} So is @strong{@emph{this}} word. This is code: @code{>}, @code{$}, @code{\}, @code{\$}, @code{}. @textstrikeout{This is @emph{strikeout}.} Superscripts: a@sup{bc}d a@sup{@emph{hello}} a@sup{hello@ there}. Subscripts: H@sub{2}O, H@sub{23}O, H@sub{many@ of@ them}O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Smart quotes ellipses dashes @chapter Smart quotes, ellipses, dashes ``Hello,'' said the spider. ```Shelob' is my name.'' `A', `B', and `C' are letters. `Oak,' `elm,' and `beech' are names of trees. So is `pine.' `He said, ``I want to go.''' Were you alive in the 70's? Here is some quoted `@code{code}' and a ``@uref{http://example.com/?foo=1&bar=2,quoted link}''. Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses@dots{}and@dots{}and@dots{}. @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node LaTeX @chapter LaTeX @itemize @item @tex \cite[22-23]{smith.1899} @end tex @item @math{2+2=4} @item @math{x \in y} @item @math{\alpha \wedge \omega} @item @math{223} @item @math{p}-Tree @item Here's some display math: @math{\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}} @item Here's one that has a line break in it: @math{\alpha + \omega \times x^2}. @end itemize These shouldn't be math: @itemize @item To get the famous equation, write @code{$e = mc^2$}. @item $22,000 is a @emph{lot} of money. So is $34,000. (It worked if ``lot'' is emphasized.) @item Shoes ($20) and socks ($5). @item Escaped @code{$}: $73 @emph{this should be emphasized} 23$. @end itemize Here's a LaTeX table: @tex \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline Dog & 2 \\ Cat & 1 \\ \hline \end{tabular} @end tex @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Special Characters @chapter Special Characters Here is some unicode: @itemize @item I hat: Î @item o umlaut: ö @item section: § @item set membership: ∈ @item copyright: © @end itemize AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: @{ Right brace: @} Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Links @chapter Links @menu * Explicit:: * Reference:: * With ampersands:: * Autolinks:: @end menu @node Explicit @section Explicit Just a @uref{/url/,URL}. @uref{/url/,URL and title}. @uref{/url/,URL and title}. @uref{/url/,URL and title}. @uref{/url/,URL and title} @uref{/url/,URL and title} @uref{/url/with_underscore,with_underscore} @uref{mailto:nobody@@nowhere.net,Email link} @uref{,Empty}. @node Reference @section Reference Foo @uref{/url/,bar}. With @uref{/url/,embedded [brackets]}. @uref{/url/,b} by itself should be a link. Indented @uref{/url,once}. Indented @uref{/url,twice}. Indented @uref{/url,thrice}. This should [not][] be a link. @verbatim [not]: /url @end verbatim Foo @uref{/url/,bar}. Foo @uref{/url/,biz}. @node With ampersands @section With ampersands Here's a @uref{http://example.com/?foo=1&bar=2,link with an ampersand in the URL}. Here's a link with an amersand in the link text: @uref{http://att.com/,AT&T}. Here's an @uref{/script?foo=1&bar=2,inline link}. Here's an @uref{/script?foo=1&bar=2,inline link in pointy braces}. @node Autolinks @section Autolinks With an ampersand: @url{http://example.com/?foo=1&bar=2} @itemize @item In a list? @item @url{http://example.com/} @item It should. @end itemize An e-mail address: @uref{mailto:nobody@@nowhere.net,nobody@@nowherenet} @quotation Blockquoted: @url{http://example.com/} @end quotation Auto-links should not occur here: @code{} @verbatim or here: @end verbatim @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Images @chapter Images From ``Voyage dans la Lune'' by Georges Melies (1902): @float @image{lalune,,,lalune,jpg} @caption{lalune} @end float Here is a movie @image{movie,,,movie,jpg} icon. @iftex @bigskip@hrule@bigskip @end iftex @ifnottex ------------------------------------------------------------------------ @end ifnottex @node Footnotes @chapter Footnotes Here is a footnote reference,@footnote{Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.} and another.@footnote{Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). @verbatim { } @end verbatim If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.} This should @emph{not} be a footnote reference, because it contains a space.[^my note] Here is an inline note.@footnote{This is @emph{easier} to type. Inline notes may contain @uref{http://google.com,links} and @code{]} verbatim characters, as well as [bracketed text].} @quotation Notes can go in quotes.@footnote{In quote.} @end quotation @enumerate @item And in list items.@footnote{In list.} @end enumerate This paragraph should not be part of the note, as it is not indented. @bye ================================================ FILE: test/writer.textile ================================================ This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite.
                h1(#headers). Headers h2(#level-2-with-an-embedded-link). Level 2 with an "embedded link":/url h3(#level-3-with-emphasis). Level 3 with _emphasis_ h4(#level-4). Level 4 h5(#level-5). Level 5 h1(#level-1). Level 1 h2(#level-2-with-emphasis). Level 2 with _emphasis_ h3(#level-3). Level 3 with no blank line h2(#level-2). Level 2 with no blank line
                h1(#paragraphs). Paragraphs Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break here.
                h1(#block-quotes). Block Quotes E-mail style: bq. This is a block quote. It is pretty short.
                Code in a block quote: bc. sub status { print "working"; } A list: # item one # item two Nested block quotes: bq. nested bq. nested
                This should not be a block quote: 2 > 1. And a following paragraph.
                h1(#code-blocks). Code Blocks Code:
                ---- (should be four hyphens)
                
                sub status {
                    print "working";
                }
                
                this code block is indented by one tab
                
                And:
                    this code block is indented by two tabs
                
                These should not be escaped:  \$ \\ \> \[ \{
                

                h1(#lists). Lists h2(#unordered). Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: * Plus 1 * Plus 2 * Plus 3 Pluses loose: * Plus 1 * Plus 2 * Plus 3 Minuses tight: * Minus 1 * Minus 2 * Minus 3 Minuses loose: * Minus 1 * Minus 2 * Minus 3 h2(#ordered). Ordered Tight: # First # Second # Third and: # One # Two # Three Loose using tabs: # First # Second # Third and using spaces: # One # Two # Three Multiple paragraphs:
                1. Item 1, graf one.

                  Item 1. graf two. The quick brown fox jumped over the lazy dog's back.

                2. Item 2.

                3. Item 3.

                h2(#nested). Nested * Tab ** Tab *** Tab Here's another: # First # Second: #* Fee #* Fie #* Foe # Third Same thing but with paragraphs: # First # Second: #* Fee #* Fie #* Foe # Third h2(#tabs-and-spaces). Tabs and spaces * this is a list item indented with tabs * this is a list item indented with spaces ** this is an example list item indented with tabs ** this is an example list item indented with spaces h2(#fancy-list-markers). Fancy list markers
                1. begins with 2

                2. and now 3

                  with a continuation

                  1. sublist with roman numerals, starting with 4
                  2. more items
                    1. a subsublist
                    2. a subsublist
                Nesting:
                1. Upper Alpha
                  1. Upper Roman.
                    1. Decimal start with 6
                      1. Lower alpha with paren
                Autonumbering: # Autonumber. # More. ## Nested. Should not be a list item: M.A. 2007 B. Williams
                h1(#definition-lists). Definition Lists Tight using spaces:
                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit
                Tight using tabs:
                apple
                red fruit
                orange
                orange fruit
                banana
                yellow fruit
                Loose:
                apple

                red fruit

                orange

                orange fruit

                banana

                yellow fruit

                Multiple blocks with italics:
                _apple_

                red fruit

                contains seeds, crisp, pleasant to taste

                _orange_

                orange fruit

                bc. { orange code block } bq.

                orange block quote

                Multiple definitions, tight:
                apple
                red fruit
                computer
                orange
                orange fruit
                bank
                Multiple definitions, loose:
                apple

                red fruit

                computer

                orange

                orange fruit

                bank

                Blank line after term, indented marker, alternate markers:
                apple

                red fruit

                computer

                orange

                orange fruit

                1. sublist
                2. sublist
                h1(#html-blocks). HTML Blocks Simple block on one line:
                foo
                And nested without indentation:
                foo
                bar
                Interpreted markdown in a table:
                This is _emphasized_ And this is *strong*
                Here's a simple block:
                foo
                This should be a code block, though: bc.
                foo
                As should this: bc.
                foo
                Now, nested:
                foo
                This should just be an HTML comment: Multiline: Code block: bc. Just plain comment, with trailing spaces on the line: Code: bc.
                Hr's:









                h1(#inline-markup). Inline Markup This is _emphasized_, and so _is this_. This is *strong*, and so *is this*. An _"emphasized link":/url_. *_This is strong and em._* So is *_this_* word. *_This is strong and em._* So is *_this_* word. This is code: @>@, @$@, @\@, @\$@, @@. -This is _strikeout_.- Superscripts: a[^bc^]d a[^_hello_^] a[^hello there^]. Subscripts: H[~2~]O, H[~23~]O, H[~many of them~]O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.
                h1(#smart-quotes-ellipses-dashes). Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70's? Here is some quoted '@code@' and a ""quoted link":http://example.com/?foo=1&bar=2". Some dashes: one -- two -- three -- four -- five. Dashes between numbers: 5 - 7, 255 - 66, 1987 - 1999. Ellipses...and...and....
                h1(#latex). LaTeX * * 2+2=4 * x \in y * \alpha \wedge \omega * 223 * p-Tree * Here's some display math: \frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h} * Here's one that has a line break in it: \alpha + \omega \times x^2. These shouldn't be math: * To get the famous equation, write @$e = mc^2$@. * $22,000 is a _lot_ of money. So is $34,000. (It worked if "lot" is emphasized.) * Shoes ($20) and socks ($5). * Escaped @$@: $73 _this should be emphasized_ 23$. Here's a LaTeX table:
                h1(#special-characters). Special Characters Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: -
                h1(#links). Links h2(#explicit). Explicit Just a "URL":/url/. "URL and title":/url/. "URL and title":/url/. "URL and title":/url/. "URL and title":/url/ "URL and title":/url/ "with_underscore":/url/with_underscore "Email link":mailto:nobody@nowhere.net "Empty":. h2(#reference). Reference Foo "bar":/url/. With "embedded [brackets]":/url/. "b":/url/ by itself should be a link. Indented "once":/url. Indented "twice":/url. Indented "thrice":/url. This should [not][] be a link. bc. [not]: /url Foo "bar":/url/. Foo "biz":/url/. h2(#with-ampersands). With ampersands Here's a "link with an ampersand in the URL":http://example.com/?foo=1&bar=2. Here's a link with an amersand in the link text: "AT&T":http://att.com/. Here's an "inline link":/script?foo=1&bar=2. Here's an "inline link in pointy braces":/script?foo=1&bar=2. h2(#autolinks). Autolinks With an ampersand: "$":http://example.com/?foo=1&bar=2 * In a list? * "$":http://example.com/ * It should. An e-mail address: "(email)nobody@nowhere.net":mailto:nobody@nowhere.net bq. Blockquoted: "$":http://example.com/ Auto-links should not occur here: @@ bc. or here:
                h1(#images). Images From "Voyage dans la Lune" by Georges Melies (1902):
                lalune
                !lalune.jpg(Voyage dans la Lune)!
                Here is a movie !movie.jpg(movie)! icon.
                h1(#footnotes). Footnotes Here is a footnote reference,[1] and another.[2] This should _not_ be a footnote reference, because it contains a space.[^my note] Here is an inline note.[3] bq. Notes can go in quotes.[4] # And in list items.[5] This paragraph should not be part of the note, as it is not indented. fn1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. fn2. Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). bc. { } If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. fn3. This is _easier_ to type. Inline notes may contain "links":http://google.com and @]@ verbatim characters, as well as [bracketed text]. fn4. In quote. fn5. In list. ================================================ FILE: test/writer.typst ================================================ #let horizontalrule = line(start: (25%,0%), end: (75%,0%)) #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, stroke: none ) #show figure.where( kind: table ): set figure.caption(position: top) #show figure.where( kind: image ): set figure.caption(position: bottom) #let content-to-string(content) = { if content.has("text") { content.text } else if content.has("children") { content.children.map(content-to-string).join("") } else if content.has("body") { content-to-string(content.body) } else if content == [ ] { " " } } #let conf( title: none, subtitle: none, authors: (), keywords: (), date: none, abstract-title: none, abstract: none, thanks: none, cols: 1, margin: (x: 1.25in, y: 1.25in), paper: "us-letter", lang: "en", region: "US", font: none, fontsize: 11pt, mathfont: none, codefont: none, linestretch: 1, sectionnumbering: none, linkcolor: none, citecolor: none, filecolor: none, pagenumbering: "1", doc, ) = { set document( title: title, keywords: keywords, ) set document( author: authors.map(author => content-to-string(author.name)).join(", ", last: " & "), ) if authors != none and authors != () set page( paper: paper, margin: margin, numbering: pagenumbering, columns: cols ) set par( justify: true, leading: linestretch * 0.65em ) set text(lang: lang, region: region, size: fontsize) set text(font: font) if font != none show math.equation: set text(font: mathfont) if mathfont != none show raw: set text(font: codefont) if codefont != none set heading(numbering: sectionnumbering) show link: set text(fill: rgb(content-to-string(linkcolor))) if linkcolor != none show ref: set text(fill: rgb(content-to-string(citecolor))) if citecolor != none show link: this => { if filecolor != none and type(this.dest) == label { text(this, fill: rgb(content-to-string(filecolor))) } else { text(this) } } if title != none { place(top, float: true, scope: "parent", clearance: 4mm, block(below: 1em, width: 100%)[ #if title != none { align(center, block[ #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, hyphenate: false)[#subtitle] } )]) } #if authors != none and authors != [] { let count = authors.len() let ncols = calc.min(count, 3) grid( columns: (1fr,) * ncols, row-gutter: 1.5em, ..authors.map(author => align(center)[ #author.name \ #author.affiliation \ #author.email ]) ) } #if date != none { align(center)[#block(inset: 1em)[ #date ]] } #if abstract != none { block(inset: 2em)[ #text(weight: "semibold")[#abstract-title] #h(1em) #abstract ] } ]) } doc } #show: doc => conf( title: [Pandoc Test Suite], authors: ( ( name: [John MacFarlane], affiliation: "", email: "" ), ( name: [Anonymous], affiliation: "", email: "" ), ), date: [July 17, 2006], abstract-title: [Abstract], pagenumbering: "1", cols: 1, doc, ) This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. #horizontalrule = Headers == Level 2 with an #link("/url")[embedded link] === Level 3 with #emph[emphasis] ==== Level 4 ===== Level 5 = Level 1 == Level 2 with #emph[emphasis] === Level 3 with no blank line == Level 2 with no blank line #horizontalrule = Paragraphs Here's a regular paragraph. 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 a list item. Here's one with a bullet. \* criminey. There should be a hard line break \ here. #horizontalrule = Block Quotes E-mail style: #quote(block: true)[ This is a block quote. It is pretty short. ] #quote(block: true)[ Code in a block quote: ``` sub status { print "working"; } ``` A list: + item one + item two Nested block quotes: #quote(block: true)[ nested ] #quote(block: true)[ nested ] ] This should not be a block quote: 2 \> 1. And a following paragraph. #horizontalrule = Code Blocks Code: ``` ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab ``` And: ``` this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ ``` #horizontalrule = Lists == Unordered Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 == Ordered Tight: + First + Second + Third and: + One + Two + Three Loose using tabs: + First + Second + Third and using spaces: + One + Two + Three Multiple paragraphs: + Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. + Item 2. + Item 3. == Nested - Tab - Tab - Tab Here's another: + First + Second: - Fee - Fie - Foe + Third Same thing but with paragraphs: + First + Second: - Fee - Fie - Foe + Third == Tabs and spaces - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces == Fancy list markers #block[ #set enum(numbering: "(1)", start: 2) + begins with 2 + and now 3 with a continuation #block[ #set enum(numbering: "i.", start: 4) + sublist with roman numerals, starting with 4 + more items #block[ #set enum(numbering: "(A)", start: 1) + a subsublist + a subsublist ] ] ] Nesting: #block[ #set enum(numbering: "A.", start: 1) + Upper Alpha #block[ #set enum(numbering: "I.", start: 1) + Upper Roman. #block[ #set enum(numbering: "(1)", start: 6) + Decimal start with 6 #block[ #set enum(numbering: "a)", start: 3) + Lower alpha with paren ] ] ] ] Autonumbering: + Autonumber. + More. + Nested. Should not be a list item: M.A.~2007 B. Williams #horizontalrule = Definition Lists Tight using spaces: / apple: #block[ red fruit ] / orange: #block[ orange fruit ] / banana: #block[ yellow fruit ] Tight using tabs: / apple: #block[ red fruit ] / orange: #block[ orange fruit ] / banana: #block[ yellow fruit ] Loose: / apple: #block[ red fruit ] / orange: #block[ orange fruit ] / banana: #block[ yellow fruit ] Multiple blocks with italics: / #emph[apple]: #block[ red fruit contains seeds, crisp, pleasant to taste ] / #emph[orange]: #block[ orange fruit ``` { orange code block } ``` #quote(block: true)[ orange block quote ] ] Multiple definitions, tight: / apple: #block[ red fruit computer ] / orange: #block[ orange fruit bank ] Multiple definitions, loose: / apple: #block[ red fruit computer ] / orange: #block[ orange fruit bank ] Blank line after term, indented marker, alternate markers: / apple: #block[ red fruit computer ] / orange: #block[ orange fruit + sublist + sublist ] = HTML Blocks Simple block on one line: #block[ foo ] And nested without indentation: #block[ #block[ #block[ foo ] ] #block[ bar ] ] Interpreted markdown in a table: This is #emph[emphasized] And this is #strong[strong] Here's a simple block: #block[ foo ] This should be a code block, though: ```
                foo
                ``` As should this: ```
                foo
                ``` Now, nested: #block[ #block[ #block[ foo ] ] ] This should just be an HTML comment: Multiline: Code block: ``` ``` Just plain comment, with trailing spaces on the line: Code: ```
                ``` Hr's: #horizontalrule = Inline Markup This is #emph[emphasized], and so #emph[is this]. This is #strong[strong], and so #strong[is this]. An #emph[#link("/url")[emphasized link]]. #strong[#emph[This is strong and em.]] So is #strong[#emph[this]] word. #strong[#emph[This is strong and em.]] So is #strong[#emph[this]] word. This is code: `>`, `$`, `\`, `\$`, ``. #strike[This is #emph[strikeout].] Superscripts: a#super[bc]d a#super[#emph[hello]] a#super[hello~there]. Subscripts: H#sub[2]O, H#sub[23]O, H#sub[many~of~them]O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a\~b c\~d. #horizontalrule = Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70's? Here is some quoted '`code`' and a "#link("http://example.com/?foo=1&bar=2")[quoted link]". Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses…and…and…. #horizontalrule = LaTeX - - $2 + 2 = 4$ - $x in y$ - $alpha and omega$ - $223$ - $p$-Tree - Here's some display math: $ frac(d, d x) f\(x\)= lim_(h arrow.r 0) frac(f\(x + h\)- f\(x\), h) $ - Here's one that has a line break in it: $alpha + omega times x^2$. These shouldn't be math: - To get the famous equation, write `$e = mc^2$`. - \$22,000 is a #emph[lot] of money. So is \$34,000. (It worked if "lot" is emphasized.) - Shoes (\$20) and socks (\$5). - Escaped `$`: \$73 #emph[this should be emphasized] 23\$. Here's a LaTeX table: #horizontalrule = Special Characters Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 \< 5. 6 \> 5. Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: { Right brace: } Left bracket: \[ Right bracket: \] Left paren: ( Right paren: ) Greater-than: \> Hash: \# Period: . Bang: ! Plus: + Minus: - #horizontalrule = Links == Explicit Just a #link("/url/")[URL]. #link("/url/")[URL and title]. #link("/url/")[URL and title]. #link("/url/")[URL and title]. #link("/url/")[URL and title] #link("/url/")[URL and title] #link("/url/with_underscore")[with\_underscore] #link("mailto:nobody@nowhere.net")[Email link] #link("")[Empty]. == Reference Foo #link("/url/")[bar]. With #link("/url/")[embedded \[brackets\]]. #link("/url/")[b] by itself should be a link. Indented #link("/url")[once]. Indented #link("/url")[twice]. Indented #link("/url")[thrice]. This should \[not\]\[\] be a link. ``` [not]: /url ``` Foo #link("/url/")[bar]. Foo #link("/url/")[biz]. == With ampersands Here's a #link("http://example.com/?foo=1&bar=2")[link with an ampersand in the URL]. Here's a link with an amersand in the link text: #link("http://att.com/")[AT&T]. Here's an #link("/script?foo=1&bar=2")[inline link]. Here's an #link("/script?foo=1&bar=2")[inline link in pointy braces]. == Autolinks With an ampersand: #link("http://example.com/?foo=1&bar=2") - In a list? - #link("http://example.com/") - It should. An e-mail address: #link("mailto:nobody@nowhere.net")[nobody\@nowhere.net] #quote(block: true)[ Blockquoted: #link("http://example.com/") ] Auto-links should not occur here: `` ``` or here: ``` #horizontalrule = Images From "Voyage dans la Lune" by Georges Melies (1902): #figure(image("lalune.jpg", alt: "lalune"), caption: [ lalune ] ) Here is a movie #box(image("movie.jpg", alt: "movie")) icon. #horizontalrule = Footnotes Here is a footnote reference,#footnote[Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.] and another.#footnote[Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). ``` { } ``` If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.] This should #emph[not] be a footnote reference, because it contains a space.\[^my note\] Here is an inline note.#footnote[This is #emph[easier] to type. Inline notes may contain #link("http://google.com")[links] and `]` verbatim characters, as well as \[bracketed text\].] #quote(block: true)[ Notes can go in quotes.#footnote[In quote.] ] + And in list items.#footnote[In list.] This paragraph should not be part of the note, as it is not indented. ================================================ FILE: test/writer.vimdoc ================================================ Pandoc Test Suite by John MacFarlane, Anonymous Type |gO| to see the table of contents. This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Headers *headers* -------------------------------------------------------------------------------- Level 2 with an embedded link /url *level-2-with-an-embedded-link* LEVEL 3 WITH EMPHASIS *level-3-with-emphasis* Level 4 *level-4* Level 5 *level-5* ================================================================================ Level 1 *level-1* -------------------------------------------------------------------------------- Level 2 with emphasis *level-2-with-emphasis* LEVEL 3 *level-3* with no blank line -------------------------------------------------------------------------------- Level 2 *level-2* with no blank line * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Paragraphs *paragraphs* Here's a regular paragraph. 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 a list item. Here's one with a bullet. * criminey. There should be a hard line break here. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Block Quotes *block-quotes* E-mail style: This is a block quote. It is pretty short. Code in a block quote: > sub status { print "working"; } < A list: 1. item one 2. item two Nested block quotes: nested nested This should not be a block quote: 2 > 1. And a following paragraph. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Code Blocks *code-blocks* Code: > ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab < And: > this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ < * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Lists *lists* -------------------------------------------------------------------------------- Unordered *unordered* Asterisks tight: - asterisk 1 - asterisk 2 - asterisk 3 Asterisks loose: - asterisk 1 - asterisk 2 - asterisk 3 Pluses tight: - Plus 1 - Plus 2 - Plus 3 Pluses loose: - Plus 1 - Plus 2 - Plus 3 Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 -------------------------------------------------------------------------------- Ordered *ordered* Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. -------------------------------------------------------------------------------- Nested *nested* - Tab - Tab - Tab Here's another: 1. First 2. Second: - Fee - Fie - Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: - Fee - Fie - Foe 3. Third -------------------------------------------------------------------------------- Tabs and spaces *tabs-and-spaces* - this is a list item indented with tabs - this is a list item indented with spaces - this is an example list item indented with tabs - this is an example list item indented with spaces -------------------------------------------------------------------------------- Fancy list markers *fancy-list-markers* (2) begins with 2 (3) and now 3 with a continuation iv. sublist with roman numerals, starting with 4 v. more items (A) a subsublist (B) a subsublist Nesting: A. Upper Alpha I. Upper Roman. (6) Decimal start with 6 c) Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Definition Lists *definition-lists* Tight using spaces: apple red fruit orange orange fruit banana yellow fruit Tight using tabs: apple red fruit orange orange fruit banana yellow fruit Loose: apple red fruit orange orange fruit banana yellow fruit Multiple blocks with italics: apple red fruit contains seeds, crisp, pleasant to taste orange orange fruit > { orange code block } < orange block quote Multiple definitions, tight: apple red fruit computer orange orange fruit bank Multiple definitions, loose: apple red fruit computer orange orange fruit bank Blank line after term, indented marker, alternate markers: apple red fruit computer orange orange fruit 1. sublist 2. sublist ================================================================================ HTML Blocks *html-blocks* Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is emphasized And this is strong Here's a simple block: foo This should be a code block, though: >
                foo
                < As should this: >
                foo
                < Now, nested: foo This should just be an HTML comment: Multiline: Code block: > < Just plain comment, with trailing spaces on the line: Code: >
                < Hr's: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Inline Markup *inline-markup* This is emphasized, and so is this. This is strong, and so is this. An emphasized link /url. This is strong and em. So is this word. This is strong and em. So is this word. This is code: `>`, `$`, `\`, `\$`, ``. This is strikeout. Superscripts: abcd ahello ahello there. Subscripts: H2O, H23O, Hmany of themO. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Smart quotes, ellipses, dashes *smart-quotes-ellipses-dashes* "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. 'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' 'He said, "I want to go."' Were you alive in the 70's? Here is some quoted '`code`' and a "quoted link http://example.com/?foo=1&bar=2". Some dashes: one---two --- three---four --- five. Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses...and...and.... * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ LaTeX *latex* - - `$2+2=4$` - `$x \in y$` - `$\alpha \wedge \omega$` - `$223$` - `$p$`-Tree - Here's some display math: `$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$` - Here's one that has a line break in it: `$\alpha + \omega \times x^2$`. These shouldn't be math: - To get the famous equation, write `$e = mc^2$`. - $22,000 is a lot of money. So is $34,000. (It worked if "lot" is emphasized.) - Shoes ($20) and socks ($5). - Escaped `$`: $73 this should be emphasized 23$. Here's a LaTeX table: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Special Characters *special-characters* Here is some unicode: - I hat: Î - o umlaut: ö - section: § - set membership: ∈ - copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Links *links* -------------------------------------------------------------------------------- Explicit *explicit* Just a URL /url/. URL and title /url/. URL and title /url/. URL and title /url/. URL and title /url/ URL and title /url/ with_underscore /url/with_underscore Email link nobody@nowhere.net Empty . -------------------------------------------------------------------------------- Reference *reference* Foo bar /url/. With embedded [brackets] /url/. b /url/ by itself should be a link. Indented once /url. Indented twice /url. Indented thrice /url. This should [not][] be a link. > [not]: /url < Foo bar /url/. Foo biz /url/. -------------------------------------------------------------------------------- With ampersands *with-ampersands* Here's a link with an ampersand in the URL http://example.com/?foo=1&bar=2. Here's a link with an amersand in the link text: AT&T http://att.com/. Here's an inline link /script?foo=1&bar=2. Here's an inline link in pointy braces /script?foo=1&bar=2. -------------------------------------------------------------------------------- Autolinks *autolinks* With an ampersand: http://example.com/?foo=1&bar=2 - In a list? - http://example.com/ - It should. An e-mail address: nobody@nowhere.net Blockquoted: http://example.com/ Auto-links should not occur here: `` > or here: < * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Images *images* From "Voyage dans la Lune" by Georges Melies (1902): Here is a movie icon. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ================================================================================ Footnotes *footnotes* Here is a footnote reference, |footnote1| and another. |footnote2| This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note. |footnote3| Notes can go in quotes. |footnote4| 1. And in list items. |footnote5| This paragraph should not be part of the note, as it is not indented. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *footnote1* Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. *footnote2* Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). > { } < If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. *footnote3* This is easier to type. Inline notes may contain links http://google.com and `]` verbatim characters, as well as [bracketed text]. *footnote4* In quote. *footnote5* In list. vim:tw=80:sw=4:ts=4:ft=help:norl:et: ================================================ FILE: test/writer.xwiki ================================================ This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ---- = Headers {{id name="headers" /}}= == Level 2 with an [[embedded link>>/url]] {{id name="level-2-with-an-embedded-link" /}}== === Level 3 with //emphasis// {{id name="level-3-with-emphasis" /}}=== ==== Level 4 {{id name="level-4" /}}==== ===== Level 5 {{id name="level-5" /}}===== = Level 1 {{id name="level-1" /}}= == Level 2 with //emphasis// {{id name="level-2-with-emphasis" /}}== === Level 3 {{id name="level-3" /}}=== with no blank line == Level 2 {{id name="level-2" /}}== with no blank line ---- = Paragraphs {{id name="paragraphs" /}}= Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break\\here. ---- = Block Quotes {{id name="block-quotes" /}}= E-mail style: >This is a block quote. It is pretty short. > >Code in a block quote: > > >{{code}} >sub status { > print "working"; >} >{{/code}} > >A list: > >1. item one >1. item two > >Nested block quotes: > >>nested >> >>nested >> This should not be a block quote: 2 > 1. And a following paragraph. ---- = Code Blocks {{id name="code-blocks" /}}= Code: {{code}} ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab {{/code}} And: {{code}} this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ {{/code}} ---- = Lists {{id name="lists" /}}= == Unordered {{id name="unordered" /}}== Asterisks tight: *. asterisk 1 *. asterisk 2 *. asterisk 3 Asterisks loose: *. asterisk 1 *. asterisk 2 *. asterisk 3 Pluses tight: *. Plus 1 *. Plus 2 *. Plus 3 Pluses loose: *. Plus 1 *. Plus 2 *. Plus 3 Minuses tight: *. Minus 1 *. Minus 2 *. Minus 3 Minuses loose: *. Minus 1 *. Minus 2 *. Minus 3 == Ordered {{id name="ordered" /}}== Tight: 1. First 1. Second 1. Third and: 1. One 1. Two 1. Three Loose using tabs: 1. First 1. Second 1. Third and using spaces: 1. One 1. Two 1. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. 1. Item 2. 1. Item 3. == Nested {{id name="nested" /}}== *. Tab **. Tab ***. Tab Here’s another: 1. First 1. Second: 1*. Fee 1*. Fie 1*. Foe 1. Third Same thing but with paragraphs: 1. First 1. Second: 1*. Fee 1*. Fie 1*. Foe 1. Third == Tabs and spaces {{id name="tabs-and-spaces" /}}== *. this is a list item indented with tabs *. this is a list item indented with spaces **. this is an example list item indented with tabs **. this is an example list item indented with spaces == Fancy list markers {{id name="fancy-list-markers" /}}== 1. begins with 2 1. and now 3 with a continuation 11. sublist with roman numerals, starting with 4 11. more items 111. a subsublist 111. a subsublist Nesting: 1. Upper Alpha 11. Upper Roman. 111. Decimal start with 6 1111. Lower alpha with paren Autonumbering: 1. Autonumber. 1. More. 11. Nested. Should not be a list item: M.A. 2007 B. Williams ---- = Definition Lists {{id name="definition-lists" /}}= Tight using spaces: ; apple : red fruit ; orange : orange fruit ; banana : yellow fruit Tight using tabs: ; apple : red fruit ; orange : orange fruit ; banana : yellow fruit Loose: ; apple : red fruit ; orange : orange fruit ; banana : yellow fruit Multiple blocks with italics: ; //apple// : red fruit contains seeds, crisp, pleasant to taste ; //orange// : orange fruit {{code}} { orange code block } {{/code}} >orange block quote > Multiple definitions, tight: ; apple : red fruit : computer ; orange : orange fruit : bank Multiple definitions, loose: ; apple : red fruit : computer ; orange : orange fruit : bank Blank line after term, indented marker, alternate markers: ; apple : red fruit : computer ; orange : orange fruit ;1. sublist ;1. sublist = HTML Blocks {{id name="html-blocks" /}}= Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is //emphasized// And this is **strong** Here’s a simple block: foo This should be a code block, though: {{code}}
                foo
                {{/code}} As should this: {{code}}
                foo
                {{/code}} Now, nested: foo This should just be an HTML comment: Multiline: Code block: {{code}} {{/code}} Just plain comment, with trailing spaces on the line: Code: {{code}}
                {{/code}} Hr’s: ---- = Inline Markup {{id name="inline-markup" /}}= This is //emphasized//, and so //is this//. This is **strong**, and so **is this**. An //[[emphasized link>>/url]]//. **//This is strong and em.//** So is **//this//** word. **//This is strong and em.//** So is **//this//** word. This is code: {{code}}>{{/code}}, {{code}}${{/code}}, {{code}}\{{/code}}, {{code}}\${{/code}}, {{code}}{{/code}}. --This is //strikeout//.-- Superscripts: a^^bc^^d a^^//hello//^^ a^^hello there^^. Subscripts: H,,2,,O, H,,23,,O, H,,many of them,,O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~~b c~~d. ---- = Smart quotes, ellipses, dashes {{id name="smart-quotes-ellipses-dashes" /}}= “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘{{code}}code{{/code}}’ and a “[[quoted link>>http://example.com/?foo=1&bar=2]]”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ---- = LaTeX {{id name="latex" /}}= *. *. {{formula}}2+2=4{{/formula}} *. {{formula}}x \in y{{/formula}} *. {{formula}}\alpha \wedge \omega{{/formula}} *. {{formula}}223{{/formula}} *. {{formula}}p{{/formula}}-Tree *. Here’s some display math: {{formula}}\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}{{/formula}} *. Here’s one that has a line break in it: {{formula}}\alpha + \omega \times x^2{{/formula}}. These shouldn’t be math: *. To get the famous equation, write {{code}}$e = mc^2${{/code}}. *. $22,000 is a //lot// of money. So is $34,000. (It worked if “lot” is emphasized.) *. Shoes ($20) and socks ($5). *. Escaped {{code}}${{/code}}: $73 //this should be emphasized// 23$. Here’s a LaTeX table: ---- = Special Characters {{id name="special-characters" /}}= Here is some unicode: *. I hat: Î *. o umlaut: ö *. section: § *. set membership: ∈ *. copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - ---- = Links {{id name="links" /}}= == Explicit {{id name="explicit" /}}== Just a [[URL>>/url/]]. [[URL and title>>/url/]]. [[URL and title>>/url/]]. [[URL and title>>/url/]]. [[URL and title>>/url/]] [[URL and title>>/url/]] [[with_underscore>>/url/with_underscore]] [[Email link>>mailto:nobody@nowhere.net]] [[Empty>>]]. == Reference {{id name="reference" /}}== Foo [[bar>>/url/]]. With [[embedded [brackets]>>/url/]]. [[b>>/url/]] by itself should be a link. Indented [[once>>/url]]. Indented [[twice>>/url]]. Indented [[thrice>>/url]]. This should [not][] be a link. {{code}} [not]: /url {{/code}} Foo [[bar>>/url/]]. Foo [[biz>>/url/]]. == With ampersands {{id name="with-ampersands" /}}== Here’s a [[link with an ampersand in the URL>>http://example.com/?foo=1&bar=2]]. Here’s a link with an amersand in the link text: [[AT&T>>http://att.com/]]. Here’s an [[inline link>>/script?foo=1&bar=2]]. Here’s an [[inline link in pointy braces>>/script?foo=1&bar=2]]. == Autolinks {{id name="autolinks" /}}== With an ampersand: http://example.com/?foo=1&bar=2 *. In a list? *. http://example.com/ *. It should. An e-mail address: [[nobody@nowhere.net>>mailto:nobody@nowhere.net]] >Blockquoted: http://example.com/ > Auto-links should not occur here: {{code}}{{/code}} {{code}} or here: {{/code}} ---- = Images {{id name="images" /}}= From “Voyage dans la Lune” by Georges Melies (1902): ((( [[image:lalune.jpg||alt="lalune" title="Voyage dans la Lune"]] ))) Here is a movie [[image:movie.jpg||alt="movie"]] icon. ---- = Footnotes {{id name="footnotes" /}}= Here is a footnote reference,{{footnote}}Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.{{/footnote}} and another.{{footnote}}Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). {{code}} { } {{/code}} If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.{{/footnote}} This should //not// be a footnote reference, because it contains a space.[^my note] Here is an inline note.{{footnote}}This is //easier// to type. Inline notes may contain [[links>>http://google.com]] and {{code}}]{{/code}} verbatim characters, as well as [bracketed text].{{/footnote}} >Notes can go in quotes.{{footnote}}In quote.{{/footnote}} > 1. And in list items.{{footnote}}In list.{{/footnote}} This paragraph should not be part of the note, as it is not indented. ================================================ FILE: test/writer.zimwiki ================================================ Content-Type: text/x-zim-wiki Wiki-Format: zim 0.4 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite. ---- ====== Headers ====== ===== Level 2 with an [[url|embedded link]] ===== ==== Level 3 with //emphasis// ==== === Level 4 === == Level 5 == ====== Level 1 ====== ===== Level 2 with //emphasis// ===== ==== Level 3 ==== with no blank line ===== Level 2 ===== with no blank line ---- ====== Paragraphs ====== Here’s a regular paragraph. 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 a list item. Here’s one with a bullet. * criminey. There should be a hard line break here. ---- ====== Block Quotes ====== E-mail style: > This is a block quote. It is pretty short. > Code in a block quote: > > ''' > sub status { > print "working"; > } > ''' > > A list: > > 1. item one > 2. item two > > Nested block quotes: > > > nested > > > nested This should not be a block quote: 2 > 1. And a following paragraph. ---- ====== Code Blocks ====== Code: ''' ---- (should be four hyphens) sub status { print "working"; } this code block is indented by one tab ''' And: ''' this code block is indented by two tabs These should not be escaped: \$ \\ \> \[ \{ ''' ---- ====== Lists ====== ===== Unordered ===== Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 Pluses tight: * Plus 1 * Plus 2 * Plus 3 Pluses loose: * Plus 1 * Plus 2 * Plus 3 Minuses tight: * Minus 1 * Minus 2 * Minus 3 Minuses loose: * Minus 1 * Minus 2 * Minus 3 ===== Ordered ===== Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. 2. Item 2. 3. Item 3. ===== Nested ===== * Tab * Tab * Tab Here’s another: 1. First 2. Second: * Fee * Fie * Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: * Fee * Fie * Foe 3. Third ===== Tabs and spaces ===== * this is a list item indented with tabs * this is a list item indented with spaces * this is an example list item indented with tabs * this is an example list item indented with spaces ===== Fancy list markers ===== 1. begins with 2 2. and now 3 with a continuation 1. sublist with roman numerals, starting with 4 2. more items 1. a subsublist 2. a subsublist Nesting: 1. Upper Alpha 1. Upper Roman. 1. Decimal start with 6 1. Lower alpha with paren Autonumbering: 1. Autonumber. 2. More. 1. Nested. Should not be a list item: M.A. 2007 B. Williams ---- ====== Definition Lists ====== Tight using spaces: * **apple** red fruit * **orange** orange fruit * **banana** yellow fruit Tight using tabs: * **apple** red fruit * **orange** orange fruit * **banana** yellow fruit Loose: * **apple** red fruit * **orange** orange fruit * **banana** yellow fruit Multiple blocks with italics: * **//apple//** red fruit contains seeds, crisp, pleasant to taste * **//orange//** orange fruit ''' { orange code block } ''' > orange block quote Multiple definitions, tight: * **apple** red fruitcomputer * **orange** orange fruitbank Multiple definitions, loose: * **apple** red fruit computer * **orange** orange fruit bank Blank line after term, indented marker, alternate markers: * **apple** red fruit computer * **orange** orange fruit 1. sublist 2. sublist ====== HTML Blocks ====== Simple block on one line: foo And nested without indentation: foo bar Interpreted markdown in a table: This is //emphasized// And this is **strong** Here’s a simple block: foo This should be a code block, though: '''
                foo
                ''' As should this: '''
                foo
                ''' Now, nested: foo This should just be an HTML comment: Multiline: Code block: ''' ''' Just plain comment, with trailing spaces on the line: Code: '''
                ''' Hr’s: ---- ====== Inline Markup ====== This is //emphasized//, and so //is this//. This is **strong**, and so **is this**. An //[[url|emphasized link]]//. **//This is strong and em.//** So is **//this//** word. **//This is strong and em.//** So is **//this//** word. This is code: ''>'', ''$'', ''\'', ''\$'', ''''. ~~This is //strikeout//.~~ Superscripts: a^{bc}d a^{//hello//} a^{hello there}. Subscripts: H_{2}O, H_{23}O, H_{many of them}O. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. ---- ====== Smart quotes, ellipses, dashes ====== “Hello,” said the spider. “‘Shelob’ is my name.” ‘A’, ‘B’, and ‘C’ are letters. ‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ ‘He said, “I want to go.”’ Were you alive in the 70’s? Here is some quoted ‘''code''’ and a “[[http://example.com/?foo=1&bar=2|quoted link]]”. Some dashes: one—two — three—four — five. Dashes between numbers: 5–7, 255–66, 1987–1999. Ellipses…and…and…. ---- ====== LaTeX ====== * * $2+2=4$ * $x \in y$ * $\alpha \wedge \omega$ * $223$ * $p$-Tree * Here’s some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ * Here’s one that has a line break in it: $\alpha + \omega \times x^2$. These shouldn’t be math: * To get the famous equation, write ''$e = mc^2$''. * $22,000 is a //lot// of money. So is $34,000. (It worked if “lot” is emphasized.) * Shoes ($20) and socks ($5). * Escaped ''$'': $73 //this should be emphasized// 23$. Here’s a LaTeX table: ---- ====== Special Characters ====== Here is some unicode: * I hat: Î * o umlaut: ö * section: § * set membership: ∈ * copyright: © AT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Backslash: \ Backtick: ` Asterisk: * Underscore: _ Left brace: { Right brace: } Left bracket: [ Right bracket: ] Left paren: ( Right paren: ) Greater-than: > Hash: # Period: . Bang: ! Plus: + Minus: - ---- ====== Links ====== ===== Explicit ===== Just a [[url/|URL]]. [[url/|URL and title]]. [[url/|URL and title]]. [[url/|URL and title]]. [[url/|URL and title]] [[url/|URL and title]] [[url/with_underscore|with_underscore]] [[mailto:nobody@nowhere.net|Email link]] [[|Empty]]. ===== Reference ===== Foo [[url/|bar]]. With [[url/|embedded [brackets]]]. [[url/|b]] by itself should be a link. Indented [[url|once]]. Indented [[url|twice]]. Indented [[url|thrice]]. This should [not][] be a link. ''' [not]: /url ''' Foo [[url/|bar]]. Foo [[url/|biz]]. ===== With ampersands ===== Here’s a [[http://example.com/?foo=1&bar=2|link with an ampersand in the URL]]. Here’s a link with an amersand in the link text: [[http://att.com/|AT&T]]. Here’s an [[script?foo=1&bar=2|inline link]]. Here’s an [[script?foo=1&bar=2|inline link in pointy braces]]. ===== Autolinks ===== With an ampersand: http://example.com/?foo=1&bar=2 * In a list? * http://example.com/ * It should. An e-mail address: > Blockquoted: http://example.com/ Auto-links should not occur here: '''' ''' or here: ''' ---- ====== Images ====== From “Voyage dans la Lune” by Georges Melies (1902): {{lalune.jpg|Voyage dans la Lune}} lalune Here is a movie {{movie.jpg|movie}} icon. ---- ====== Footnotes ====== Here is a footnote reference, **{Note:** Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.**}** and another. **{Note:** Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). ''' { } ''' If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.**}** This should //not// be a footnote reference, because it contains a space.[^my note] Here is an inline note. **{Note:** This is //easier// to type. Inline notes may contain [[http://google.com|links]] and '']'' verbatim characters, as well as [bracketed text].**}** > Notes can go in quotes. **{Note:** In quote.**}** 1. And in list items. **{Note:** In list.**}** This paragraph should not be part of the note, as it is not indented. ================================================ FILE: test/writers-lang-and-dir.context ================================================ % Enable hyperlinks \setupinteraction [state=start, style=, color=, contrastcolor=] \setupurl[style=] % make chapter, section bookmarks visible when opening document \placebookmarks[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][chapter, section] \setupinteractionscreen[option={bookmark,title}] \setuppagenumbering[location={footer,middle}] \setupstructure[state=start,method=auto] % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] \definefontfeature[default:tnum][default][tnum=yes, pnum=no] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] \setupbodyfontenvironment[default][em=italic] % use italic as em, not slanted \definefallbackfamily[mainface][rm][CMU Serif][preset=range:greek, force=yes] \definefontfamily[mainface][rm][Latin Modern Roman] \definefontfamily[mainface][mm][Latin Modern Math] \definefontfamily[mainface][ss][Latin Modern Sans] \definefontfamily[mainface][tt][Latin Modern Typewriter][features=none] \setupbodyfont[mainface] \setupwhitespace[medium] \setuphead[chapter] [style=\tfd\setupinterlinespace,header=empty] \setuphead[section] [style=\tfc\setupinterlinespace] \setuphead[subsection] [style=\tfb\setupinterlinespace] \setuphead[subsubsection] [style=\bf] \setuphead[subsubsubsection] [style=\sc] \setuphead[subsubsubsubsection][style=\it] \definesectionlevels [default] [section, subsection, subsubsection, subsubsubsection, subsubsubsubsection] \setuphead[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][number=no] \definedescription [description] [headstyle=bold, style=normal, location=hanging, width=broad, margin=1cm, alternative=hanging] \setupitemize[autointro] % prevent orphan list intro \setupitemize[indentnext=no] \defineitemgroup[enumerate] \setupenumerate[each][fit][itemalign=left,distance=.5em,style={\feature[+][default:tnum]}] \setupfloat[figure][default={here,nonumber}] \setupfloat[table][default={here,nonumber}] \setupxtable[frame=off] \setupxtable[head][topframe=on] \setupxtable[body][] \setupxtable[foot][] \setupxtable[lastrow][bottomframe=on] \starttext \startsectionlevel[title={Empty Divs and Spans},reference={empty-divs-and-spans}] Some text and div contents and more text. Next paragraph with a span and a word-thatincludesaspanright? \stopsectionlevel \startsectionlevel[title={Directionality},reference={directionality}] Some text and \startalignment[righttoleft] rtl div contents \stopalignment and more text. \startalignment[lefttoright] and a ltr div. with a {\righttoleft rtl span}. \stopalignment Next paragraph with a {\righttoleft rtl span} and a word-that-includesa{\lefttoright ltrspan}right? \stopsectionlevel \startsectionlevel[title={Languages},reference={languages}] Some text and \start\language[de] German div contents \stop and more text. Next paragraph with a {\language[en-gb]British span} and a word-that-includesa{\language[de-ch]Swiss German span}right? Some {\language[es]Spanish text}. \stopsectionlevel \startsectionlevel[title={Combined},reference={combined}] Some text and \start\language[fr] \startalignment[righttoleft] French rtl div contents \stopalignment \stop and more text. Next paragraph with a {\language[en-gb]{\lefttoright British ltr span}} and a word-that-includesa{\language[de-ch]{\lefttoright Swiss German ltr span}}right? \stopsectionlevel \stoptext ================================================ FILE: test/writers-lang-and-dir.latex ================================================ % Options for packages loaded elsewhere \PassOptionsToPackage{unicode}{hyperref} \PassOptionsToPackage{hyphens}{url} \documentclass[ ngerman, british, nswissgerman, spanish, french, english, ]{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}} \ifPDFTeX \TeXXeTstate=1 \newcommand{\RL}[1]{\beginR #1\endR} \newcommand{\LR}[1]{\beginL #1\endL} \newenvironment{RTL}{\beginR}{\endR} \newenvironment{LTR}{\beginL}{\endL} \fi \ifluatex \newcommand{\RL}[1]{\bgroup\textdir TRT#1\egroup} \newcommand{\LR}[1]{\bgroup\textdir TLT#1\egroup} \newenvironment{RTL}{\textdir TRT\pardir TRT\bodydir TRT}{} \newenvironment{LTR}{\textdir TLT\pardir TLT\bodydir TLT}{} \fi \usepackage{bookmark} \IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available \urlstyle{same} \hypersetup{ pdflang={en}, hidelinks, pdfcreator={LaTeX via pandoc}} \author{} \date{} \begin{document} \section{Empty Divs and Spans}\label{empty-divs-and-spans} Some text and div contents and more text. Next paragraph with a {span} and a word-thatincludesa{span}right? \section{Directionality}\label{directionality} Some text and \begin{RTL} rtl div contents \end{RTL} and more text. \begin{LTR} and a ltr div. with a \RL{rtl span}. \end{LTR} Next paragraph with a \RL{rtl span} and a word-that-includesa\LR{ltrspan}right? \section{Languages}\label{languages} Some text and \begin{otherlanguage}{ngerman} German div contents \end{otherlanguage} and more text. Next paragraph with a \foreignlanguage{british}{British span} and a word-that-includesa\foreignlanguage{nswissgerman}{Swiss German span}right? Some \foreignlanguage{spanish}{Spanish text}. \section{Combined}\label{combined} Some text and \begin{RTL} \begin{otherlanguage}{french} French rtl div contents \end{otherlanguage} \end{RTL} and more text. Next paragraph with a \LR{\foreignlanguage{british}{British ltr span}} and a word-that-includesa\LR{\foreignlanguage{nswissgerman}{Swiss German ltr span}}right? \end{document} ================================================ FILE: test/writers-lang-and-dir.native ================================================ Pandoc (Meta {unMeta = fromList []}) [Header 1 ("empty-divs-and-spans",[],[]) [Str "Empty",Space,Str "Divs",Space,Str "and",Space,Str "Spans"] ,Plain [Str "Some",Space,Str "text",Space,Str "and"] ,Div ("",[],[]) [Para [Str "div",Space,Str "contents"]] ,Para [Str "and",Space,Str "more",Space,Str "text."] ,Para [Str "Next",Space,Str "paragraph",Space,Str "with",Space,Str "a",Space,Span ("",[],[]) [Str "span"],Space,Str "and",Space,Str "a",Space,Str "word-thatincludesa",Span ("",[],[]) [Str "span"],Str "right?"] ,Header 1 ("directionality",[],[]) [Str "Directionality"] ,Plain [Str "Some",Space,Str "text",Space,Str "and"] ,Div ("",[],[("dir","rtl")]) [Para [Str "rtl",Space,Str "div",Space,Str "contents"]] ,Para [Str "and",Space,Str "more",Space,Str "text."] ,Div ("",[],[("dir","ltr")]) [Para [Str "and",Space,Str "a",Space,Str "ltr",Space,Str "div.",Space,Str "with",Space,Str "a",Space,Span ("",[],[("dir","rtl")]) [Str "rtl",Space,Str "span"],Str "."]] ,Para [Str "Next",Space,Str "paragraph",Space,Str "with",Space,Str "a",Space,Span ("",[],[("dir","rtl")]) [Str "rtl",Space,Str "span"],Space,Str "and",Space,Str "a",Space,Str "word-that-includesa",Span ("",[],[("dir","ltr")]) [Str "ltrspan"],Str "right?"] ,Header 1 ("languages",[],[]) [Str "Languages"] ,Plain [Str "Some",Space,Str "text",Space,Str "and"] ,Div ("",[],[("lang","de")]) [Para [Str "German",Space,Str "div",Space,Str "contents"]] ,Para [Str "and",Space,Str "more",Space,Str "text."] ,Para [Str "Next",Space,Str "paragraph",Space,Str "with",Space,Str "a",Space,Span ("",[],[("lang","en-GB")]) [Str "British",Space,Str "span"],Space,Str "and",Space,Str "a",Space,Str "word-that-includesa",Span ("",[],[("lang","de-CH")]) [Str "Swiss",Space,Str "German",Space,Str "span"],Str "right?"] ,Para [Str "Some",Space,Span ("",[],[("lang","es")]) [Str "Spanish",Space,Str "text"],Str "."] ,Header 1 ("combined",[],[]) [Str "Combined"] ,Plain [Str "Some",Space,Str "text",Space,Str "and"] ,Div ("",[],[("lang","fr"),("dir","rtl")]) [Para [Str "French",Space,Str "rtl",Space,Str "div",Space,Str "contents"]] ,Para [Str "and",Space,Str "more",Space,Str "text."] ,Para [Str "Next",Space,Str "paragraph",Space,Str "with",Space,Str "a",Space,Span ("",[],[("lang","en-GB"),("dir","ltr")]) [Str "British",Space,Str "ltr",Space,Str "span"],Space,Str "and",Space,Str "a",Space,Str "word-that-includesa",Span ("",[],[("lang","de-CH"),("dir","ltr")]) [Str "Swiss",Space,Str "German",Space,Str "ltr",Space,Str "span"],Str "right?"]] ================================================ FILE: test/xlsx-reader/basic.native ================================================ Pandoc Meta { unMeta = fromList [] } [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Main" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "Person" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "Age" ] ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Strong [ Str "Location" ] ] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Anton" , Space , Str "Antich" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "23.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Switzerland" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "James" , Space , Str "Bond" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "35.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Moscow" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Just" , Space , Str "a" , Space , Str "random" , Space , Str "cell" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] ] ] (TableFoot ( "" , [] , [] ) []) , Header 2 ( "sheet-2" , [] , [] ) [ Str "Secondary" ] , Table ( "" , [] , [] ) (Caption Nothing []) [ ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) , ( AlignDefault , ColWidthDefault ) ] (TableHead ( "" , [] , [] ) [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Sum" , Space , Str "of" , Space , Str "Age" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Column" , Space , Str "Labels" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] ]) [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [ Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Row" , Space , Str "Labels" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Moscow" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Switzerland" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "(blank)" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Grand" , Space , Str "Total" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Anton" , Space , Str "Antich" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "23.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "23.0" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "James" , Space , Str "Bond" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "35.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "35.0" ] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "(blank)" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] ] , Row ( "" , [] , [] ) [ Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "Grand" , Space , Str "Total" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "35.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "23.0" ] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [] ] , Cell ( "" , [] , [] ) AlignDefault (RowSpan 1) (ColSpan 1) [ Plain [ Str "58.0" ] ] ] ] ] (TableFoot ( "" , [] , [] ) []) ] ================================================ FILE: tools/.editorconfig ================================================ [*.py] indent_size = 4 ================================================ FILE: tools/build-and-upload-api-docs.sh ================================================ #!/bin/sh set -e dir=$(mktemp -d dist-docs.XXXXXX) trap 'rm -r "$dir"' EXIT cabal v2-haddock --builddir="$dir" --haddock-for-hackage cabal upload -d --publish $dir/*-docs.tar.gz ================================================ FILE: tools/build-arm.sh ================================================ #!/bin/sh #old version: #IMAGE_ID=ami-0fa8979d18f69948b IMAGE_ID=ami-0cd6b53a434812702 INSTANCE_TYPE=t4g.2xlarge KEY_NAME=debian-arm-us-east-2 SECURITY_GROUP_ID=sg-086ffbadc286c5c00 ARTIFACTS="${ARTIFACTS:-build-artifacts-$(date +%s)}" STARTTIME=$(date +%H:%M) # Spin up an ARM build machine using aws cli, build pandoc, and # download the artifact. # # We need to use us-east-2; since my us-west-1 has EC2-classic. # docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-classic-platform.html aws configure set default.region us-east-2 echo "Creating instance..." aws ec2 run-instances --image-id "$IMAGE_ID" --count 1 --instance-type "$INSTANCE_TYPE" --block-device-mapping 'DeviceName=/dev/xvda,Ebs={VolumeSize=16}' --key-name "$KEY_NAME" --security-group-ids "$SECURITY_GROUP_ID" > ec2.json jq < ec2.json # Now get the public IP address. INSTANCEID=$(jq '.Instances[0].InstanceId' ec2.json | sed -e 's/"//g') IPADDR=$(aws ec2 describe-instances --instance-ids="$INSTANCEID" --query 'Reservations[0].Instances[0].PublicIpAddress' | sed -e 's/"//g') echo "IP address is $IPADDR" clean_up() { echo "Terminating the instance in 20 seconds..." echo "Ctrl-C to preserve it." sleep 20 && aws ec2 terminate-instances --instance-ids "$INSTANCEID" } trap clean_up EXIT echo "Waiting for instance to start up..." STATUS=none while [ "$STATUS" != "running" ] do sleep 20 STATUS=$(aws ec2 describe-instance-status --instance-id "$INSTANCEID" | jq '.InstanceStatuses[0].InstanceState.Name' | sed -e 's/"//g') echo "...$STATUS" done # At this point you can connect via SSH, or run this script: # $ ssh -i ~/.ssh/debian-arm-us-east-2.pem admin@$IPADDR ssh -o "StrictHostKeyChecking=no" -i "~/.ssh/$KEY_NAME.pem" admin@$IPADDR uname -a SSH="ssh -i ~/.ssh/$KEY_NAME.pem admin@$IPADDR" echo "Provisioning..." $SSH </dev/null" && break done # Retrieve the artifacts echo "Successful build. Retrieving artifacts..." scp -i "$HOME/.ssh/$KEY_NAME.pem" -r "admin@$IPADDR:src/pandoc/linux/artifacts" "$ARTIFACTS" echo "Artifacts saved in $ARTIFACTS" ls "$ARTIFACTS" ENDTIME=$(date +%H:%M) echo "Started: $STARTTIME" echo "Finished: $ENDTIME" exit 0 ================================================ FILE: tools/changelog-helper.sh ================================================ #!/bin/sh # generate preliminary list of changes since changelog # was last modified lastmod=`git log -n1 --format=oneline changelog.md | awk '{print $1;}'` files=`git ls-tree -r master --name-only | grep --invert-match '^test\/\|^data\/docx\/\|^data\/odt\/\|^data\/pptx\/\|^citeproc\/\|^data\/translations\/'` for x in $files do echo $x 1>&2 commits=`git log -n1 $lastmod..HEAD $x` if [ ! -z "$commits" ] then if echo $x | grep -q "src\/.*\.hs" then file=`echo $x | sed -e 's/src\///' | sed -e 's/\//\./g' | sed -e 's/\.hs$//'` else file=$x fi echo " * $file" git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" "$x" fi done echo "test/" 1>&2 git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" test/ git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" data/docx/ git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" data/odt/ git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" data/pptx/ git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" data/translations/ git log --pretty=format:'%n%w(78,4,6)+ %s (%aN)%n%n%w(78,6,6)%b%n' "$lastmod..HEAD" citeproc/ ================================================ FILE: tools/changes_template.html ================================================
                Click to expand changelog $body$
                ================================================ FILE: tools/cliptree.gvpr ================================================ /* Construct subgraph reachable from node ARGV[0] by forward edges */ BEG_G { node_t r = node($,ARGV[0]); $tvroot = r; $tvtype = TV_fwd; //print ("// Staring node: ", r.name); } N{ print ("// subgraph node: ", $.name); $tvroot=NULL; subnode($T,$); } E{ print ("// subgraph edge: ", $.name); subedge($T,$); } ================================================ FILE: tools/diff-zip.sh ================================================ #!/bin/sh # This script allows you to compare two epub, odt, or docx # containers, ignoring insignificant formatting differences # in the XML contents. UNAME=$(uname) if [ "$UNAME" = "Darwin" ]; then FIND="find -E" else FIND="find -regextype posix-extended" fi f1="$1" f2="$2" test -f "$f1" -a -f "$f2" || { echo "Usage: diff-zip firstfile secondfile" && exit 1 } WORKDIR=$(mktemp -d -t diff-zip.XXX) trap "{ rm -r $WORKDIR; }" EXIT unzip -q -d "$WORKDIR/a" "$f1" unzip -q -d "$WORKDIR/b" "$f2" cd "$WORKDIR" mkdir tidy for x in a b; do cp -r $x tidy/ $FIND $x -iregex '.*\.(xhtml|xml|rdf|rels)' -exec sh -c 'mkdir -p "$(dirname tidy/$1)" && tidy -q -xml -utf8 -i "$1" > "tidy/$1"' _ {} \; done cd tidy mkdir c cp -r a/* c/ cp -r b/* c/ find c -type f -exec sh -c 'echo -e "\033[1m=== ${1#*/} ===\033[0m" ; diff -u "a/${1#*/}" "b/${1#*/}" 2>&1' _ {} \; ================================================ FILE: tools/extract-changes.lua ================================================ -- Extract changes from latest version in changelog. function Pandoc(el) local newblocks = {} i = 1 while i <= #el.blocks and not (el.blocks[i].t == "Header" and el.blocks[i].level == 2) do i = i+1 end while i <= #el.blocks do i = i+1 if el.blocks[i].t == "Header" and el.blocks[i].level == 2 then break end table.insert(newblocks, el.blocks[i]) end return pandoc.Pandoc(newblocks) end ================================================ FILE: tools/github-upload.sh ================================================ #!/bin/bash VERSION=$1 FULLNAME=pandoc-$VERSION read -s -p "Token (https://github.com/settings/applications): " TOKEN curl -H "Authorization: token $TOKEN" \ -H "Accept: application/vnd.github.manifold-preview" \ -H "Content-Type: application/x-apple-diskimage" \ --data-binary @$FULLNAME.pkg.zip \ "https://uploads.github.com/repos/jgm/pandoc/releases/$VERSION/assets?name=$FULLNAME.pkg.zip" curl -H "Authorization: token $TOKEN" \ -H "Accept: application/vnd.github.manifold-preview" \ -H "Content-Type: application/x-msi" \ --data-binary @$FULLNAME.msi \ "https://uploads.github.com/repos/jgm/pandoc/releases/$VERSION/assets?name=$FULLNAME.msi" ================================================ FILE: tools/latex-package-dependencies.lua ================================================ -- Print latex packages needed by pandoc's default latex template. -- Usage: pandoc lua tools/latex-package-dependencies.lua local templ = pandoc.template.default("latex") local packages = {} templ:gsub("\\usepackage *%b[] *%{([^}]*)%}", function(capt) capt:gsub("([^,]+)", function (pkg) if not pkg:find("%$") then packages[pkg] = true end end) end) templ:gsub("\\usepackage *%{([^}]*)%}", function(capt) capt:gsub("([^,]+)", function (pkg) if not pkg:find("%$") then packages[pkg] = true end end) end) for pkg,_ in pairs(packages) do print(pkg) end ================================================ FILE: tools/moduledeps.lua ================================================ -- Construct module dependency tree from modules.csv local dependencies = {} local csv = io.open("modules.csv") local lines = csv:lines() local mode = arg[1] local roots = {} local i = 2 while i <= #arg do roots[i - 1] = arg[i] i = i + 1 end if not (mode == "tree" or mode == "transitive") then io.write("Usage: lua moduledeps (tree|transitive) modulename\n") io.exit(1) end if #roots == 0 then io.write("Usage: lua moduledeps modulename+\n") io.exit(1) end for line in lines do local _,_,mod,dep = string.find(line, "([^,]+),([^,]+)") if not dependencies[mod] then dependencies[mod] = {} end if not dependencies[dep] then dependencies[dep] = {} end dependencies[mod][dep] = true end local transitive = {} function prind(ind, s) io.write(string.rep(" ",ind) .. s .. "\n") end function add_transitive_deps(mod) if transitive[mod] then return end transitive[mod] = {} for dep,_ in pairs(dependencies[mod]) do transitive[mod][dep] = true add_transitive_deps(dep) for indirectdep,_ in pairs(transitive[dep]) do transitive[mod][indirectdep] = true end end end function print_direct_deps(mod, ind) ind = ind or 0 prind(ind, mod) for dep,_ in pairs(dependencies[mod]) do print_direct_deps(dep, ind + 2) end end local seen = {} for _,root in ipairs(roots) do if mode == "transitive" then add_transitive_deps(root) for dep,_ in pairs(transitive[root]) do if not seen[dep] then prind(2,dep) seen[dep] = true end end elseif mode == "tree" then print_direct_deps(root, 0) end end ================================================ FILE: tools/pandoc-template-mode.el ================================================ ;;; pandoc-template-mode.el --- Pandoc-Template major mode ;; Copyright (C) 2017 ;; Author: Václav Haisman ;; Keywords: extensions ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; ;;; Code: (defvar pandoc-template-font-lock-keywords '(("\\(\\$--.*\\)$" (1 font-lock-comment-face)) ("\\(\\$\\)\\(if\\|for\\)(\\([^)]+\\))\\(\\$\\)" (1 font-lock-preprocessor-face) (2 font-lock-keyword-face) (3 font-lock-variable-name-face) (4 font-lock-preprocessor-face)) ("\\(\\$\\)\\(endif\\|endfor\\|else\\)\\(\\$\\)" (1 font-lock-preprocessor-face) (2 font-lock-keyword-face) (3 font-lock-preprocessor-face)) ("\\(\\$\\)\\(sep\\)\\(\\$\\)" (1 font-lock-preprocessor-face) (2 font-lock-builtin-face) (3 font-lock-preprocessor-face)) ("\\(\\$\\)\\([^$]+\\)\\(\\$\\)" (1 font-lock-preprocessor-face) (2 font-lock-variable-name-face) (3 font-lock-preprocessor-face)) ) "Keyword highlighting specification for `pandoc-template-mode'.") ;;;###autoload (define-derived-mode pandoc-template-mode fundamental-mode "Pandoc-Template" "A major mode for editing Pandoc-Template files." :syntax-table text-mode-syntax-table (setq-local font-lock-defaults '(pandoc-template-font-lock-keywords)) (setq-local comment-start "$--") (setq-local comment-start-skip "\\$--[ \t]*") (setq-local comment-end "") (setq-local comment-end-skip "[ \t]*$")) (provide 'pandoc-template-mode) ;;; pandoc-template.el ends here ================================================ FILE: tools/pandoc-xml.dtd ================================================ ================================================ FILE: tools/pandoc-xml.rnc ================================================ # A RELAX NG schema for Pandoc XML format. # Copyright : Copyright (C) 2025- Massimiliano Farinella # License : GNU GPL, version 2 or above # Maintainer : Massimiliano Farinella # # This is a RELAX NG schema for the XML representation of Pandoc AST. # It's an equivalent of native and JSON formats, but modeled as XML. # You can use this schema to validate Pandoc XML documents. # It's translated from pandoc-xml.dtd with the "Trang" software by James Clark, # and adjusted manually to add some constraints: # - elements with Attr can have arbitrary attributes (this is not possible with a DTD) # - Header's "level", OrderedList's "start" and Cell's "rowspan" and "colspan" attributes # must be a positive integer and are equal to 1 if not specified # - column widths in ColSpec must be between 0 and 1 (inclusive, with 0=ColWidthDefault) # - the "count" attribute in the "" element must be positive and equal to 1 if not specified namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0" Pandoc = element Pandoc { attlist_Pandoc, meta, blocks } attlist_Pandoc &= attribute api-version { text } block = Para | Plain | Header | Div | BlockQuote | HorizontalRule | BulletList | OrderedList | DefinitionList | Table | Figure | LineBlock | CodeBlock | RawBlock inline_element = Str | Space | Emph | Strong | Underline | Strikeout | Superscript | Subscript | SmallCaps | Quoted | Cite | Code | SoftBreak | LineBreak | Math | RawInline | Link | Image | Note | Span inline = text | inline_element attr = attribute id { xsd:ID }?, attribute class { text }?, attribute * { text }* metavalue = MetaMap | MetaList | MetaBool | MetaString | MetaInlines | MetaBlocks meta = element meta { attlist_meta, entry* } attlist_meta &= empty MetaMap = element MetaMap { attlist_MetaMap, entry* } attlist_MetaMap &= empty entry = element entry { attlist_entry, metavalue* } attlist_entry &= attribute key { text } MetaList = element MetaList { attlist_MetaList, metavalue* } attlist_MetaList &= empty MetaBool = element MetaBool { attlist_MetaBool, empty } attlist_MetaBool &= attribute value { "true" | "false" } MetaString = element MetaString { attlist_MetaString, text } attlist_MetaString &= empty MetaInlines = element MetaInlines { attlist_MetaInlines, inline* } attlist_MetaInlines &= empty MetaBlocks = element MetaBlocks { attlist_MetaBlocks, block* } attlist_MetaBlocks &= empty blocks = element blocks { attlist_blocks, block* } attlist_blocks &= empty Para = element Para { attlist_Para, inline* } attlist_Para &= empty Plain = element Plain { attlist_Plain, inline* } attlist_Plain &= empty Header = element Header { attlist_Header, inline* } attlist_Header &= [ a:defaultValue = "1" ] attribute level { xsd:positiveInteger }?, attr Div = element Div { attlist_Div, block* } attlist_Div &= attr BlockQuote = element BlockQuote { attlist_BlockQuote, block* } attlist_BlockQuote &= empty HorizontalRule = element HorizontalRule { attlist_HorizontalRule, empty } attlist_HorizontalRule &= empty BulletList = element BulletList { attlist_BulletList, item+ } attlist_BulletList &= empty OrderedList = element OrderedList { attlist_OrderedList, item+ } attlist_OrderedList &= [ a:defaultValue = "1" ] attribute start { xsd:positiveInteger }?, [ a:defaultValue = "DefaultStyle" ] attribute number-style { "DefaultStyle" | "Example" | "Decimal" | "LowerRoman" | "UpperRoman" | "LowerAlpha" | "UpperAlpha" }?, [ a:defaultValue = "DefaultDelim" ] attribute number-delim { "DefaultDelim" | "Period" | "OneParen" | "TwoParens" }? DefinitionList = element DefinitionList { attlist_DefinitionList, item+ } attlist_DefinitionList &= empty item = element item { attlist_item, (block* | (term, def+)) } attlist_item &= empty term = element term { attlist_term, inline* } attlist_term &= empty def = element def { attlist_def, block* } attlist_def &= empty Table = element Table { attlist_Table, Caption, colspecs, TableHead, TableBody+, TableFoot } attlist_Table &= attr Caption = element Caption { attlist_Caption, ShortCaption?, block* } attlist_Caption &= empty ShortCaption = element ShortCaption { attlist_ShortCaption, inline* } attlist_ShortCaption &= empty colspecs = element colspecs { attlist_colspecs, ColSpec+ } attlist_colspecs &= empty ColSpec = element ColSpec { attlist_ColSpec, empty } attlist_ColSpec &= [ a:defaultValue = "AlignDefault" ] attribute alignment { "AlignLeft" | "AlignRight" | "AlignCenter" | "AlignDefault" }?, [ a:defaultValue = "0" ] attribute col-width { xsd:double { minInclusive = "0" maxInclusive = "1" } }? TableHead = element TableHead { attlist_TableHead, Row* } attlist_TableHead &= attr TableFoot = element TableFoot { attlist_TableFoot, Row* } attlist_TableFoot &= attr TableBody = element TableBody { attlist_TableBody, header, body } attlist_TableBody &= [ a:defaultValue = "0" ] attribute row-head-columns { text }?, attr header = element header { attlist_header, Row* } attlist_header &= empty body = element body { attlist_body, Row* } attlist_body &= empty Row = element Row { attlist_Row, Cell* } attlist_Row &= attr Cell = element Cell { attlist_Cell, block* } attlist_Cell &= [ a:defaultValue = "AlignDefault" ] attribute alignment { "AlignLeft" | "AlignRight" | "AlignCenter" | "AlignDefault" }?, [ a:defaultValue = "1" ] attribute row-span { xsd:positiveInteger }?, [ a:defaultValue = "1" ] attribute col-span { xsd:positiveInteger }?, attr Figure = element Figure { attlist_Figure, Caption, block* } attlist_Figure &= attr LineBlock = element LineBlock { attlist_LineBlock, line+ } attlist_LineBlock &= empty line = element line { attlist_line, inline* } attlist_line &= empty CodeBlock = element CodeBlock { attlist_CodeBlock, text } attlist_CodeBlock &= attr RawBlock = element RawBlock { attlist_RawBlock, text } attlist_RawBlock &= attribute format { text } Space = element Space { attlist_Space, empty } attlist_Space &= [ a:defaultValue = "1" ] attribute count { xsd:positiveInteger }? Str = element Str { attlist_Str, empty } attlist_Str &= [ a:defaultValue = "" ] attribute content { text }? Emph = element Emph { attlist_Emph, inline* } attlist_Emph &= empty Strong = element Strong { attlist_Strong, inline* } attlist_Strong &= empty Underline = element Underline { attlist_Underline, inline* } attlist_Underline &= empty Strikeout = element Strikeout { attlist_Strikeout, inline* } attlist_Strikeout &= empty Superscript = element Superscript { attlist_Superscript, inline* } attlist_Superscript &= empty Subscript = element Subscript { attlist_Subscript, inline* } attlist_Subscript &= empty SmallCaps = element SmallCaps { attlist_SmallCaps, inline* } attlist_SmallCaps &= empty Span = element Span { attlist_Span, inline* } attlist_Span &= attr Quoted = element Quoted { attlist_Quoted, inline* } attlist_Quoted &= [ a:defaultValue = "DoubleQuote" ] attribute quote-type { "SingleQuote" | "DoubleQuote" }? Math = element Math { attlist_Math, text } attlist_Math &= [ a:defaultValue = "InlineMath" ] attribute math-type { "DisplayMath" | "InlineMath" }? RawInline = element RawInline { attlist_RawInline, text } attlist_RawInline &= attribute format { text } Cite = element Cite { attlist_Cite, (text | citations | inline_element)* } attlist_Cite &= empty citations = element citations { attlist_citations, Citation+ } attlist_citations &= empty Citation = element Citation { attlist_Citation, prefix?, suffix? } prefix = element prefix { attlist_prefix, inline* } attlist_prefix &= empty suffix = element suffix { attlist_suffix, inline* } attlist_suffix &= empty attlist_Citation &= attribute id { text }?, attribute note-num { text }?, [ a:defaultValue = "0" ] attribute hash { text }?, [ a:defaultValue = "AuthorInText" ] attribute mode { "AuthorInText" | "SuppressAuthor" | "NormalCitation" }? Code = element Code { attlist_Code, text } attlist_Code &= attr Image = element Image { attlist_Image, inline* } attlist_Image &= attribute title { text }?, attribute src { text }?, attr Link = element Link { attlist_Link, inline* } attlist_Link &= attribute title { text }?, attribute href { text }?, attr SoftBreak = element SoftBreak { attlist_SoftBreak, empty } attlist_SoftBreak &= empty LineBreak = element LineBreak { attlist_LineBreak, empty } attlist_LineBreak &= empty Note = element Note { attlist_Note, block* } attlist_Note &= empty start = Pandoc ================================================ FILE: tools/pandoc-xml.rng ================================================ true false DefaultStyle Example Decimal LowerRoman UpperRoman LowerAlpha UpperAlpha DefaultDelim Period OneParen TwoParens AlignLeft AlignRight AlignCenter AlignDefault 0 1 AlignLeft AlignRight AlignCenter AlignDefault SingleQuote DoubleQuote DisplayMath InlineMath AuthorInText SuppressAuthor NormalCitation ================================================ FILE: tools/pandoc-xml.xsd ================================================ ================================================ FILE: tools/parseTimings.pl ================================================ #!/usr/bin/env perl # Breaks down compilation time and memory usage by module. # To use this script, do # # stack clean # stack build --ghc-options='-dshow-passes' 2>output.txt # perl parseTimings.pl output.txt while (<>) { if (/!!! (.*) \[(.*)\]: finished in (.*) milliseconds, allocated (.*) megabytes/) { $phase = $1; $module = $2; $milliseconds = $3; $megabytes = $4; if (!$timings{$module}) { $timings{$module} = {'milliseconds' => 0, 'megabytes' => 0}; }; $timings{$module}{'milliseconds'} += $milliseconds; $timings{$module}{'megabytes'} += $megabytes; } } printf("%10s %10s %s\n", "Time (ms)", "Alloc (Mb)", "LOC", "Module"); for (keys %timings) { $path = $_; $path =~ s/\./\//g; $path = "src/$path.hs"; $loc = `wc -l $path 2>/dev/null`; if ($loc) { $loc =~ s/^\s*(\d+).*/\1/; printf("%10d %10d %6d %s\n", $timings{$_}{'milliseconds'}, $timings{$_}{'megabytes'}, $loc, $_); } } ================================================ FILE: tools/update-lua-module-docs.lua ================================================ --- Generate documentation for a pandoc Lua module. -- Copyright: © 2022-2024 Albert Krewinkel -- License: MIT -- -- This script can be used as either a custom reader, or as a standalone -- pandoc Lua script. In the latter case, it expects a module name as -- argument. local ipairs, next, pairs, print, tostring, type, warn = ipairs, next, pairs, print, tostring, type, warn local string, table = string, table local pandoc = require 'pandoc' local utils = require 'pandoc.utils' local read, write = pandoc.read, pandoc.write local Pandoc = pandoc.Pandoc local Blocks, Inlines, List = pandoc.Blocks, pandoc.Inlines, pandoc.List local Code, Emph, Link, Span, Str = pandoc.Code, pandoc.Emph, pandoc.Link, pandoc.Span, pandoc.Str local BulletList, DefinitionList, Header, Para, Plain, RawBlock = pandoc.BulletList, pandoc.DefinitionList, pandoc.Header, pandoc.Para, pandoc.Plain, pandoc.RawBlock local registry = debug.getregistry() --- Retrieves the documentation object for the given value. local function documentation (value) local docobj = registry['HsLua docs'][value] if type(docobj) == 'userdata' then -- Get the table representation by calling the object return docobj() else return docobj end end --- Table containing all known modules local modules = {} for k, v in pairs(pandoc) do local docs = documentation(v) if docs and docs.fields then modules[k] = v end end modules['pandoc'] = pandoc --- Creates an iterator triple that will return values sorted by key names. -- @param tbl table with string keys -- @return iterator triple to be used in a `for` loop. local function sorted (tbl) local keys = {} for key in pairs(tbl) do table.insert(keys, key) end table.sort(keys) local i = 0 local iter = function (_state, ctrl) if i > 0 and ctrl == nil then return nil else i = i + 1 return keys[i], tbl[keys[i]] end end return iter, nil, nil end --- Parses text to a list of Block values. -- @param txt string value -- @return {Block,...} local function read_blocks (txt) return read(txt, 'commonmark+smart+wikilinks_title_before_pipe').blocks end --- Parses text to a list of Inline values. -- @param txt string value -- @return {Inline,...} local function read_inlines (txt) return utils.blocks_to_inlines(read_blocks(txt)) end --- Map of all known data types to a heading ID. Used to create hyperlinks. local known_types = { Alignment = 'type-alignment', Attr = 'type-attr', AttributeList = 'type-attributes', Block = 'type-block', Blocks = 'type-blocks', Caption = 'type-caption', Cell = 'type-cell', ColSpec = 'type-colspec', Doc = 'type-doc', ChunkedDoc = 'type-chunkeddoc', Figure = 'type-figure', Inline = 'type-inline', Inlines = 'type-inlines', ListAttributes = 'type-listattributes', Meta = 'type-meta', MetaValue = 'type-metavalue', Pandoc = 'type-pandoc', ReaderOptions = 'type-readeroptions', Row = 'type-row', SimpleTable = 'type-simpletable', Source = 'type-pandoc.types.Source', Sources = 'pandoc.types.Sources', Span = 'type-span', Str = 'type-str', Table = 'type-table', TableBody = 'type-tablebody', TableHead = 'type-tablehead', TableFoot = 'type-tablefoot', Template = 'type-template', WriterOptions = 'type-writeroptions', Version = 'type-version', } local function render_typespec (typespec) if typespec.basic then return Inlines(Span(typespec.basic, {class="builtin-lua-type"})) elseif typespec.named then return Inlines(Span(typespec.named, {['unknown-type'] = typespec.named})) elseif typespec.sequence then local typeinlns = render_typespec(typespec.sequence) return Inlines({'{'} .. typeinlns .. {',...}'}) elseif typespec.sum then local result = Inlines{} for i, tspec in pairs(List.map(typespec.sum, render_typespec)) do if i >= 2 then result:insert(Str '|') end result:extend(tspec) end return result elseif typespec.any then return Inlines('any') end warn("falling back to string representation for type " .. tostring(typespec)) return Inlines(tostring(typespec)) end --- Render a type marker. -- E.g., the type of a parameter. local function type_to_inlines (typeobj) return Inlines ' (' .. render_typespec(typeobj) .. Inlines ')' end --- Append inlines to the last block if possible, or append a new Plain. local function append_inlines (blocks, inlines) local last = blocks[#blocks] if last and (last.t == 'Plain' or last.t == 'Para') then blocks[#blocks] = Plain(last.content .. inlines) else table.insert(blocks, Plain(inlines)) end return blocks end --- Returns a list of function arguments. -- -- The parameters are comma-separated; optional arguments are put in brackets. -- -- @param parameters list of function parameters -- @return string local function argslist (parameters) local required = List{} local optional = List{} for _, param in ipairs(parameters) do if param.optional then optional:insert(param.name) else required:extend(optional) required:insert(param.name) optional = List{} end end if #optional == 0 then return table.concat(required, ', ') end return table.concat(required, ', ') .. (#required > 0 and '[, ' or '[') .. table.concat(optional, '[, ') .. string.rep(']', #optional) end --- Generates rendered documentation for the return values of a function. -- @param results list of function results -- @return {Block,...} local function render_results (results) if type(results) == 'string' then return read_blocks(results) elseif type(results) == 'table' then return {BulletList( List(results):map( function (res) return append_inlines( read_blocks(res.description), type_to_inlines(res.type) ) end ) )} else return Blocks{} end end --- Renders function documentation. -- -- @param doc documentation object -- @param level the current heading level in the document -- @param modulename name of the module that contains this function -- @return Documentation rendered as list of Blocks local function render_function (doc, level, modulename) local name = doc.name:match('[^%.]*$') level = level or 1 local args = argslist(doc.parameters) local paramlist = DefinitionList( List(doc.parameters):map( function (p) return { Inlines{Code(p.name)}, {append_inlines( read_blocks(p.description), type_to_inlines(p.type) )} } end ) ) return Blocks{ Header(level, name, {doc.name}), Plain{Code(string.format('%s (%s)', name, args))}, } .. read_blocks(doc.description) .. List(#doc.parameters > 0 and {Para 'Parameters:'} or {}) .. List{paramlist} .. List(#doc.results > 0 and {Para 'Returns:'} or {}) .. render_results(doc.results) .. Blocks(doc.since and {Para{Emph{'Since: ' .. doc.since}}} or {}) end --- Renders documentation of a module field. -- -- @param field field documentation object -- @param level the current heading level in the document -- @param modulename name of the module that contains this function -- @return {Block,...} local function render_field (field, level, modulename) local name = field.name:match('[^.]*$') return Blocks{Header(level, name, {field.name})} .. {Plain(read_inlines(field.description) .. type_to_inlines(field.type))} end --- Renders documentation of a data type associated with a module. -- -- @param name data type name -- @param level the current heading level in the document -- @param modulename name of the module that contains this function -- @return {Block,...} local function render_type (name, level, modulename) -- FIXME: SPECIAL CASE -- Ignore Template type in `pandoc.template` module, as the automatic -- content doesn't describe it yet. if name == 'pandoc Template' then return {} end -- We just want the modulename prefix, as the type names should already -- contain the module name to some extend. local nameprefix = modulename and modulename:match('(.*)%.[a-z]*$') or 'pandoc' local id = nameprefix .. '.' .. name local metatable = registry[name] local properties = Blocks{} if next(metatable.docs.properties) then local propattr = {'type-' .. id .. '-properties'} local attr properties:insert(Header(level + 1, "Properties", propattr)) for propname, prop in sorted(metatable.docs.properties) do attr = {'type-' .. nameprefix .. '.' .. name .. '.' .. propname} properties:insert(Header(level + 2, propname, attr)) properties:insert( Plain(read_inlines(prop.description) .. type_to_inlines(prop.type)) ) end end local methods = Blocks{} if next(metatable.methods) then local attr = {'type-' .. id .. '-methods'} methods:insert(Header(level + 1, "Methods", attr)) for _, method in sorted(metatable.methods) do -- attr = {'type-' .. modulename .. '.' .. name .. '.' .. name} -- methods:insert(Header(level + 2, name, attr)) methods:extend(render_function(documentation(method), level+2, id)) end end local type_description = properties .. methods if name == 'Doc' then type_description = Blocks{ Para {"See the description ", Link("above", "#type-doc"), "."} } end local header_id = 'type-' .. nameprefix .. '.' .. name known_types[name] = known_types[name] or header_id return {Header(level, name, {header_id})} .. type_description end --- Renders module documentation. -- -- @param doc documentation object of the module -- @return {Block,...} local function render_module (doc) local fields = Blocks{} if #doc.fields > 0 then fields:insert(Header(2, 'Fields', {doc.name .. '-' .. 'fields'})) for _, fld in ipairs(doc.fields) do fields:extend(render_field(fld, 3, doc.name)) end end local functions = Blocks{} if #doc.functions > 0 then functions:insert(Header(2, 'Functions', {doc.name .. '-' .. 'functions'})) for _, fun in ipairs(doc.functions) do functions:extend(render_function(fun, 3, doc.name)) end end local typedocs = Blocks{} local types = type(doc.types) == 'function' and doc.types() or {} for _, ty in ipairs(types) do typedocs:extend(render_type(ty, 3, doc.name)) end if #typedocs > 0 then typedocs:insert(1, Header(2, 'Types', {doc.name .. '-' .. 'types'})) end return Blocks{ Header(1, Inlines('Module ' .. doc.name), {'module-' .. doc.name})} .. read_blocks(doc.description) .. fields .. functions .. typedocs end --- Renders the documentation of the main "pandoc" module. -- FIXME: This function shouldn't exist. local function render_main_pandoc_module (doc) local constants_section = Blocks{Header(2, "Constants")} local fields = List{} for _, field in ipairs(doc.fields) do if tostring(field.type) == 'string' then constants_section:extend(render_field(field, 2, "pandoc")) elseif field.name:match '^pandoc%.[A-Z]' then -- Ignore (these are the `Block` and `Inline` tables) else fields:insert(field) end end local stop_rendering = false local functions = List{} for _, fn in ipairs(doc.functions) do if stop_rendering then pandoc.log.info("Not rendered in module pandoc: " .. fn.name .. '\n') else functions:insert(fn) end if fn.name == 'pandoc.SimpleTable' then stop_rendering = true end end doc.fields = fields doc.functions = functions -- product types don't render well, so we document those manually doc.types = {} return render_module(doc) end local autogen_start = '\n' local autogen_end = '\n' local reflinks_marker = '\n' --- Create a raw Markdown block. -- @param str Markdown text -- @return Block local rawmd = function (str) return RawBlock('markdown', str) end --- Generate documentation for content marked for auto-generation. -- Skips all other contents and includes it as raw Markdown. local function process_document (input, blocks, start) local mstart, mstop, module_name = input:find(autogen_start, start) if mstart and mstop and module_name then print('Generating docs for module ' .. module_name) blocks:insert(rawmd(input:sub(start, mstop))) local object = require(module_name) local docblocks = (object == pandoc) and render_main_pandoc_module(documentation(object)) or pandoc.utils.documentation(object, 'blocks') blocks:extend(docblocks) return process_document(input, blocks, input:find(autogen_end, mstop) or -1) else local reflinks_stop = select(2, input:find(reflinks_marker, start)) blocks:insert(rawmd(input:sub(start, reflinks_stop))) return blocks end end --- Custom reader function -- Processes all markers for auto-generated contents, ignores the rest. function Reader (inputs) local blocks = process_document(tostring(inputs), Blocks{}, 1) blocks = blocks:walk { Link = function (link) if link.classes == pandoc.List{'documented-type'} or link.classes == pandoc.List{'wikilink'} then link.classes = {} local ident = link.target:gsub('^#', '') if known_types[ident] then link.target = '#' .. known_types[ident] else link.target = '#' .. ident warn('Unknown type: ' .. ident) end return link end end, Span = function (span) local unknown_type = span.attributes['unknown-type'] if unknown_type and known_types[unknown_type] then return Link(span.content, '#' .. known_types[unknown_type]) elseif span.classes:includes 'builtin-lua-type' then return span.content -- unwrap end end, } return Pandoc(blocks) end ================================================ FILE: tools/update-readme.lua ================================================ -- update README.md based on MANUAL.txt -- inserts contents of input-formats and output-formats local f = assert(io.open("MANUAL.txt", "r")) local manual = f:read("*all") mdoc = pandoc.read(manual, "markdown") f:close() result = {} function Div(elem) local ident = elem.identifier or "" local fixrel = function(el) if el.target:match("^#") then el.target = "https://pandoc.org/MANUAL.html" .. el.target end return el end local get = function(el) if el.identifier == ident then result = pandoc.walk_block(el, { Link = fixrel }) end end if ident == 'input-formats' or ident == 'output-formats' then pandoc.walk_block(pandoc.Div(mdoc.blocks), { Div = get }) return result end end ================================================ FILE: tools/update-translations.py ================================================ #!/usr/bin/env python3 # Update translations in data/translations # based on data from Polyglossia and Babel. # # usage: python tools/update-translations.py import json import re import subprocess import sys from configparser import ConfigParser from importlib.util import module_from_spec, spec_from_file_location from pathlib import Path from shutil import rmtree YAML = dict[str, str] AST = dict[str, "AST"] | list["AST"] | str # missing Listing BABEL_KEYS = { "abstract": "Abstract", "also": "SeeAlso", "appendix": "Appendix", # "appendix.template", # [chapter].[ ][[appendix]], take appendix "bib": "Bibliography", "cc": "Cc", "chapter": "Chapter", # "chapter.template", # [chapter].[ ][[chapter]], take chapter # [[prechapter]] [chapter] [[postchapter]], take postchapter (cjk) "contents": "Contents", "encl": "Encl", "figure": "Figure", # "figure.template", # [figure].[ ][[figure]], take figure "glossary": "Glossary", "headto": "To", "index": "Index", "listfigure": "ListOfFigures", "listtable": "ListOfTables", "page": "Page", "part": "Part", # "part.template", # [part][ ][[part]], take part # [[prepart]] [part] [[postpart]], take postpart (cjk) # "postchapter", # see chapter.template # "postpart", # see part.template # "prechapter", # see chapter.template "preface": "Preface", # "prepart", # see part.template "proof": "Proof", "ref": "References", "see": "See", "table": "Table", # "table.template", # [table].[ ][[table]], take table } POLYGLOSSIA_KEYS = { "abstract": "Abstract", "also": "SeeAlso", "appendix": "Appendix", "bib": "Bibliography", "cc": "Cc", "chapter": "Chapter", "contents": "Contents", "encl": "Encl", "figure": "Figure", "glossary": "Glossary", "headto": "To", "index": "Index", "listfigure": "ListOfFigures", "listtable": "ListOfTables", "page": "Page", "part": "Part", "preface": "Preface", "proof": "Proof", "ref": "References", "see": "See", "table": "Table", } def git_clone(url: str, branch: str | None = None) -> Path: """Download the Git repository at the provided url.""" return Path( subprocess.run( ["git", "clone", "--depth", "1", url] + ([] if branch is None else ["--branch", branch]), capture_output=True, text=True, ).stderr.split("'")[1] ) def parse_ast(tree: AST, is_map: bool = False): """Parse the pandoc value into a string.""" if isinstance(tree, dict) and is_map: return {key: parse_ast(value) for key, value in tree.items()} elif isinstance(tree, list): return [parse_ast(value) for value in tree] elif isinstance(tree, str): return tree ast_type = tree["t"] assert ast_type in [ "MetaInlines", "MetaString", "Str", "Space", ], f"Type {ast_type} is unsupported." if ast_type == "MetaInlines": return "".join(map(parse_ast, tree["c"])) elif ast_type == "Space": return " " else: return parse_ast(tree["c"]) def pandoc_parse(src: Path) -> YAML: """Parse YAML with pandoc's metadata parser.""" with src.open() as f: data = f.read() try: # HACK: disable (most) markdown parsing of strings # TODO: commonmark, with unicode normalization disabled # https://github.com/jgm/pandoc/issues/8341 format = "markdown-smart-subscript" pandoc_ast = json.loads( subprocess.run( ["pandoc", f"--from={format}", "--to=json"], input=f"---\n{data}\n---", capture_output=True, text=True, check=True, ).stdout ) except subprocess.CalledProcessError as error: raise ValueError(error.stderr.strip()) return parse_ast(pandoc_ast["meta"], is_map=True) # type: ignore def read_yaml(src: Path) -> YAML: """Read the YAML data with an ad hoc reader.""" with src.open() as f: return { (tokens := line.split(": "))[0]: ( value if not (value := ": ".join(tokens[1:]).strip()).startswith("'") or not value.endswith("'") else value[1:-1] ) for line in f.readlines() } def save_yaml(data: YAML, dst: Path) -> None: """Save the YAML data with an ad hoc writer.""" with dst.open("w") as f: f.write( "\n".join( f"{key}: {value}" for key, value in sorted(data.items(), key=lambda x: x[0]) ) + "\n" ) assert pandoc_parse(dst) == data, "Serialized different from expected." def parse_babel(src: Path) -> tuple[str, set[str], YAML]: """Parse Babel's language files.""" # TODO: strict=True once https://github.com/latex3/babel/pull/303 lands config = ConfigParser(strict=False) config.read(src) bcp47tag = config["identification"]["tag.bcp47"] captions = config["captions"] # HACK: manual modifications (see BABEL_KEYS) if "postchapter" in captions: captions["chapter"] = captions["postchapter"] if "postpart" in captions: captions["part"] = captions["postpart"] data = { BABEL_KEYS[key]: value if not value.endswith(":") else value[:-1] for key, value in captions.items() if key in BABEL_KEYS and len(value) > 0 and value != "<++>" } return bcp47tag, set(captions.keys()), data def parse_braces(s: str, i: int) -> str: """Return the contiguous block starting at index i defined by braces.""" assert s[i] == "{", f"{s} at character {i} does not start with a brace." left = 0 for j in range(i, len(s)): ch = s[j] if ch == "{": left += 1 elif ch == "}": left -= 1 if left == 0: return s[i : j + 1] raise ValueError(f"{s} is mismatched.") def parse_polyglossia_value(value: str) -> str: """Parse the Polyglossia value.""" patterns = [ re.compile(r"\\@ensure@RTL\{(.*)\}"), re.compile(r".*##1##2\s*(.*)"), re.compile(r"\\textsc\{(.*)\}"), ] for pattern in patterns: match = pattern.match(value) if match is not None: return match.group(1) if "xpg@hr@digraph" in value: value = re.sub( r"\\xpg@hr@digraph\{(.)\}\{(.)\}", lambda match: match.group(1) + match.group(2), value, ) return value.replace("\\", "").replace(":", "").strip() def parse_polyglossia(data: list[tuple[str, str]]) -> YAML: """Process the Polyglossia data.""" return { POLYGLOSSIA_KEYS[key]: parse_polyglossia_value(value) for key, value in data if key in POLYGLOSSIA_KEYS and value not in ["", r"$\rightarrow$", r"$\Rightarrow$"] } def parse_ldf(src: Path) -> list[tuple[str, dict[str, str], YAML]]: """Parse Polyglossia's language definition files.""" with src.open() as f: data = f.read() # HACK: regex parsing of latex pattern = re.compile(r"(?m:^)[^%]*\\def\\([^\{]+)name[^\{]*\{(.*)\}") languages = [] extra = [] for match in re.finditer( r"\\def\\captions@?([^@\{]*)@?([^@\{]*)@?([^@\{]*)\{", data ): language, variant, script = match.groups() options = {} if len(variant) > 0: options["variant"] = variant if len(script) > 0: options["script"] = script body = parse_braces(data, match.end() - 1) matches = pattern.findall(body) lines = [ line for line in body.strip().splitlines() if "def" in line and "name" in line and not line.strip().startswith("%") ] assert len(lines) == len(matches), "Missing matches." captions = parse_polyglossia(matches) languages.append((language, options, captions)) if len(options) == 1 and "variant" in options: extra.append((language, {"script": options["variant"]}, captions)) assert len(languages) == data.count(r"\def\captions"), "Missing captions." return languages + extra def get_tags( bcp472lang: dict[str, str], bcp472opts: dict[str, str], language: str ) -> dict[str, dict[str, str]]: """Get the bcp47 tags and options matching the language.""" bcp47tags = {} for bcp47tag, name in bcp472lang.items(): if name == language: options = ( bcp472opts[bcp47tag].lower().split(",") if bcp47tag in bcp472opts else () ) bcp47tags[bcp47tag] = { s[0]: s[1] for s in map(lambda s: s.split("="), options) } return bcp47tags if __name__ == "__main__": translations = Path("data/translations") translations.mkdir(parents=True, exist_ok=True) for translation in translations.rglob("*.yaml"): assert read_yaml(translation) == pandoc_parse( translation ), f"Pandoc parsing doesn't match on {translation}." # original repository: https://github.com/latex3/babel/ babel = git_clone( "https://github.com/stephen-huan/babel/", branch="pandoc" ) # original repository: https://github.com/reutenauer/polyglossia/ polyglossia = git_clone( "https://github.com/stephen-huan/polyglossia/", branch="pandoc" ) babel_data = {} babel_keys = set() for path in (babel / "locale").glob("*/*.ini"): bcp47tag, keys, data = parse_babel(path) babel_keys |= keys babel_data[bcp47tag] = data # print(f"Babel keys: {babel_keys}") # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly spec = spec_from_file_location("bcp47", polyglossia / "tools" / "bcp47.py") assert spec is not None and spec.loader is not None, "Can't find bcp47.py." bcp47 = module_from_spec(spec) sys.modules["bcp47"] = bcp47 spec.loader.exec_module(bcp47) # check coverage of bcp47.bcp472lang against actual polyglossia/tex/*.ldf known = bcp47.bcp472lang.keys() assert set(bcp47.babelname2bcp47.values()) <= known, "Some values missing." known |= bcp47.babelname2bcp47.keys() known |= set(bcp47.bcp472lang.values()) for path in (polyglossia / "tex").glob("*.ldf"): name = path.stem.split("-")[1] assert name in known or name == "latex", f"{name} not found." polyglossia_data = {} polyglossia_keys = set() for name in sorted(set(bcp47.bcp472lang.values())): bcp47tags = get_tags(bcp47.bcp472lang, bcp47.bcp472opts, name) ldf = parse_ldf(polyglossia / "tex" / f"gloss-{name}.ldf") if len(ldf) > 0: languages, _, entries = zip(*ldf) for data in entries: polyglossia_keys |= data.keys() assert {name} == set(languages), "Languages don't match." for bcp47tag, options in bcp47tags.items(): polyglossia_data[bcp47tag] = {} # go in order of specificity and only apply if more general for language, opt, data in sorted(ldf, key=lambda x: len(x[1])): if opt.keys() <= options.keys() and all( value == options[key] for key, value in opt.items() ): polyglossia_data[bcp47tag] |= data # print(f"Polyglossia keys: {polyglossia_keys}") # bcp47tag unique to either Babel or Polyglossia unique = babel_data.keys() ^ polyglossia_data.keys() shared = babel_data.keys() & polyglossia_data.keys() assert ( unique | shared == babel_data.keys() | polyglossia_data.keys() ), "Missing keys." for bcp47tag in unique: data, other_data = ( (babel_data, polyglossia_data) if bcp47tag in babel_data else (polyglossia_data, babel_data) ) assert bcp47tag not in other_data, "Shared key." captions = data[bcp47tag] if len(captions) > 0: save_yaml(captions, translations / f"{bcp47tag}.yaml") # merge Babel and Polyglossia data for bcp47tag in shared: data, other_data = ( (babel_data, polyglossia_data) if len(babel_data[bcp47tag]) >= len(polyglossia_data[bcp47tag]) else (polyglossia_data, babel_data) ) # prefer values from data over other_data captions = other_data[bcp47tag] | data[bcp47tag] if len(captions) > 0: save_yaml(captions, translations / f"{bcp47tag}.yaml") # clean up after ourselves rmtree(babel) rmtree(polyglossia) ================================================ FILE: tools/validate-docx.sh ================================================ #!/bin/sh # Modified by edwintorok from https://github.com/devoidfury/docx-validator # to look at more files than just document.xml. # Further modified by jgm for portability. tmpdir=$(mktemp -d) error_files="" errors=0 for file in "$@"; do file_errors=0 echo "*** Checking $file" rm -rf "$tmpdir" unzip -q -o -j "$file" "word/*.xml" -d "$tmpdir" for i in "$tmpdir"/*.xml ; do xmllint --format "${i}" > "${i}.pretty.xml" done XSD="./docx-validator/schemas/microsoft/wml-2010.xsd" for i in "$tmpdir"/*.pretty.xml; do xmllint -noout -nonet \ -schema "${XSD}" \ "${i}" 2>&1 || file_errors=$((file_errors + 1)) done if [ $file_errors -gt 0 ]; then errors=$((file_errors + errors)) error_files="$error_files\n$file" fi done if [ $errors -gt 0 ]; then echo "These files failed validation:$error_files" fi exit $errors ================================================ FILE: tools/validate-docx2.sh ================================================ #!/bin/bash set -eu (for i in "$@"; do dotnet run --configuration=Release --framework=net8.0 --no-build --no-restore --project OOXML-Validator/OOXMLValidatorCLI -- "${i}" -r done) >validation jq $@ site/index.js: index.js perl -p -e "s/SHA1_PANDOC_JS/$(SHA1_PANDOC_JS)/g; s/SHA1_EXAMPLES/$(SHA1_EXAMPLES)/g" $< > $@ site/%.html: %.html perl -p -e "s/SHA1_INDEX_JS/$(SHA1_INDEX_JS)/g" $< > $@ site/examples/%.zip: examples/% $(wildcard examples/%/*) cd "$<" && zip -r "$(CURDIR)/$@" . site/%: % cp $< $@ upload: site rsync -av site/ website:pandoc.org/app .PHONY: upload serve: site cd site && python3 -m http.server .PHONY: serve ================================================ FILE: wasm/examples/bibtex-to-csljson/options.json ================================================ { "from": "bibtex", "to": "csljson" } ================================================ FILE: wasm/examples/bibtex-to-csljson/stdin ================================================ @BOOK{Wurm2011-ho, title = "{Substanz und Qualität : Ein Beitrag zur Interpretation der plotinischen Traktate VI,1, 2 und 3}", author = "Wurm, Klaus", publisher = "De Gruyter", series = "Quellen und Studien zur Philosophie", edition = "Reprint 2011", year = 2011, address = "Berlin", keywords = "!!! Plotinus translation", language = "de" } ================================================ FILE: wasm/examples/csv-table-to-org/options.json ================================================ { "from": "csv", "to": "org" } ================================================ FILE: wasm/examples/csv-table-to-org/stdin ================================================ "Year", "Score", "Title" 1968, 86, "Greetings" 1970, 17, "Bloody Mama" 1970, 73, "Hi, Mom!" 1971, 40, "Born to Win" 1973, 98, "Mean Streets" 1973, 88, "Bang the Drum Slowly" 1974, 97, "The Godfather, Part II" 1976, 41, "The Last Tycoon" 1976, 99, "Taxi Driver" ================================================ FILE: wasm/examples/custom-template/custom.tpl ================================================

                $title$

                by $author$

                Keywords: $for(keywords)$$it$$sep$; $endfor$

                $body$
                ================================================ FILE: wasm/examples/custom-template/options.json ================================================ { "from": "markdown", "to": "html", "template": "custom.tpl", "standalone": true } ================================================ FILE: wasm/examples/custom-template/stdin ================================================ --- keywords: - bee - ant - ladybug author: E. N. Tymologist title: Some bugs ... This is a book about bugs. ================================================ FILE: wasm/examples/docx-with-equations-to-latex/options.json ================================================ { "from": "docx", "to": "latex", "standalone": true, "input-files": ["equations.docx"] } ================================================ FILE: wasm/examples/hello-world/options.json ================================================ { "from": "markdown", "to": "html5" } ================================================ FILE: wasm/examples/hello-world/stdin ================================================ *Hello* world! ================================================ FILE: wasm/examples/highlighted-code-to-html/options.json ================================================ { "to": "html", "from": "markdown", "standalone": true, "embed-resources": false, "table-of-contents": false, "number-sections": false, "citeproc": false, "html-math-method": "plain", "wrap": "preserve", "highlight-style": "kate", "template": null } ================================================ FILE: wasm/examples/highlighted-code-to-html/stdin ================================================ --- title: Code with syntax highlighting lang: en-US ... Here's some code with syntax highlighting: ``` haskell -- | Inefficient quicksort in haskell. qsort :: (Enum a) => [a] -> [a] qsort [] = [] qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs) ``` Try changing the highlighting style to see what effect this has. Here's some python, with numbered lines: ``` python {.numberLines} class FSM(object): """This is a Finite State Machine (FSM). """ def __init__(self, initial_state, memory=None): """This creates the FSM. You set the initial state here. The "memory" attribute is any object that you want to pass along to the action functions. It is not used by the FSM. For parsing you would typically pass a list to be used as a stack. """ # Map (input_symbol, current_state) --> (action, next_state). self.state_transitions = {} # Map (current_state) --> (action, next_state). self.state_transitions_any = {} self.default_transition = None ... ``` ================================================ FILE: wasm/examples/ipynb-to-rtf/options.json ================================================ { "from": "ipynb", "to": "rtf", "standalone": true } ================================================ FILE: wasm/examples/ipynb-to-rtf/stdin ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lorem ipsum\n", "\n", "**Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus\n", "bibendum felis dictum sodales." ], "id": "42a14256-91c8-446e-92a7-ab6bf11055d3" }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"hello\")" ], "id": "98ee7437-e11a-4c16-b642-9e7911f32cd2" }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pyout" ], "id": "2df739f6-1afd-400b-845c-ad1efebc209f" }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import HTML\n", "HTML(\"\"\"\n", "\n", "HTML\n", "\"\"\")" ], "id": "622b77f5-76e7-46cb-a694-56007ed6adbe" }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Image\n", "\n", "This image ![the moon](attachment:lalune.jpg) will be included as a cell\n", "attachment." ], "attachments": { "lalune.jpg": { "image/jpeg": "/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcU\nFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/wAALCAD6APoBAREA/8QAHAAAAAcBAQAA\nAAAAAAAAAAAAAQIDBAUGBwAI/8QAPxAAAgEDAwIEBAQEBQMEAgMAAQIDAAQRBRIhBjETQVFhByJx\ngRQykaFCUrHBFRYjM/BictEIJOHxQ1MmgpL/2gAIAQEAAD8A1x0YoQpwT2rFfjf1B4Kw2NnqDNMu\nfFRG4H6fSsVmluJ5h40jyYHGSeKTw7ZAACk4x2pNosn2HrQpwMAZx2486By4c9vLNC0jDCgmmrE5\nJHJHmfOjhWZcrn6UmEO09wRRiR4YCHOeTx2NJOSFyzHFJn5uf3oWxjBb5selEDsFwzHb2x3FJqwZ\nRtIP9hSqYxx3Poc4o0agA7+T5c8CgYlee5oYhnlskjvQkKBjk8dqFCADzj0GaNjdk5IwP1NGGGzz\n3oXYb8kkeXApVXATIJ+lcGBRskkeQ9KB5AMDGccYoDGcENjOfOi8A4/Wu3+37VtnxA+Jl5Lq81vo\nkxSyVQFYH8x9eKya9nkurt5rhmaSQlmJ5z60h/GWyAPU0fdgDcuM8n3oGBZSMiiNk8DjH70m43HO\nTn6d6A7SMjJIHOKS/iPzdue1CMruJ4B7Dzosjhs8bSfLPNNhKwcqRijQia4IS3jaR2OCEXcf0FWC\nw6E6q1EZttEvirHAZ4ig/U4q26N8D+qbuZV1CS0sYTyzGTxCB/2jufvU/qX/AKfpVjZtN15JJAPl\nSa32549VJ/pVKvfg11naqSunxzgE/wCzOpJ+xxVZ1HprXdLlCalpF/Aw5+aA4P3HeozmNgXXafNW\nB4/WkS+5h6A+tKRs2CAPvmgeQ9vXijhQo+YZOKdyLbeDAIY5UlCnxmdwwY542jHAxj1oNoPriiui\n5UYYduxoUG72yM5z3pd40TARmcbQTlcYNIOozlST54NHyceefSiEZ57HzBomf+mpYoTIGLY98Hmi\nyoFbDflBJzTdlL52lTj17UeM4j3OO/7e9FfBPynOeeDXd1zjn3NF3bTxgknB4oYbea4kKQICRGW5\nIUYUZJ5700Ocknt3FFZsrkHnHnUx0r0vrXVN14WjWjzKpCyTdo0+rGtp6X+B+j6VF+L6mu21B0G4\nxINkQ9u+Wq+9EXOn3McqaLokWnW1vIYSxRV3AehHc/rVvEZxkmhKFsDy9qHw8EH+lcEwMjvRWTcS\nCMj0qO1LQNK1GPw9Q0+0uFx/+SINiqJrvwV6S1EFoLaWwmPIe2fAz7qcis26j+A+s2YLaJfxX6L2\nSX/TfH9P6Vmev9O6xoE2zWdOubUDszr8p+jdj+tRcbKfU8+dOUkBB2kYx96XAyMkkepFFLAHBJ4H\nagRtw4OfQA0SQ5YKuTxShx/Dn3ojHJG0EmjwoXfYu0Z9Tj96JvUfxn//ADUqxz8wwq54AoszplTJ\nHvUHkZxkemaQgXumdpOTjyrpSSwQntSbBio4IIGfTg1zg457Um2UCkHOR6dqGe/uJLeGCSVjDCWK\nJnhScZx+lO+m9C1LqbVo9P0mHxJXGWYnCov8x9BW66F8CdHt/wANLrF1cXciKDJCrbI2b7c4+9a1\npGm2ul2qW1hbRW0CfljjXAFO3jR1KOoZT5MM0EVvHAipCioi9goAFGuLiOBVMrhNxwM+Zoj3ltFI\nsZnQzOMqgOSaXIOB60JUgegrl5HPei4zxxzXFeOBSZUEDaeab3thBfWzwXsUc8DjDJIoII+hrHus\n/gXp14slx0zM1lcnJ8CRt0THvgea/wBKwrXdB1LQL97PV7R7ade2/s3uD2IpkknG3PHmKBuNuDwf\najbwGwSPYCuB4wTknjjzo+MEZ8vKgDFW48/WiYC53c4oP9L+Zv0FTKDdHsK8A5PNGtZreKaXx4PH\nBjZVUsV2MR8rcd8d6ZrksQSMfzetGlgmjjMmQq4HBPfPYgefahuZ4Xt7ZBbiJ0B8SUE5kJ7Z8sCk\npOUB4PHnTSY/Ke5z/LVi6E6I1frG622MXhWKviS6k4Vcd8ebH2Femvh/0NpvR1gYrBWkuZQDNcv+\naQ/2HHb+tW/GTgd6MRz7iuGS3NAzYBLZ+UZNYv1Z1Nqt/qRmsl8Wxgf5U/lK98L5k1f+l7dj4OoX\nEpe6uYVYBo9pXODgj9vKnlxqV7penRTXFu93dPLsESYB2k8n7Cp62nS6tVmiDhSPyuNrD2IoUG5T\njvXBTu70O1jXKuM8c0UsD8uQGxnFFICrknnyqG6o0LSNdsBYa3FDIkp2xhzhgx/lPka80fEv4Zan\n0lM9zb7rzSC2EmUfNHz2cf37fSs+XnkH+9KLkkgqCPKhPzIMDkelH2tjPv5mjhdw/hz6Umw3A575\nouypjJHzcA844oWOxSTgk8NntTUnErAEceQojMrAA8kHFAiqcZfaCQST2AomorHFI6xSCaNWIVwp\nG4euKvnwo+Gk3VUyalqivDoyH5R2ac57D/p969L6ZZWml2cVrZwRwW0a7UjjUKFHtTq4leOFnhh8\nSQDKoDgmjWkzTQJIybGYZK5zillySc0bHGP0NFckLxnd7U1g062hYskKAscnCjk0pPKLaNpGRQB5\nj0ppp0b3MjXdzvw5HhKy4ZFqSUh0LJk4JHPHNEeRxhdpUnncBkD2pYrxk9/OkrlplhBhdEOfmZxk\nAfTzpQYdQwyAeaLgOzAEMRx9KbXjzWdsZUie5ZTkqv5seeB5mqB1u+o6jqlvBH05NevEBPBK0pQR\n4I4BHZj+1W/SEu5bBbTVrWMAxhWAfxFx5qSe/wBaxj4q/CI26zar0pD8gy81mpyR5koPT2rFMcMS\nPmHfPH2xQx8gBe+Oc0ct/Ce/eub5RgDgjOPOgDd8jH1rtp9V/UU8kUkkYPfuPKpHp3RZ9f1ZLSJx\nHCBvnlY4WKMfmc/QVOajF06kj2OiafNdpCctqM0u0yY7gDgAfvU4mi6LFYtqum2CXAgUfjtOuDlg\nnbxIj+/pVZ616ZttPu7Kbp9Zbi0vYzNCcgjHmoHqKlPhX8PZ+p9Ua71eOWDS7V8SKV2tKw/gHt6n\n2r0xBDHaW6RW8KpFGu1I1AAAHYAVC9TdUR9P6E2pXMMfiAAi2kkCsefL6Zpv0B1jH1ZbTubU27xk\nkAnKsuSMg/UVZbi5S2iVxE8nzhQEHPPGcUvFc28jOkU8TyKcMquCQfQ0lqF0LSEP4byMWAVEHJJp\nJjdM29FUs2AATwvqT7/tT4HHfik5IY5P9wK3OeRmlAOSD3IoQCBx2oX5X6VwyMnJ+meKTleMAGTB\nOeAfWknuRbjN5JBFu/Ll8f1/tR7TZJCJIzlZOc+tdIj/ADDe2CMAelRZ1uystSg0m5eRJ2UbHcfK\n+Pf1qZAUgFSCPUUV1JHArDvjN8LvxIm17pyHFyAWubVBxJ/1qPX1HnWD+IfBSPw1UoTuYghj7H6V\nwXJ/Ke1c2QchuMYOaVIBXOcH0om8fz09kZl/Iwxz96tt5u0Dp6PSbYut9fotxfsgyyR/wR/odx9c\nim80U2nWcUk8Qit5EWVMEhX5K7iCe/Bp90vqclvr1pcySKsdy3gzbjnch424981e+jLCwudD1HSr\n+Zok02/lijduMK4wAc+9G0bqjU+koun7K7gkuNPuYn3Kyrv4YnepB54PY+latpGr2OsWwl065SZC\nOR/Ep917ioTrnoqx6vs1gv5JYnjz4ckZ/Ln286fdGaAvTOg22mRTtcLCCviuoUkEk9h9anJHCtHt\nU89iB2pvJplm8njCFFmyTvUbTn6inccexQGJYjjLcmhGANqgCgYcjI+9GQjbx2offn70ZcnPpQHu\nRQOWAyORXSoJADnJqs2/SFit/Jd3AmvJ2bO64ctt5zwD2qzIpVQFA4HAHlQktuGBnPemN9HPIYmh\ntrWRw2czjt6Y96fQ7zEpddregOaF9xbABoHTIyACOx5zXnT45/D06dM/UOjRhbR2/wDdQKOEY/xj\nHkfP3rHm3HHh8H0omSMg4GfWlV5wpJHH61232NWXpLT4dQ16IXhxZ26tcXJ4P+moyR9zgfejT3Uu\npapcXrKS9zISgDdhnAGPTsKt2izwR6vadNXOm22oDxNt5KSWIZs/JGSQABkfU5qM6d0t26vt7SCF\nZIY74RgsfmQB+5+wxVogu5H0jrPUYXK+Lq0aRse/yv8A/XFV+51qC9udCk1BGa0gvJ0LA90JByAw\n4xkVqc+h6JrAF30zqz2N4o+VreUhT7EU4septa6fuFtuqbRrmzx8t/AgO0erdgfP0PtV3069s9Tt\n1udNuY7iFv4ozkfShkj3XiSFpE2jhd3BP0ol3b2+oWpjMoZWbOVfnI9MUq11DbLFFPMN7EKCR+Y0\nu/BBALc0KvuUPtK54wa4Nt7+VDvB4Gc0IOBijK/ka6N1lB2EHHfBoVG0Z7UVnwDkEn2oUORuIIGK\njda1W302OAXF7bWktxII4TOM729AKfx+ICFfkAfmAxzSwYYIPcVH67dSWmm3E0OzxQh2B22gt5Am\nsF0fVeqJ5fGs1ureOaVncRyMVgP8Tbc+gPBrZLW7t9f6e3Qn8fZzgwyGZChbyYFSOPrXlj4g9Lzd\nIdUXFg5Y2zHfbyHHzoTx9x2+1V6QZbhR96MVAAKgnFG3D2q6dH6fIvTWu6k6lVYR2aHOAzMy5BP0\npTTIoV1OS4is4mt7CBp5EU5G5RhTk9/mxTzpRFsL+O8ubjwvwsb3Lykg5fGQOe5JOKddASeDd6z1\nLd+GfwNu9xkjkyv+UfvXTTNp3w20+zidTqN5cPqcwLAFEXlc58zxgedUfVVuLWC2sbnA2KZRtbIO\n/nOR7AU1stQu7Ni9tPLGSQflcgVeE+J+qTdPz6Xd7ZXk+Xxm7hf+fWrNoGnaff2sV50Xrc2l6p4a\nmSN2xHI+OeOwJP1+lWKw+Jl7o0x07rnTnt5ACv4uFSUcds48/LtVl6TOgajKNS6YFtLIqFSPEIMZ\nPqvOKtNvDOfnujG8g5TamAv0pwWGdndsZwKHDbhtUEeeT2rpOOaJkjtz5/8AxTS8vpIIi0ds0g/i\nQsEI+54rrWWS5TfcFVGPlijbIH1YdzTPUIWnhWKxkuYFRs5gcKWbPY58qZMeorFXkhlt9STcB4cj\nlJAPqPl/pU/HIZR+WSKVUDMjeVObWYTo5CuMHB3LjP0plqNhZXl7BJf2Edw0Slo5XUMEOfLPY/Sp\nMFZFB5wR5jFRV3Be26BtNKzEEDwpnIGPPDYJ+1ROuWF/qeim3uIosTSoJIw+fkzk4OBg9j9qk9O0\ntbW3jiLtIqrj5h8x9yfOpNEVIwqAADsBWa/HfphNa6Va8ij3XmnZmUgclP4h/f7V5oKhkz9aL4g3\nj6Y7d6HHtWq3kSaJ8Oen7NkQyX0z3kqvxkYwv9VP2pr01Zf4h05r62qSyXzLGAkfbZuzz9cU11kv\nounNpFxGhvb1hNcrzvjRfyofLk88VbdItrPS9M0/SdRtXf8AE51TUgij/TVf9tWz5Zx+grPOpdRj\nvtRvbm8t8TXjKYuOUj7g+mT/AEqvX9wJ2gI2nwk8MHGOATjP6ikowOA4PI9e1FYlSCcn6U+srhYb\npJ7aZoJ0UEeJyrN2/wCZq23PXV7Lpb6R1FYi7tXAVSflZMdyretJ9OaLqaIdc6NvpXmtnzJa/lmR\ncemcMK1boL4ow6u0em9RD8JqTDaJD8qsfcZ4NahFHtRACTjAznOaNLGrphh55oJF4ogVEO5ieOc0\ndgHXjBHvQrGCvyjgelBHEUx8oHoKJ8kbgEohJ5yQM1ExzajDq9yGUPbSDEMhIyreg9RUurLBE8kp\nO7GWPfNI3Ut14e+yhWRj28Q7RjH61H6Rd61P4y6hax20uP8ATVSSuPr51IteRwRK164jYL82Mn/n\naj21zb39pFcWcqzW8g3I47Ypyoxwe9GKHBxim93AtxbPFIu5HUq4PmCMGvHXWujt071Nf6YSSkUn\nyE8ZU8j9v6VAsABux9qT8R/52/WtY+JVzCX0GO3BaOPTItpH8OfOq1p+o3+lTCbS714JSuxyAMbf\ncVPdE2y3mrXev67IbiysP9eeaY5Lyj8qj7kcVE6jql3fx6z1Dc3DRNek28MX8yZBIA9AAP3pK66j\nsdVs5v8AE9LjkvgixwTRyGNIwAACVHc1Vn24I7Ee/ekpHIUAkEDzBpWSRXQfLg+oPNIpgnGe3vzT\nmS+n/CtbGTfCcfK/OOe49O9PeltVvdP1a0bTZJEn8UBfCOcknHbz+laJ1Iul9UXNy9tGLLqSBkEb\nKwVbsHzPocc1qfUepaxZ6PbXGlzL+IsYk/FQMPlcFRk59u9R3QXxLbqHW/8ACb2CFJ/n2yQvkHb3\nH/yK0c8Hvwab2l3bXO78PKsoyVyvIBHBFKEpG38IZ+BnzpOeZYU3vJsC9zgnP2FRdx1dp0S3Dw+J\nJHbDdPIUZUjX1yRz9Bms26m+MnTsYaOGwe+dTuRz8i59DnmoDp742E6kDqNjFHaSOoxAxAQZ/MRz\n2H0rf7S8t7yziubeZJbeRdyyocgj1oYLuK5XMDEjzOCP60rGWLMPLyqG1O4ddbtrePTpZVljZvxS\nEbIyOACP3p9DaS2lvDDZeDGgOX3AnjzxSHUGv2GixM13Mkcm0squwUH3+lYhrfxjuLnWvDt70W1l\nCCRIkJImb3Gc49P1rT+gPiDpvV7y2unxTxywRhz4ozuHbOR2+9Zn/wCpHSBFqWmaoqKBKrQSEeZB\nyP2rGGJbuAVHApHdB6H9KtD3s9/Damdnk8FPCOfJM8YxU3oXTd3qh8dlex02IAyXs8m0ADuQKDXd\nZTUwmhdPq8Og2pzLKeDKR3kc/rgVXupb+C+uEhskMdjbKI4lPc4/iJ9TmoeJiG4ACnn5a7BLZx39\nRzSUibck54oqsWP8IBoQBye+K5nxE7cceffmjaXd/h7tJUlMUiHKyBclTjg1MwahMusW8gWN7gqi\ngqflcj+In1xWvfDn4go891o3V0qRXZYhLiQjYw7bCe3l3q69LdI6XpXUcmq6RZQxJKrq7FidvPdP\nLB7GlOpusY9I1y3sIo5LuSRPmigQs6ZPykgDsefPyp/p2s289sJtGjieBmxIeFCsfI+4HepuG1SJ\nWfxNxc7iS2Rn2z2qA/znp9na6pdanLFCttK0ax5DMwHYgD1rz78QOvNQ6u1L8PYeLDpwO2OFRtz7\ntj+9Wv4ffB+xvII73qC8iuXYb1tYZeF9NxHf6VatX+DnSl86x6cr2dwjbnEUmcj6Enj6VLdI9EX/\nAEpqMCadrU0+jnPi2txztOONvpzV/UuZFSONAB+Zz5ew9aUOSMZIOMbhTTTbR7OIxs7SgsW+Y9vp\n/wCKNqt0bLTbq5wcQxNJj1wpNeY9M0fqX4jawb6+Se5s0Yo0rMEVBzwPpnyqw9Oa/wBM9J6rc9Nd\nR9PwP4E5QXQjEzH3bIzjHpWnPpmldO3Fpq+lJb6dZyMBMsaEGcNjau3yPOajPj3pwvugZ5QuWtZU\nm9wM4P8AWvLu87zk4OM0QsM/mqx6feyWF14sDlcja+BnI+h4qwOl3r6RxPrU1zaJ834dFIK5/wCn\nge2fKoDWdYHOkWVn+EtYzgofzOw82PmaiEcEZIIycmuQZBZsgHypWMKF2kY78UXYpOHLBe/FEKqA\nSWwPTFEbGC2ASP8Amam+gho7dU2X+YmjXTFJdzIMoSBkA+2at/xRv+i9XtXbQkhgvrYriSGEIs4J\nwRwB271mCuEQvxvyFUg42+f/AD61qPTFrH8RdFj02ezS31O0z4GoIoCMRyVcD1yOf6VdhZ9XdHaW\nlxp14l3bWoEk1h4OFCfxbGbJPrV103Uhq+mQ6nZWqJbXkG+TcAsoP8p9fPnyx71n3SnS+qyapNct\nctHpUQykanYxx5Mg4Y8d/PvVmvt9qBbSPeS6XeIRNGoeR4XxkFSORkjGO2TmvOnVuq/jtTnFtFLb\n2kbGOKFnyygcc+pP9zTjo7pfWupLpY9LtX2/xysCqL9/Wt46W6C1DSotkus+BIwKl8hn2+nPapy5\n6e1u2jZ7HUhPKgzEzZD5A4y3OfPipHQNeeayVdYEdteAqjYcbXJwMr9zU3LcSW8bgRSXEqLu2quA\nfofX2pxLO8cYcW7vnuqkAijRSrPAssZcBv4XGCPtTfVrIX+m3Vq5IWaJoz7ZGKwfoK413o3qqfSL\nyUSwodogAyZVGcbPLPn61od707pPWFrd3YsWsr2ZWjaXCrJkds4qaNhc3ekw6VIgV7ZYcXLY+fbw\nSOOD3o3W2ltqnSWo6TayKks8HhoWycdsE15A1exFhqFxbeKkngyMhdOxIPcUz8Rf/wBgqYfCr2Jz\n+n1p503DHP1Dp1tcAmKW4jWQZPzAsO9ehNR+GnTV3GUh0+O2YsGaWHIf9ayH4m9J6N0tFbRWNzNN\nNMzMyybSyr65Hl7Y+9Z+q8DYwPHI9K4ttUBs5z2NCJc4DDGePWhlAxwDj17U2PnxzkHk0Eg2qNw+\nY+9ELkq2QMAUgivPP4cKtK5OAqDJJ7YAFehfg9oWtaRBBcaxaQWVrGsrICSs8pfB5H/9fP1rW7Bv\nEjkaSN1VzuKy84GO2PSlYoVmJDwIkIHyKOxHuMcUrDbQ20ey3RY1HOAOPsKh9butQj0i7fTLZV1B\n1K2wmyQWz3bb2HnXlPXLa50PqWeLUSsl0ku+UrjBY4J4+9WK/wDiXfXai2t4GhsVGBFE5j3H1Yry\nfoCKjNQ6s16IxSeDDZqnygw26rk9xknkn71I6J8Wte014wHik5Gdy/mGeze30rX+k+pNM6umgkMG\n3EgBV1DYbG4kegz2NaQLjcI/ww8UE43KwwAPem0OqP4l0bq18C1iYpG7t80pA5wPT09a611iyvVj\naCXbcMm8Qyjw3AJxyKkGJ8sHPOaaz2UFzPFNNbwvLCcxuyglT6inUUSoPlQDPJIAGaJPcxQPEksg\nQytsQH+I+g/SojXrsCx1Dc8QgS0d2kV/nHfnHpx3rxlqDeI8p7gnzPeovwZf5lq0NkjaWY/9OccV\nJ9HKq9W6Sx5X8VETn/uFehfiR1JN0tosd7bokheURkOM8EE/2rzn1frk+v3oubxYQVXCiMcAZ/c0\n2vNOgsbSGOZ5DqUuHaMY2xKRwD/1eePKm4vrWSMJfWu7Iws0Z2uPL6H6U+t+ktQltJ7yKS3jgiTx\nUFw/hySp6qp71XpiQGBZQ2MGjHgDg5x50ixBzgn6U4s7P8Zc21qvDXEixAnyycZr1PoOhab0zaW+\nj6Dax/jNgeSd0DMD/MWPn6DNTNrpTLqFtNdSGZ0y25uRk/8A3VhMahB2C98HzpQDsa7aWPbPrSMo\n/wBQdsY7VnXW3RNvqM1/LDbAzXyBXkBwF2nPI96xTqLpR+mI5mvGu0mcgxSRJuhKnuD5jHlmqlNq\n00kTQvKJYN2QCvnjuab2ltcX90YrCCSWT8xVFJ48zWr/AAd0/UtO1/wSJIp5lXaSMoynlsHtnHr6\nV6MtmiEYSALiP5do4wapfXP+toE7nUWsjJE0nigCXc65KhfJe3fvWKf5Z67uwnUAt3u1Zw42yhmx\n3/Ln8v0rfOitXdNIt4tVIhkCKHDZxG55Kk9sYIq4RsjgMjKynsQc0ZnxUP1LJLHo9xLApeRFLYUf\nNjHO33I4qvpCundHXkuoRwiWaB2kUYCxjadq88kCvJtwd8rnGBuJz5Gmmw+p/SrDMoDlmwCOefSp\njoIR/wCcNIDqCrXUZwf+4YNbp8XobJ+jr65vQJHgQ+AhJwsh4Bx9zXmzTbC61a+S3sYTPKzAbV8h\n6/StB+Jen2lgunx6VaxQzXUDNcyq3zuR3B3HtxxUD0r0nP1Hsm8BlsLZSm8D8z9/71bOtLfS7LqH\nTrTVp2S1toMKhTcGOOBgevr5Vj1xKplJHzgngZxx6UpZWdzfzS/g4mcRKZHAI+VfUk0lFbyXdykU\nCNJK5wir3J9Kn+kujtY1XqOOwmik06S3xNJLKuGQZ4wO5JPAxXqfRnhtNKhOpXES3JUCV5GVWLe4\nzx9KmbU28qK8ToynsynI/UUtFPDLnwXRyO4ByR9qOTtO3+I8ijFgiFmzj25psJoJywjdXKH5gpzj\n60D7JEZgQfPjyqP1bSLbV7GS1uYUaKQcgj/zWRa78Ebae63aXK1tG3LAncM+1OukfhBLoN/FeNqY\nmmUEBNmVGfP38q0zSNKEMdo9xGnjxuzkqMAEgjj7GpqWNZAyDIJHJBwahOodPur9rfT4baMWbAtL\nOXwY8EYUDzzk1PRwJDbpHCipEi4Cjtiqn1pbagmhvFoTW8cszgSLPFvBXGOPeof4WXMukDVdO1yT\nw5bXbIZHJCMnPIzwMe3rVll686aELyNqduqJnndnOPQVXLPrIdWarBa6fFJBpK5aW4fgzc4VV9s8\nmpvr5LKLpK7W/YLbiM8kcDj0868jSYDkL2ycUXYfX96lJSS4B44yNx5pfSrr8HqlncZIaOZH7ccM\nP/Feo+pdNTqDp+5sd5i/Fxf7i91HBrD9K6M1fROuPw+lySKY4y8c7/IJB5jHZufKtC1foVNask/x\nOV3vViVGnYDGfPGPvUrbWU2g6YbHT7XfbJCfDbzMpzkn27VkvXg1qXUtOvddgRY1R3j2YHiMvZSD\n27Cs0gsrjUNSitIIi9zM+FQDnP8A4qeg006dod6JbyGCSS78CXb8zFVBPl5ZqJ06VU1JTDFHOiuC\nPF4GPfHatyS7gkbT5kFrA4TaYbVQHJ88NwTxQ3VhqV9fzmHTrW20q5UPNLMx3Z8hkduP3NG1npnW\nrS20yXRrySCcqQ0MDFd3ocDj0qRfo/W45bTV7vX5Dq8TRLEBwjYYZDAdzjIrWQFAVmA3YxmkvxMY\nB3nYu4KCfM+gpvd3Nnp1rNcXDxQwqfmYkAZ96YaBPbaxpLXNlJmGR2wV48+3vTrTZJPFmhmMRSM8\nMGy33p/KgI4pNUGCKOoAA9MdqLHComeTBywA79vtTfVby4tvBFpbJM7Nlyz7RGg7seDk+gpnpOsv\nrFxdxixurSK3fw8zrjxeM7l9qkriN2aMRlQoOW3DOR/5rFep+ttO1LqW607U5Ug0O3EiMuz5piBx\nv88Z5wKr/SR6NHUSyT27XULZDTyAC2jJzg7Dz5Vtuk6BptrIL3RfDWOUbgqcwnn8wHl9qp3xb0ey\nPTV/qbyyy3hXajPMdgGecKTgfYV5ybudw788UXK+i1LKhaQNID3J4H7UmVAZnHck4A716v0G9t/8\nsaXcSzBUlgjAdvM4xj9ad3ywxwN4sotyxwrjGQT6Z86jri6k06xX8PFNfBUJ3ltzM3ocVjfVvVXV\nNit5dapNDZGQGK3shjeAe7YHIwAOT3zUBqcV31FdaPFPc311JIqPcO6Z8MEAbVAOCMc54/MK0/pf\nRbPQHvLy6s7WxsxGMXDgeIPI8/8AO9QXU2g9Nx9Mm60+W1nthJ45O4DxGwe/me/asp6gmGoXcMek\nWfgRlQqQxJtLHz+v3rbfhl06Qq3Oo3AluYYwojSPCRg+WfM+taRdNao8UTzQAn5jEeSR6gfWnEAt\nYF8XAXOeT3x96YRo2oavHd+IG0+0O5Fx3fzJ9uf61m+q/EqS868stL6fuDcWEkghLMCNrtkZ9Tjg\n/arh1VqZ6f02VoLu1RogW8KaceLLgclcnGc54Ned+rOvLnqSyWO4jZSru/yOduSfT6DFWL4a/FXV\nNBkhsbxo7nTVG0Iw2sgH8pH9DVs1vW9TaZOq5bWZNNkuAIrcuVIhwAHZexywBrXOjdetupNGS8tT\n8wJSRf5WHepdSPmBIzRlXgc0DSbZAoRiCMk44H3puZzJeGBrUPbeGG8fP8eeVx++aUltxLLFIrsp\nTPAPBz60hrDSRWrG3YiYDCDH5j6V51/yzb9Zanf3eq6uunzJdNB+HSIO5YnJPcHGTVqX4H2MMcLx\n39zcMCCQ4C5GMdvrg81bvhn0jqfSMV7b6jqZvLGQAwxAEBDk54PqD5VVvj3rkMGmJo1p4X+qQ0gH\ndMdhWDEZQZ59qR8M+/61YER1GWfuOMeVJSRBWwBgZ4r0H8HrqPVOh47a42yNaymPBOcDupqR6n6b\nvdXuD/79orUKMLySMeY9/esb6i1bWOnOqJ7HRdQuXjAAUj5s7hnseM1MaJ0JbXWkt1H1vLcztcHc\nI9+OPIse/PkKtfTenWuj3trALiRfxUWYkt4Qp2jJ+diSe2O2OwpnpPUUesa1dXF7brJaTAQWxAJE\naKT+dT5knNQXXmhQqNMt47eOOAyN4yxNtOGOQw/rTz4bdDWkbT6vfZkizsttpzn1Yeua0LS9PubW\n4ka8aKKLcBbxQNtAB4O71NQlyw0XVNU6quRC8Eai2t4pWw20NgkH1Jz9qk7XqWw6psSbKFxexAF4\nZFIMZ8s+RGatum2n4awjgY72xlzjuT3qKtuk9ItbuWe3s4onY7soMEN6j0rM7n4cXV/1pqF4/gT6\ncdyO12TJl2HJHPBFSmj/AAi6deKQtC7E5BJOcHOPpUjonw90XSrq3S3s7dplJcl1EhGDx39auOqa\nHBqsbQXahoSm0oB3/wCZrCLp9X+E/WgWImXSp2LRq7fJIp7g+hH9q3fT9fttU0aHVbDEtqy5k28l\nBjnjzxUpbXC3FtHNb4eJl3Iw7MKMt1GbgW8hKyBd5OCEPOMA9s+2c0qLiFndEcFosbvQZ9TR1ZJE\nzGwbz49KrfWlpqlzYxLorol8JDteT8qgqRnHmayTQOnJemOorf8AxOB9RaadXnQIf9JyCdyHu/fJ\nIHFbnbPBcxxT20wkjKkqYzlT9aZa7fy6bYxssQnuJG2hR2z6+wA5ry38Q9UXUep7ubxfHA+Qv23E\nelVVDlcrxzXYX1NT5KhSEB2nkUlJkgMxOBxg1pPwK1bwOpJbEsFiuYyVX1deR98ZrZm1GCaORJFm\nhwSnzqV3Y9PWsP636RRNduJlaXw+JpNjFnQnOAPaofXbzX9SYWemSXM9hEUEQb5cMFxkg+pP61oG\nh6Pea0bG4ud1mscKxzpuwY2HB578/wB6lrDTuldIjkhbUrQJCcOGkBYHzHr3qudY62msTRW+gadI\n8KnDXMkLLv8ALAPBxjird8P7i6uovw98v+tZqIuBtAHcHH04+1WG/tGOowFXkAkVkeRHwVGMjHkK\npXVNjJ1JqNn07p26CztSJLlpIydyj0J9+M1fNP0y1tZ1FtbpFGkQQY7kDsKlgMnGCDnFVrqXrnp3\np+Z7XVL1hcqBuhRSW5Gah4Piv0VPJHbLdyIG43NCQoPvS7fE/oq3laFdVHy87kiYqfocUOi9f9M3\nd6yrqtubk5wQjKrL5dx39qtU+s6baw+JcX1umRkAuM/p3qv9bdK2HV+nKlyhEqjfFIPzKfL9az3p\ntNR6M6omsTZMdPuIx4EAciMuSBgk5GTzWyQzS2+mJJcWyrIAN8MJ3YPbA7UN3aw3ZKTwLLEQOGOR\nn6eRptb2AMNxYpEbeyUrh9x3PxknOT9Kefho7a4a5jKKix7TjjAFIm6F1dwfh5ARs8R8L2BHGfej\n3+npcHx41jF2ilY5WTcVB70WLdBBFHb2scKA/MBhQnuB/as56j1ktaal1BMrS2sAaK1OQDD5fKPM\nse5PkK8539w1zNJNI255DksabgnAJ5B7e1Gx7ipdZiwO4jA4A/m9a5ZN7AEgDOSDT/p/UpdF1W01\nCBv9qQPgjuM/+M16osJ7bVbO1vIQjxyIJUb0yKNc2MUwYTIpyMHI71HLoFpDuADIrDBAPDefbt7U\niNZ0KxuJrGXULVLmIDxI5nwfbvRItK0uWb8RZ2lnLHL8zuihuRyDxxUlJp8NyirJGgiHPAwc/QUr\nDZxWqZUJGM5DYxg+9KlzKjoAyshwzY4Pnwab6Jbbllu5Cd88hYBu6rztWpUjZH83B/es01b4i217\n1hpfTujtNDc/jlFxIduxkGcr68/2rMPj/HD/AJ8Eq3CFZreNiV+bbjI8vpWe2Nl+NvPBivraMEZW\nV2Kr/TipCLpiZtPa7XUbNolYqdjFsEHjyqFnhubdiwL/ACnh0J/WpLSuoLix1OG6ugLpUw2yVjgn\n7edbz0r8bdDuTBa6hb3FpIRiSUkOoP174qd0/rHp7q2/t7XTWFzNHOs21oWIUL/ET2B7VfpY/G2A\nk4DBufPFLg4FQOo2eoXepFTeL/hhA3WwTBbHkW74NLX2kw3Ok3Vjas9qJ48Exd1PHb9KhOkNBvOl\nLe5iu9SiuIJJTO08ikSAY7Y7Y4/eg6t650myswlteSyTyMEJtV3NGM43EEc//NQ9x1jcWGkERWWq\nXdpMPCt7x4wfFOOWPbA9+BWa/EyfUG6dsprxI7K0kxFb2sMh+fHJd/InGB9TWUvkHupxXKwI55NB\nu9v2qdaP/Tzx8vcAYpRgpG1goQgN8w5ojgKNy+Rwc+Vaj8Murri30eTSortY7lDvgjeIyb1PdRjn\nOc8e9O7j4gdRXl61rbXFrF2/1PC8M4xzw3nTy261tdD0u6Z7651LWwCqG4AVFJPY4PH/ANUtq8nT\nHUUmkalqCxG8eFHn8MZC9gVb75/Sr904dJT8RZ6OsCeCFZhDjBB7H9sU9nuJLO5/1owbQRl3lXko\nRjgj3zTq1b8TCszJsRhlQecjyJ/8Uzu7G4LXTWs3+pIm1Q2cA575+lR3WOnahqfTUtppl4lneMgX\nc5+UjzHb96wbrDT+r+kupLCeK9nu3KBYGRy+QvdWFSnTN9oHUOsRQdRaMNM1N8mOe1PhIzeufUnP\nPaq/fabo9r1x4N1vGmoQFF2S4zjt7gEmrTr3TfRuthprL8LA8XBaylESt9Q3pWZX2hWSiY6ZrMc0\nCvhUlBUk/UcGoi6tprRvBeeMg8Hw3yKaStGxJlfA4HbOasvwz6XHVfUcFvI22xQ753Ze4H8P3OBX\nrXS9DsdMto7bTYY7JEKtiFQNwHkeOc4qaCj1NGwMZPP2qN0q7k1B5Z/Akitg22LxBgv/ANWPIUGu\nXxs4kjgMZupmChWOMLnlvtTbRbSFJpEeaS5mUYaV1IUZPYfb0p3PpVpJKJHtYmcEHcUGe+f60z1S\n0S6MNgtw8Cvl3SM8uo7gnyBzXmz4y63Dq/U721mUNjp6/hotpyCR3I+/n7VQHXIyfP04oFQknAKm\ni7j6j96tEysWA8hnjNJAbc4wCOR70hIxwzE4PJyfOltJu5tPvre8tSVnikVlwf2r0jZ2ehdW6Lb6\ni1nbscbmVuNj+efvTPV+g9K1BUtY44LaTiQog7DnJ9+9NdSgmsrWDR2/D2qyylbeddo3ooysecfm\nJ5J9KpujXrdE60i3FhJ4Cokl1O0hJG5uy4O0jPP3rcbXUrK/0+G5t5klhnwq855PkaT1C8e0KQ21\nu0k3dFPCn7/2peO0a8e3ubpGjmiB2qH4BI57d+KXuYztYMobjsWxk1T4YoDr1zfStGrqShZANqbc\nDBz5nkZqt9c3MUOk2t1cWMMthHI5MsR2CPP5ckg45J7VjuoaDrOvTS3Wm6fMLAsWhaVjhwx4K575\npjrPw86p0gJ4unyy7/8A9HzY9artxpepaZgXtnc25LFAHQjcfam7I653JIpXvlSK0L4S9MPfal+P\nutN/HW+CsaOhaPdnHzfv3rW7rTrXpHWrSW2YK7lmXTbaIFpCRjjHOOSeeBitLtXuIre3R43lnkGX\nYADZnnmn6Dkbzn6UxlnvWv4Sht47FvlcSBhKW9F8venrOAxCYO0ZPtWa30upSdQfj3tbmdJGKxRg\nc7cHgHHH3rQdLWVbFGvAElI3FSc7PYn2HeobXet9E0kBZLpbiRsBY7c7yxJwBxWd/EvqSfRLKS7e\nWVNa1OHworXI22sGfmJx/EfWsAkkJYefPPnRMI454Ge1cAe5YAeWf6UXj2/WrLnDAsuFJPbuf1pG\nQFs5G3bwMf3pBkC4f5eOCD5Gis3y4znJBGf2q9/Cvqj/AAvVYbS5umhtbmRVLE/Kpz5+xr0aI0kC\nvGyvGy/UEe1ML3TLG8NvJeWylLVmaMNjCkjGf0NNrkWotEs5tOkeBwUSMRbkwOwOO1U74W6RrMGq\nX51xGg0+CV1tImAG4k/m9Tgds1qDwJIF3gNtORkdj611xAtzGYyXXkZKOVPH0ppNpkHgNHEnhu3z\nBwTuDeuTVW1fpi5ltTbW0qJPcHmYgtszyxGfvxUzpejjT9Nh0vwmvbUbi8ly4JyTnkdsUpD+Hi1H\n8NPPajxMrbWqYyAo5/55ZqSWMbWLkEcjnsPaq7rfS2m9SwOt/Fuh/LG6Da6kHJKt5Z7VFax8Port\nIYLe9MVqFAljaJWaQjz34yKntA6dg0OwEGnIqNyWYKo3E988ZNONK6etbPUZdSlzcanMMNPJyVGM\nbU/lX2qaSFUZyq4ZuWb1pDUZZYLGVrdN8+MIvqx7Vik3V3W2kaxb22sW1jeHefAZsAoxzzkcnC8d\nqs171L1N03ojXeqRabNJd3AEIaU7iGIwoAHYDzrRbJJDCksu0yMNxAGFXPkKoHxF17XbnUYunulg\nsUsu4TTvjIUAEhR+x4rOFsh0XfXOp65NDc3FtEI7aELsDzkc7R6Lxz61mWt6xd6xqEt7fzNJNK2S\newHoAPIVHlyAdwBzQDIwAo7+dHbPhkgtuJ7Gg2/9A/SrQx+U5AUBsbgaQYbWJALIe+POkrgykK6o\nRnIBIxzRbdBv+fHiAds96Sl3BS64BP5RWxfCX4iNa20Oj6v4kqKcRTseVH8p9a2shJ4QRskjkGfU\nEGuZSkRESgkDgHgUQJ4gUziN5EIfAH5T5fenIO4ED81ItA43urKZSMKxHb9O9JWd54j/AIW6Kx3q\ngkpniQA43L7f0o11dwRskBnVZp28NAMk7sZ/pzUD1dqWv2enzw6Rb24nZfku7iYIiDzY5GMj3OKW\n0+x/D2VkyubmfaN15tUsxK8uT6E+lUX4mdR7NJn0u21GzaSbabkpIVZcNkjj1UDIHNK6H8ULCRtP\njee0tbaGAtdNISdoXgLEo5JJxyfKrpoPV+ja8QtlcMk7HCQzJsdh6gelWIlYl3ysqqO5NcZV8SNI\n0dy43BlGVA9zQzxCaIqd2099pwc5ql/EJtam1rpqx0OUp4k7SXKg4zEoGc+3J++Kca3qXTPTuTqc\nkLXbceEo8WVs+W0ZOP2qsdFTL1Prcur6kLaVpWaOCznRg1rGpPAH5cnIJPn9qvGtavDp2lyzzs9p\nAiFpGfgqo4GMeZ8sVkmp9Sabp0KdRyI5vJkaGw08MVKIDw8jA557896xrVtRu9UvXub2d5ZJHPLP\nnBPt6UwIZX+YcZokinK4Pn5UtsDKcckHijbSc8dqHaPUVZHVMM+VJAA2+R9aTuCDCf5e6r6ikppy\nY4oHkJhjHypnAGe+PemwD5OApJHAHnRAJPDVnTacfl3ZANAgaGQZY5ByCDxWw/DL4kmwhi07WCTZ\nqAiSbstGc4+pFbfZzR3Vsk1vIs0TjIdTwaZw6kP8Re0nha3fjw3cjbN/2n19qRmnt9Slf8FemC9h\nlMJJG07u+3B7+tdY6jeyatPp91FEPw8aSNMoYbi2cADt5Z700utClu5biXVdSZ4icwbVERtm8ije\nvr60pJZRacsuo3dxIs/h7HljyPGI/KSvbf5e+cVSuoeqdd0rVNM/zRa21v05cOqSNGd8jcfxj05G\nQM4q0axLFr+nLB0rrUMM0bqCIGGGXzAH09Kznqj4LyavqsN3p93NB4xZrw3TbmZvUY9alenPgzb2\n9hbwatcxztG5cyQpsYg/w7s9qudzqugdLyxWEUMs14kYwlvbmZkXHGSO3606s9UuZLSe51XS5klQ\n4jjjXxDIhOF+XyPqKc2uoTSeIh0+6s1I4uJQoVftnIx7ii6zqNv0zpEl/dzzzLGmNzEvu4zk47ce\ndVTSupoOuYdZMTSpptriNFtXKXDA+/HDZ7DtinE9r0z0tbJe6rFbWYVRtjYb5WPqT3Y1YNM1SxfT\nhfW1qLaGX52Mi+Gx49MZrG/i11dLfo1pdzmCxkzts48eIdp+VmY9g3H6VjbyyTMJZHLueDuNJS5L\ng4yBzmgdjwOx9DR4lQuhkLBMgEr3o8o/1CI2JjycZ74oQpxgN+tF2/X9asKgu5ds8Hy/tQknnvjs\nTmmTqZArKOfUcftR2VsJjg9t2eDQSRMQGbBHqDkCm7KSSE5HsKGJ5IWGDtfuCOCOc1fPh98QLrpp\ndryPNAXx+GfsQe7Z8j2/Wt80nV9H6s09PBeN93zGFjh1I9Pp6ioHrdb7TL+3vrO1trxIwVkO3bPD\nkYDB89/LkVTrn4h6roWl3LT9O3aSltrXN5Jhmcn5QOPmwPSrB0v8QorzRP8A+RWrsyDM5ijLeGPI\nsnfHuM1Ym6/6aFkJhesVwNsZhYOfTCkZNLadA/UoW91vS/AhQk20MpyxU/xMPInHapDS9A0vR5p7\nqzs44pJOXkUc/SjX2v6Tb2xeS9iYE7AqNlic4xijWNw1zE4X+L8jTNu3j6DGB5c05s7CCxWWYQxp\nNLgyMiY3nt2p2o3LnsQKQvULW7IWRUc7WLEdvPv7VgnxW6us9T1RdLsdTlg0mxUqXiHyySDjaM9x\n5Z+tZ7oGqa3azXtl01I0t1dlRiCMtK+Du4IHHPerjpukXGo3cP8AiUxuNTsmE+p3V3P/AKcAXlYg\ncnngE/pTb4ofExtdaGx0cmG1iJ3yRvxMfLA9PrWY3NxNdO0lxJJJIcfmOeAMAfSkdxbg5A9QKMz8\nDvn1pIt7ZNLQj5fMfelznBxgE+1Bn5doHNdhxx836VY2lXftDlMjB4zu79/2o9vNYpFML2KaR8Yi\naJgAh55PrUftdiioD4xOFwOc9qGUshKPuV1bBBHOe1JNnwznOCe+KKrDaAMhj557D3pO5Yb/AJJA\n5H8QyKR4AI88cYNTum9U3dq8IkllMcLBlw5Urx5VpfTnxiiXFtr1obqEAZuAB4mB5MOxrSNN13pr\nqsxPY3ttctEfEFrMoDbscEBu32qTl6bsZXjkNosciH5WViCo88Yo17daV07apJrV9CsZbELTgbu3\nYY78VB3/AF68ngr0/omoaiJHVfGMJSMAnk5Pc1M9YdSWPTenwy3t7bWbSuBunUthfMhRyT+1QNr1\n50v4xnXqPS5yB8sbxeCQT3OcE09HWdnqlvINC1nQ0uwMATylhu9P4c1WEvr6bVnh6rvr23uBLugk\njRltnHfCFeeMdzkVYOo+oLfS0hurrqSG3sVH+zGRI87egxk449BWUfEH4wxaxCtnp2kwGGM7llvB\nvIOO4XOAe/fNZbqusalfLBHfzSvFH+SIgKo+igYqwWfWV30rpp03p6exJnUPJexQnxuR+Ulu2PYV\nVp9RuJRJ4szt4hLOCxO4nkk+tIIcqG/L6Yo21CCOSQMd8UEYQcsTg8c80chTja3agdRgkZGcciuB\nKrny/elQcg7gc0YbSOe4oPmqfiV3Bb5SOzGkZFVHPz4UcH/4pOQFS3YY5APlQq5RCQSX79+/vTYy\nNuORlc5I/wDFELAEkNnzwaQl3YzkZz5HPNJl5Nvzgg9sCgTcZdxywx3rix27i3AwPTNDBctb3KPG\n5DIcjBI/cc1b9L+JvUWnBjHqdyy5wEkcuFH3pwnxMv59WivdSt7e7ZCQCygMM9zn1+1XyD462UFt\nGkOlSCRVI+cg4OO3GKresfELSNc1n8VrFtAw2ZBiiywwcgfN58Y7YqwaBrvRmpR+MdUsrFmG5rO/\n05WRD/3gc/rUb1bqXROp3KRXmpIklrGXW40i38NWbPyquRg4HfOKoY6mkTqZLr/HNae3jBVJ948Z\nVPkMnHpVf1jUri/1Ke5nu5Z5ZGJEkgAY+hPvTWe78WBIfBgBBzvAwx9j7Ug8ryMfFLMVAAyc4omQ\nXA8vpRlAII5z3zQo5wOTild64BP0oBMA208DtxR0Yc4FLcqMnH3PagP6+3qaDheDwfrmjhtoz3FE\n3n1qcLk7yx2qQCAO1Jl2UknsR2zzRJNxZMkEd+aT3AE5bOO+KRdnRcc7hx9jSbElTz8vfFFkZlV1\n4IPqM/vSIYkZbcR+ooY5yhzsBz55AIpN33H5Mnng/WiyOVBPBIPYjvSe4lu5DHnA4FFRxuIbOc54\nPnStvMLO+he9thMiOHeByVDj09ecimbyh5CyLgE9s9vvXJggfMeMjBoyNjdg5B7GjBzsAIJY8Ckg\nTkcefah2uO33x2oSH4wOBRcupwfOg3sickDPHaj78YJPPajeIdpxyO1CrAAZyWpRGYLxkGlzJn5m\nOG/WjKwySe45oScjI5xxig3cEMRjy5ou/wD5gVOQM23G44486LN+Z6Gb8p+tNoPyL/2miNy3PpRV\n/Kv0pFv9umiflf6Ghf8A3G/55VwOIuOOfKkyTzz6UmCQ/BNEP5F/7665OZjnnk96SP5ceVG//L9q\nMP8Abahi/wBr70b+Whb87/Sij8poH/M1JD8gpReSM+v9qWX+L60Vu/3p1H2eub8ufOlIgMDgUqgw\n3FNQTvPJ7UYAelf/2Q==\n" } }, "id": "0db42901-5dc0-4fd3-b2db-b83b3c584437" } ], "nbformat": 4, "nbformat_minor": 5, "metadata": {} } ================================================ FILE: wasm/examples/latex-to-docbook-with-mathml/options.json ================================================ { "from": "latex", "to": "docbook5", "html-math-method": "mathml", "standalone": true } ================================================ FILE: wasm/examples/latex-to-docbook-with-mathml/stdin ================================================ \newtheorem{theorem}{Theorem} \newtheorem{corollary}[theorem]{Corollary} \newtheorem{lemma}[theorem]{Lemma} \theoremstyle{definition} \newtheorem{definition}[theorem]{Definition} \theoremstyle{remark} \newtheorem{remark}{Remark} \begin{definition}[right-angled triangles] \label{def:tri} A \emph{right-angled triangle} is a triangle whose sides of length~\(a\), \(b\) and~\(c\), in some permutation of order, satisfies \(a^2+b^2=c^2\). \end{definition} \begin{lemma} The triangle with sides of length~\(3\), \(4\) and~\(5\) is right-angled. \end{lemma} \begin{proof} This lemma follows from \cref{def:tri} since \(3^2+4^2=9+16=25=5^2\). \end{proof} \begin{theorem}[Pythagorean triplets] \label{thm:py} Triangles with sides of length \(a=p^2-q^2\), \(b=2pq\) and \(c=p^2+q^2\) are right-angled triangles. \end{theorem} \begin{remark} These are all pretty interesting facts. \end{remark} ================================================ FILE: wasm/examples/latex-with-macros-to-restructured-text/options.json ================================================ { "from": "latex", "to": "rst", "standalone": true, "citeproc": false } ================================================ FILE: wasm/examples/latex-with-macros-to-restructured-text/stdin ================================================ % from https://en.wikibooks.org/wiki/LaTeX/Macros \newcommand{\wbalTwo}[2][Wikimedia]{ This is the Wikibook about LaTeX supported by {#1} and {#2}!} \begin{itemize} \item \wbalTwo{John Doe} \item \wbalTwo[lots of users]{John Doe} \end{itemize} ================================================ FILE: wasm/examples/lua-filters/count_words.lua ================================================ -- counts words in a document words = 0 characters = 0 characters_and_spaces = 0 process_anyway = false wordcount = { Str = function(el) -- we don't count a word if it's entirely punctuation: if el.text:match("%P") then words = words + 1 end characters = characters + utf8.len(el.text) characters_and_spaces = characters_and_spaces + utf8.len(el.text) end, Space = function(el) characters_and_spaces = characters_and_spaces + 1 end, Code = function(el) _,n = el.text:gsub("%S+","") words = words + n text_nospace = el.text:gsub("%s", "") characters = characters + utf8.len(text_nospace) characters_and_spaces = characters_and_spaces + utf8.len(el.text) end, CodeBlock = function(el) _,n = el.text:gsub("%S+","") words = words + n text_nospace = el.text:gsub("%s", "") characters = characters + utf8.len(text_nospace) characters_and_spaces = characters_and_spaces + utf8.len(el.text) end } function Pandoc(el) -- skip metadata, just count body: pandoc.walk_block(pandoc.Div(el.blocks), wordcount) el.blocks = el.blocks .. { pandoc.Para(words .. " words in body.") } return el end ================================================ FILE: wasm/examples/lua-filters/emph_to_allcaps.lua ================================================ function Emph(el) return el:walk({ Str = function(el) el.text = pandoc.text.upper(el.text) return el end }) end ================================================ FILE: wasm/examples/lua-filters/options.json ================================================ { "from": "markdown", "to": "html5", "standalone": false, "wrap": "preserve", "filters": ["emph_to_allcaps.lua", "count_words.lua"] } ================================================ FILE: wasm/examples/lua-filters/stdin ================================================ The first filter turns all *emphasized text* into ALL CAPS, even when it is [*embedded* in a *link*](https://pandoc.org). The second filter adds a word count to the end of the document. ================================================ FILE: wasm/examples/man-page-to-context/options.json ================================================ { "from": "man", "to": "context" } ================================================ FILE: wasm/examples/man-page-to-context/stdin ================================================ .TP \f[C]-L\f[R] \f[I]SCRIPT\f[R], \f[C]--lua-filter=\f[R]\f[I]SCRIPT\f[R] Transform the document in a similar fashion as JSON filters (see \f[C]--filter\f[R]), but use pandoc\[cq]s built-in Lua filtering system. The given Lua script is expected to return a list of Lua filters which will be applied in order. Each Lua filter must contain element-transforming functions indexed by the name of the AST element on which the filter function should be applied. .RS .PP The \f[C]pandoc\f[R] Lua module provides helper functions for element creation. It is always loaded into the script\[cq]s Lua environment. .PP See the Lua filters documentation for further details. .PP In order of preference, pandoc will look for Lua filters in .IP "1." 3 a specified full or relative path, .IP "2." 3 \f[C]$DATADIR/filters\f[R] where \f[C]$DATADIR\f[R] is the user data directory (see \f[C]--data-dir\f[R], above). .PP Filters, Lua filters, and citeproc processing are applied in the order specified on the command line. .RE .TP \f[C]-M\f[R] \f[I]KEY\f[R][\f[C]=\f[R]\f[I]VAL\f[R]], \f[C]--metadata=\f[R]\f[I]KEY\f[R][\f[C]:\f[R]\f[I]VAL\f[R]] Set the metadata field \f[I]KEY\f[R] to the value \f[I]VAL\f[R]. A value specified on the command line overrides a value specified in the document using YAML metadata blocks. Values will be parsed as YAML boolean or string values. If no value is specified, the value will be treated as Boolean true. Like \f[C]--variable\f[R], \f[C]--metadata\f[R] causes template variables to be set. But unlike \f[C]--variable\f[R], \f[C]--metadata\f[R] affects the metadata of the underlying document (which is accessible from filters and may be printed in some output formats) and metadata values will be escaped when inserted into the template. ================================================ FILE: wasm/examples/markdown-citations-to-plain-with-csl-style/le-tapuscrit-note.csl ================================================ ================================================ FILE: wasm/examples/markdown-citations-to-plain-with-csl-style/options.json ================================================ { "from": "markdown", "to": "plain", "citeproc": true, "csl": "le-tapuscrit-note.csl", "bibliography": "refs.bib" } ================================================ FILE: wasm/examples/markdown-citations-to-plain-with-csl-style/refs.bib ================================================ @book{legras_michel_2010, author = {Le~Gras, Gwénaëlle}, publisher = {Scope}, title = {Michel Simon~: l’art de la disgrâce}, series = {Jeux d’acteurs}, date = {2010}, address = {Paris}, isbn = {978-2-912573-52-0}, langid = {fre}} ================================================ FILE: wasm/examples/markdown-citations-to-plain-with-csl-style/stdin ================================================ --- csl: 'le-tapuscrit-note.csl' lang: fr-FR bibliography: refs.bib --- Foo [@legras_michel_2010]. ================================================ FILE: wasm/examples/markdown-to-docbook-with-citations/options.json ================================================ { "from": "markdown", "to": "docbook5", "standalone": true, "citeproc": true } ================================================ FILE: wasm/examples/markdown-to-docbook-with-citations/stdin ================================================ --- references: - author: - family: Salam given: Abdus container-title: "Elementary particle theory: Relativistic groups and analyticity. Proceedings of the eighth Nobel symposium" editor: - family: Svartholm given: Nils event-date: 1968-05-19/1968-05-25 event-place: Aspenäsgarden, Lerum id: salam issued: 1968 page: 367-377 publisher: Almquist & Wiksell publisher-place: Stockholm title: Weak and electromagnetic interactions type: paper-conference --- @salam [p. 370] says some interesting things. ================================================ FILE: wasm/examples/markdown-to-revealjs-slides/options.json ================================================ { "to": "revealjs", "from": "markdown", "standalone": true, "citeproc": false, "html-math-method": "mathjax", "highlight-style": "pygments", "template": null } ================================================ FILE: wasm/examples/markdown-to-revealjs-slides/stdin ================================================ --- title: Pandoc for TeXnicians author: John MacFarlane date: TUG 2020, 2020-07-26 theme: solarized header-includes: | ... # Overview ## - What is pandoc? - Using pandoc to convert to and from LaTeX - Why write in Markdown? - Overcoming Markdown's limitations # What is pandoc? ## ## Let's take it for a spin ``` % cat simple.tex \section{On $e=mc^2$}\label{einstein} ``` ``` % pandoc -f latex -t native simple.tex % pandoc -f latex -t html simple.tex % pandoc -t html --mathml simple.tex % pandoc -t html --mathjax simple.tex % pandoc -t -html --mathjax -s simple.tex % pandoc -t ms simple.tex % pandoc -t gfm simple.tex % pandoc -t context simple.tex % pandoc -t jats simple.tex ``` ## Some math Let's try with a sample TeX document by Professor A.J. Roberts at the University of Adelaide (CC licensed). ## Some math ``` % pandoc maths.tex -o maths.docx ``` . . . Two problems: - the use of a low-level TeX primitive `\mathcode`. - the use of `\parbox` (line 288) Fix by removing the `\mathcode` stuff and redefining the `\parmath` macro as a no-op: ```latex \newcommand{\parmath}[2][]{#2} ``` ## Take two ``` % pandoc maths.tex --number-sections -o maths.docx % open maths.docx ``` - AMS theorem environments come out right, including references. - Math is translated into native Word equation objects, which can be edited and which match the font, rather than images. - Still missing: equation numbers. ## Going the other way ``` % pandoc maths.docx -o newmaths.tex -s % xelatex newmaths % xelatex newmaths ``` ## Converting to HTML ``` % pandoc maths.tex -s -o maths.html --mathml \ --number-sections --toc % open maths.html ``` ## Comparison with latex2rtf ``` % latex2rtf maths.tex % open -a "Microsoft Word" maths.rtf ``` - References not resolved in Section 1 - Accents in Section 2 not above the letters, math generally ugly - Arrays in Section 8 totally broken; same with subequations in Section 9 - But at least we do get equation numbers in Section 9 ## Comparison with tex4ht ``` % make4ht maths % open maths.html ``` - Theorem environments not handled in Section 1 (except for one?). - Missing accents in Section 2. - Ugly equations that incorporate both text and images in different fonts. ## Comparison with Word from PDF ``` % pdflatex maths % pdflatex maths % open -a "Microsoft Word" maths.pdf ``` - Section 2, accents messed up. - Some formulas are rendered with images, others with regular characters, in non-matching font. - The 'where' in Section 6 is badly mispleacd. - The integral is missing in Section 7 - The diagonal ellipses are missing in the arrays ## Pandoc can interpret TeX macros ``` % cat macros.tex \newcommand{\nec}{\Box} \newcommand{\if}[2]{#1 \rightarrow #2} \newenvironment{warning}% {\begin{quote}\textbf{WARNING!}}% {\end{quote}} $\if{\nec \phi}{\phi}$ \begin{warning} Don't try this at home. \end{warning} ``` ``` % pandoc macros.tex -t html ``` ## Pandoc can resolve bibtex citations With the help of the `pandoc-citeproc` filter (included in the released binaries). ``` % pandoc --filter pandoc-citeproc bib.tex \ -t plain --csl ieee.csl ``` ## Limitations Pandoc is far from being able to convert arbitrary tex files with high accuracy. Let's try with a real-world example I got at random from arxiv. ``` % cd arxiv.2007.07694v1 % pandoc arxiv.tex -o arxiv.docx ``` # An alternative ## An alternative So you can't just write in LaTeX and expect to convert at the last minute to docx (for a publisher) or epub (for your students) or HTML (for your website). An alternative: write your document in pandoc's extended version of Markdown, which pandoc can convert with complete accuracy to any of its output formats. ## What is Markdown? Markdown is a set of conventions for indicating document formatting in plain text, mostly inherited from the pre-internet days of bulletin boards and email. It was designed in 2004 by John Gruber with help from Aaron Schwartz, and it is currently much used by programmers, and on forums like stackoverflow and reddit, and by data scientists via Jupyter notebooks and RMarkdown. ## Appealing things about Markdown The source text is readable as it is. When writing and revising, you don't have to parse through command-words which aren't part of the content. . . . If you're writing in a language other than English, you don't have to have English words sprinkled in the text. . . . There's no boilerplate at the beginning. The document just starts with the text. ## Real separation of content from formatting. \vspace{1em} > The paucity of means is the greatest virtue of markdown and > pandoc markdown. > > It is strangely difficult to get people to see the point, but the > defects of LaTeX for concentration, writing and thought, are at least > as great as those of Word, for the simple reason that it gives the > writer too much power; there is always another package to call in the > preamble, as there is always another drop down menu in Word. > ... > > In markdown - not to put too fine a point on it - the writer is only > ever faced with one question, and it is the right one: what the next > sentence should be. > > --- Michael Thompson, pandoc-discuss mailing list ## Appealing things about Markdown Using Markdown makes it possible to collaborate with others who don't know LaTeX. ## Appealing things about Markdown Markdown can be converted with complete, reliable accuracy into many different formats. It's often not enough just to produce a PDF. - JATS for publication or archiving - EPUB for convenient reading on mobile devices - Docx or ICML for a publisher - HTML for a website (or accessibility) - Jupyter notebook for research - Beamer or reveal.js slides for presentation TeX is a great assembly language for publication-quality documents. ## Limitations of Markdown John Gruber's original markdown syntax lacks support for: - [ ] tables - [ ] figures - [ ] footnotes - [ ] definition lists - [ ] ordered lists other than decimal-numbered - [ ] super/subscript - [ ] math - [ ] document metadata - [ ] attributes or metadata on individual elements like sections - [ ] labels and cross-references - [ ] numbering for running examples or equations ## Limitations of Markdown We couldn't live without these things in academic writing. And we definitely couldn't live without - [ ] bibtex/biblatex - [ ] macros How can we overcome these limitations? # Overcoming Markdown's limitations ## Pandoc's extended Markdown syntax - [x] tables (limited) - [x] figures (limited) - [x] math - [x] footnotes - [x] definition lists - [x] more flexible ordered lists - [x] running example lists - [x] super/subscript - [x] strikeout - [x] metadata - [x] attributes - [x] generic containers ## Pandoc also understands LaTeX macro definitions, which you can use for math (no matter what the output format). ## Labels and cross-references are still a work in progress, but you can get good support for them using an external filter, `pandoc-crossref`, by pandoc contributor Nikolay Yakimov. ## You can use the `--citeproc` filter to resolve citations in this syntax: ``` Blah blah [@putnam:empirical, p. 33; see also @dummett:empirical]. ``` Change the style by specifying a CSL stylesheet. (You can even change between author-date, numerical, and footnote sytles with no modifications to the source.) You can use your existing bibtex or biblatex bibliography file, or a CSL JSON bibliography such as can be produced by Zotero. ## LaTeX macros allow you to define new constructions that exactly fit what you're writing about. Can we recover this flexibility? ## Raw TeX in Markdown One approach is to just include bits of raw TeX in your markdown file. Pandoc allows that. - There is a special syntax for indicating chunks of raw TeX, but pandoc will also recognize obvious bits of raw TeX and pass them through as such. - The raw TeX chunks will be passed on unchanged if the output format is `latex`, `beamer`, or `context`, and otherwise simply omitted. ## ``` % cat raw.md % pandoc raw.md -o raw.pdf % open raw.pdf ``` But: ``` % pandoc raw.md -s -o raw.html % open raw.html ``` ## Drawbacks: - With this approach you lose the ability to target multiple formats. - Your source is now an ugly mix of Markdown and TeX, compromising readability. ## A better approach 1. Adopt the convention that a certain thing representable in pandoc's markdown should be interpreted as, say, a dropped capital letter. 2. Write a filter that does the interpretation. ## Example: drop caps In LaTeX we can use the `lettrine` package to get dropped capitals at the beginning of chapters: ```latex \lettrine{T}{his} is a pulley ``` We will use a generic bracketed span with a class to represent this in Markdown: ``` [This]{.dropcap} is a pulley. ``` ## Example: drop caps Now we need a filter that replaces `Span` elements with class `dropcap` in the Pandoc AST with something appropriate for the output format. ![](pandoc-architecture.pdf){height=3in} ## Two kinds of filters - **JSON filters** operate on a serialized JSON representation of the pandoc AST. They can be written in any language that can consume and produce JSON. - **Lua filters** use a Lua interpreter and environment built into pandoc. No external software need be installed, and the filters are more efficient, because we don't need to serialize and deserialize as JSON. Documentation: https://pandoc.org/lua-filters.html ## Example: drop caps In a Lua filter we define functions that match different kinds of AST elements. Here we want to match a Span. Create a file `dropcap.lua`: ```lua function Span(el) -- do something with the Span (el) -- return the transformed element or a new element end ``` ## Example: drop caps We only want to do something if the Span has the class `dropcap` and its contents begin with a Str element. ```lua function Span(el) if el.classes:includes('dropcap') then return make_dropcap(el.content) end end ``` ## Example: drop caps Now we just have to define `make_dropcap`. It takes a list of Inline elements (`el.content`) and returns a list of Inline elements. \small ```lua local function make_dropcap(els) if els[1] and els[1].t == 'Str' then -- arrays start at 1! local first_letter, rest = els[1].text:match('(%a)(.*)') if FORMAT == 'latex' then els[1] = pandoc.RawInline('latex', '\\lettrine{' .. first_letter .. '}{' .. rest .. '}') elseif FORMAT:match('html') then els[1] = pandoc.Span({ pandoc.Span(pandoc.Str(first_letter), {class='dropcap-first'}), pandoc.Span(pandoc.Str(rest), {class='dropcap-rest'})}) end return els end end ``` ## Example: drop caps ``` % pandoc -L dropcap.lua -t latex -o dropcap.pdf % pandoc -L dropcap.lua -t html -s --css dropcap.css \ dropcap.md -o dropcap.html ``` ## Example: tikz diagrams To get a tikz diagram, we could have a filter turn specially marked code blocks into images. In fact, there is already a very nice general diagram filter at https://github.com/pandoc/lua-filters. ``` % cat diagram.md % pandoc diagram.md -L diagram-generator.lua -s \ --extract-media=media -o diagram.html % pandoc diagram.md -L diagram-generator.lua \ -o diagram.docx ``` ## Example: theorems How to reproduce LaTeX `theorem` environments? Markdown version: ``` ::: {.theorem #pythagoras} #### Pythagoras's Theorem In a right triangle, the lengths of the two shorter sides $a$, $b$ and the longer side $c$ stand in the relation $$ a^2 + b^2 = c^2. $$ ::: ``` ## Example: theorems ``` % cat theorem.lua % cat theorem.md % pandoc -L theorem.lua theorem.md -t latex % pandoc theorem.md -L theorem.lua -t plain % pandoc theorem.md -L theorem.lua -t rst % pandoc theorem.md -L theorem.lua -t html ``` ## The end - For pandoc questions, come to pandoc-discuss on google groups: - For bug reports, the tracker at https://github.com/jgm/pandoc - If you'd like to improve pandoc's handling of LaTeX, we can always use new contributors! Questions? ================================================ FILE: wasm/examples/markdown-to-rst/options.json ================================================ { "to": "rst", "from": "markdown", "standalone": true, "embed-resources": false, "citeproc": false, "html-math-method": "plain", "wrap": "auto", "template": null } ================================================ FILE: wasm/examples/markdown-to-rst/stdin ================================================ --- author: - Albert Krewinkel - John MacFarlane date: 'January 10, 2020' title: Pandoc Lua Filters --- # Introduction Pandoc has long supported filters, which allow the pandoc abstract syntax tree (AST) to be manipulated between the parsing and the writing phase. [Traditional pandoc filters](https://pandoc.org/filters.html) accept a JSON representation of the pandoc AST and produce an altered JSON representation of the AST. They may be written in any programming language, and invoked from pandoc using the `--filter` option. Although traditional filters are very flexible, they have a couple of disadvantages. First, there is some overhead in writing JSON to stdout and reading it from stdin (twice, once on each side of the filter). Second, whether a filter will work will depend on details of the user's environment. A filter may require an interpreter for a certain programming language to be available, as well as a library for manipulating the pandoc AST in JSON form. One cannot simply provide a filter that can be used by anyone who has a certain version of the pandoc executable. Starting with version 2.0, pandoc makes it possible to write filters in Lua without any external dependencies at all. A Lua interpreter (version 5.3) and a Lua library for creating pandoc filters is built into the pandoc executable. Pandoc data types are marshaled to Lua directly, avoiding the overhead of writing JSON to stdout and reading it from stdin. Here is an example of a Lua filter that converts strong emphasis to small caps: ``` lua return { { Strong = function (elem) return pandoc.SmallCaps(elem.c) end, } } ``` or equivalently, ``` lua function Strong(elem) return pandoc.SmallCaps(elem.c) end ``` This says: walk the AST, and when you find a Strong element, replace it with a SmallCaps element with the same content. To run it, save it in a file, say `smallcaps.lua`, and invoke pandoc with `--lua-filter=smallcaps.lua`. Here's a quick performance comparison, converting the pandoc manual (MANUAL.txt) to HTML, with versions of the same JSON filter written in compiled Haskell (`smallcaps`) and interpreted Python (`smallcaps.py`): Command Time --------------------------------------- ------- `pandoc` 1.01s `pandoc --filter ./smallcaps` 1.36s `pandoc --filter ./smallcaps.py` 1.40s `pandoc --lua-filter ./smallcaps.lua` 1.03s As you can see, the Lua filter avoids the substantial overhead associated with marshaling to and from JSON over a pipe. # Lua filter structure Lua filters are tables with element names as keys and values consisting of functions acting on those elements. Filters are expected to be put into separate files and are passed via the `--lua-filter` command-line argument. For example, if a filter is defined in a file `current-date.lua`, then it would be applied like this: pandoc --lua-filter=current-date.lua -f markdown MANUAL.txt The `--lua-filter` option may be supplied multiple times. Pandoc applies all filters (including JSON filters specified via `--filter` and Lua filters specified via `--lua-filter`) in the order they appear on the command line. Pandoc expects each Lua file to return a list of filters. The filters in that list are called sequentially, each on the result of the previous filter. If there is no value returned by the filter script, then pandoc will try to generate a single filter by collecting all top-level functions whose names correspond to those of pandoc elements (e.g., `Str`, `Para`, `Meta`, or `Pandoc`). (That is why the two examples above are equivalent.) For each filter, the document is traversed and each element subjected to the filter. Elements for which the filter contains an entry (i.e. a function of the same name) are passed to Lua element filtering function. In other words, filter entries will be called for each corresponding element in the document, getting the respective element as input. The return value of a filter function must be one of the following: - nil: this means that the object should remain unchanged. - a pandoc object: this must be of the same type as the input and will replace the original object. - a list of pandoc objects: these will replace the original object; the list is merged with the neighbors of the original objects (spliced into the list the original object belongs to); returning an empty list deletes the object. The function's output must result in an element of the same type as the input. This means a filter function acting on an inline element must return either nil, an inline, or a list of inlines, and a function filtering a block element must return one of nil, a block, or a list of block elements. Pandoc will throw an error if this condition is violated. If there is no function matching the element's node type, then the filtering system will look for a more general fallback function. Two fallback functions are supported, `Inline` and `Block`. Each matches elements of the respective type. Elements without matching functions are left untouched. See [module documentation](#module-pandoc) for a list of pandoc elements. ## Filters on element sequences For some filtering tasks, it is necessary to know the order in which elements occur in the document. It is not enough then to inspect a single element at a time. There are two special function names, which can be used to define filters on lists of blocks or lists of inlines. [`Inlines (inlines)`]{#inlines-filter} : If present in a filter, this function will be called on all lists of inline elements, like the content of a [Para] (paragraph) block, or the description of an [Image]. The `inlines` argument passed to the function will be a [List] of [Inline] elements for each call. [`Blocks (blocks)`]{#blocks-filter} : If present in a filter, this function will be called on all lists of block elements, like the content of a [MetaBlocks] meta element block, on each item of a list, and the main content of the [Pandoc] document. The `blocks` argument passed to the function will be a [List] of [Block] elements for each call. These filter functions are special in that the result must either be nil, in which case the list is left unchanged, or must be a list of the correct type, i.e., the same type as the input argument. Single elements are **not** allowed as return values, as a single element in this context usually hints at a bug. See ["Remove spaces before normal citations"][Inlines filter example] for an example. This functionality has been added in pandoc 2.9.2. [Inlines filter example]: #remove-spaces-before-citations ## Traversal order The traversal order of filters can be selected by setting the key `traverse` to either `'topdown'` or `'typewise'`; the default is `'typewise'`. Example: ``` lua local filter = { traverse = 'topdown', -- ... filter functions ... } return {filter} ``` Support for this was added in pandoc 2.17; previous versions ignore the `traverse` setting. ### Typewise traversal Element filter functions within a filter set are called in a fixed order, skipping any which are not present: 1. functions for [*Inline* elements](#type-inline), 2. the [`Inlines`](#inlines-filter) filter function, 2. functions for [*Block* elements](#type-block) , 2. the [`Blocks`](#inlines-filter) filter function, 3. the [`Meta`](#type-meta) filter function, and last 4. the [`Pandoc`](#type-pandoc) filter function. It is still possible to force a different order by explicitly returning multiple filter sets. For example, if the filter for *Meta* is to be run before that for *Str*, one can write ``` lua -- ... filter definitions ... return { { Meta = Meta }, -- (1) { Str = Str } -- (2) } ``` Filter sets are applied in the order in which they are returned. All functions in set (1) are thus run before those in (2), causing the filter function for *Meta* to be run before the filtering of *Str* elements is started. ### Topdown traversal It is sometimes more natural to traverse the document tree depth-first from the root towards the leaves, and all in a single run. For example, a block list `[Plain [Str "a"], Para [Str "b"]]`{.haskell} will try the following filter functions, in order: `Blocks`, `Plain`, `Inlines`, `Str`, `Para`, `Inlines`, `Str`. Topdown traversals can be cut short by returning `false` as a second value from the filter function. No child-element of the returned element is processed in that case. For example, to exclude the contents of a footnote from being processed, one might write ``` lua traverse = 'topdown' function Note (n) return n, false end ``` ================================================ FILE: wasm/examples/mediawiki-to-docx-with-equations/options.json ================================================ { "from": "mediawiki", "to": "docx", "output-file": "vectors.docx", "standalone": true } ================================================ FILE: wasm/examples/mediawiki-to-docx-with-equations/stdin ================================================ Just as the components of a vector change when we change the [[basis (linear algebra)|basis]] of the vector space, the components of a tensor also change under such a transformation. Each type of tensor comes equipped with a ''transformation law'' that details how the components of the tensor respond to a [[change of basis]]. The components of a vector can respond in two distinct ways to a [[change of basis]] (see [[covariance and contravariance of vectors]]), where the new [[basis vectors]] \mathbf{\hat{e}}_i are expressed in terms of the old basis vectors \mathbf{e}_j as, :\mathbf{\hat{e}}_i = \sum_{j=1}^n \mathbf{e}_j R^j_i = \mathbf{e}_j R^j_i . Here ''R'''' j''''i'' are the entries of the change of basis matrix, and in the rightmost expression the [[summation]] sign was suppressed: this is the [[Einstein summation convention]], which will be used throughout this article.The Einstein summation convention, in brief, requires the sum to be taken over all values of the index whenever the same symbol appears as a subscript and superscript in the same term. For example, under this convention B_i C^i = B_1 C^1 + B_2 C^2 + \cdots B_n C^n The components ''v''''i'' of a column vector '''v''' transform with the [[matrix inverse|inverse]] of the matrix ''R'', :\hat{v}^i = \left(R^{-1}\right)^i_j v^j, where the hat denotes the components in the new basis. This is called a ''contravariant'' transformation law, because the vector components transform by the ''inverse'' of the change of basis. In contrast, the components, ''w''''i'', of a covector (or row vector), '''w''', transform with the matrix ''R'' itself, :\hat{w}_i = w_j R^j_i . ================================================ FILE: wasm/examples/ris-to-formatted-markdown-bibliography/options.json ================================================ { "to": "markdown_strict", "from": "ris", "standalone": false, "embed-resources": false, "citeproc": true, "html-math-method": "mathml", "wrap": "auto" } ================================================ FILE: wasm/examples/ris-to-formatted-markdown-bibliography/stdin ================================================ TY - JOUR T1 - On computable numbers, with an application to the Entscheidungsproblem A1 - Turing, Alan Mathison JO - Proceedings of London Mathematical Society VL - 47 IS - 1 SP - 230 EP - 265 Y1 - 1937 ER - TY - JOUR AU - Shannon, Claude E. PY - 1948 DA - July TI - A Mathematical Theory of Communication T2 - Bell System Technical Journal SP - 379 EP - 423 VL - 27 ER - ================================================ FILE: wasm/examples/rst-table-from-yaml-data/custom.rst ================================================ +------------------+----------+-----------------------------------------------+ | Species | Calories | Location | +==================+==========+===============================================+ $for(fish)$ ${ it:species()/left 16 "| " " | "}${ it.calories/right 8 "" " | " }${ it.location/left 45 "" " |"} +------------------+----------+-----------------------------------------------+ $endfor$ ================================================ FILE: wasm/examples/rst-table-from-yaml-data/options.json ================================================ { "to": "rst", "from": "markdown", "standalone": true, "citeproc": false, "html-math-method": "plain", "template": "custom.rst" } ================================================ FILE: wasm/examples/rst-table-from-yaml-data/species.rst ================================================ ${ it.species_name/uppercase } *${ it.scientific_name }* ================================================ FILE: wasm/examples/rst-table-from-yaml-data/stdin ================================================ --- # Data from https://www.fishwatch.gov fish: - calories: 92 human_health: texture: Firm and meaty. environmental_considerations: species_name: Shortfin Squid diseases_in_salmon: path: | /profiles/shortfin-squid habitat_impacts: | Fishing gears used to harvest shortfin squid have minimal impacts on habitat. location: | - Shortfin squid inhabits the continental shelf and slope waters of the Northwest Atlantic Ocean, from Newfoundland to the central east coast of Florida. - In the northwest Atlantic Ocean, shortfin squid are most often caught along the continental shelf break in depths between 150 to 275 meters. color: | Raw squid is ivory colored with orange speckling and a brown stripe that runs down the mantle. Cooked squid is opaque white. species_aliases: | [Illex squid](/species-aliases/illex-squid), [Summer squid](/species-aliases/summer-squid) image_gallery: harvest_type: Wild selenium: 44.8 mcg management: scientific_name: Illex illecebrosus production: fat_total: 1.38 g bycatch: | Regulations are in place to minimize bycatch. availability: | Summer and fall. research: fishing_rate: | At recommended level. sugars_total: | 0 g taste: | Mild, and subtly sweet.   health_benefits: | Squid are an excellent source of selenium, riboflavin, and vitamin B12. disease_treatment_and_prevention: species_illustration_photo: src: | https://www.fishwatch.gov/sites/default/files/Squid\_Illex\_NB\_W.png title: | Shortfin Squid alt: | shortfin squid saturated_fatty_acids_total: | 0.358 g quote: | U.S. wild-caught shortfin squid is a smart seafood choice because it is sustainably managed and responsibly harvested under U.S. regulations. carbohydrate: | 3.08 g serving_weight: | 100 g ecosystem_services: source: | U.S. wild-caught from Maine to North Carolina. noaa_fisheries_region: | Greater Atlantic animal_health: population_status: | - According to the latest assessment, shortfin squid is not subject to overfishing. There is currently not enough information to determine the population size, so it is unknown. population: | The population level is unknown. The species has a lifespan of less than one year. protein: | 15.58 g environmental_effects:   cholesterol: | 233 mg displayed_seafood_profile_illustration: fiber_total_dietary: | 0 g sodium: | 44 mg feeds: servings: | 1 - calories: 90 human_health: texture: The meat is firm and somewhat fibrous. The tail meat is firmer than the meat from the claws. environmental_considerations: species_name: American Lobster diseases_in_salmon: path: | /profiles/american-lobster habitat_impacts: | Fishing gears used to harvest American lobster have minimal impacts on habitat. location: | - American lobsters are found in the northwest Atlantic Ocean from Labrador to Cape Hatteras. They’re most abundant in coastal waters from Maine through New Jersey, and are also common offshore to depths of 2,300 feet from Maine through North Carolina. color: | The meat is white with red tinges. species_aliases: | [Lobster](/species-aliases/lobster) image_gallery: - src: | https://www.fishwatch.gov/sites/default/files/1.JPG title: | American Lobster alt: | American Lobster - src: | https://www.fishwatch.gov/sites/default/files/2\_6.jpg title: | American Lobster alt: | American Lobster - src: | https://www.fishwatch.gov/sites/default/files/3\_5.jpg title: | American Lobster alt: | American Lobster - src: | https://www.fishwatch.gov/sites/default/files/4\_0.png title: | American Lobster alt: | American Lobster - src: | https://www.fishwatch.gov/sites/default/files/5\_3.jpg title: | American Lobster alt: | American Lobster harvest_type: Wild selenium: 41.4 mcg management: scientific_name: Homarus americanus production: fat_total: 0.9 g bycatch: | Regulations are in place to minimize bycatch. availability: | Year-round. In New England, where most lobsters are landed, the peak harvest season extends from May to November. research: | - State scientists, in cooperation with the lobster industry, are conducting projects to assist with the effective management of the lobster resource. Many states have established [ventless trap survey](http://www.asmfc.org/fisheries-science/surveys)s to quantify the abundance of juvenile lobsters. By removing escape vents from the lobster traps and randomly placing those traps within certain depth categories and geographic areas, researchers can assess the abundance of juvenile lobsters and the potential for young lobsters to reach a size or life stage that can be caught by the fishing gear (recruitment) in the future. These surveys complement longstanding fishery-independent bottom trawl surveys conducted by NOAA Fisheries and the states. Because trawl gear cannot effectively sample rocky or shallow coastal bottom types, the ventless trap surveys attempt to fill this data gap by using fixed lobster gear without escape vents. fishing_rate: | At recommended levels. sugars_total: | 0 g taste: | Mild and sweet. health_benefits: | Lobster is low in saturated fat and is a very good source of protein and selenium. The FDA [advises](https://www.fda.gov/downloads/food/guidanceregulation/ucm252395.pdf) consumers to not eat the tomalley, the light-green substance found in the lobster. disease_treatment_and_prevention: species_illustration_photo: src: | https://www.fishwatch.gov/sites/default/files/Lobster\_American\_NB\_Web.png title: | American Lobster alt: | American Lobster saturated_fatty_acids_total: | 0.18 g quote: | U.S. wild-caught American lobster is a smart seafood choice because it is sustainably managed and responsibly harvested under U.S. regulations. carbohydrate: | 0.5 g serving_weight: | 100 g (raw) ecosystem_services: source: | U.S. wild-caught from Maine to North Carolina. noaa_fisheries_region: | Greater Atlantic animal_health: population_status: | - According to the [2015 stock assessment](http://www.asmfc.org/uploads/file/55d61d73AmLobsterStockAssmt_PeerReviewReport_Aug2015_red2.pdf) conducted by the [Atlantic States Marine Fisheries Commission](http://www.asmfc.org/) (ASMFC), there is record high stock abundance and recruitment in the Gulf of Maine and Georges Bank, and record low abundance and recruitment failure in Southern New England. The Gulf of Maine and Georges Bank stock is not overfished. However, the ASMFC considers the Southern New England stock severely depleted due to environmental factors and fishing pressure. Neither stock is subject to overfishing. - Since 2012, [Young of Year surveys](http://umaine.edu/wahlelab/american-lobster-settlement-index-alsi/american-lobster-settlement-index/) in the Gulf of Maine and George’s Bank stock have shown consistent declines, which could indicate future declines in recruitment and landings. population: | Above target population levels in the Gulf of Maine and Georges Bank. Significantly below target levels in Southern New England. protein: | 18.80 g environmental_effects: cholesterol: | 95 mg displayed_seafood_profile_illustration: fiber_total_dietary: | 0 g sodium: | 296 mg feeds: servings: | 1 - calories: 90 human_health: texture: Very lean with medium to firm texture and medium sized flakes. environmental_considerations: species_name: Yellowtail rockfish diseases_in_salmon: path: | /profiles/yellowtail-rockfish habitat_impacts: | Most fishing gear used to harvest yellowtail rockfish rarely contacts the ocean floor and has minimal impacts on habitat. Area closures and gear restrictions protect sensitive rocky, cold-water coral and sponge habitats from bottom trawl gear. location: | - Yellowtail rockfish are found along the Pacific coast of North America and range from Kodiak Island, Alaska to Baja California, Mexico. color: | Meat is glistening bright white with a pinkish sheen. species_aliases: | [Yellowtail rockfish](/species-aliases/yellowtail-rockfish), [Greenie](/species-aliases/greenie), [Yellow sea perch](/species-aliases/yellow-sea-perch), [Rock Cod](/species-aliases/rock-cod), [Pacific Snapper](/species-aliases/pacific-snapper) image_gallery: harvest_type: Wild selenium: 63 mcg management: scientific_name: Sebastes flavidus production: fat_total: 1.34 g bycatch: | Regulations are in place to minimize bycatch of overfished and protected species. availability: | Year-round. research: | - NOAA’s [Northwest](https://www.nwfsc.noaa.gov/) and [Alaska](Alaska) Fisheries Science Centers survey the abundance of yellowtail rockfish off the West Coast and Alaska. - Yellowtail rockfish is not typically assessed as part of a single-species abundance survey. It is more commonly assessed along with other groundfish. fishing_rate: | At recommended levels. sugars_total: | 0 taste: | Very mild, slightly sweet flavor. health_benefits: | Rockfish are high in selenium. disease_treatment_and_prevention: species_illustration_photo: src: | https://www.fishwatch.gov/sites/default/files/Rockfish\_Yellowtail\_NB\_W.png title: | Yellowtail rockfish alt: | Yellowtail rockfish saturated_fatty_acids_total: | 0.34 g quote: | U.S. wild-caught Yellowtail rockfish is a smart seafood choice because it is sustainably managed and responsibly harvested under U.S. regulations. carbohydrate: | 0 serving_weight: | 100 g (raw) ecosystem_services: source: | U.S. wild-caught from Kodiak Island Alaska to Baja California. noaa_fisheries_region: | West Coast, Alaska animal_health: population_status: | - According to the [2017 stock assessment](https://www.pcouncil.org/wp-content/uploads/2018/01/YTRK_2017_Final.pdf), the northern Pacific coast stock of yellowtail rockfish is not overfished and not subject to overfishing. - The yellowtail rockfish stock on the West Coast is part of the southern Pacific coast minor shelf rockfish complex. The overfished status of this complex is unknown. The stock complex is not subject to overfishing based on [2016 catch data](https://www.nwfsc.noaa.gov/research/divisions/fram/observation/pdf/Groundfish_Mortality_2016.pdf). population: | The northern Pacific coast stock is above its target population level. The southern Pacific coast stock is unknown. protein: | 18.36 g environmental_effects: cholesterol: | 50 mg displayed_seafood_profile_illustration: fiber_total_dietary: | 0 sodium: | 74 mg feeds: servings: | 1 - calories: 90 human_health: texture: Lean and medium-firm, with a fine flake. environmental_considerations: species_name: Bocaccio diseases_in_salmon: path: | /profiles/bocaccio habitat_impacts: | Area closures and gear restrictions protect sensitive rocky, cold-water coral and sponge habitats from bottom trawl gear. location: | - Bocaccio are found between Punta Blanca, Baja California, and the Gulf of Alaska off Krozoff and Kodiak Islands. Within this range, bocaccio is most common between Oregon and northern Baja California. - There are two partially isolated populations; one southern population centered in California, and one northern population centered in British Columbia.  color: | Whole fish should have shiny and bright skin. The raw flesh is white, but turns opaque white when cooked. species_aliases: | [Bocaccio](/species-aliases/bocaccio), [Rock Salmon](/species-aliases/rock-salmon), [Salmon Rockfish](/species-aliases/salmon-rockfish), [Pacific Red Snapper](/species-aliases/pacific-red-snapper), [Pacific Snapper](/species-aliases/pacific-snapper), [Oregon Red Snapper](/species-aliases/oregon-red-snapper), [Oregon Snapper](/species-aliases/oregon-snapper), [Longjaw](/species-aliases/longjaw), [Merou](/species-aliases/merou), [Jack](/species-aliases/jack), [Snapper](/species-aliases/snapper), [Rock Cod](/species-aliases/rock-cod), [Rockfish](/species-aliases/rockfish) image_gallery: harvest_type: Wild selenium: 63 mcg management: scientific_name: Sebastes paucispinis production: fat_total: 1.34 g bycatch: | Regulations are in place to minimize bycatch. availability: | Year-round. research: | - [New Fishing Opportunities Emerge from Resurgence of West Coast Groundfish](https://www.fisheries.noaa.gov/feature-story/new-fishing-opportunities-emerge-resurgence-west-coast-groundfish) - [Rebuilding success continues for West Coast groundfish](https://www.westcoast.fisheries.noaa.gov/stories/2017/19_06192017_.html) - [Threatened Yelloweye and Endangered Bocaccio in Puget Sound/Georgia Basin](https://www.westcoast.fisheries.noaa.gov/protected_species/rockfish/rockfish_in_puget_sound.html) fishing_rate: | At recommended level. sugars_total: | 0 taste: | Delicate, nutty, sweet flavor. health_benefits: | Low in saturated fat and very high in selenium, phosphorus, and potassium. disease_treatment_and_prevention: species_illustration_photo: src: | https://www.fishwatch.gov/sites/default/files/Bocaccio\_NB\_W.png title: | Bocaccio rockfish. alt: | Illustration of a Bocaccio rockfish. saturated_fatty_acids_total: | 0.34 g quote: | U.S. wild-caught bocaccio is a smart seafood choice because it is sustainably managed and responsibly harvested under U.S. regulations. carbohydrate: | 0 serving_weight: | 100 g (raw) ecosystem_services: source: | U.S. wild-caught from California to Alaska. noaa_fisheries_region: | West Coast, Alaska animal_health: population_status: | - According to the [2018 stock assessment](https://www.pcouncil.org/wp-content/uploads/2018/02/FINAL_2017_Bocaccio_Update_Assessment_February_2_2018.pdf), the bocaccio stock on the southern Pacific coast is not overfished, and is not subject to overfishing. The stock rebuilt in 2017, faster than estimated in the rebuilding plan, due in large part to several strong year classes and an improved understanding of the productivity of this stock. - Along the northern Pacific coast, bocaccio is part of the northern Pacific coast minor shelf rockfish complex and the status of this complex is unknown. - In the Gulf of Alaska, bocaccio is part of the other rockfish complex. - According to the [2017 stock assessment](https://www.afsc.noaa.gov/REFM/Docs/2017/GOAorock.pdf), the status of this complex is unknown. population: | Above target population levels. protein: | 18.36 g environmental_effects: cholesterol: | 50 mg displayed_seafood_profile_illustration: fiber_total_dietary: | 0 sodium: | 74 mg feeds: servings: | 1 - calories: 110 human_health: texture: A lean fish with fine-grained, dense meat. When cooked, the meat is firm yet flaky and tender. environmental_considerations: species_name: Atlantic Halibut diseases_in_salmon: path: | /profiles/atlantic-halibut habitat_impacts: | Trawl gear used to harvest Atlantic halibut have minimal or temporary effects on habitat. Area closures and gear restrictions protect sensitive habitats from bottom trawl gear. Hook and line gear has little or no impact on habitat. location: | - Atlantic halibut are found from Labrador and Greenland to Iceland, and from the Barents Sea south to the Bay of Biscay and Virginia. - In U.S. waters, halibut is most common in the Gulf of Maine. color: | Uncooked, white and almost translucent. It should not look dull, yellowish or dried out. When cooked, the meat is white. species_aliases: | [Atlantic halibut](/species-aliases/atlantic-halibut), [Halibut](/species-aliases/halibut) image_gallery: - src: | https://www.fishwatch.gov/sites/default/files/1%20-%20atl\_halibut\_noa.jpg title: | Atlantic halibut face and mouth. Photo credit: NOAA. alt: | Picture of an Atlantic halibut face and mouth. - src: | https://www.fishwatch.gov/sites/default/files/2%20-%20nefsc.jpg title: | Atlantic halibut. Photo credit: NOAA. alt: | Picture of Atlantic halibut. - src: | https://www.fishwatch.gov/sites/default/files/3%20-%20halibut2\_fullsize.jpg title: | Picture of Atlantic halibut. Photo credit: NOAA. alt: | Picture of Atlantic halibut. harvest_type: Wild selenium: 36.5 mcg management: scientific_name: Hippoglossus hippoglossus production: fat_total: 2.29 g bycatch: | Regulations are in place to minimize bycatch. availability: | Year-round. research: | - Scientists at NOAA’s [Northeast Fisheries Science Center](https://www.nefsc.noaa.gov/) conduct research bottom trawl surveys throughout the Northeast continental shelf every year during the fall and spring. These surveys collect data on the environment as well as biological samples from fish caught during research trawling. The data from these and other sources are used by scientists in stock assessments to estimate population size and fishing pressure. fishing_rate: | At recommended levels. sugars_total: | 0 taste: | Halibut has a very mild, sweet taste. health_benefits: | Halibut is low in saturated fat and sodium, and is a very good source of protein, niacin, phosphorus, and selenium. disease_treatment_and_prevention: species_illustration_photo: src: | https://www.fishwatch.gov/sites/default/files/atlantic-halibut-illustration.png title: | Illustration of Atlantic Halibut. alt: | Illustration of Atlantic Halibut. saturated_fatty_acids_total: | 0.325 g quote: | Although populations are well below target levels, U.S. wild-caught Atlantic halibut is still a smart seafood choice because it is sustainably managed under a rebuilding plan that allows limited harvest by U.S. fishermen. carbohydrate: | 0 serving_weight: | 100 g (raw) ecosystem_services: source: | Wild-caught from Maine to Connecticut. noaa_fisheries_region: | Greater Atlantic animal_health: population_status: | - The Atlantic halibut stock is at a very low level. Fishing is still allowed, but at reduced levels. - According to the [2012 stock assessment](https://www.nefsc.noaa.gov/publications/crd/crd1206/), the Atlantic halibut stock is overfished, but is not subject to overfishing. The estimated biomass is only 3 percent of its target level. It will remain in a rebuilding plan for the foreseeable future. population: | Significantly below target population levels. protein: | 20.81 g environmental_effects: cholesterol: | 32 mg displayed_seafood_profile_illustration: fiber_total_dietary: | 0 sodium: | 54 mg feeds: servings: | 1 - calories: 90 human_health: texture: Firm, coarse flake. environmental_considerations: species_name: Shortspine Thornyhead diseases_in_salmon: path: | /profiles/shortspine-thornyhead habitat_impacts: | The trawl, longline, and pot gear used to harvest shortspine thornyhead have minimal or temporary effects on habitat. Area closures and gear restrictions protect sensitive rocky, cold-water coral, and sponge habitats from bottom trawl gear. location: | - Shortspine thornyhead are found from the Bering Sea to Baja California, Mexico. color: | White. species_aliases: | [Thornyhead](/species-aliases/thornyhead), [Idiot fish](/species-aliases/idiot-fish), [Idiot cod](/species-aliases/idiot-cod), [Rockfish](/species-aliases/rockfish) image_gallery: - src: | https://www.fishwatch.gov/sites/default/files/1%20photo-west-coast-region-photo-gallery.jpg title: | Close-up photo of a shortspine thornyhead. (Photo credit: NOAA) alt: | Close-up photo of a shortspine thornyhead. (Photo credit: NOAA) - src: | https://www.fishwatch.gov/sites/default/files/2%20basket%20of%20shortspine%20thornyhead.jpg title: | Basket of shortspine thornyhead. (Photo credit: NOAA) alt: | Basket of shortspine thornyhead. (Photo credit: NOAA) - src: | https://www.fishwatch.gov/sites/default/files/3%20graphic%20with%20morphology%20ID\_large.jpg title: | Shortspine thornyhead graphic identifying several physical characteristics, including head spines, pelvic and anal fins. (Photo credit: NOAA) alt: | Shortspine thornyhead graphic identifying several physical characteristics, including head spines, pelvic and anal fins. (Photo credit: NOAA) - src: | https://www.fishwatch.gov/sites/default/files/4%20Head-on%20view%20of%20shortspine%20thornyhead.jpg title: | Head-on view of shortspine thornyhead. (Photo credit: NOAA) alt: | Head-on view of shortspine thornyhead. (Photo credit: NOAA) - src: | https://www.fishwatch.gov/sites/default/files/5%20shortspine%20thornyhead%20amongst%20its%20habitat.jpg title: | Shortspine thornyhead rockfish snuggled amongst a sea star, smaller brittle stars, and sea cucumbers with white tentacles on a mixed rocky and mud-covered habitat. (Photo credit: NOAA/OER) alt: | Shortspine thornyhead rockfish snuggled amongst a sea star, smaller brittle stars, and sea cucumbers with white tentacles on a mixed rocky and mud-covered habitat. (Photo credit: NOAA/OER) harvest_type: Wild selenium: 63 mcg management: scientific_name: Sebastolobus alascanus production: fat_total: 1.34 g bycatch: | Regulations are in place to minimize bycatch of overfished and protected species. availability: | Year-round. research: | [Tagging study of shortspine thornyhead in Alaska](http://agris.fao.org/agris-search/search.do?recordID=US201700202112) confirms that the management range is appropriate. fishing_rate: | At recommended levels. sugars_total: | 0 taste: | Sweet and mild. health_benefits: | Rockfish are high in selenium. disease_treatment_and_prevention: species_illustration_photo: src: | https://www.fishwatch.gov/sites/default/files/shortspine-thornyhead-illustration.png title: | Illustration of shortspine thornyhead. alt: | Illustration of shortspine thornyhead. saturated_fatty_acids_total: | 0.34 g quote: | U.S. wild-caught shortspine thornyhead is a smart seafood choice because it is sustainably managed and responsibly harvested under U.S. regulations. carbohydrate: | 0 serving_weight: | 100 g (raw) ecosystem_services: source: | U.S. wild-caught from the Bering Sea to Baja California, Mexico. noaa_fisheries_region: | West Coast animal_health: population_status: | - According to the [2013 stock assessment](https://www.pcouncil.org/wp-content/uploads/Shortspine_2013_Assessment.pdf), shortspine thornyhead on the Pacific Coast are not overfished and are not subject to overfishing based on the [2016 catch data](https://www.nwfsc.noaa.gov/research/divisions/fram/observation/pdf/Groundfish_Mortality_2016.pdf). - In the Gulf of Alaska, shortspine thornyhead are part of the thornyhead rockfish complex, which also contains longspine and broadfin thornyhead. - According to the [2018 stock assessment](https://www.fisheries.noaa.gov/resource/data/2018-assessment-thornyhead-stock-complex-gulf-alaska), the status of this complex is unknown. - According to the 2017 catch data, the complex was not subject to overfishing, and the fishery’s total allowable catch has not been attained since 1995. - In the Bering Sea and Aleutian Islands, shortspine thornyhead are part of the other rockfish complex. - According to the [2018 stock assessment](https://www.fisheries.noaa.gov/resource/data/2018-assessment-other-rockfish-stock-complex-bering-sea-and-aleutian-islands), the status of this complex is unknown. - According to the 2017 catch data, the complex was not subject to overfishing. population: | Above target population levels on the Pacific Coast. protein: | 18.36 g environmental_effects: cholesterol: | 50 mg displayed_seafood_profile_illustration: fiber_total_dietary: | 0 sodium: | 74 mg feeds: servings: | 1 ... ================================================ FILE: wasm/index.html ================================================ pandoc for the people

                pandoc for the people

                Convert documents without leaving the browser Help
                📄
                Drop files here or click to browse
                Supports: .md, .docx, .html, .tex, .rst, .epub, .odt, and more
                📄 {{ name }} {{ formatFileSize(files[name].size) }}
                Additional files (images, includes, etc.)
                🖼 {{ name }} {{ formatFileSize(file.size) }}

                Controls how tracked changes and comments in Word documents are handled during conversion.

                From ({{ inputFormat }})
                To ({{ effectiveOutputFormat }})
                📄 {{ file.name }}
                🎨 {{ file.name }}

                Output will be generated as Typst markup, then compiled to PDF.

                🔠 {{ file.name }}
                Files to include
                📄 {{ file.name }}
                📄 {{ file.name }}
                📄 {{ file.name }}
                📄 {{ file.name }}

                Drag to reorder. Filters are applied in order from top to bottom.

                Converting...

                {{ msg.text }}
                {{ outputPreview }}

                Loading pandoc.wasm...

                This is pandoc.wasm running in your browser. Nothing is being transmitted to the server!
                Copyright © 2026 John MacFarlane
                ================================================ FILE: wasm/index.js ================================================ import { zipSync, unzipSync, strToU8, strFromU8 } from 'https://esm.sh/fflate@0.8.2'; // Make fflate available globally window.fflate = { zipSync, unzipSync, strToU8, strFromU8 }; // Pandoc loading - starts immediately in background, but doesn't block UI let pandocReadyPromise = null; let onFormatsLoaded = null; // Callback to update Vue app when formats load function loadPandoc() { // Only load once if (pandocReadyPromise) return pandocReadyPromise; pandocReadyPromise = (async () => { // Dynamic import so WASM loading happens in background const { convert, query } = await import("./pandoc.js?sha1=SHA1_PANDOC_JS"); window.pandocModule = { convert, query }; // Query version and formats const pandocVersion = await query({ query: "version" }); document.getElementById("pandoc-version").innerText = pandocVersion; const inputFormats = await query({ query: "input-formats" }); const outputFormats = await query({ query: "output-formats" }); // Update Vue app if callback is registered if (onFormatsLoaded) { onFormatsLoaded(inputFormats, outputFormats); } })(); return pandocReadyPromise; } // Start loading immediately in background (fire and forget) loadPandoc().catch(err => console.error("Failed to load pandoc:", err)); // Lazy-load typst library only when needed let typstLoaded = false; let typstLoadingPromise = null; window.loadTypst = async function() { if (typstLoaded && typeof $typst !== 'undefined') return; if (typstLoadingPromise) return typstLoadingPromise; typstLoadingPromise = new Promise((resolve, reject) => { const typstScript = document.createElement('script'); typstScript.type = 'module'; typstScript.src = 'https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-all-in-one.ts@0.7.0-rc2/dist/esm/index.js'; typstScript.onload = () => { const checkTypst = () => { if (typeof $typst !== 'undefined') { typstLoaded = true; resolve(); } else { setTimeout(checkTypst, 100); } }; checkTypst(); }; typstScript.onerror = () => reject(new Error('Failed to load Typst library')); document.head.appendChild(typstScript); }); return typstLoadingPromise; }; // Petite Vue app definition window.pandocApp = function() { return { // State pandocReady: false, waitingForPandoc: false, selectedExample: '', showExamplesBar: false, inputMode: 'file', files: {}, fileOrder: [], auxFiles: {}, textInput: '', inputFormat: 'auto', outputFormat: 'auto', outputFilename: '', activeTab: 'general', dragOver: false, fileDraggingIdx: -1, fileDragOverIdx: -1, // Options opts: { standalone: true, toc: false, numberSections: false, fileScope: false, extractMedia: false, tocDepth: '3', shiftHeading: '0', tabStop: '4', preserveTabs: false, eol: '', dpi: '', numberOffset: '', resourcePath: '', defaultImageExtension: '', stripComments: false, trackChanges: '', citationMethod: '', mathMethod: '', highlightStyle: '', indentedCodeClasses: '', selfContained: false, htmlQTags: false, sectionDivs: false, asciiHtml: false, titlePrefix: '', idPrefix: '', emailObfuscation: '', referenceLinks: false, referenceLocation: '', markdownHeadings: '', referenceLinksRst: false, listTables: false, lof: false, lot: false, figureCaptionPosition: '', tableCaptionPosition: '', topLevelDivision: '', ascii: false, epubTitlePage: true, epubSubdirectory: '', splitLevel: '', chunkTemplate: '', ipynbOutput: '', slideLevel: '', incremental: false, wrap: 'auto', columns: '', linkImages: false }, // Metadata customMetadata: [{ key: '', value: '' }], customVariables: [{ key: '', value: '' }], // Resource files bibFile: null, cslFile: null, abbrevFile: null, abbreviationsFile: null, cssFiles: [], highlightThemeFile: null, syntaxDefinitions: [], templateFile: null, referenceDoc: null, metadataFile: null, epubCoverImage: null, epubMetadataFile: null, epubFonts: [], headerFiles: [], beforeBodyFiles: [], afterBodyFiles: [], luaFilters: [], filterDraggingIdx: -1, filterDragOverIdx: -1, // CSL cslStyleName: '', cslStylesList: [], cslStylesLoaded: false, // Extensions inputExtensions: {}, outputExtensions: {}, inputExtensionsList: [], outputExtensionsList: [], // Output hasChanges: false, isConverting: false, showOutput: false, output: null, outputFilenameActual: '', outputPreview: '', messages: [], verbosity: 'info', copyBtnText: '📋 Copy to Clipboard', mediaZip: null, // Format names: maps format ID to human-readable name // Some formats have different names for input vs output formatNames: { ansi: 'ANSI terminal', asciidoc: 'modern AsciiDoc', asciidoc_legacy: 'AsciiDoc for asciidoc-py', asciidoctor: 'AsciiDoctor (= modern AsciiDoc)', bbcode: 'BBCode', beamer: 'LaTeX Beamer slides', biblatex: 'BibLaTeX bibliography', bibtex: 'BibTeX bibliography', bits: 'BITS XML, alias for jats', chunkedhtml: 'zip of linked HTML files', commonmark: 'CommonMark Markdown', commonmark_x: 'CommonMark with extensions', context: 'ConTeXt', creole: 'Creole 1.0', csljson: 'CSL JSON bibliography', csv: 'CSV table', djot: 'Djot markup', docbook: 'DocBook v4', docbook5: 'DocBook v5', docx: 'Word', dokuwiki: 'DokuWiki markup', dzslides: 'DZSlides HTML slides', endnotexml: 'EndNote XML bibliography', epub: 'EPUB v3', epub2: 'EPUB v2', epub3: 'EPUB v3', fb2: 'FictionBook2', gfm: 'GitHub-Flavored Markdown', haddock: 'Haddock markup', html: 'HTML', html4: 'XHTML 1.0 Transitional', html5: 'HTML', icml: 'InDesign ICML', ipynb: 'Jupyter notebook', jats: 'JATS XML', jira: 'Jira/Confluence wiki markup', json: 'JSON version of native AST', latex: 'LaTeX', man: 'roff man', markdown: "Pandoc's Markdown", markdown_mmd: 'MultiMarkdown', markdown_phpextra: 'PHP Markdown Extra', markdown_strict: 'original unextended Markdown', markua: 'Markua', mdoc: 'mdoc manual page markup', mediawiki: 'MediaWiki markup', ms: 'roff ms', muse: 'Muse', native: 'native Haskell', odt: 'OpenDocument text', opendocument: 'OpenDocument XML', opml: 'OPML', org: 'Emacs Org mode', pdf: 'PDF via Typst', plain: 'plain text', pod: 'Perl POD', pptx: 'PowerPoint', revealjs: 'reveal.js HTML slides', ris: 'RIS bibliography', rst: 'reStructuredText', rtf: 'Rich Text Format', s5: 'S5 HTML slides', slideous: 'Slideous HTML slides', slidy: 'Slidy HTML slides', t2t: 'txt2tags', tei: 'TEI Simple', texinfo: 'GNU Texinfo', textile: 'Textile', tikiwiki: 'TikiWiki markup', tsv: 'TSV table', twiki: 'TWiki markup', typst: 'Typst', vimdoc: 'Vimdoc', vimwiki: 'Vimwiki', xlsx: 'Excel spreadsheet', xml: 'XML version of native AST', xwiki: 'XWiki markup', zimwiki: 'ZimWiki markup' }, // List of supported formats (populated when pandoc loads) inputFormats: [], outputFormats: [], formatsLoaded: false, // Format constants formatByExtension: { 'md': 'markdown', 'markdown': 'markdown', 'mkd': 'markdown', 'html': 'html', 'htm': 'html', 'tex': 'latex', 'latex': 'latex', 'rst': 'rst', 'org': 'org', 'docx': 'docx', 'odt': 'odt', 'epub': 'epub', 'txt': 'markdown', 'json': 'json', 'ipynb': 'ipynb', 'xml': 'docbook', 'wiki': 'mediawiki', 'textile': 'textile', 'rtf': 'rtf', 'bib': 'bibtex', 'csv': 'csv', 'tsv': 'tsv', 'typ': 'typst', 'typst': 'typst', 'pptx': 'pptx' }, extensionByFormat: { 'html': 'html', 'html5': 'html', 'html4': 'html', 'chunkedhtml': 'zip', 'markdown': 'md', 'markdown_strict': 'md', 'markdown_mmd': 'md', 'markdown_phpextra': 'md', 'gfm': 'md', 'commonmark': 'md', 'commonmark_x': 'md', 'latex': 'tex', 'beamer': 'tex', 'context': 'tex', 'pdf': 'pdf', 'docx': 'docx', 'odt': 'odt', 'epub': 'epub', 'epub2': 'epub', 'epub3': 'epub', 'rst': 'rst', 'org': 'org', 'plain': 'txt', 'json': 'json', 'native': 'native', 'docbook': 'xml', 'docbook4': 'xml', 'docbook5': 'xml', 'jats': 'xml', 'tei': 'xml', 'man': '1', 'rtf': 'rtf', 'textile': 'textile', 'mediawiki': 'wiki', 'asciidoc': 'adoc', 'asciidoctor': 'adoc', 'asciidoc_legacy': 'adoc', 'revealjs': 'html', 'slidy': 'html', 'slideous': 'html', 'dzslides': 'html', 's5': 'html', 'ipynb': 'ipynb', 'typst': 'typ', 'texinfo': 'texi', 'ms': 'ms', 'icml': 'icml', 'opml': 'opml', 'bibtex': 'bib', 'biblatex': 'bib', 'csljson': 'json', 'pptx': 'pptx', 'djot': 'dj', 'fb2': 'fb2', 'opendocument': 'xml', 'vimdoc': 'txt' }, slideFormats: ['revealjs', 'slidy', 'slideous', 'dzslides', 's5', 'beamer', 'pptx'], htmlFormats: ['html', 'html4', 'html5', 'revealjs', 'slidy', 'slideous', 'dzslides', 's5', 'epub', 'epub2', 'epub3', 'chunkedhtml'], docFormats: ['docx', 'odt', 'pptx'], binaryFormats: ['docx', 'odt', 'pptx', 'epub', 'epub2', 'epub3', 'pdf', 'chunkedhtml'], markdownFormats: ['markdown', 'markdown_strict', 'markdown_mmd', 'markdown_phpextra', 'gfm', 'commonmark', 'commonmark_x'], captionPositionFormats: ['html', 'html4', 'html5', 'latex', 'beamer', 'docx', 'odt', 'typst', 'pdf'], asciiFormats: ['html', 'html4', 'html5', 'markdown', 'markdown_strict', 'markdown_mmd', 'markdown_phpextra', 'gfm', 'commonmark', 'commonmark_x', 'docbook', 'docbook4', 'docbook5', 'jats', 'man', 'ms', 'latex', 'beamer'], topLevelDivisionFormats: ['latex', 'beamer', 'context', 'docbook', 'docbook4', 'docbook5', 'tei'], listOfFormats: ['latex', 'beamer', 'context'], latexCitationFormats: ['latex', 'beamer', 'context'], // Template variables by category templateVariablesByCategory: { all: ['title', 'author', 'date', 'subtitle', 'abstract', 'abstract-title', 'keywords', 'subject', 'description', 'category', 'lang', 'dir', 'header-includes', 'include-before', 'include-after', 'toc-title'], html: ['document-css', 'mainfont', 'fontsize', 'fontcolor', 'linkcolor', 'monofont', 'monobackgroundcolor', 'linestretch', 'maxwidth', 'backgroundcolor', 'margin-left', 'margin-right', 'margin-top', 'margin-bottom'], htmlSlides: ['institute', 'revealjs-url', 's5-url', 'slidy-url', 'slideous-url', 'title-slide-attributes'], beamer: ['aspectratio', 'beameroption', 'institute', 'logo', 'navigation', 'section-titles', 'theme', 'colortheme', 'fonttheme', 'innertheme', 'outertheme', 'themeoptions', 'titlegraphic'], latex: ['block-headings', 'classoption', 'documentclass', 'geometry', 'hyperrefoptions', 'indent', 'linestretch', 'margin-left', 'margin-right', 'margin-top', 'margin-bottom', 'pagestyle', 'papersize', 'secnumdepth', 'fontenc', 'fontfamily', 'fontfamilyoptions', 'fontsize', 'mainfont', 'sansfont', 'monofont', 'mathfont', 'CJKmainfont', 'mainfontoptions', 'sansfontoptions', 'monofontoptions', 'colorlinks', 'linkcolor', 'filecolor', 'citecolor', 'urlcolor', 'toccolor', 'links-as-notes', 'lof', 'lot', 'thanks', 'toc-depth', 'biblatexoptions', 'biblio-style', 'biblio-title', 'natbiboptions'], context: ['fontsize', 'headertext', 'footertext', 'indenting', 'interlinespace', 'layout', 'linkcolor', 'contrastcolor', 'linkstyle', 'lof', 'lot', 'mainfont', 'sansfont', 'monofont', 'mathfont', 'margin-left', 'margin-right', 'margin-top', 'margin-bottom', 'pagenumbering', 'papersize', 'whitespace'], typst: ['papersize', 'mainfont', 'fontsize', 'section-numbering', 'page-numbering', 'columns', 'thanks', 'mathfont', 'codefont', 'linestretch', 'linkcolor', 'filecolor', 'citecolor', 'margin'], ms: ['fontfamily', 'indent', 'lineheight', 'pointsize'], man: ['adjusting', 'footer', 'header', 'section'], epub: ['cover-image', 'epub-title-page'], docx: ['reference-doc'], pptx: ['monofont'], odt: ['reference-doc'] }, // Format to variable categories mapping formatVariableCategories: { html: ['all', 'html'], html4: ['all', 'html'], html5: ['all', 'html'], revealjs: ['all', 'html', 'htmlSlides'], slidy: ['all', 'html', 'htmlSlides'], slideous: ['all', 'html', 'htmlSlides'], dzslides: ['all', 'html', 'htmlSlides'], s5: ['all', 'html', 'htmlSlides'], epub: ['all', 'html', 'epub'], epub2: ['all', 'html', 'epub'], epub3: ['all', 'html', 'epub'], chunkedhtml: ['all', 'html'], latex: ['all', 'latex'], beamer: ['all', 'latex', 'beamer'], context: ['all', 'context'], typst: ['all', 'typst'], pdf: ['all', 'typst'], ms: ['all', 'ms'], man: ['all', 'man'], docx: ['all', 'docx'], pptx: ['all', 'pptx'], odt: ['all', 'odt'], opendocument: ['all', 'odt'] }, // Computed-like getters get canConvert() { const hasInput = this.inputMode === 'file' ? this.fileOrder.length > 0 : this.textInput.trim().length > 0; return hasInput && this.hasChanges; }, get effectiveOutputFormat() { if (this.outputFormat !== 'auto') return this.outputFormat; const outFile = this.outputFilename.trim() || this.outputFilenamePlaceholder; if (outFile && outFile !== '(preview)') { const ext = outFile.split('.').pop().toLowerCase(); const formatByOutputExtension = { 'html': 'html', 'htm': 'html', 'md': 'markdown', 'markdown': 'markdown', 'tex': 'latex', 'pdf': 'pdf', 'docx': 'docx', 'odt': 'odt', 'epub': 'epub', 'rst': 'rst', 'org': 'org', 'txt': 'plain', 'json': 'json', 'native': 'native', 'xml': 'docbook', 'rtf': 'rtf', 'adoc': 'asciidoc', 'ipynb': 'ipynb', 'typ': 'typst', 'pptx': 'pptx', 'dj': 'djot', 'man': 'man', '1': 'man', 'ms': 'ms', 'texi': 'texinfo', 'icml': 'icml', 'opml': 'opml', 'bib': 'bibtex', 'wiki': 'mediawiki' }; return formatByOutputExtension[ext] || 'html'; } return 'html'; }, get outputFilenamePlaceholder() { if (this.outputFormat !== 'auto') { const outExt = this.extensionByFormat[this.outputFormat] || this.outputFormat; if (this.fileOrder.length > 0) { const baseName = this.fileOrder[0].replace(/\.[^.]+$/, ''); return `${baseName}.${outExt}`; } return `output.${outExt}`; } return '(preview)'; }, get isHtmlFormat() { return this.htmlFormats.includes(this.effectiveOutputFormat); }, get isMarkdownFormat() { return this.markdownFormats.includes(this.effectiveOutputFormat); }, get isLatexLikeFormat() { return this.latexCitationFormats.includes(this.effectiveOutputFormat); }, get supportsCaptionPosition() { return this.captionPositionFormats.includes(this.effectiveOutputFormat); }, get supportsAscii() { return this.asciiFormats.includes(this.effectiveOutputFormat); }, get supportsTopLevelDivision() { return this.topLevelDivisionFormats.includes(this.effectiveOutputFormat); }, get supportsListOf() { return this.listOfFormats.includes(this.effectiveOutputFormat); }, get isBinaryOutput() { return this.binaryFormats.includes(this.effectiveOutputFormat); }, get filteredMessages() { if (this.verbosity === 'error') { return this.messages.filter(m => m.type === 'error'); } else if (this.verbosity === 'warning') { return this.messages.filter(m => m.type === 'error' || m.type === 'warning'); } return this.messages; }, get suggestedVariables() { const fmt = this.effectiveOutputFormat; const categories = this.formatVariableCategories[fmt] || ['all']; const vars = new Set(); for (const cat of categories) { const catVars = this.templateVariablesByCategory[cat] || []; for (const v of catVars) vars.add(v); } return Array.from(vars).sort(); }, get effectiveInputFormat() { let inFmt = this.inputFormat; if (inFmt === 'auto' && this.fileOrder.length > 0) { const ext = this.fileOrder[0].split('.').pop().toLowerCase(); inFmt = this.formatByExtension[ext] || ''; } return inFmt; }, get showTrackChangesTab() { return this.effectiveInputFormat === 'docx'; }, get showStripComments() { const fmt = this.effectiveInputFormat; return fmt === 'auto' || this.markdownFormats.includes(fmt) || fmt === 'textile'; }, get showDefaultImageExtension() { const fmt = this.effectiveInputFormat; return fmt === 'auto' || this.markdownFormats.includes(fmt) || fmt === 'latex'; }, get showAbbreviations() { const fmt = this.effectiveInputFormat; return fmt === 'auto' || fmt === 'markdown'; }, get showExtensionsTab() { return (this.inputFormat !== 'auto' && this.inputExtensionsList.length > 0) || (this.outputFormat !== 'auto' && this.outputExtensionsList.length > 0); }, get showFormatTab() { const fmt = this.effectiveOutputFormat; return this.showStripComments || this.showDefaultImageExtension || this.showAbbreviations || this.isHtmlFormat || this.isMarkdownFormat || fmt === 'rst' || ['latex', 'beamer'].includes(fmt) || ['typst', 'pdf'].includes(fmt) || this.supportsCaptionPosition || this.supportsAscii || this.supportsTopLevelDivision || this.supportsListOf || this.docFormats.includes(fmt); }, get formatTabName() { return 'Format-specific'; }, get isEpubFormat() { return ['epub', 'epub2', 'epub3'].includes(this.effectiveOutputFormat); }, get showChunkedTab() { return this.effectiveOutputFormat === 'chunkedhtml'; }, get showIpynbTab() { return this.effectiveOutputFormat === 'ipynb'; }, get showSlidesTab() { return this.slideFormats.includes(this.effectiveOutputFormat); }, get showReferenceDoc() { return this.docFormats.includes(this.effectiveOutputFormat); }, // Methods getFormatLabel(fmt, direction) { const name = this.formatNames[fmt] || fmt; return `${fmt} (${name})`; }, init() { // Register callback to populate formats when pandoc loads onFormatsLoaded = (inputFmts, outputFmts) => { this.inputFormats = inputFmts; this.outputFormats = outputFmts; this.formatsLoaded = true; }; console.log('Pandoc converter initialized (pandoc.wasm loading in background)'); }, async ensurePandocLoaded() { if (this.pandocReady) return; this.waitingForPandoc = true; try { await loadPandoc(); this.pandocReady = true; } finally { this.waitingForPandoc = false; } }, async loadExample() { const zipPath = this.selectedExample; if (!zipPath) return; try { // Fetch the example first const response = await fetch(zipPath + "?sha1=SHA1_EXAMPLES"); const arrayBuffer = await response.arrayBuffer(); // Ensure pandoc is loaded before processing (which auto-converts) await this.ensurePandocLoaded(); await this.loadExampleFromBuffer(arrayBuffer, zipPath.split('/').pop()); } catch (err) { this.messages.push({ type: 'error', text: `Failed to load example: ${err.message}` }); } }, async uploadExample(e) { const file = e.target.files[0]; if (!file) return; try { const arrayBuffer = await file.arrayBuffer(); e.target.value = ''; // Ensure pandoc is loaded before processing (which auto-converts) await this.ensurePandocLoaded(); await this.loadExampleFromBuffer(arrayBuffer, file.name); } catch (err) { this.showOutput = true; this.messages.push({ type: 'error', text: `Failed to load example: ${err.message}` }); } }, async loadExampleFromBuffer(arrayBuffer, filename) { const zipData = fflate.unzipSync(new Uint8Array(arrayBuffer)); // Clear existing state this.files = {}; this.fileOrder = []; this.auxFiles = {}; this.textInput = ''; this.templateFile = null; this.referenceDoc = null; this.bibFile = null; this.cslFile = null; this.cslStyleName = ''; this.cssFiles = []; this.headerFiles = []; this.beforeBodyFiles = []; this.afterBodyFiles = []; this.highlightThemeFile = null; this.syntaxDefinitions = []; this.abbrevFile = null; this.abbreviationsFile = null; this.metadataFile = null; this.epubCoverImage = null; this.epubMetadataFile = null; this.epubFonts = []; this.luaFilters = []; let optionsJson = null; let stdinContent = null; const extractedFiles = {}; // First pass: extract all files for (const [name, data] of Object.entries(zipData)) { if (name.endsWith('/') || data.length === 0) continue; const fname = name.split('/').pop(); if (fname === 'options.json') { const txt = fflate.strFromU8(data); optionsJson = JSON.parse(txt); } else if (fname === 'stdin') { stdinContent = fflate.strFromU8(data); } else { extractedFiles[fname] = new File([data], fname); } } // Helper to get file and remove from extractedFiles const takeFile = (name) => { if (name && extractedFiles[name]) { const f = extractedFiles[name]; delete extractedFiles[name]; return f; } return null; }; // Helper for arrays of filenames const takeFiles = (names) => { if (!names) return []; const arr = Array.isArray(names) ? names : [names]; return arr.map(takeFile).filter(f => f); }; // Route files based on options.json if (optionsJson) { // Template this.templateFile = takeFile(optionsJson.template); // Reference doc this.referenceDoc = takeFile(optionsJson['reference-doc']); // Bibliography const bibFiles = takeFiles(optionsJson.bibliography); if (bibFiles.length > 0) this.bibFile = bibFiles[0]; // CSL style this.cslFile = takeFile(optionsJson.csl); // CSS files this.cssFiles = takeFiles(optionsJson.css); // Include files this.headerFiles = takeFiles(optionsJson['include-in-header']); this.beforeBodyFiles = takeFiles(optionsJson['include-before-body']); this.afterBodyFiles = takeFiles(optionsJson['include-after-body']); // Highlight theme (if it's a file) const hlStyle = optionsJson['highlight-style']; if (hlStyle && extractedFiles[hlStyle]) { this.highlightThemeFile = takeFile(hlStyle); this.opts.highlightStyle = 'custom'; } else if (hlStyle) { this.opts.highlightStyle = hlStyle; } // Syntax definitions this.syntaxDefinitions = takeFiles(optionsJson['syntax-definition']); // Abbreviations this.abbrevFile = takeFile(optionsJson.abbreviations); // Metadata file this.metadataFile = takeFile(optionsJson['metadata-file']); // EPUB cover image this.epubCoverImage = takeFile(optionsJson['epub-cover-image']); // EPUB fonts this.epubFonts = takeFiles(optionsJson['epub-fonts']); // Lua filters this.luaFilters = takeFiles(optionsJson.filters); // Input files go to main files area const inputFileNames = optionsJson['input-files']; if (inputFileNames) { const inputArr = Array.isArray(inputFileNames) ? inputFileNames : [inputFileNames]; for (const name of inputArr) { const f = takeFile(name); if (f) { this.files[name] = f; this.fileOrder.push(name); } } } // Set input format if (optionsJson.from) { this.inputFormat = optionsJson.from.split(/[+-]/)[0]; } // Set output format if (optionsJson.to) { this.outputFormat = optionsJson.to.split(/[+-]/)[0]; } // Set standalone if (optionsJson.standalone !== undefined) { this.opts.standalone = optionsJson.standalone; } // Other options if (optionsJson['wrap']) this.opts.wrap = optionsJson['wrap']; if (optionsJson['table-of-contents']) this.opts.toc = true; if (optionsJson['number-sections']) this.opts.numberSections = true; if (optionsJson.citeproc) this.opts.citationMethod = 'citeproc'; if (optionsJson['embed-resources']) this.opts.selfContained = true; if (optionsJson['mathml']) this.opts.mathMethod = 'mathml'; } // Remaining files go to auxiliary files for (const [name, file] of Object.entries(extractedFiles)) { this.auxFiles[name] = file; } // Handle stdin content if (stdinContent) { this.inputMode = 'text'; this.textInput = stdinContent; } else if (this.fileOrder.length > 0) { this.inputMode = 'file'; } // Auto-convert after loading example await this.convert(); // Update extensions UI after conversion completes this.onInputFormatChange(); this.onOutputFormatChange(); }, async downloadAsExample() { const zipFiles = {}; // Build options for the example const opts = {}; if (this.inputFormat !== 'auto') opts.from = this.inputFormat; if (this.outputFormat !== 'auto') opts.to = this.outputFormat; if (this.opts.standalone) opts.standalone = true; if (this.opts.toc) opts['table-of-contents'] = true; if (this.opts.numberSections) opts['number-sections'] = true; if (this.opts.fileScope) opts['file-scope'] = true; if (this.opts.citationMethod === 'citeproc') opts.citeproc = true; if (this.opts.mathMethod === 'mathml') opts.mathml = true; if (this.opts.selfContained) opts['embed-resources'] = true; if (this.opts.highlightStyle && this.opts.highlightStyle !== 'custom') { opts['highlight-style'] = this.opts.highlightStyle; } // Helper to add a file to the zip const addFile = async (file, name) => { const arrayBuffer = await file.arrayBuffer(); zipFiles[name] = new Uint8Array(arrayBuffer); }; // Input files or stdin if (this.inputMode === 'text' && this.textInput.trim()) { zipFiles['stdin'] = fflate.strToU8(this.textInput); } else if (this.inputMode === 'file' && this.fileOrder.length > 0) { opts['input-files'] = this.fileOrder; for (const name of this.fileOrder) { if (this.files[name]) await addFile(this.files[name], name); } } // Auxiliary files for (const [name, file] of Object.entries(this.auxFiles)) { await addFile(file, name); } // Resource files if (this.templateFile) { opts.template = this.templateFile.name; await addFile(this.templateFile, this.templateFile.name); } if (this.referenceDoc) { opts['reference-doc'] = this.referenceDoc.name; await addFile(this.referenceDoc, this.referenceDoc.name); } if (this.bibFile) { opts.bibliography = this.bibFile.name; await addFile(this.bibFile, this.bibFile.name); } if (this.cslFile) { opts.csl = this.cslFile.name; await addFile(this.cslFile, this.cslFile.name); } if (this.abbrevFile) { opts['citation-abbreviations'] = this.abbrevFile.name; await addFile(this.abbrevFile, this.abbrevFile.name); } if (this.abbreviationsFile) { opts.abbreviations = this.abbreviationsFile.name; await addFile(this.abbreviationsFile, this.abbreviationsFile.name); } if (this.metadataFile) { opts['metadata-file'] = this.metadataFile.name; await addFile(this.metadataFile, this.metadataFile.name); } if (this.highlightThemeFile) { opts['highlight-style'] = this.highlightThemeFile.name; await addFile(this.highlightThemeFile, this.highlightThemeFile.name); } if (this.cssFiles.length > 0) { opts.css = this.cssFiles.map(f => f.name); for (const file of this.cssFiles) await addFile(file, file.name); } if (this.syntaxDefinitions.length > 0) { opts['syntax-definition'] = this.syntaxDefinitions.map(f => f.name); for (const file of this.syntaxDefinitions) await addFile(file, file.name); } if (this.headerFiles.length > 0) { opts['include-in-header'] = this.headerFiles.map(f => f.name); for (const file of this.headerFiles) await addFile(file, file.name); } if (this.beforeBodyFiles.length > 0) { opts['include-before-body'] = this.beforeBodyFiles.map(f => f.name); for (const file of this.beforeBodyFiles) await addFile(file, file.name); } if (this.afterBodyFiles.length > 0) { opts['include-after-body'] = this.afterBodyFiles.map(f => f.name); for (const file of this.afterBodyFiles) await addFile(file, file.name); } if (this.epubCoverImage) { opts['epub-cover-image'] = this.epubCoverImage.name; await addFile(this.epubCoverImage, this.epubCoverImage.name); } if (this.epubFonts.length > 0) { opts['epub-fonts'] = this.epubFonts.map(f => f.name); for (const file of this.epubFonts) await addFile(file, file.name); } if (this.luaFilters.length > 0) { opts.filters = this.luaFilters.map(f => f.name); for (const file of this.luaFilters) await addFile(file, file.name); } // Add options.json zipFiles['options.json'] = fflate.strToU8(JSON.stringify(opts, null, 2)); // Create zip and download const zipData = fflate.zipSync(zipFiles); const blob = new Blob([zipData], { type: 'application/zip' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'example.zip'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }, formatFileSize(bytes) { if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; }, downloadFile(file, name) { const url = URL.createObjectURL(file); const a = document.createElement('a'); a.href = url; a.download = name; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }, handleDrop(e) { this.dragOver = false; this.handleFiles(Array.from(e.dataTransfer.files)); }, handleFileInput(e) { this.handleFiles(Array.from(e.target.files)); e.target.value = ''; }, handleFiles(fileList) { fileList.forEach(file => { if (!this.files[file.name]) { this.fileOrder.push(file.name); } this.files[file.name] = file; }); this.updateExtensions(); this.hasChanges = true; }, removeFile(name) { delete this.files[name]; this.fileOrder = this.fileOrder.filter(n => n !== name); this.hasChanges = true; }, onFileDragStart(idx, e) { this.fileDraggingIdx = idx; e.dataTransfer.effectAllowed = 'move'; }, onFileDragOver(idx) { this.fileDragOverIdx = idx; }, onFileDrop(idx) { this.fileDragOverIdx = -1; if (this.fileDraggingIdx !== -1 && this.fileDraggingIdx !== idx) { const draggedName = this.fileOrder[this.fileDraggingIdx]; this.fileOrder.splice(this.fileDraggingIdx, 1); this.fileOrder.splice(idx, 0, draggedName); this.hasChanges = true; } }, handleAuxFiles(e) { Array.from(e.target.files).forEach(file => { this.auxFiles[file.name] = file; }); e.target.value = ''; this.hasChanges = true; }, handleAuxDir(e) { Array.from(e.target.files).forEach(file => { const path = file.webkitRelativePath || file.name; this.auxFiles[path] = file; }); e.target.value = ''; this.hasChanges = true; }, removeAuxFile(name) { delete this.auxFiles[name]; this.hasChanges = true; }, // Resource file handlers handleBibFile(e) { if (e.target.files[0]) { this.bibFile = e.target.files[0]; this.hasChanges = true; } }, handleCslFile(e) { if (e.target.files[0]) { this.cslFile = e.target.files[0]; this.cslStyleName = ''; this.hasChanges = true; } }, handleAbbrevFile(e) { if (e.target.files[0]) { this.abbrevFile = e.target.files[0]; this.hasChanges = true; } }, handleAbbreviationsFile(e) { if (e.target.files[0]) { this.abbreviationsFile = e.target.files[0]; this.hasChanges = true; } }, handleHighlightTheme(e) { if (e.target.files[0]) { this.highlightThemeFile = e.target.files[0]; this.hasChanges = true; } }, handleTemplateFile(e) { if (e.target.files[0]) { this.templateFile = e.target.files[0]; this.hasChanges = true; } }, handleReferenceDoc(e) { if (e.target.files[0]) { this.referenceDoc = e.target.files[0]; this.hasChanges = true; } }, handleMetadataFile(e) { if (e.target.files[0]) { this.metadataFile = e.target.files[0]; this.hasChanges = true; } }, handleEpubCover(e) { if (e.target.files[0]) { this.epubCoverImage = e.target.files[0]; this.hasChanges = true; } }, handleEpubMetadata(e) { if (e.target.files[0]) { this.epubMetadataFile = e.target.files[0]; this.hasChanges = true; } }, // Generic file list handlers addFilesToList(e, list) { Array.from(e.target.files).forEach(file => { if (!list.find(f => f.name === file.name)) list.push(file); }); e.target.value = ''; this.hasChanges = true; }, removeFileFromList(name, list) { const idx = list.findIndex(f => f.name === name); if (idx !== -1) list.splice(idx, 1); this.hasChanges = true; }, handleCssFiles(e) { this.addFilesToList(e, this.cssFiles); }, removeCssFile(name) { this.removeFileFromList(name, this.cssFiles); }, handleSyntaxDefs(e) { this.addFilesToList(e, this.syntaxDefinitions); }, removeSyntaxDef(name) { this.removeFileFromList(name, this.syntaxDefinitions); }, handleEpubFonts(e) { this.addFilesToList(e, this.epubFonts); }, removeEpubFont(name) { this.removeFileFromList(name, this.epubFonts); }, handleHeaderFiles(e) { this.addFilesToList(e, this.headerFiles); }, removeHeaderFile(name) { this.removeFileFromList(name, this.headerFiles); }, handleBeforeBodyFiles(e) { this.addFilesToList(e, this.beforeBodyFiles); }, removeBeforeBodyFile(name) { this.removeFileFromList(name, this.beforeBodyFiles); }, handleAfterBodyFiles(e) { this.addFilesToList(e, this.afterBodyFiles); }, removeAfterBodyFile(name) { this.removeFileFromList(name, this.afterBodyFiles); }, handleLuaFilters(e) { this.addFilesToList(e, this.luaFilters); }, removeLuaFilter(name) { this.removeFileFromList(name, this.luaFilters); }, onFilterDragStart(idx, e) { this.filterDraggingIdx = idx; e.dataTransfer.effectAllowed = 'move'; }, onFilterDragOver(idx) { this.filterDragOverIdx = idx; }, onFilterDrop(idx) { this.filterDragOverIdx = -1; if (this.filterDraggingIdx !== -1 && this.filterDraggingIdx !== idx) { const draggedFilter = this.luaFilters[this.filterDraggingIdx]; this.luaFilters.splice(this.filterDraggingIdx, 1); this.luaFilters.splice(idx, 0, draggedFilter); this.hasChanges = true; } }, // Metadata auto-add row onMetadataInput() { const last = this.customMetadata[this.customMetadata.length - 1]; if (last && (last.key || last.value)) { this.customMetadata.push({ key: '', value: '' }); } this.hasChanges = true; }, removeMetadata(idx) { this.customMetadata.splice(idx, 1); if (this.customMetadata.length === 0) { this.customMetadata.push({ key: '', value: '' }); } this.hasChanges = true; }, // Variables auto-add row onVariableInput() { const last = this.customVariables[this.customVariables.length - 1]; if (last && (last.key || last.value)) { this.customVariables.push({ key: '', value: '' }); } this.hasChanges = true; }, removeVariable(idx) { this.customVariables.splice(idx, 1); if (this.customVariables.length === 0) { this.customVariables.push({ key: '', value: '' }); } this.hasChanges = true; }, // CSL styles async loadCslStyles() { if (this.cslStylesLoaded) return; try { const response = await fetch('https://data.jsdelivr.com/v1/package/gh/citation-style-language/styles@master/flat'); if (!response.ok) throw new Error('Failed to fetch styles list'); const data = await response.json(); this.cslStylesList = data.files .map(f => f.name) .filter(name => name.endsWith('.csl') && name.lastIndexOf('/') === 0) .map(name => name.slice(1, -4)) .sort(); this.cslStylesLoaded = true; } catch (err) { console.error('Failed to load CSL styles list:', err); this.cslStylesList = ['apa', 'chicago-author-date', 'ieee', 'modern-language-association', 'vancouver', 'harvard-cite-them-right', 'nature', 'science']; } }, async fetchCslStyle() { if (!this.cslStyleName.trim()) { alert('Please enter or select a style name'); return; } try { const url = `https://cdn.jsdelivr.net/gh/citation-style-language/styles@master/${this.cslStyleName}.csl`; const response = await fetch(url); if (!response.ok) throw new Error(`Style "${this.cslStyleName}" not found`); const cslContent = await response.text(); const blob = new Blob([cslContent], { type: 'application/xml' }); this.cslFile = new File([blob], `${this.cslStyleName}.csl`, { type: 'application/xml' }); this.hasChanges = true; } catch (err) { alert(`Failed to fetch style: ${err.message}`); } }, // Extensions async updateExtensions() { this.inputExtensions = {}; this.outputExtensions = {}; this.inputExtensionsList = []; this.outputExtensionsList = []; if (this.inputFormat !== 'auto') { try { const extData = await window.pandocModule.query({ query: "extensions-for-format", format: this.inputFormat }); this.inputExtensionsList = Object.entries(extData) .map(([name, defaultOn]) => ({ name, defaultOn })) .sort((a, b) => a.name.localeCompare(b.name)); } catch (e) { console.warn('Could not get input extensions:', e); } } const outFmt = this.outputFormat === 'pdf' ? 'typst' : this.outputFormat; if (outFmt !== 'auto') { try { const extData = await window.pandocModule.query({ query: "extensions-for-format", format: outFmt}); this.outputExtensionsList = Object.entries(extData) .map(([name, defaultOn]) => ({ name, defaultOn })) .sort((a, b) => a.name.localeCompare(b.name)); } catch (e) { console.warn('Could not get output extensions:', e); } } }, getExtensionChecked(direction, ext) { const extState = direction === 'input' ? this.inputExtensions : this.outputExtensions; if (extState[ext.name] !== undefined) return extState[ext.name]; return ext.defaultOn; }, toggleExtension(direction, ext, e) { const extState = direction === 'input' ? this.inputExtensions : this.outputExtensions; if (e.target.checked) { extState[ext.name] = true; } else { if (ext.defaultOn) { extState[ext.name] = false; } else { delete extState[ext.name]; } } }, onInputFormatChange() { this.updateExtensions(); }, onOutputFormatChange() { this.updateExtensions(); }, buildFormatWithExtensions(baseFormat, extensions) { if (!baseFormat || baseFormat === 'auto') return baseFormat; let formatStr = baseFormat; for (const [ext, enabled] of Object.entries(extensions)) { if (enabled === true) formatStr += `+${ext}`; else if (enabled === false) formatStr += `-${ext}`; } return formatStr; }, buildOptions() { const opts = {}; // Input format if (this.inputFormat !== 'auto') { opts.from = this.buildFormatWithExtensions(this.inputFormat, this.inputExtensions); } else if (this.inputMode === 'file' && this.fileOrder.length > 0) { const ext = this.fileOrder[0].split('.').pop().toLowerCase(); if (this.formatByExtension[ext]) { opts.from = this.buildFormatWithExtensions(this.formatByExtension[ext], this.inputExtensions); } } // Output format if (this.outputFormat !== 'auto') { opts.to = this.buildFormatWithExtensions(this.outputFormat, this.outputExtensions); } // Output file let finalOutFile = this.outputFilename.trim() || this.outputFilenamePlaceholder; if (!finalOutFile || finalOutFile === '(preview)') { const effectiveFmt = this.effectiveOutputFormat || 'html'; const defaultExt = this.extensionByFormat[effectiveFmt] || 'html'; finalOutFile = `output.${defaultExt}`; } opts['output-file'] = finalOutFile; this.outputFilenameActual = finalOutFile; // Input files if (this.inputMode === 'file') { opts['input-files'] = this.fileOrder; } // General options if (this.opts.standalone) opts.standalone = true; if (this.opts.toc) { opts['table-of-contents'] = true; opts['toc-depth'] = parseInt(this.opts.tocDepth); } if (this.opts.numberSections) opts['number-sections'] = true; if (this.opts.fileScope) opts['file-scope'] = true; const shiftHeading = parseInt(this.opts.shiftHeading); if (shiftHeading !== 0) opts['shift-heading-level-by'] = shiftHeading; if (this.opts.preserveTabs) opts['preserve-tabs'] = true; const tabStop = parseInt(this.opts.tabStop); if (tabStop !== 4) opts['tab-stop'] = tabStop; if (this.opts.eol) opts.eol = this.opts.eol; if (this.opts.dpi) opts.dpi = parseInt(this.opts.dpi); if (this.opts.numberOffset.trim()) { opts['number-offset'] = this.opts.numberOffset.trim().split(',').map(n => parseInt(n.trim()) || 0); } if (this.opts.resourcePath.trim()) opts['resource-path'] = this.opts.resourcePath.trim(); if (this.opts.stripComments) opts['strip-comments'] = true; if (this.opts.defaultImageExtension.trim()) opts['default-image-extension'] = this.opts.defaultImageExtension.trim(); if (this.abbreviationsFile) opts.abbreviations = this.abbreviationsFile.name; // Track changes if (this.opts.trackChanges) opts['track-changes'] = this.opts.trackChanges; // Citations if (this.opts.citationMethod === 'citeproc') opts.citeproc = true; else if (this.opts.citationMethod === 'natbib') opts.natbib = true; else if (this.opts.citationMethod === 'biblatex') opts.biblatex = true; if (this.bibFile) opts.bibliography = this.bibFile.name; if (this.opts.citationMethod === 'citeproc') { if (this.cslFile) opts.csl = this.cslFile.name; if (this.abbrevFile) opts['citation-abbreviations'] = this.abbrevFile.name; } // Math if (this.opts.mathMethod) opts['html-math-method'] = this.opts.mathMethod; // Code highlighting if (this.opts.highlightStyle === 'custom' && this.highlightThemeFile) { opts['highlight-style'] = this.highlightThemeFile.name; } else if (this.opts.highlightStyle && this.opts.highlightStyle !== 'custom') { opts['highlight-style'] = this.opts.highlightStyle; } if (this.syntaxDefinitions.length > 0) { opts['syntax-definition'] = this.syntaxDefinitions.map(f => f.name); } if (this.opts.indentedCodeClasses.trim()) { opts['indented-code-classes'] = this.opts.indentedCodeClasses.trim(); } const baseOutFmt = this.effectiveOutputFormat; // HTML options if (this.htmlFormats.includes(baseOutFmt)) { if (this.opts.selfContained) opts['embed-resources'] = true; if (this.opts.htmlQTags) opts['html-q-tags'] = true; if (this.opts.sectionDivs) opts['section-divs'] = true; if (this.opts.titlePrefix.trim()) opts['title-prefix'] = this.opts.titlePrefix.trim(); if (this.opts.emailObfuscation) opts['email-obfuscation'] = this.opts.emailObfuscation; if (this.opts.idPrefix.trim()) opts['id-prefix'] = this.opts.idPrefix.trim(); if (this.cssFiles.length > 0) opts.css = this.cssFiles.map(f => f.name); } // Markdown options if (this.markdownFormats.includes(baseOutFmt)) { if (this.opts.referenceLinks) opts['reference-links'] = true; if (this.opts.referenceLocation) opts['reference-location'] = this.opts.referenceLocation; if (this.opts.markdownHeadings) opts['markdown-headings'] = this.opts.markdownHeadings; } // RST options if (baseOutFmt === 'rst') { if (this.opts.referenceLinksRst) opts['reference-links'] = true; if (this.opts.listTables) opts['list-tables'] = true; } // Caption position if (this.captionPositionFormats.includes(baseOutFmt)) { if (this.opts.figureCaptionPosition) opts['figure-caption-position'] = this.opts.figureCaptionPosition; if (this.opts.tableCaptionPosition) opts['table-caption-position'] = this.opts.tableCaptionPosition; } // ASCII if (this.asciiFormats.includes(baseOutFmt)) { const asciiChecked = this.htmlFormats.includes(baseOutFmt) ? this.opts.asciiHtml : this.opts.ascii; if (asciiChecked) opts.ascii = true; } // List of figures/tables if (this.listOfFormats.includes(baseOutFmt)) { if (this.opts.lof) opts['list-of-figures'] = true; if (this.opts.lot) opts['list-of-tables'] = true; } // Top-level division if (this.topLevelDivisionFormats.includes(baseOutFmt) && this.opts.topLevelDivision) { opts['top-level-division'] = this.opts.topLevelDivision; } // EPUB options if (['epub', 'epub2', 'epub3'].includes(baseOutFmt)) { if (this.epubCoverImage) opts['epub-cover-image'] = this.epubCoverImage.name; if (this.epubMetadataFile) opts['epub-metadata'] = this.epubMetadataFile.name; if (this.epubFonts.length > 0) opts['epub-fonts'] = this.epubFonts.map(f => f.name); if (!this.opts.epubTitlePage) opts['epub-title-page'] = false; if (this.opts.epubSubdirectory.trim()) opts['epub-subdirectory'] = this.opts.epubSubdirectory.trim(); } // ODT options if (baseOutFmt === 'odt') { if (this.opts.linkImages) opts['link-images'] = true; } // Template if (this.templateFile) opts.template = this.templateFile.name; // Include files if (this.headerFiles.length > 0) opts['include-in-header'] = this.headerFiles.map(f => f.name); if (this.beforeBodyFiles.length > 0) opts['include-before-body'] = this.beforeBodyFiles.map(f => f.name); if (this.afterBodyFiles.length > 0) opts['include-after-body'] = this.afterBodyFiles.map(f => f.name); // Variables const variables = {}; this.customVariables.forEach(v => { if (v.key.trim() && v.value.trim()) variables[v.key.trim()] = v.value.trim(); }); if (Object.keys(variables).length > 0) opts.variables = variables; // Chunked HTML if (baseOutFmt === 'chunkedhtml') { if (this.opts.splitLevel) opts['split-level'] = parseInt(this.opts.splitLevel); if (this.opts.chunkTemplate.trim()) opts['chunk-template'] = this.opts.chunkTemplate.trim(); } // Notebook if (baseOutFmt === 'ipynb' && this.opts.ipynbOutput) { opts['ipynb-output'] = this.opts.ipynbOutput; } // Reference doc if (this.docFormats.includes(baseOutFmt) && this.referenceDoc) { opts['reference-doc'] = this.referenceDoc.name; } // Slides if (this.slideFormats.includes(baseOutFmt)) { if (this.opts.slideLevel) opts['slide-level'] = parseInt(this.opts.slideLevel); if (this.opts.incremental) opts.incremental = true; } // Metadata file if (this.metadataFile) opts['metadata-file'] = this.metadataFile.name; // Metadata const metadata = {}; this.customMetadata.forEach(m => { if (m.key.trim() && m.value.trim()) { const key = m.key.trim(); const value = m.value.trim(); // author should be an array if (key === 'author') { metadata[key] = [value]; } else { metadata[key] = value; } } }); if (Object.keys(metadata).length > 0) opts.metadata = metadata; // Wrapping if (this.opts.wrap !== 'auto') opts.wrap = this.opts.wrap; if (this.opts.columns.trim()) opts.columns = parseInt(this.opts.columns); // Extract media if (this.opts.extractMedia) opts['extract-media'] = 'media.zip'; // Lua filters if (this.luaFilters.length > 0) { opts.filters = this.luaFilters.map(f => f.name); } return opts; }, async convert() { // Ensure pandoc is loaded before converting await this.ensurePandocLoaded(); this.isConverting = true; this.showOutput = false; this.messages = []; this.output = null; this.outputPreview = ''; this.mediaZip = null; try { const options = this.buildOptions(); // Build files object const files = { ...this.files, ...this.auxFiles }; if (this.bibFile) files[this.bibFile.name] = this.bibFile; if (this.cslFile) files[this.cslFile.name] = this.cslFile; if (this.abbrevFile) files[this.abbrevFile.name] = this.abbrevFile; if (this.abbreviationsFile) files[this.abbreviationsFile.name] = this.abbreviationsFile; this.cssFiles.forEach(file => { files[file.name] = file; }); if (this.highlightThemeFile) files[this.highlightThemeFile.name] = this.highlightThemeFile; this.syntaxDefinitions.forEach(file => { files[file.name] = file; }); if (this.templateFile) files[this.templateFile.name] = this.templateFile; if (this.referenceDoc) files[this.referenceDoc.name] = this.referenceDoc; if (this.metadataFile) files[this.metadataFile.name] = this.metadataFile; if (this.epubCoverImage) files[this.epubCoverImage.name] = this.epubCoverImage; if (this.epubMetadataFile) files[this.epubMetadataFile.name] = this.epubMetadataFile; this.epubFonts.forEach(font => { files[font.name] = font; }); this.headerFiles.forEach(file => { files[file.name] = file; }); this.beforeBodyFiles.forEach(file => { files[file.name] = file; }); this.afterBodyFiles.forEach(file => { files[file.name] = file; }); this.luaFilters.forEach(file => { files[file.name] = file; }); const stdin = this.inputMode === 'text' ? this.textInput : null; const isPdfTypst = this.outputFormat === 'pdf'; if (isPdfTypst) { await window.loadTypst(); const typstOptions = { ...options }; typstOptions.to = this.buildFormatWithExtensions('typst', this.outputExtensions); typstOptions.standalone = true; delete typstOptions['output-file']; const typstResult = await window.pandocModule.convert(typstOptions, stdin, files); if (typstResult.stderr && typstResult.stderr.includes('ERROR')) { throw new Error(typstResult.stderr); } const typstContent = typstResult.stdout; let pdfData; try { $typst.resetShadow(); for (const [path, file] of Object.entries(files)) { const arrayBuffer = await file.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); $typst.mapShadow('/' + path, uint8Array); if (!path.startsWith('/')) $typst.mapShadow(path, uint8Array); } const mainTypstPath = '/main.typ'; const typstBytes = new TextEncoder().encode(typstContent); $typst.mapShadow(mainTypstPath, typstBytes); pdfData = await $typst.pdf({ mainFilePath: mainTypstPath }); } catch (typstError) { const errorStr = String(typstError); const messageMatch = errorStr.match(/message:\s*"([^"]+)"/); let errorMsg = messageMatch ? messageMatch[1] : errorStr; throw new Error(`Typst: ${errorMsg}`); } if (!pdfData || pdfData.length === 0) { throw new Error('Typst produced empty PDF output'); } const pdfBlob = new Blob([pdfData], { type: 'application/pdf' }); this.output = pdfBlob; this.displayResults(typstResult, options, files); } else { const result = await window.pandocModule.convert(options, stdin, files); this.displayResults(result, options, files); } } catch (err) { this.showOutput = true; this.messages.push({ type: 'error', text: `Conversion failed: ${err.message}` }); } finally { this.isConverting = false; } }, displayResults(result, options, files) { this.showOutput = true; if (result.warnings && result.warnings.length > 0) { result.warnings.forEach(w => { const msgType = w.verbosity === 'INFO' ? 'info' : 'warning'; this.messages.push({ type: msgType, text: w.pretty || w.message || JSON.stringify(w) }); }); } if (result.stderr && result.stderr.includes('ERROR')) { this.messages.push({ type: 'error', text: result.stderr }); return; } const isBinary = this.binaryFormats.includes(options.to); if (this.output) { // Already set (e.g., pdf via typst) } else if (options['output-file'] && files[options['output-file']]) { this.output = files[options['output-file']]; if (!isBinary) { const reader = new FileReader(); reader.onload = () => { this.outputPreview = reader.result.substring(0, 50000); if (reader.result.length > 50000) { this.outputPreview += '\n\n... (truncated)'; } }; reader.readAsText(this.output); } } else if (result.stdout) { this.output = new Blob([result.stdout], { type: 'text/plain' }); if (!isBinary) { this.outputPreview = result.stdout; } } // Check for extracted media if (files['media.zip'] && files['media.zip'].size > 0) { this.mediaZip = files['media.zip']; } // Disable convert button until next change this.hasChanges = false; }, download() { if (!this.output) return; const url = URL.createObjectURL(this.output); const a = document.createElement('a'); a.href = url; a.download = this.outputFilenameActual || 'output'; a.click(); URL.revokeObjectURL(url); }, downloadMedia() { if (!this.mediaZip) return; const url = URL.createObjectURL(this.mediaZip); const a = document.createElement('a'); a.href = url; a.download = 'media.zip'; a.click(); URL.revokeObjectURL(url); }, async copyToClipboard() { try { await navigator.clipboard.writeText(this.outputPreview); this.copyBtnText = '✓ Copied!'; setTimeout(() => { this.copyBtnText = '📋 Copy to Clipboard'; }, 2000); } catch (err) { this.messages.push({ type: 'error', text: 'Failed to copy to clipboard' }); } } }; }; // Mount Petite Vue after module loads (pandoc.wasm is ready) // Using mount() without selector scans DOM for v-scope attributes PetiteVue.createApp().mount(); ================================================ FILE: wasm/pandoc.js ================================================ /* pandoc.js: JavaScript interface to pandoc.wasm. Copyright (c) 2025 Tweag I/O Limited and John MacFarlane. MIT License. Interface: await convert(options, stdin, files) - options is a JavaScript object representing pandoc options: this should correspond to the format used in pandoc's default files. - stdin is a string or nil - files is a JavaScript object whose keys are filenames and whose values are the data in the corresponding file, as Blobs. The return value is a JavaScript object with 3 properties, stdout, stderr, and warnings, all strings. warnings is a JSON-encoded version of the warnings produced by pandoc. If the pandoc process produces an output file, it will be added to files. await query(options) - options is a JavaScript object with a 'query' property and in some cases a 'format' property. Possible queries include 'version', 'highlight-styles', 'highlight-languages', 'input-formats', 'output-formats', 'default-template' (requires 'format'), and 'extensions-for-format' (requires 'format'). The return value is a JavaScript string or in some cases a list of strings. */ import { WASI, OpenFile, File, ConsoleStdout, PreopenDirectory, } from "https://cdn.jsdelivr.net/npm/@bjorn3/browser_wasi_shim@0.3.0/dist/index.js"; const args = ["pandoc.wasm", "+RTS", "-H64m", "-RTS"]; const env = []; const in_file = new File(new Uint8Array(), { readonly: true }); const out_file = new File(new Uint8Array(), { readonly: false }); const err_file = new File(new Uint8Array(), { readonly: false }); const warnings_file = new File(new Uint8Array(), { readonly: false }); const fileSystem = new Map(); const fds = [ new OpenFile(new File(new Uint8Array(), { readonly: true })), ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ${msg}`)), ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)), new PreopenDirectory("/", fileSystem), ]; const options = { debug: false }; const wasi = new WASI(args, env, fds, options); const { instance } = await WebAssembly.instantiateStreaming( fetch("./pandoc.wasm?sha1=SHA1_PANDOC_WASM"), { wasi_snapshot_preview1: wasi.wasiImport, } ); wasi.initialize(instance); instance.exports.__wasm_call_ctors(); function memory_data_view() { return new DataView(instance.exports.memory.buffer); } const argc_ptr = instance.exports.malloc(4); memory_data_view().setUint32(argc_ptr, args.length, true); const argv = instance.exports.malloc(4 * (args.length + 1)); for (let i = 0; i < args.length; ++i) { const arg = instance.exports.malloc(args[i].length + 1); new TextEncoder().encodeInto( args[i], new Uint8Array(instance.exports.memory.buffer, arg, args[i].length) ); memory_data_view().setUint8(arg + args[i].length, 0); memory_data_view().setUint32(argv + 4 * i, arg, true); } memory_data_view().setUint32(argv + 4 * args.length, 0, true); const argv_ptr = instance.exports.malloc(4); memory_data_view().setUint32(argv_ptr, argv, true); instance.exports.hs_init_with_rtsopts(argc_ptr, argv_ptr); export async function query(options) { const opts_str = JSON.stringify(options); const opts_bytes = new TextEncoder().encode(opts_str); const opts_ptr = instance.exports.malloc(opts_bytes.length); new Uint8Array(instance.exports.memory.buffer, opts_ptr, opts_bytes.length) .set(opts_bytes); // add input files to fileSystem fileSystem.clear() const out_file = new File(new Uint8Array(), { readonly: false }); const err_file = new File(new Uint8Array(), { readonly: false }); fileSystem.set("stdout", out_file); fileSystem.set("stderr", err_file); instance.exports.query(opts_ptr, opts_bytes.length); const err_text = new TextDecoder("utf-8", { fatal: true }).decode(err_file.data); if (err_text) console.log(err_text); const out_text = new TextDecoder("utf-8", { fatal: true }).decode(out_file.data); return JSON.parse(out_text); } export async function convert(options, stdin, files) { const opts_str = JSON.stringify(options); const opts_bytes = new TextEncoder().encode(opts_str); const opts_ptr = instance.exports.malloc(opts_bytes.length); new Uint8Array(instance.exports.memory.buffer, opts_ptr, opts_bytes.length) .set(opts_bytes); // add input files to fileSystem fileSystem.clear() const in_file = new File(new Uint8Array(), { readonly: true }); const out_file = new File(new Uint8Array(), { readonly: false }); const err_file = new File(new Uint8Array(), { readonly: false }); const warnings_file = new File(new Uint8Array(), { readonly: false }); fileSystem.set("stdin", in_file); fileSystem.set("stdout", out_file); fileSystem.set("stderr", err_file); fileSystem.set("warnings", warnings_file); for (const file in files) { await addFile(file, files[file], true); } // add output file if any if (options["output-file"]) { await addFile(options["output-file"], new Blob(), false); } // add media file for extracted media if (options["extract-media"]) { await addFile(options["extract-media"], new Blob(), false); } if (stdin) { in_file.data = new TextEncoder().encode(stdin); } instance.exports.convert(opts_ptr, opts_bytes.length); if (options["output-file"]) { files[options["output-file"]] = new Blob([fileSystem.get(options["output-file"]).data]); } if (options["extract-media"]) { const mediaFile = fileSystem.get(options["extract-media"]); if (mediaFile && mediaFile.data && mediaFile.data.length > 0) { files[options["extract-media"]] = new Blob([mediaFile.data], { type: 'application/zip' }); } } const rawWarnings = new TextDecoder("utf-8", { fatal: true }) .decode(warnings_file.data); let warnings = []; if (rawWarnings) { warnings = JSON.parse(rawWarnings); } return { stdout: new TextDecoder("utf-8", { fatal: true }).decode(out_file.data), stderr: new TextDecoder("utf-8", { fatal: true }).decode(err_file.data), warnings: warnings }; } async function addFile(filename, blob, readonly) { const buffer = await blob.arrayBuffer(); const file = new File(new Uint8Array(buffer), { readonly: readonly }); fileSystem.set(filename, file); } ================================================ FILE: wasm/patches/conduit-extra.patch ================================================ --- a/conduit-extra/conduit-extra.cabal +++ b/conduit-extra/conduit-extra.cabal @@ -26,9 +26,6 @@ Library Data.Conduit.Filesystem Data.Conduit.Foldl Data.Conduit.Lazy - Data.Conduit.Network - Data.Conduit.Network.UDP - Data.Conduit.Network.Unix Data.Conduit.Process Data.Conduit.Process.Typed Data.Conduit.Text @@ -38,6 +35,12 @@ Library -- These architectures are able to perform unaligned memory accesses cpp-options: -DALLOW_UNALIGNED_ACCESS + if !arch(wasm32) + Exposed-modules: Data.Conduit.Network + Data.Conduit.Network.UDP + if !os(windows) && !arch(wasm32) + Exposed-modules: Data.Conduit.Network.Unix + Build-depends: base >= 4.12 && < 5 , conduit >= 1.3 && < 1.4 @@ -49,7 +52,6 @@ Library , async , attoparsec >= 0.10 , directory , filepath - , network >= 2.3 , primitive >= 0.5 , process , resourcet >= 1.1 @@ -58,6 +60,9 @@ Library , unliftio-core , typed-process >= 0.2.6 + if !arch(wasm32) + Build-depends: network >= 2.3 + ghc-options: -Wall test-suite test ================================================ FILE: wasm/patches/lua.patch ================================================ --- a/lua/cbits/lua-5.4.8/liolib.c +++ b/lua/cbits/lua-5.4.8/liolib.c @@ -301,10 +301,14 @@ static int io_tmpfile (lua_State *L) { +#if defined(LUA_STUB_TMPFILE) + return 1; +#else LStream *p = newfile(L); errno = 0; p->f = tmpfile(); return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; +#endif } --- a/lua/cbits/lua-5.4.8/loslib.c +++ b/lua/cbits/lua-5.4.8/loslib.c @@ -101,7 +101,12 @@ */ #if !defined(lua_tmpnam) /* { */ -#if defined(LUA_USE_POSIX) /* { */ +#if defined(LUA_STUB_TMPNAM) /* { */ + +#define LUA_TMPNAMBUFSIZE 32 +#define lua_tmpnam(b,e) { e = 1; } + +#elif defined(LUA_USE_POSIX) /* }{ */ #include @@ -131,7 +136,7 @@ /* }================================================================== */ -#if !defined(l_system) +#if !defined(l_system) && !defined(LUA_STUB_SYSTEM) #if defined(LUA_USE_IOS) /* Despite claiming to be ISO C, iOS does not implement 'system'. */ #define l_system(cmd) ((cmd) == NULL ? 0 : -1) @@ -142,6 +147,9 @@ static int os_execute (lua_State *L) { +#if defined(LUA_STUB_SYSTEM) + return 1; +#else const char *cmd = luaL_optstring(L, 1, NULL); int stat; errno = 0; @@ -152,6 +160,7 @@ lua_pushboolean(L, stat); /* true if there is a shell */ return 1; } +#endif } ================================================ FILE: wasm/patches/memory.patch ================================================ --- a/Data/Memory/MemMap/Posix.hsc +++ b/Data/Memory/MemMap/Posix.hsc @@ -17,7 +17,9 @@ -- ----------------------------------------------------------------------------- +#if !defined(wasm32_HOST_ARCH) #include +#endif #include {-# LANGUAGE ForeignFunctionInterface #-} @@ -117,18 +119,26 @@ deriving (Show,Read,Eq) cvalueOfMemoryProts :: [MemoryProtection] -> CInt +#if defined(wasm32_HOST_ARCH) +cvalueOfMemoryProts _ = error "Data.Memory.MemMap.cvalueOfMemoryProts" +#else cvalueOfMemoryProts = foldl (.|.) 0 . map toProt where toProt :: MemoryProtection -> CInt toProt MemoryProtectionNone = (#const PROT_NONE) toProt MemoryProtectionRead = (#const PROT_READ) toProt MemoryProtectionWrite = (#const PROT_WRITE) toProt MemoryProtectionExecute = (#const PROT_EXEC) +#endif cvalueOfMemorySync :: [MemorySyncFlag] -> CInt +#if defined(wasm32_HOST_ARCH) +cvalueOfMemorySync _ = error "Data.Memory.MemMap.cvalueOfMemorySync" +#else cvalueOfMemorySync = foldl (.|.) 0 . map toSync where toSync MemorySyncAsync = (#const MS_ASYNC) toSync MemorySyncSync = (#const MS_SYNC) toSync MemorySyncInvalidate = (#const MS_INVALIDATE) +#endif -- | Map pages of memory. -- @@ -139,10 +149,13 @@ memoryMap :: Maybe (Ptr a) -- ^ The address to map to if MapFixed is used. -> CSize -- ^ The length of the mapping -> [MemoryProtection] -- ^ the memory protection associated with the mapping - -> MemoryMapFlag -- ^ + -> MemoryMapFlag -- ^ -> Maybe Fd -> COff -> IO (Ptr a) +#if defined(wasm32_HOST_ARCH) +memoryMap _ _ _ _ _ _ = error "Data.Memory.MemMap.memoryMap" +#else memoryMap initPtr sz prots flag mfd off = throwErrnoIf (== m1ptr) "mmap" (c_mmap (maybe nullPtr id initPtr) sz cprot cflags fd off) where m1ptr = nullPtr `plusPtr` (-1) @@ -161,6 +174,7 @@ toMapFlag MemoryMapShared = (#const MAP_SHARED) toMapFlag MemoryMapPrivate = (#const MAP_PRIVATE) +#endif -- | Unmap pages of memory -- @@ -172,6 +186,9 @@ -- -- call 'madvise' memoryAdvise :: Ptr a -> CSize -> MemoryAdvice -> IO () +#if defined(wasm32_HOST_ARCH) +memoryAdvise _ _ _ = error "Data.Memory.MemMap.memoryAdvise" +#else memoryAdvise ptr sz adv = throwErrnoIfMinus1_ "madvise" (c_madvise ptr sz cadv) where cadv = toAdvice adv #if defined(POSIX_MADV_NORMAL) @@ -187,6 +204,7 @@ toAdvice MemoryAdviceWillNeed = (#const MADV_WILLNEED) toAdvice MemoryAdviceDontNeed = (#const MADV_DONTNEED) #endif +#endif -- | lock a range of process address space -- --- a/Data/Memory/PtrMethods.hs +++ b/Data/Memory/PtrMethods.hs @@ -35,7 +35,7 @@ memCreateTemporary size f = allocaBytesAligned size 8 f -- | xor bytes from source1 and source2 to destination --- +-- -- d = s1 xor s2 -- -- s1, nor s2 are modified unless d point to s1 or s2 @@ -63,7 +63,7 @@ -- | Copy a set number of bytes from @src to @dst memCopy :: Ptr Word8 -> Ptr Word8 -> Int -> IO () -memCopy dst src n = c_memcpy dst src (fromIntegral n) +memCopy dst src n = c_memcpy dst src (fromIntegral n) >>= \_ -> return () {-# INLINE memCopy #-} -- | Set @n number of bytes to the same value @v @@ -114,7 +114,7 @@ loop (i+1) (acc .|. e) foreign import ccall unsafe "memset" - c_memset :: Ptr Word8 -> Word8 -> CSize -> IO () + c_memset :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8) foreign import ccall unsafe "memcpy" - c_memcpy :: Ptr Word8 -> Ptr Word8 -> CSize -> IO () + c_memcpy :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8) ================================================ FILE: wasm/patches/splitmix.patch ================================================ --- /dev/null +++ b/cbits-wasi/init.c @@ -0,0 +1,8 @@ +#include +#include + +uint64_t splitmix_init() { + uint64_t result; + int r = getentropy(&result, sizeof(uint64_t)); + return r == 0 ? result : 0xfeed1000; +} --- a/splitmix.cabal +++ b/splitmix.cabal @@ -80,7 +80,10 @@ library frameworks: Security else - c-sources: cbits-unix/init.c + if arch(wasm32) + c-sources: cbits-wasi/init.c + else + c-sources: cbits-unix/init.c else cpp-options: -DSPLITMIX_INIT_COMPAT=1 ================================================ FILE: wasm/patches/streaming-commons.patch ================================================ --- a/streaming-commons.cabal +++ b/streaming-commons.cabal @@ -30,20 +30,21 @@ library Data.Streaming.ByteString.Builder.Buffer Data.Streaming.FileRead Data.Streaming.Filesystem - Data.Streaming.Network - Data.Streaming.Network.Internal Data.Streaming.Process Data.Streaming.Process.Internal Data.Streaming.Text Data.Streaming.Zlib Data.Streaming.Zlib.Lowlevel + if !arch(wasm32) + exposed-modules: Data.Streaming.Network + Data.Streaming.Network.Internal + build-depends: base >= 4.12 && < 5 , array , async , bytestring , directory - , network >= 2.4.0.0 , random , process , stm @@ -51,6 +52,9 @@ library , transformers , zlib + if !arch(wasm32) + build-depends: network >= 2.4.0.0 + c-sources: cbits/zlib-helper.c cbits/text-helper.c include-dirs: include ================================================ FILE: wasm/patches/xml-conduit.patch ================================================ --- a/xml-conduit/xml-conduit.cabal +++ b/xml-conduit/xml-conduit.cabal @@ -12,13 +12,10 @@ description: Hackage documentation generation is not reliable. For up to dat category: XML, Conduit stability: Stable -build-type: Custom +build-type: Simple homepage: http://github.com/snoyberg/xml extra-source-files: README.md ChangeLog.md -tested-with: GHC >=8.0 && <9.12 - -custom-setup - setup-depends: base >= 4 && <5, Cabal <4, cabal-doctest >= 1 && <1.1 +tested-with: GHC >=8.0 && <9.14 library build-depends: base >= 4.12 && < 5 ================================================ FILE: weeder.toml ================================================ roots = [ "^Main.main$", "^Paths_.*$" ] type-class-roots = true ================================================ FILE: windows/AdvancedWelcomeEulaDlg_Custom.wxs ================================================ LicenseAccepted AND (WixUISupportPerUser = 1) (NOT LicenseAccepted) OR (WixUISupportPerUser = 0) ALLUSERS NOT (ALLUSERS = 1) NOT LicenseAccepted LicenseAccepted 1 NOT LicenseAccepted OR NOT (ALLUSERS = 1) (LicenseAccepted = "1") AND (ALLUSERS = 1) 1 NOT Installed ================================================ FILE: windows/Makefile ================================================ UNSIGNED=$(wildcard pandoc-*-UNSIGNED.msi) SIGNED=$(patsubst %-UNSIGNED.msi, %.msi, $(UNSIGNED)) all: $(SIGNED) .PHONY: all pandoc-%.msi: pandoc-%-UNSIGNED.msi osslsigncode sign -pkcs12 $$HOME/Private/SectigoCodeSigning.exp2026.pfx -in $< -i https://johnmacfarlane.net/ -t http://timestamp.comodoca.com/ -out $@ -askpass && rm $< ================================================ FILE: windows/Pandoc-en-us.wxl ================================================ 1033 COPYING.rtf [ProductName] was installed in [APPLICATIONFOLDER]. You may need to restart Cmd/Powershell Windows before using it. ================================================ FILE: windows/WixUI_Advanced_Custom.wxs ================================================ ACTION="INSTALL" AND APPLICATIONFOLDER="" AND (ALLUSERS="" OR (ALLUSERS=2 AND (NOT Privileged))) ACTION="INSTALL" AND APPLICATIONFOLDER="" AND (ALLUSERS=1 OR (ALLUSERS=2 AND Privileged)) ACTION="INSTALL" AND APPLICATIONFOLDER="" AND (ALLUSERS="" OR (ALLUSERS=2 AND (NOT Privileged))) ACTION="INSTALL" AND APPLICATIONFOLDER="" AND (ALLUSERS=1 OR (ALLUSERS=2 AND Privileged)) 1 1 "1"]]> ALLUSERS = 1 ALLUSERS = 1 !(wix.WixUISupportPerMachine) AND !(wix.WixUISupportPerUser) ALLUSERS = 1 ALLUSERS = 1 NOT (ALLUSERS = 1) NOT (ALLUSERS = 1) 1 1 NOT WIXUI_DONTVALIDATEPATH "1"]]> WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" 1 1 NOT Installed AND WixAppFolder = "WixPerMachineFolder" Installed 1 1 1 1 Installed AND NOT PATCH Installed AND PATCH Installed AND PATCH Installed AND PATCH ================================================ FILE: windows/pandoc.wxs ================================================ ALLUSERS = "" OR ALLUSERS = 2 ALLUSERS = 1 MsiNTProductType > 1 MsiNTProductType = 1 MsiNTProductType > 1 (NOT ALLUSERS = 1) AND MsiNTProductType = 1 (NOT ALLUSERS = 1) AND MsiNTProductType = 1 MsiNTProductType > 1 NOT Installed ================================================ FILE: xml-light/Text/Pandoc/XML/Light/Output.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.XML.Light.Output Copyright : Copyright (C) 2007 Galois, Inc., 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable This code is based on code from xml-light, released under the BSD3 license. We use a text Builder instead of ShowS. -} module Text.Pandoc.XML.Light.Output ( -- * Replacement for xml-light's Text.XML.Output ppTopElement , ppElement , ppContent , ppcElement , ppcContent , showTopElement , showElement , showContent , useShortEmptyTags , defaultConfigPP , ConfigPP(..) ) where import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Text.Lazy.Builder (Builder, singleton, fromText, toLazyText) import Text.Pandoc.XML.Light.Types -- -- duplicates functions from Text.XML.Output -- -- | The XML 1.0 header xmlHeader :: Text xmlHeader = "" -------------------------------------------------------------------------------- data ConfigPP = ConfigPP { shortEmptyTag :: QName -> Bool , prettify :: Bool } -- | Default pretty orinting configuration. -- * Always use abbreviate empty tags. defaultConfigPP :: ConfigPP defaultConfigPP = ConfigPP { shortEmptyTag = const True , prettify = False } -- | The predicate specifies for which empty tags we should use XML's -- abbreviated notation . This is useful if we are working with -- some XML-ish standards (such as certain versions of HTML) where some -- empty tags should always be displayed in the form. useShortEmptyTags :: (QName -> Bool) -> ConfigPP -> ConfigPP useShortEmptyTags p c = c { shortEmptyTag = p } -- | Specify if we should use extra white-space to make document more readable. -- WARNING: This adds additional white-space to text elements, -- and so it may change the meaning of the document. useExtraWhiteSpace :: Bool -> ConfigPP -> ConfigPP useExtraWhiteSpace p c = c { prettify = p } -- | A configuration that tries to make things pretty -- (possibly at the cost of changing the semantics a bit -- through adding white space.) prettyConfigPP :: ConfigPP prettyConfigPP = useExtraWhiteSpace True defaultConfigPP -------------------------------------------------------------------------------- -- | Pretty printing renders XML documents faithfully, -- with the exception that whitespace may be added\/removed -- in non-verbatim character data. ppTopElement :: Element -> Text ppTopElement = ppcTopElement prettyConfigPP -- | Pretty printing elements ppElement :: Element -> Text ppElement = ppcElement prettyConfigPP -- | Pretty printing content ppContent :: Content -> Text ppContent = ppcContent prettyConfigPP -- | Pretty printing renders XML documents faithfully, -- with the exception that whitespace may be added\/removed -- in non-verbatim character data. ppcTopElement :: ConfigPP -> Element -> Text ppcTopElement c e = T.unlines [xmlHeader,ppcElement c e] -- | Pretty printing elements ppcElement :: ConfigPP -> Element -> Text ppcElement c = TL.toStrict . toLazyText . ppElementS c mempty -- | Pretty printing content ppcContent :: ConfigPP -> Content -> Text ppcContent c = TL.toStrict . toLazyText . ppContentS c mempty ppcCData :: ConfigPP -> CData -> Text ppcCData c = TL.toStrict . toLazyText . ppCDataS c mempty type Indent = Builder -- | Pretty printing content using ShowT ppContentS :: ConfigPP -> Indent -> Content -> Builder ppContentS c i x = case x of Elem e -> ppElementS c i e Text t -> ppCDataS c i t CRef r -> showCRefS r ppElementS :: ConfigPP -> Indent -> Element -> Builder ppElementS c i e = i <> tagStart (elName e) (elAttribs e) <> (case elContent e of [] | "?" `T.isPrefixOf` qName name -> fromText " ?>" | shortEmptyTag c name -> fromText " />" [Text t] -> singleton '>' <> ppCDataS c mempty t <> tagEnd name cs -> singleton '>' <> nl <> mconcat (map ((<> nl) . ppContentS c (sp <> i)) cs) <> i <> tagEnd name where (nl,sp) = if prettify c then ("\n"," ") else ("","") ) where name = elName e ppCDataS :: ConfigPP -> Indent -> CData -> Builder ppCDataS c i t = i <> if cdVerbatim t /= CDataText || not (prettify c) then showCDataS t else foldr cons mempty (T.unpack (showCData t)) where cons :: Char -> Builder -> Builder cons '\n' ys = singleton '\n' <> i <> ys cons y ys = singleton y <> ys -------------------------------------------------------------------------------- -- | Adds the header. showTopElement :: Element -> Text showTopElement c = xmlHeader <> showElement c showContent :: Content -> Text showContent = ppcContent defaultConfigPP showElement :: Element -> Text showElement = ppcElement defaultConfigPP showCData :: CData -> Text showCData = ppcCData defaultConfigPP -- Note: crefs should not contain '&', ';', etc. showCRefS :: Text -> Builder showCRefS r = singleton '&' <> fromText r <> singleton ';' -- | Convert a text element to characters. showCDataS :: CData -> Builder showCDataS cd = case cdVerbatim cd of CDataText -> escStr (cdData cd) CDataVerbatim -> fromText " escCData (cdData cd) <> fromText "]]>" CDataRaw -> fromText (cdData cd) -------------------------------------------------------------------------------- escCData :: Text -> Builder escCData t | "]]>" `T.isPrefixOf` t = fromText "]]]]>" <> fromText (T.drop 3 t) escCData t = case T.uncons t of Nothing -> mempty Just (c,t') -> singleton c <> escCData t' escChar :: Char -> Builder escChar c = case c of '<' -> fromText "<" '>' -> fromText ">" '&' -> fromText "&" '"' -> fromText """ -- we use ' instead of ' because IE apparently has difficulties -- rendering ' in xhtml. -- Reported by Rohan Drape . '\'' -> fromText "'" _ -> singleton c {- original xml-light version: -- NOTE: We escape '\r' explicitly because otherwise they get lost -- when parsed back in because of then end-of-line normalization rules. _ | isPrint c || c == '\n' -> singleton c | otherwise -> showText "&#" . showsT oc . singleton ';' where oc = ord c -} escStr :: Text -> Builder escStr cs = if T.any needsEscape cs then mconcat (map escChar (T.unpack cs)) else fromText cs where needsEscape '<' = True needsEscape '>' = True needsEscape '&' = True needsEscape '"' = True needsEscape '\'' = True needsEscape _ = False tagEnd :: QName -> Builder tagEnd qn = fromText " showQName qn <> singleton '>' tagStart :: QName -> [Attr] -> Builder tagStart qn as = singleton '<' <> showQName qn <> as_str where as_str = if null as then mempty else mconcat (map showAttr as) showAttr :: Attr -> Builder showAttr (Attr qn v) = singleton ' ' <> showQName qn <> singleton '=' <> singleton '"' <> escStr v <> singleton '"' showQName :: QName -> Builder showQName q = case qPrefix q of Nothing -> fromText (qName q) Just p -> fromText p <> singleton ':' <> fromText (qName q) ================================================ FILE: xml-light/Text/Pandoc/XML/Light/Proc.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {- | Module : Text.Pandoc.XML.Light.Proc Copyright : Copyright (C) 2007 Galois, Inc., 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable This code is taken from xml-light, released under the BSD3 license. -} module Text.Pandoc.XML.Light.Proc ( -- * Replacement for xml-light's Text.XML.Proc strContent , onlyElems , elChildren , onlyText , findChildren , filterChildren , filterChildrenName , findChild , filterChild , filterChildName , findElement , filterElement , filterElementName , findElements , filterElements , filterElementsName , findAttr , lookupAttr , lookupAttrBy , findAttrBy ) where import Data.Text (Text) import Data.Maybe (listToMaybe) import Data.List(find) import Text.Pandoc.XML.Light.Types -- -- copied from xml-light Text.XML.Proc -- -- | Get the text value of an XML element. This function -- ignores non-text elements, and concatenates all text elements. strContent :: Element -> Text strContent = mconcat . map cdData . onlyText . elContent -- | Select only the elements from a list of XML content. onlyElems :: [Content] -> [Element] onlyElems xs = [ x | Elem x <- xs ] -- | Select only the elements from a parent. elChildren :: Element -> [Element] elChildren e = [ x | Elem x <- elContent e ] -- | Select only the text from a list of XML content. onlyText :: [Content] -> [CData] onlyText xs = [ x | Text x <- xs ] -- | Find all immediate children with the given name. findChildren :: QName -> Element -> [Element] findChildren q e = filterChildren ((q ==) . elName) e -- | Filter all immediate children wrt a given predicate. filterChildren :: (Element -> Bool) -> Element -> [Element] filterChildren p e = filter p (onlyElems (elContent e)) -- | Filter all immediate children wrt a given predicate over their names. filterChildrenName :: (QName -> Bool) -> Element -> [Element] filterChildrenName p e = filter (p.elName) (onlyElems (elContent e)) -- | Find an immediate child with the given name. findChild :: QName -> Element -> Maybe Element findChild q e = listToMaybe (findChildren q e) -- | Find an immediate child with the given name. filterChild :: (Element -> Bool) -> Element -> Maybe Element filterChild p e = listToMaybe (filterChildren p e) -- | Find an immediate child with name matching a predicate. filterChildName :: (QName -> Bool) -> Element -> Maybe Element filterChildName p e = listToMaybe (filterChildrenName p e) -- | Find the left-most occurrence of an element matching given name. findElement :: QName -> Element -> Maybe Element findElement q e = listToMaybe (findElements q e) -- | Filter the left-most occurrence of an element wrt. given predicate. filterElement :: (Element -> Bool) -> Element -> Maybe Element filterElement p e = listToMaybe (filterElements p e) -- | Filter the left-most occurrence of an element wrt. given predicate. filterElementName :: (QName -> Bool) -> Element -> Maybe Element filterElementName p e = listToMaybe (filterElementsName p e) -- | Find all non-nested occurrences of an element. -- (i.e., once we have found an element, we do not search -- for more occurrences among the element's children). findElements :: QName -> Element -> [Element] findElements qn e = filterElementsName (qn==) e -- | Find all non-nested occurrences of an element wrt. given predicate. -- (i.e., once we have found an element, we do not search -- for more occurrences among the element's children). filterElements :: (Element -> Bool) -> Element -> [Element] filterElements p e | p e = [e] | otherwise = concatMap (filterElements p) $ onlyElems $ elContent e -- | Find all non-nested occurrences of an element wrt a predicate over element names. -- (i.e., once we have found an element, we do not search -- for more occurrences among the element's children). filterElementsName :: (QName -> Bool) -> Element -> [Element] filterElementsName p e = filterElements (p.elName) e -- | Lookup the value of an attribute. findAttr :: QName -> Element -> Maybe Text findAttr x e = lookupAttr x (elAttribs e) -- | Lookup attribute name from list. lookupAttr :: QName -> [Attr] -> Maybe Text lookupAttr x = lookupAttrBy (x ==) -- | Lookup the first attribute whose name satisfies the given predicate. lookupAttrBy :: (QName -> Bool) -> [Attr] -> Maybe Text lookupAttrBy p as = attrVal `fmap` find (p . attrKey) as -- | Lookup the value of the first attribute whose name -- satisfies the given predicate. findAttrBy :: (QName -> Bool) -> Element -> Maybe Text findAttrBy p e = lookupAttrBy p (elAttribs e) ================================================ FILE: xml-light/Text/Pandoc/XML/Light/Types.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE DeriveDataTypeable #-} {- | Module : Text.Pandoc.XML.Light.Types Copyright : Copyright (C) 2007 Galois, Inc., 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable This code is taken from xml-light, released under the BSD3 license. It has been modified to use Text instead of String, and the fromXL* functions have been added. -} module Text.Pandoc.XML.Light.Types ( -- * Basic types, duplicating those from xml-light but with Text -- instead of String Line , Content(..) , Element(..) , Attr(..) , CData(..) , CDataKind(..) , QName(..) , Node(..) , unode , unqual , add_attr , add_attrs -- * Conversion functions from xml-light types , fromXLQName , fromXLCData , fromXLElement , fromXLAttr , fromXLContent ) where import Data.Text (Text) import qualified Data.Text as T import Data.Data (Data) import Data.Typeable (Typeable) import qualified Text.XML.Light as XL -- -- type definitions lightly modified from xml-light -- -- | A line is an Integer type Line = Integer -- | XML content data Content = Elem Element | Text CData | CRef Text deriving (Show, Typeable, Data, Ord, Eq) -- | XML elements data Element = Element { elName :: QName, elAttribs :: [Attr], elContent :: [Content], elLine :: Maybe Line } deriving (Show, Typeable, Data, Ord, Eq) -- | XML attributes data Attr = Attr { attrKey :: QName, attrVal :: Text } deriving (Eq, Ord, Show, Typeable, Data) -- | XML CData data CData = CData { cdVerbatim :: CDataKind, cdData :: Text, cdLine :: Maybe Line } deriving (Show, Typeable, Data, Ord, Eq) data CDataKind = CDataText -- ^ Ordinary character data; pretty printer escapes &, < etc. | CDataVerbatim -- ^ Unescaped character data; pretty printer embeds it in case (qURI q1, qURI q2) of (Nothing,Nothing) -> compare (qPrefix q1) (qPrefix q2) (u1,u2) -> compare u1 u2 x -> x class Node t where node :: QName -> t -> Element instance Node ([Attr],[Content]) where node n (attrs,cont) = Element { elName = n , elAttribs = attrs , elContent = cont , elLine = Nothing } instance Node [Attr] where node n as = node n (as,[]::[Content]) instance Node Attr where node n a = node n [a] instance Node () where node n () = node n ([]::[Attr]) instance Node [Content] where node n cs = node n ([]::[Attr],cs) instance Node Content where node n c = node n [c] instance Node ([Attr],Content) where node n (as,c) = node n (as,[c]) instance Node (Attr,Content) where node n (a,c) = node n ([a],[c]) instance Node ([Attr],[Element]) where node n (as,cs) = node n (as,map Elem cs) instance Node ([Attr],Element) where node n (as,c) = node n (as,[c]) instance Node (Attr,Element) where node n (a,c) = node n ([a],c) instance Node [Element] where node n es = node n ([]::[Attr],es) instance Node Element where node n e = node n [e] instance Node ([Attr],[CData]) where node n (as,cs) = node n (as,map Text cs) instance Node ([Attr],CData) where node n (as,c) = node n (as,[c]) instance Node (Attr,CData) where node n (a,c) = node n ([a],c) instance Node [CData] where node n es = node n ([]::[Attr],es) instance Node CData where node n e = node n [e] instance Node ([Attr],Text) where node n (as,t) = node n (as, CData { cdVerbatim = CDataText , cdData = t , cdLine = Nothing }) instance Node (Attr,Text ) where node n (a,t) = node n ([a],t) instance Node Text where node n t = node n ([]::[Attr],t) -- | Create node with unqualified name unode :: Node t => Text -> t -> Element unode = node . unqual unqual :: Text -> QName unqual x = QName x Nothing Nothing -- | Add an attribute to an element. add_attr :: Attr -> Element -> Element add_attr a e = add_attrs [a] e -- | Add some attributes to an element. add_attrs :: [Attr] -> Element -> Element add_attrs as e = e { elAttribs = as ++ elAttribs e } -- -- conversion from xml-light -- fromXLQName :: XL.QName -> QName fromXLQName qn = QName { qName = T.pack $ XL.qName qn , qURI = T.pack <$> XL.qURI qn , qPrefix = T.pack <$> XL.qPrefix qn } fromXLCData :: XL.CData -> CData fromXLCData cd = CData { cdVerbatim = case XL.cdVerbatim cd of XL.CDataText -> CDataText XL.CDataVerbatim -> CDataVerbatim XL.CDataRaw -> CDataRaw , cdData = T.pack $ XL.cdData cd , cdLine = XL.cdLine cd } fromXLElement :: XL.Element -> Element fromXLElement el = Element { elName = fromXLQName $ XL.elName el , elAttribs = map fromXLAttr $ XL.elAttribs el , elContent = map fromXLContent $ XL.elContent el , elLine = XL.elLine el } fromXLAttr :: XL.Attr -> Attr fromXLAttr (XL.Attr qn s) = Attr (fromXLQName qn) (T.pack s) fromXLContent :: XL.Content -> Content fromXLContent (XL.Elem el) = Elem $ fromXLElement el fromXLContent (XL.Text cd) = Text $ fromXLCData cd fromXLContent (XL.CRef s) = CRef (T.pack s) ================================================ FILE: xml-light/Text/Pandoc/XML/Light.hs ================================================ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.XML.Light Copyright : Copyright (C) 2021-2024 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable xml-light, which we used in pandoc's the XML-based readers, has some limitations: in particular, it produces nodes with String instead of Text, and the parser falls over on processing instructions (see #7091). This module exports much of the API of xml-light, but using Text instead of String. In addition, the xml-light parsers are replaced by xml-conduit's well-tested parser. (The xml-conduit types are mapped to types isomorphic to xml-light's, to avoid the need for massive code modifications elsewhere.) Bridge functions to map xml-light types to this module's types are also provided (since libraries like texmath still use xml-light). Another advantage of the xml-conduit parser is that it gives us detailed information on xml parse errors. In the future we may want to move to using xml-conduit or another xml library in the code base, but this change gives us better performance and accuracy without much change in the code that used xml-light. -} module Text.Pandoc.XML.Light ( module Text.Pandoc.XML.Light.Types , module Text.Pandoc.XML.Light.Proc , module Text.Pandoc.XML.Light.Output -- * Replacement for xml-light's Text.XML.Input , parseXMLElement , parseXMLContents -- * Versions that allow passing in a custom entity table , parseXMLElementWithEntities , parseXMLContentsWithEntities ) where import qualified Control.Exception as E import qualified Text.XML as Conduit import Text.XML.Unresolved (InvalidEventStream(..)) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Map as M import Data.Maybe (mapMaybe) import Text.Pandoc.XML.Light.Types import Text.Pandoc.XML.Light.Proc import Text.Pandoc.XML.Light.Output import qualified Data.XML.Types as XML -- Drop in replacement for parseXMLDoc in xml-light. parseXMLElement :: TL.Text -> Either T.Text Element parseXMLElement = parseXMLElementWithEntities mempty -- Drop in replacement for parseXMLDoc in xml-light. parseXMLElementWithEntities :: M.Map T.Text T.Text -> TL.Text -> Either T.Text Element parseXMLElementWithEntities entityMap t = elementToElement . Conduit.documentRoot <$> either (Left . T.pack . E.displayException) Right (Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True , Conduit.psDecodeEntities = decodeEnts } t) where decodeEnts ref = case M.lookup ref entityMap of Nothing -> XML.ContentEntity ref Just t' -> XML.ContentText t' parseXMLContents :: TL.Text -> Either T.Text [Content] parseXMLContents = parseXMLContentsWithEntities mempty parseXMLContentsWithEntities :: M.Map T.Text T.Text -> TL.Text -> Either T.Text [Content] parseXMLContentsWithEntities entityMap t = case Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True , Conduit.psDecodeEntities = decodeEnts } t of Left e -> case E.fromException e of Just (ContentAfterRoot _) -> elContent <$> parseXMLElementWithEntities entityMap ("" <> t <> "") _ -> Left . T.pack . E.displayException $ e Right x -> Right [Elem . elementToElement . Conduit.documentRoot $ x] where decodeEnts ref = case M.lookup ref entityMap of Nothing -> XML.ContentEntity ref Just t' -> XML.ContentText t' elementToElement :: Conduit.Element -> Element elementToElement (Conduit.Element name attribMap nodes) = Element (nameToQname name) attrs (mapMaybe nodeToContent nodes) Nothing where attrs = map (\(n,v) -> Attr (nameToQname n) v) $ M.toList attribMap nameToQname (Conduit.Name localName mbns mbpref) = case mbpref of Nothing -> case T.stripPrefix "xmlns:" localName of Just rest -> QName rest mbns (Just "xmlns") Nothing -> QName localName mbns mbpref _ -> QName localName mbns mbpref nodeToContent :: Conduit.Node -> Maybe Content nodeToContent (Conduit.NodeElement el) = Just (Elem (elementToElement el)) nodeToContent (Conduit.NodeContent t) = Just (Text (CData CDataText t Nothing)) nodeToContent _ = Nothing